From 6a16371a966525b9d4bac0e4bee4e6faeca70929 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 23 Apr 2016 16:27:48 +0100 Subject: [PATCH 01/90] all commits of old branch --- regression/modular/Makefile | 20 + regression/modular/cex-struct1/main.c | 180 +++++ regression/modular/cex-struct1/test.desc | 6 + regression/modular/cex-struct2/main.c | 22 + regression/modular/cex-struct2/test.desc | 6 + regression/modular/cex-struct3/main.c | 23 + regression/modular/cex-struct3/test.desc | 6 + regression/modular/cex1/main.c | 15 + regression/modular/cex1/test.desc | 6 + regression/modular/cex10/main.c | 21 + regression/modular/cex10/test.desc | 6 + regression/modular/cex11/main.c | 35 + regression/modular/cex12/main.c | 40 + regression/modular/cex13/main.c | 16 + regression/modular/cex13/main.c~ | 16 + regression/modular/cex13/test.desc | 6 + regression/modular/cex2/main.c | 16 + regression/modular/cex2/test.desc | 6 + regression/modular/cex3/main.c | 16 + regression/modular/cex3/test.desc | 6 + regression/modular/cex4/main.c | 11 + regression/modular/cex4/test.desc | 6 + regression/modular/cex5/main.c | 15 + regression/modular/cex5/test.desc | 6 + regression/modular/cex6/main.c | 20 + regression/modular/cex6/test.desc | 6 + regression/modular/cex7/main.c | 15 + regression/modular/cex7/test.desc | 6 + regression/modular/cex8/main.c | 20 + regression/modular/cex8/test.desc | 6 + regression/modular/cex9/main.c | 24 + regression/modular/cex9/test.desc | 6 + regression/modular/nocex1/main.c | 15 + regression/modular/nocex1/test.desc | 6 + regression/modular/nocex2/main.c | 16 + regression/modular/nocex2/test.desc | 6 + regression/modular/nocex3/main.c | 18 + regression/modular/nocex3/test.desc | 6 + regression/modular/nocex4/main.c | 20 + regression/modular/nocex4/test.desc | 6 + regression/modular/nocex5/main.c | 30 + regression/modular/nocex5/test.desc | 6 + regression/modular/nocex6/main.c | 15 + regression/modular/nocex6/test.desc | 6 + regression/modular/nocex7/main.c | 34 + regression/modular/nocex7/test.desc | 6 + regression/modular/nocex8/main.c | 16 + regression/modular/nocex8/test.desc | 6 + src/Makefile | 8 +- src/config.inc.template | 6 +- src/deltacheck/Makefile | 13 +- src/deltacheck/analyzer.cpp | 590 ++++---------- src/deltacheck/analyzer.h | 16 +- src/deltacheck/change_impact.cpp | 264 ++----- src/deltacheck/change_impact.h | 55 +- src/deltacheck/deltacheck_main.cpp | 10 +- src/deltacheck/html_report.cpp | 43 +- src/deltacheck/html_report.h | 9 +- src/deltacheck/report_header.html | 11 +- src/deltacheck/report_source_code.cpp | 48 +- src/deltagit/Makefile | 5 +- src/deltagit/deltagit_main.cpp | 10 +- src/deltagit/revisions_report.cpp | 21 +- src/domains/Makefile | 14 +- src/domains/domain.h | 35 +- src/domains/equality_domain.cpp | 46 +- src/domains/equality_domain.h | 1 + src/domains/incremental_solver.cpp | 62 +- src/domains/incremental_solver.h | 117 ++- src/domains/ssa_analyzer.cpp | 136 ++-- src/domains/ssa_analyzer.h | 18 +- src/domains/strategy_solver_base.h | 11 +- src/domains/strategy_solver_binsearch.cpp | 82 +- src/domains/strategy_solver_binsearch.h | 5 +- src/domains/strategy_solver_binsearch2.cpp | 86 ++- src/domains/strategy_solver_binsearch2.h | 13 +- src/domains/strategy_solver_binsearch3.cpp | 140 ++-- src/domains/strategy_solver_binsearch3.h | 21 +- src/domains/strategy_solver_enumeration.cpp | 63 +- src/domains/strategy_solver_enumeration.h | 5 +- src/domains/strategy_solver_equality.cpp | 13 +- src/domains/strategy_solver_equality.h | 5 +- src/domains/template_generator_base.cpp | 408 ++++++++-- src/domains/template_generator_base.h | 39 +- src/domains/template_generator_summary.cpp | 15 +- src/domains/tpolyhedra_domain.cpp | 464 ++++++----- src/domains/tpolyhedra_domain.h | 48 +- src/domains/util.cpp | 149 ++-- src/domains/util.h | 5 +- src/functions/Makefile | 3 + src/functions/get_function.cpp | 2 +- src/functions/index.cpp | 2 +- src/html/Makefile | 2 +- src/solver/Makefile | 2 + src/ssa/Makefile | 13 +- src/ssa/assignments.cpp | 39 +- src/ssa/assignments.h | 20 +- src/ssa/local_ssa.cpp | 669 +++++++++++----- src/ssa/local_ssa.h | 56 +- src/ssa/malloc_ssa.cpp | 74 +- src/ssa/malloc_ssa.h | 9 +- src/ssa/ssa_domain.cpp | 2 +- src/ssa/ssa_inliner.cpp | 770 +++++++++++++------ src/ssa/ssa_inliner.h | 132 +++- src/ssa/ssa_object.cpp | 173 ++++- src/ssa/ssa_object.h | 43 +- src/ssa/ssa_unwinder.cpp | 802 +++++++++++++------- src/ssa/ssa_unwinder.h | 72 +- src/summarizer/Makefile | 55 +- src/summarizer/array_abstraction.cpp | 6 +- src/summarizer/array_abstraction.h | 4 +- src/summarizer/cover_goals_ext.cpp | 111 ++- src/summarizer/cover_goals_ext.h | 22 +- src/summarizer/show.cpp | 288 ++++++- src/summarizer/show.h | 17 + src/summarizer/ssa_db.cpp | 9 + src/summarizer/ssa_db.h | 24 +- src/summarizer/summarizer_base.cpp | 51 +- src/summarizer/summarizer_base.h | 7 + src/summarizer/summarizer_bw.cpp | 1 - src/summarizer/summarizer_bw.h | 4 +- src/summarizer/summarizer_fw.cpp | 63 +- src/summarizer/summarizer_fw.h | 2 +- src/summarizer/summarizer_main.cpp | 10 +- src/summarizer/summary.cpp | 58 +- src/summarizer/summary.h | 40 +- src/summarizer/summary_db.cpp | 77 ++ src/summarizer/summary_db.h | 30 +- src/summarizer/version.h | 2 +- 129 files changed, 5203 insertions(+), 2494 deletions(-) create mode 100644 regression/modular/Makefile create mode 100644 regression/modular/cex-struct1/main.c create mode 100644 regression/modular/cex-struct1/test.desc create mode 100644 regression/modular/cex-struct2/main.c create mode 100644 regression/modular/cex-struct2/test.desc create mode 100644 regression/modular/cex-struct3/main.c create mode 100644 regression/modular/cex-struct3/test.desc create mode 100644 regression/modular/cex1/main.c create mode 100644 regression/modular/cex1/test.desc create mode 100644 regression/modular/cex10/main.c create mode 100644 regression/modular/cex10/test.desc create mode 100644 regression/modular/cex11/main.c create mode 100644 regression/modular/cex12/main.c create mode 100644 regression/modular/cex13/main.c create mode 100644 regression/modular/cex13/main.c~ create mode 100644 regression/modular/cex13/test.desc create mode 100644 regression/modular/cex2/main.c create mode 100644 regression/modular/cex2/test.desc create mode 100644 regression/modular/cex3/main.c create mode 100644 regression/modular/cex3/test.desc create mode 100644 regression/modular/cex4/main.c create mode 100644 regression/modular/cex4/test.desc create mode 100644 regression/modular/cex5/main.c create mode 100644 regression/modular/cex5/test.desc create mode 100644 regression/modular/cex6/main.c create mode 100644 regression/modular/cex6/test.desc create mode 100644 regression/modular/cex7/main.c create mode 100644 regression/modular/cex7/test.desc create mode 100644 regression/modular/cex8/main.c create mode 100644 regression/modular/cex8/test.desc create mode 100644 regression/modular/cex9/main.c create mode 100644 regression/modular/cex9/test.desc create mode 100644 regression/modular/nocex1/main.c create mode 100644 regression/modular/nocex1/test.desc create mode 100644 regression/modular/nocex2/main.c create mode 100644 regression/modular/nocex2/test.desc create mode 100644 regression/modular/nocex3/main.c create mode 100644 regression/modular/nocex3/test.desc create mode 100644 regression/modular/nocex4/main.c create mode 100644 regression/modular/nocex4/test.desc create mode 100644 regression/modular/nocex5/main.c create mode 100644 regression/modular/nocex5/test.desc create mode 100644 regression/modular/nocex6/main.c create mode 100644 regression/modular/nocex6/test.desc create mode 100644 regression/modular/nocex7/main.c create mode 100644 regression/modular/nocex7/test.desc create mode 100644 regression/modular/nocex8/main.c create mode 100644 regression/modular/nocex8/test.desc diff --git a/regression/modular/Makefile b/regression/modular/Makefile new file mode 100644 index 000000000..19c23b682 --- /dev/null +++ b/regression/modular/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check concrete + +test: + @../test.pl -c "../../../src/summarizer/summarizer $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/summarizer $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/modular/cex-struct1/main.c b/regression/modular/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/modular/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/modular/cex-struct1/test.desc b/regression/modular/cex-struct1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex-struct2/main.c b/regression/modular/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/modular/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/modular/cex-struct2/test.desc b/regression/modular/cex-struct2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex-struct3/main.c b/regression/modular/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/modular/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/modular/cex-struct3/test.desc b/regression/modular/cex-struct3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex1/main.c b/regression/modular/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/modular/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex1/test.desc b/regression/modular/cex1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex10/main.c b/regression/modular/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/modular/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex10/test.desc b/regression/modular/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex11/main.c b/regression/modular/cex11/main.c new file mode 100644 index 000000000..30592979f --- /dev/null +++ b/regression/modular/cex11/main.c @@ -0,0 +1,35 @@ + +// for complete to work faster: introduce a function call to modify an irrelevant variable + +#include + +unsigned nondet_int(); + +int foo(int a){ + + a = a + (2*a) + (3*a); + a = a - (2*a) - (3*a); + a = -a; + + return a; + +} + +int main(){ + + int x; + int y; + + __CPROVER_assume(x > 0 and x < 10); + + while(x < 10000){ + x = x + 1; + y = foo(y); + + if(nondet_int()) + break; + } + + assert(x < 10001); + +} diff --git a/regression/modular/cex12/main.c b/regression/modular/cex12/main.c new file mode 100644 index 000000000..dbebaeb45 --- /dev/null +++ b/regression/modular/cex12/main.c @@ -0,0 +1,40 @@ + +// for concrete to work faster: a false assertion such that all counterexamples are valid counterexamples + +#include + +unsigned nondet_int(); + +int bar(int x){ + if((x > 0) || (x <= 0)) + assert(0); + + return 0; +} + +int inc_or_dec(int x){ + if(nondet_int()) + x = x + 1; + else + x = x - 1; + + return x; +} + +int main(){ + + int k; + int counter = 0 + + while(counter < 100000){ + k = inc_or_dec(k); + if(nondet_int()) + break; + counter++; + } + + bar(k); + + return 0; + +} diff --git a/regression/modular/cex13/main.c b/regression/modular/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/modular/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/modular/cex13/main.c~ b/regression/modular/cex13/main.c~ new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/modular/cex13/main.c~ @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/modular/cex13/test.desc b/regression/modular/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex2/main.c b/regression/modular/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/modular/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/modular/cex2/test.desc b/regression/modular/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex3/main.c b/regression/modular/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/modular/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex3/test.desc b/regression/modular/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex4/main.c b/regression/modular/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/modular/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/modular/cex4/test.desc b/regression/modular/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex5/main.c b/regression/modular/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/modular/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex5/test.desc b/regression/modular/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex6/main.c b/regression/modular/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/modular/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/modular/cex6/test.desc b/regression/modular/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex7/main.c b/regression/modular/cex7/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/modular/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/modular/cex7/test.desc b/regression/modular/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/modular/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex8/main.c b/regression/modular/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/modular/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex8/test.desc b/regression/modular/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex9/main.c b/regression/modular/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/modular/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex9/test.desc b/regression/modular/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/modular/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/nocex1/main.c b/regression/modular/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/modular/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/modular/nocex1/test.desc b/regression/modular/nocex1/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex2/main.c b/regression/modular/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/modular/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/modular/nocex2/test.desc b/regression/modular/nocex2/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex3/main.c b/regression/modular/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/modular/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/modular/nocex3/test.desc b/regression/modular/nocex3/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex4/main.c b/regression/modular/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/modular/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/modular/nocex4/test.desc b/regression/modular/nocex4/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex5/main.c b/regression/modular/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/modular/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/modular/nocex5/test.desc b/regression/modular/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex6/main.c b/regression/modular/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/modular/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/modular/nocex6/test.desc b/regression/modular/nocex6/test.desc new file mode 100644 index 000000000..895f3abe9 --- /dev/null +++ b/regression/modular/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/nocex7/main.c b/regression/modular/nocex7/main.c new file mode 100644 index 000000000..4b957b301 --- /dev/null +++ b/regression/modular/nocex7/main.c @@ -0,0 +1,34 @@ + +#include + +int inc(int c) +{ + return c+1; +} + +int dec(int b) +{ + return b-1; +} + +int add(int i, int j) +{ + int b = i; + int c = j; + int ret = c; + + while(b > 0){ + b = dec(b); + c = inc(c); + ret = c; + assert((ret + b) == (i + j)); //loop invariant + } + assert(ret == (i + j)); + return ret; +} + +void main() { + int x = 5; + int y = 3; + int result = add(x, y); +} diff --git a/regression/modular/nocex7/test.desc b/regression/modular/nocex7/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex8/main.c b/regression/modular/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/modular/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/modular/nocex8/test.desc b/regression/modular/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/modular/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/src/Makefile b/src/Makefile index 134d4db7e..bae5bbc03 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,3 +1,5 @@ +include config.inc + SUBDIRS = summarizer ssa solver domains functions all: summarizer @@ -11,10 +13,9 @@ $(SUBDIRS): # Dependencies -summarizer: domains ssa solver +summarizer: domains ssa deltacheck: ssa solver functions html deltagit: html -summarizer: ssa solver functions domains clean: $(patsubst %, %_clean, $(SUBDIRS)) @@ -24,3 +25,6 @@ $(patsubst %, %_clean, $(SUBDIRS)): $(MAKE) $(MAKEARGS) -C $(patsubst %_clean, %, $@) clean ; \ fi +cbmc-patch: $(CBMC_PATCH) + PWD=`pwd`; cd $(CBMC); patch -p0 < $(PWD)/$(CBMC_PATCH) + diff --git a/src/config.inc.template b/src/config.inc.template index 99a223d5f..ed6ba6648 100644 --- a/src/config.inc.template +++ b/src/config.inc.template @@ -3,6 +3,10 @@ CBMC = ~/cbmc # Variables you may want to override #CXXFLAGS = -Wall -O0 -g -Werror -Wno-long-long -Wno-sign-compare -Wno-parentheses -Wno-strict-aliasing -pedantic -# CBMC = ~/cbmc +SUMMARIZERFLAGS = -DREUSE_INVARIANTS -DASSERTION_HOISTING +#SUMMARIZERFLAGS= -DDEBUG_FORMULA -DDISPLAY_FORMULA -DDEBUG_OUTPUT -DNON_INCREMENTAL -DSLICING -DSHOW_COUNTEREXAMPLE -DASSERTION_HOISTING -DSHOW_CALLING_CONTEXTS -DREUSE_INVARIANTS -DSHOW_TEMPLATE_VARIABLES -DSHOW_TEMPLATE +# + # CUDD = ~/progr/deltacheck/trunk/cudd-2.5.0 +CBMC_PATCH = ../patches/lazy-array.patch diff --git a/src/deltacheck/Makefile b/src/deltacheck/Makefile index 013f29679..07353ef3e 100644 --- a/src/deltacheck/Makefile +++ b/src/deltacheck/Makefile @@ -1,10 +1,9 @@ include ../config.inc CBMC ?= ../.. -SRC = deltacheck_main.cpp deltacheck_parseoptions.cpp \ +SRC = deltacheck_main.cpp deltacheck_parse_options.cpp \ rename.cpp ssa_fixed_point.cpp source_diff.cpp change_impact.cpp \ - html_report.cpp analyzer.cpp syntax_highlighting.cpp \ - show.cpp properties.cpp report_source_code.cpp \ + html_report.cpp analyzer.cpp properties.cpp report_source_code.cpp \ get_source.cpp statistics.cpp \ $(CBMC)/src/cbmc/xml_interface.cpp @@ -22,21 +21,21 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/util/util$(LIBEXT) \ ../html/logo$(OBJEXT) \ ../html/html_escape$(OBJEXT) \ + ../html/syntax_highlighting$(OBJEXT) \ ../ssa/local_ssa$(OBJEXT) \ ../ssa/malloc_ssa$(OBJEXT) \ ../ssa/ssa_domain$(OBJEXT) \ + ../ssa/ssa_value_set$(OBJEXT) \ ../ssa/assignments$(OBJEXT) \ ../ssa/guard_map$(OBJEXT) \ ../ssa/ssa_object$(OBJEXT) \ ../ssa/address_canonizer$(OBJEXT) \ - ../ssa/ssa_aliasing$(OBJEXT) \ + ../ssa/ssa_dereference$(OBJEXT) \ ../solver/predicate$(OBJEXT) \ ../solver/solver$(OBJEXT) \ ../solver/fixed_point$(OBJEXT) \ ../functions/summary$(OBJEXT) \ - ../functions/get_function$(OBJEXT) \ - ../functions/path_util$(OBJEXT) \ - ../functions/index$(OBJEXT) + ../functions/path_util$(OBJEXT) include $(CBMC)/src/config.inc include $(CBMC)/src/common diff --git a/src/deltacheck/analyzer.cpp b/src/deltacheck/analyzer.cpp index 449f85cfe..d5d00af92 100644 --- a/src/deltacheck/analyzer.cpp +++ b/src/deltacheck/analyzer.cpp @@ -12,23 +12,12 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include +//#include +//#include #include "../html/html_escape.h" -#include "../functions/index.h" -#include "../functions/get_function.h" #include "../functions/path_util.h" + #include "html_report.h" #include "ssa_fixed_point.h" #include "statistics.h" @@ -40,23 +29,18 @@ class deltacheck_analyzert:public messaget { public: deltacheck_analyzert( - const indext &_index, - const optionst &_options, - message_handlert &message_handler): - messaget(message_handler), - use_index_old(false), - index_old(dummy_index_old), index_new(_index), options(_options) - { - } - - deltacheck_analyzert( - const indext &_index_old, - const indext &_index_new, + const std::string &_path_old, + const goto_modelt &_goto_model_old, + const std::string &_path_new, + const goto_modelt &_goto_model_new, const optionst &_options, message_handlert &message_handler): messaget(message_handler), - use_index_old(true), - index_old(_index_old), index_new(_index_new), options(_options) + path_old(_path_old), + path_new(_path_new), + goto_model_old(_goto_model_old), + goto_model_new(_goto_model_new), + options(_options) { } @@ -65,34 +49,17 @@ class deltacheck_analyzert:public messaget void operator()(); protected: - bool use_index_old; - indext dummy_index_old; - const indext &index_old; - const indext &index_new; + const std::string &path_old; + const std::string &path_new; + const goto_modelt &goto_model_old; + const goto_modelt &goto_model_new; const optionst &options; change_impactt change_impact; void check_function( - const std::string &path_prefix, - const symbolt &symbol, - goto_functionst::goto_functiont &f, - const namespacet &ns, - std::ostream &file_report); - - void check_function_delta( - // old - const std::string &path_prefix_old, - const symbolt &symbol_old, - goto_functionst::goto_functiont &f_old, - const namespacet &ns_old, - // new - const std::string &path_prefix_new, - const symbolt &symbol, - goto_functionst::goto_functiont &f, - const namespacet &ns, - // output - std::ostream &file_report); + const irep_idt &, + std::ostream &global_report); void check_all(std::ostream &global_report); @@ -117,98 +84,84 @@ Function: deltacheck_analyzert::check_function \*******************************************************************/ void deltacheck_analyzert::check_function( - const std::string &path_prefix, - const symbolt &symbol, - goto_functionst::goto_functiont &f, - const namespacet &ns, - std::ostream &file_report) + const irep_idt &function, + std::ostream &global_report) { - // add properties - status() << "Generating properties" << eom; - statistics.start("Properties"); - goto_check(ns, options, f); - f.body.update(); - label_properties(f.body); - statistics.stop("Properties"); - - // build SSA - status() << "Building SSA" << eom; - statistics.start("SSA"); - local_SSAt SSA(f, ns); - statistics.stop("SSA"); - - // now do fixed-point - status() << "Data-flow fixed-point" << eom; - statistics.start("Fixed-point"); - ssa_fixed_pointt ssa_fixed_point(SSA, ns); - statistics.stop("Fixed-point"); - - // now report on assertions - status() << "Reporting" << eom; - statistics.start("Reporting"); - report_properties(ssa_fixed_point.properties, file_report); - report_properties(ssa_fixed_point.properties, *this); - report_countermodels(SSA, ssa_fixed_point.properties, file_report); - report_source_code( - path_prefix, symbol.location, f.body, - ssa_fixed_point.properties, file_report, - get_message_handler()); - file_report << "\n"; - statistics.stop("Reporting"); - - // dump statistics - statistics.html_report_last(file_report); - - // collect some more data - collect_statistics(ssa_fixed_point.properties); -} - -/*******************************************************************\ + const goto_functionst::function_mapt::const_iterator + fmap_it_new=goto_model_new.goto_functions.function_map.find(function); + + if(fmap_it_new==goto_model_new.goto_functions.function_map.end()) + { + error() << "failed to find function `" << function + << "'" << eom; + return; + } + + const goto_functionst::goto_functiont &fkt_new= + fmap_it_new->second; -Function: deltacheck_analyzert::check_function_delta + // update statistics + LOCs_in_file+=fkt_new.body.instructions.size(); + collect_statistics(fkt_new); + statistics.number_map["Functions"]++; - Inputs: + // Is this function at all affected? + if(!change_impact.function_map[function].is_affected()) + { + status() << "Function \"" << function << "\" is not affected" << eom; - Outputs: + unsigned count=0; + forall_goto_program_instructions(i_it, fkt_new.body) + if(i_it->is_assert()) + count++; + + unaffected_in_file+=count; + statistics.number_map["Unaffected"]+=count; + return; // next function + } + + status() << "Checking \"" << function << "\"" << eom; + + const namespacet ns_new(goto_model_new.symbol_table); + const namespacet ns_old(goto_model_old.symbol_table); + + const symbolt &symbol_new=ns_new.lookup(function); + + // get corresponding goto_model_old function, if available - Purpose: + const goto_functionst::function_mapt::const_iterator + fmap_it_old=goto_model_old.goto_functions.function_map.find(function); + + goto_functionst::goto_functiont fkt_old_dummy; + symbolt symbol_old_dummy; -\*******************************************************************/ + const goto_functionst::goto_functiont &fkt_old= + fmap_it_old==goto_model_old.goto_functions.function_map.end()?fkt_old_dummy: + fmap_it_old->second; + + const symbolt &symbol_old= + fmap_it_old==goto_model_old.goto_functions.function_map.end()?symbol_old_dummy: + ns_old.lookup(function); + + // set up report -void deltacheck_analyzert::check_function_delta( - // old - const std::string &path_prefix_old, - const symbolt &symbol_old, - goto_functionst::goto_functiont &f_old, - const namespacet &ns_old, - // new - const std::string &path_prefix_new, - const symbolt &symbol_new, - goto_functionst::goto_functiont &f_new, - const namespacet &ns_new, - std::ostream &file_report) -{ - // add properties to each - status() << "Generating properties" << eom; - statistics.start("Properties"); - goto_check(ns_old, options, f_old); - f_old.body.update(); - label_properties(f_old.body); - goto_check(ns_new, options, f_new); - f_new.body.update(); - label_properties(f_new.body); - statistics.stop("Properties"); + std::string report_file_name= + make_relative_path(path_new, "deltacheck."+id2string(function)+".html"); + + std::ofstream function_report(report_file_name.c_str()); + + html_report_header("Function "+id2string(symbol_new.display_name()), function_report); // build SSA for each status() << "Building SSA" << eom; statistics.start("SSA"); - local_SSAt SSA_old(f_old, ns_old, "@old"); - local_SSAt SSA_new(f_new, ns_new); + local_SSAt SSA_old(fkt_old, ns_old, "@old"); + local_SSAt SSA_new(fkt_new, ns_new); statistics.stop("SSA"); // add assertions in old version as assumptions SSA_old.assertions_to_constraints(); - + // now do _joint_ fixed-point namespacet joint_ns( ns_new.get_symbol_table(), @@ -220,249 +173,43 @@ void deltacheck_analyzert::check_function_delta( // now report on assertions std::string description_old= - index_old.description==""?"old version":index_old.description; + options.get_option("description-old"); std::string description_new= - index_new.description==""?"new version":index_new.description; - + options.get_option("description-new"); + status() << "Reporting" << eom; statistics.start("Reporting"); - report_properties(ssa_fixed_point.properties, file_report); + //report_properties(ssa_fixed_point.properties, function_report); report_properties(ssa_fixed_point.properties, *this); report_countermodels(SSA_old, SSA_new, - ssa_fixed_point.properties, file_report); + ssa_fixed_point.properties, function_report); report_source_code( - path_prefix_old, symbol_old.location, f_old.body, description_old, - path_prefix_new, symbol_new.location, f_new.body, description_new, + path_old, symbol_old.location, fkt_old.body, description_old, + path_new, symbol_new.location, fkt_new.body, description_new, ssa_fixed_point.properties, - file_report, get_message_handler()); - file_report << "\n"; + function_report, get_message_handler()); statistics.stop("Reporting"); // dump statistics - statistics.html_report_last(file_report); + statistics.html_report_last(function_report); // collect some more data + #if 0 collect_statistics(ssa_fixed_point.properties); -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::check_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool loops(const goto_programt &src) -{ - forall_goto_program_instructions(it, src) - if(it->is_backwards_goto()) return true; - return false; -} - -void deltacheck_analyzert::check_all(std::ostream &global_report) -{ - // we do this by file in the index - - status() << "Starting analysis" << eom; + #endif - get_functiont get_old_function(index_old); - get_old_function.set_message_handler(get_message_handler()); + function_report << "\n"; + #if 0 global_report << "\n" << "" << "" << "" << "\n"; + #endif - for(indext::file_to_functiont::const_iterator - file_it=index_new.file_to_function.begin(); - file_it!=index_new.file_to_function.end(); - file_it++) - { - std::string full_path=index_new.full_path(file_it->first); - std::string path_prefix=get_directory(full_path); - - status() << "Processing \"" << full_path << "\"" << eom; - - errors_in_file=unknown_in_file=passed_in_file=unaffected_in_file=0; - LOCs_in_file=0; - - std::string file_suffix= - use_index_old?".deltacheck-diff.html":".deltacheck.html"; - - std::string file_report_name=full_path+file_suffix; - std::string report_url=id2string(file_it->first)+file_suffix; - - std::ofstream file_report(file_report_name.c_str()); - - if(!file_report) - { - error() << "failed to open report file `" << file_report - << "'" << eom; - return; - } - - std::string title="DeltaCheck File"; - - if(use_index_old) - html_report_header(file_report, index_old, index_new, title); - else - html_report_header(file_report, index_new, title); - - // read the goto-binary file - goto_modelt model; - read_goto_binary(full_path, model, get_message_handler()); - - // do partial inlining to increase precision - if(options.get_bool_option("partial-inlining")) - { - #if 0 - status() << "Partial inlining" << eom; - goto_partial_inline(model, get_message_handler(), 30); - #endif - } - - const namespacet ns_new(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::iterator - fmap_it=model.goto_functions.function_map.find(id); - - if(fmap_it==model.goto_functions.function_map.end()) - { - error() << "failed to find function `" << id2string(id) - << "'" << eom; - continue; - } - - goto_functionst::goto_functiont *index_new_fkt= - &fmap_it->second; - - // update statistics - LOCs_in_file+=index_new_fkt->body.instructions.size(); - collect_statistics(*index_new_fkt); - statistics.number_map["Functions"]++; - - // In case of differential checking, is this function at all affected? - if(use_index_old) - if(!change_impact.file_map[file_it->first][id].is_affected()) - { - status() << "Function \"" << id2string(id) << "\" is not affected" << eom; - - // add properties to function - statistics.start("Properties"); - goto_check(ns_new, options, *index_new_fkt); - index_new_fkt->body.update(); - statistics.stop("Properties"); - - unsigned count=0; - forall_goto_program_instructions(i_it, index_new_fkt->body) - if(i_it->is_assert()) - count++; - - unaffected_in_file+=count; - statistics.number_map["Unaffected"]+=count; - continue; // next function - } - - status() << "Checking \"" << id2string(id) << "\"" << eom; - - const symbolt &symbol=ns_new.lookup(id); - - file_report << "

Function " << html_escape(symbol.display_name()) - << " in " << html_escape(file_it->first) - << "

\n"; - - // get corresponding index_old function, if available - - std::string path_prefix_old; - - goto_functionst::goto_functiont *index_old_fkt= - get_old_function(id); - - if(index_old_fkt!=NULL) - { - const namespacet &ns_old=get_old_function.ns; - const symbolt &symbol_old=ns_old.lookup(id); - std::string path_prefix_old= - get_directory(id2string(get_old_function.get_file_name())); - - check_function_delta( - path_prefix_old, symbol_old, *index_old_fkt, ns_old, - path_prefix, symbol, *index_new_fkt, ns_new, - file_report); - } - else - { - #if 1 - check_function(path_prefix, symbol, *index_new_fkt, ns_new, - file_report); - #else - if(symbol.name==ID_main) - { - } - else if(loops(index_new_fkt->body) || symbol.name!="main") - check_function(path_prefix, symbol, *index_new_fkt, ns_new, - file_report); - else - { - goto_check(ns_new, options, *index_new_fkt); - index_new_fkt->body.update(); - - symbol_tablet d; - namespacet joint(d, model.symbol_table); - symex_target_equationt e(joint); - goto_symext symex(joint, d, e); - - symex(model.goto_functions, index_new_fkt->body); - - satcheckt satcheck; - satcheck.set_message_handler(get_message_handler()); - bv_pointerst solver(joint, satcheck); - solver.set_message_handler(get_message_handler()); - e.convert(solver); - decision_proceduret::resultt r=solver.dec_solve(); - - for(symex_target_equationt::SSA_stepst::iterator - it=e.SSA_steps.begin(); - it!=e.SSA_steps.end(); - it++) - { - if(it->is_assert()) - { - tvt result; - if(r==decision_proceduret::D_SATISFIABLE) - result=solver.prop.l_get(it->cond_literal); - else - result=tvt(true); - - if(result.is_false()) - statistics.number_map["Errors"]++; - else - statistics.number_map["Passed"]++; - } - } - } - #endif - } - } - - html_report_footer(file_report); - + #if 0 // add link to global report global_report << "
FileLOCs# Errors
" << html_escape(file_it->first) @@ -473,6 +220,31 @@ void deltacheck_analyzert::check_all(std::ostream &global_report) } global_report << "
\n\n"; + #endif +} + +/*******************************************************************\ + +Function: deltacheck_analyzert::check_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void deltacheck_analyzert::check_all(std::ostream &global_report) +{ + // we do this by function in the new goto_model + for(goto_functionst::function_mapt::const_iterator + fmap_it=goto_model_new.goto_functions.function_map.begin(); + fmap_it!=goto_model_new.goto_functions.function_map.end(); + fmap_it++) + { + check_function(fmap_it->first, global_report); + } } /*******************************************************************\ @@ -505,7 +277,8 @@ Function: deltacheck_analyzert::collect_statistics \*******************************************************************/ -void deltacheck_analyzert::collect_statistics(const propertiest &properties) +void deltacheck_analyzert::collect_statistics( + const propertiest &properties) { for(propertiest::const_iterator p_it=properties.begin(); @@ -545,51 +318,41 @@ Function: deltacheck_analyzert::operator() void deltacheck_analyzert::operator()() { statistics.start("Total-time"); - + std::string report_file_name= - use_index_old?"deltacheck-diff.html":"deltacheck.html"; + make_relative_path(path_new, "deltacheck.html"); - std::string report_full_path= - make_relative_path(index_new.path_prefix, report_file_name); - - std::ofstream out(report_full_path.c_str()); + std::ofstream out(report_file_name.c_str()); if(!out) { error() << "failed to write to \"" - << report_full_path << "\"" << eom; + << report_file_name << "\"" << eom; return; } status() << "Writing report into \"" - << report_full_path << "\"" << eom; + << report_file_name << "\"" << eom; std::string title="DeltaCheck Summary"; - if(use_index_old) - html_report_header(out, index_old, index_new, title); - else - html_report_header(out, index_new, title); + html_report_header( + out, options.get_option("description-old"), + options.get_option("description-new"), title); - if(use_index_old) - { - status() << "Path prefix old: " << index_old.path_prefix << eom; - status() << "Path prefix new: " << index_new.path_prefix << eom; - } - else - status() << "Path prefix: " << index_new.path_prefix << eom; - - if(use_index_old) - { - statistics.start("Change-impact"); - status() << "Computing syntactic difference" << eom; - change_impact.diff(index_old, index_new); - status() << "Change-impact analysis" << eom; - change_impact.change_impact(index_new); - statistics.stop("Change-impact"); - } + statistics.start("Change-impact"); + status() << "Computing syntactic difference" << eom; + change_impact.diff(goto_model_old, goto_model_new); + status() << "Change-impact analysis" << eom; + change_impact.change_impact(goto_model_new); + statistics.stop("Change-impact"); + + status() << "Starting analysis" << eom; - check_all(out); + if(options.get_option("function")!="") + check_function(options.get_option("function"), out); + else + check_all(out); statistics.stop("Total-time"); @@ -611,49 +374,25 @@ void deltacheck_analyzert::operator()() html_report_footer(out); - // Write some statistics into an XML file, for the benefit + // Write some statistics into a JSON file, for the benefit // of other programs. - std::string stat_xml_full_path= - make_relative_path(index_new.path_prefix, "deltacheck-stat.xml"); - - std::ofstream xml_out(stat_xml_full_path.c_str()); + std::string stat_file_name= + make_relative_path(path_new, "deltacheck-stat.json"); + std::ofstream json_out(stat_file_name.c_str()); - xml_out << "\n"; - xml_out << "\n"; - xml_out << "\n"; - xml_out << "\n"; - xml_out << "\n"; - xml_out << "\n"; -} - -/*******************************************************************\ - -Function: one_program_analyzer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void one_program_analyzer( - const indext &index, - const optionst &options, - message_handlert &message_handler) -{ - deltacheck_analyzert checker(index, options, message_handler); - checker(); + json_out << "{\n"; + json_out << " \"properties\": {\n"; + json_out << " \"unaffected\": " << statistics.number_map["Unaffected"] << ",\n"; + json_out << " \"passed\": " << statistics.number_map["Passed"] << ",\n"; + json_out << " \"failed\": " << statistics.number_map["Errors"] << ",\n"; + json_out << " \"warned\": " << statistics.number_map["Unknown"] << "\n"; + json_out << " },\n"; + json_out << " \"program\": {\n"; + json_out << " \"LOCs\": " << statistics.number_map["LOCs"] << ",\n"; + json_out << " \"functions\": " << statistics.number_map["Functions"] << "\n"; + json_out << " }\n"; + json_out << "}\n"; } /*******************************************************************\ @@ -669,11 +408,16 @@ Function: deltacheck_analyzer \*******************************************************************/ void deltacheck_analyzer( - const indext &index1, - const indext &index2, + const std::string &path1, + const goto_modelt &goto_model1, + const std::string &path2, + const goto_modelt &goto_model2, const optionst &options, message_handlert &message_handler) { - deltacheck_analyzert checker(index1, index2, options, message_handler); + deltacheck_analyzert checker( + path1, goto_model1, + path2, goto_model2, + options, message_handler); checker(); } diff --git a/src/deltacheck/analyzer.h b/src/deltacheck/analyzer.h index ab5f05e80..a83d0e163 100644 --- a/src/deltacheck/analyzer.h +++ b/src/deltacheck/analyzer.h @@ -9,17 +9,17 @@ Author: Daniel Kroening, kroening@kroening.com #ifndef CPROVER_DELTACHECK_CHECKER_H #define CPROVER_DELTACHECK_CHECKER_H +#include + +#include + class message_handlert; -class indext; void deltacheck_analyzer( - const indext &index1, - const indext &index2, - const optionst &options, - message_handlert &); - -void one_program_analyzer( - const indext &index, + const std::string &path1, + const goto_modelt &goto_model1, + const std::string &path2, + const goto_modelt &goto_model2, const optionst &options, message_handlert &); diff --git a/src/deltacheck/change_impact.cpp b/src/deltacheck/change_impact.cpp index 77caf84b1..533512603 100644 --- a/src/deltacheck/change_impact.cpp +++ b/src/deltacheck/change_impact.cpp @@ -27,70 +27,22 @@ Function: change_impactt::diff \*******************************************************************/ void change_impactt::diff( - const indext &old_index, - const indext &new_index) + const goto_modelt &old_model, + const goto_modelt &new_model) { - for(indext::file_to_functiont::const_iterator - new_file_it=new_index.file_to_function.begin(); - new_file_it!=new_index.file_to_function.end(); - new_file_it++) + for(goto_functionst::function_mapt::const_iterator + new_fkt_it=new_model.goto_functions.function_map.begin(); + new_fkt_it!=new_model.goto_functions.function_map.end(); + new_fkt_it++) { - // read the new file - goto_modelt new_model; - read_goto_binary(new_index.full_path(new_file_it), new_model, get_message_handler()); - - // do call graph edges - do_call_graph(new_index, new_file_it->first, new_model); + // try to find 'corresponding function' in old_model + goto_functionst::function_mapt::const_iterator + old_fkt_it=old_model.goto_functions.function_map.find(new_fkt_it->first); - function_mapt &functions=file_map[new_file_it->first]; - - // is there a corresponding old file? - indext::file_to_functiont::const_iterator old_file_it= - old_index.file_to_function.find(new_file_it->first); - - if(old_file_it==old_index.file_to_function.end()) - { - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=new_model.goto_functions.function_map.begin(); - new_fkt_it!=new_model.goto_functions.function_map.end(); - new_fkt_it++) - { - // no corresponding old file, try elsewhere - get_functiont get_function(old_index); - - goto_functionst::goto_functiont *old_fkt= - get_function(new_fkt_it->first); - - if(old_fkt==NULL) - { - // old not found, mark as changed - functions[new_fkt_it->first].fully_changed=true; - } - else - diff_functions(new_file_it->first, new_fkt_it->first, *old_fkt, new_fkt_it->second); - } - } + if(old_fkt_it==old_model.goto_functions.function_map.end()) + function_map[new_fkt_it->first].fully_changed=true; else - { - // read the old file - goto_modelt old_model; - read_goto_binary(old_index.full_path(old_file_it), old_model, get_message_handler()); - - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=new_model.goto_functions.function_map.begin(); - new_fkt_it!=new_model.goto_functions.function_map.end(); - new_fkt_it++) - { - // try to find 'corresponding function' in old_model - goto_functionst::function_mapt::const_iterator - old_fkt_it=old_model.goto_functions.function_map.find(new_fkt_it->first); - - if(old_fkt_it==old_model.goto_functions.function_map.end()) - functions[new_fkt_it->first].fully_changed=true; - else - diff_functions(new_file_it->first, new_fkt_it->first, old_fkt_it->second, new_fkt_it->second); - } - } + diff_functions(new_fkt_it->first, old_fkt_it->second, new_fkt_it->second); } } @@ -107,7 +59,6 @@ Function: change_impactt::diff_functions \*******************************************************************/ void change_impactt::diff_functions( - const irep_idt &file, const irep_idt &function_id, const goto_functionst::goto_functiont &old_f, const goto_functionst::goto_functiont &new_f) @@ -134,7 +85,7 @@ void change_impactt::diff_functions( } // now diff - datat &data=file_map[file][function_id]; + datat &data=function_map[function_id]; goto_programt::instructionst::const_iterator old_it=old_body.instructions.begin(); @@ -178,48 +129,24 @@ Function: change_impactt::output_diff void change_impactt::output_diff(std::ostream &out) { - for(file_mapt::const_iterator - file_it=file_map.begin(); file_it!=file_map.end(); file_it++) + for(function_mapt::const_iterator + fkt_it=function_map.begin(); + fkt_it!=function_map.end(); + fkt_it++) { - const function_mapt &function_map=file_it->second; - - bool change_found=false; - - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - if(fkt_it->second.has_change()) - { - change_found=true; - break; - } - - if(!change_found) continue; - - out << "******* File " << file_it->first << "\n"; - - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) + if(fkt_it->second.fully_changed) + out << fkt_it->first << ": *\n"; + else if(!fkt_it->second.locs_changed.empty()) { - if(fkt_it->second.fully_changed) - out << fkt_it->first << ": *\n"; - else if(!fkt_it->second.locs_changed.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_changed.begin(); - l_it!=fkt_it->second.locs_changed.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } + out << fkt_it->first << ":"; + for(std::set::const_iterator + l_it=fkt_it->second.locs_changed.begin(); + l_it!=fkt_it->second.locs_changed.end(); + l_it++) + out << " " << *l_it; + + out << "\n"; } - - out << "\n"; } } @@ -237,49 +164,27 @@ Function: change_impactt::output_change_impact void change_impactt::output_change_impact(std::ostream &out) { - for(file_mapt::const_iterator - file_it=file_map.begin(); file_it!=file_map.end(); file_it++) + for(function_mapt::const_iterator + fkt_it=function_map.begin(); + fkt_it!=function_map.end(); + fkt_it++) { - const function_mapt &function_map=file_it->second; - - bool is_affected=false; - - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - if(fkt_it->second.is_affected()) - { - is_affected=true; - break; - } - - if(!is_affected) continue; - - out << "******* File " << file_it->first << "\n"; - - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) + if(fkt_it->second.fully_affected) + out << fkt_it->first << "\n"; + else if(!fkt_it->second.locs_affected.empty()) { - if(fkt_it->second.fully_affected) - out << fkt_it->first << "\n"; - else if(!fkt_it->second.locs_affected.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_affected.begin(); - l_it!=fkt_it->second.locs_affected.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } + out << fkt_it->first << ":"; + for(std::set::const_iterator + l_it=fkt_it->second.locs_affected.begin(); + l_it!=fkt_it->second.locs_affected.end(); + l_it++) + out << " " << *l_it; + + out << "\n"; } - - out << "\n"; } + + out << "\n"; } /*******************************************************************\ @@ -294,40 +199,29 @@ Function: change_impactt::change_impact \*******************************************************************/ -void change_impactt::change_impact(const indext &new_index) +void change_impactt::change_impact(const goto_modelt &new_model) { - std::stack working; + std::stack working; // stash everything with change into the working set - for(file_mapt::const_iterator - file_it=file_map.begin(); - file_it!=file_map.end(); - file_it++) + for(function_mapt::const_iterator + function_it=function_map.begin(); + function_it!=function_map.end(); + function_it++) { - for(function_mapt::const_iterator - function_it=file_it->second.begin(); - function_it!=file_it->second.end(); - function_it++) + if(function_it->second.has_change()) { - if(function_it->second.has_change()) - { - f_idt f_id; - f_id.function_id=function_it->first; - f_id.file=file_it->first; - working.push(f_id); - } + working.push(function_it->first); } } - get_functiont get_function(new_index); - // main loop while(!working.empty()) { - const f_idt f_id=working.top(); + const irep_idt f_id=working.top(); working.pop(); - propagate_affected(new_index, get_function, f_id, working); + propagate_affected(new_model, f_id, working); } } @@ -344,21 +238,22 @@ Function: change_impactt::propagate_affected \*******************************************************************/ void change_impactt::propagate_affected( - const indext &new_index, - get_functiont &get_function, - const f_idt &f_id, - std::stack &working_fkts) + const goto_modelt &new_model, + const irep_idt &f_id, + std::stack &working_fkts) { - datat &data=file_map[f_id.file][f_id.function_id]; + datat &data=function_map[f_id]; if(data.fully_affected) return; // done already // get it - goto_functionst::goto_functiont *fkt= - get_function(f_id.function_id); - - if(fkt==NULL) return; // give up - const goto_programt &body=fkt->body; + goto_functionst::function_mapt::const_iterator f_it= + new_model.goto_functions.function_map.find(f_id); + + if(f_it==new_model.goto_functions.function_map.end()) + return; // give up + + const goto_programt &body=f_it->second.body; if(body.empty()) return; // give up std::stack working_locs; @@ -376,8 +271,8 @@ void change_impactt::propagate_affected( if(call.function().id()==ID_symbol) { const symbol_exprt &symbol=to_symbol_expr(call.function()); - f_idt called_f_id=get_f_id(new_index, f_id.file, symbol.get_identifier()); - if(file_map[called_f_id.file][called_f_id.function_id].is_affected()) + irep_idt called_f_id=symbol.get_identifier(); + if(function_map[called_f_id].is_affected()) working_locs.push(l); } } @@ -398,7 +293,7 @@ void change_impactt::propagate_affected( if(call.function().id()==ID_symbol) { const symbol_exprt &symbol=to_symbol_expr(call.function()); - f_idt called_f_id=get_f_id(new_index, f_id.file, symbol.get_identifier()); + irep_idt called_f_id=symbol.get_identifier(); make_fully_affected(called_f_id); } } @@ -430,23 +325,23 @@ Function: change_impactt::make_fully_affected \*******************************************************************/ -void change_impactt::make_fully_affected(const f_idt &f_id) +void change_impactt::make_fully_affected(const irep_idt &f_id) { - std::stack working; + std::stack working; working.push(f_id); while(!working.empty()) { - const f_idt f_id=working.top(); + const irep_idt f_id=working.top(); working.pop(); - datat &data=file_map[f_id.file][f_id.function_id]; + datat &data=function_map[f_id]; if(data.fully_affected) continue; data.fully_affected=true; // recursively make all functions that are called fully affected - for(std::set::const_iterator + for(std::set::const_iterator called_it=data.calls.begin(); called_it!=data.calls.end(); called_it++) @@ -469,8 +364,6 @@ Function: change_impactt::do_call_graph \*******************************************************************/ void change_impactt::do_call_graph( - const indext &index, - const irep_idt &file, const goto_modelt &model) { for(goto_functionst::function_mapt::const_iterator @@ -478,11 +371,7 @@ void change_impactt::do_call_graph( new_fkt_it!=model.goto_functions.function_map.end(); new_fkt_it++) { - f_idt this_f_id; - this_f_id.file=file; - this_f_id.function_id=new_fkt_it->first; - - datat &data=file_map[file][new_fkt_it->first]; + datat &data=function_map[new_fkt_it->first]; const goto_programt &body=new_fkt_it->second.body; @@ -493,9 +382,8 @@ void change_impactt::do_call_graph( if(call.function().id()==ID_symbol) { const symbol_exprt &symbol=to_symbol_expr(call.function()); - const f_idt called_f_id=get_f_id(index, file, symbol.get_identifier()); + const irep_idt called_f_id=symbol.get_identifier(); data.calls.insert(called_f_id); - file_map[called_f_id.file][called_f_id.function_id].called_by.insert(this_f_id); } } } diff --git a/src/deltacheck/change_impact.h b/src/deltacheck/change_impact.h index a7a4aec82..3860841c3 100644 --- a/src/deltacheck/change_impact.h +++ b/src/deltacheck/change_impact.h @@ -11,34 +11,19 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../functions/get_function.h" -#include "../functions/index.h" - class change_impactt:public messaget { public: void diff( - const indext &old_index, - const indext &new_index); + const goto_modelt &old_model, + const goto_modelt &new_model); void change_impact( - const indext &new_index); + const goto_modelt &new_model); void output_diff(std::ostream &); void output_change_impact(std::ostream &); - struct f_idt - { - irep_idt file, function_id; - }; - - friend bool operator<(const f_idt &f1, const f_idt &f2) - { - if(f1.file locs_changed, locs_affected; - std::set calls; - std::set called_by; + std::set calls; + std::set called_by; }; - // functions to 'affected' map + // functions to 'datat' map typedef std::map function_mapt; - - // file to functions map - typedef std::map file_mapt; - file_mapt file_map; + function_mapt function_map; protected: void diff_functions( - const irep_idt &file, const irep_idt &function_id, const goto_functionst::goto_functiont &, const goto_functionst::goto_functiont &); void propagate_affected( - const indext &new_index, - get_functiont &get_function, - const f_idt &f_id, - std::stack &working); + const goto_modelt &new_index, + const irep_idt &id, + std::stack &working); - void make_fully_affected(const f_idt &f_id); + void make_fully_affected(const irep_idt &); void do_call_graph( - const indext &index, - const irep_idt &file, const goto_modelt &); - - f_idt get_f_id( - const indext &index, - const irep_idt &file, - const irep_idt &function_id) - { - f_idt result; - result.file=index.get_file_for_function(file, function_id); - result.function_id=function_id; - return result; - } }; #endif diff --git a/src/deltacheck/deltacheck_main.cpp b/src/deltacheck/deltacheck_main.cpp index 66ac301e2..c369d3d58 100644 --- a/src/deltacheck/deltacheck_main.cpp +++ b/src/deltacheck/deltacheck_main.cpp @@ -8,7 +8,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "deltacheck_parseoptions.h" +#include "deltacheck_parse_options.h" /*******************************************************************\ @@ -26,13 +26,13 @@ Function: main int wmain(int argc, const wchar_t **argv_wide) { const char **argv=narrow_argv(argc, argv_wide); - deltacheck_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + deltacheck_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #else int main(int argc, const char **argv) { - deltacheck_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + deltacheck_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #endif diff --git a/src/deltacheck/html_report.cpp b/src/deltacheck/html_report.cpp index 79dc03b5a..455845b21 100644 --- a/src/deltacheck/html_report.cpp +++ b/src/deltacheck/html_report.cpp @@ -8,6 +8,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "../html/html_escape.h" #include "../html/logo.h" + #include "html_report.h" #include "version.h" @@ -59,7 +60,8 @@ Function: html_report_header void html_report_header( std::ostream &out, - const indext &index1, const indext &index2, + const std::string &old_desc, + const std::string &new_desc, const std::string &title) { html_report_header(title, out); @@ -74,46 +76,13 @@ void html_report_header( out << "

Software under analysis

\n"; out << "

\n" - << "\n"; - out << "\n" + << "\n"; + out << "\n" << "
Old version:" << html_escape(index1.file_name) - << "" << html_escape(index1.description) << "
New version:" << html_escape(index2.file_name) - << "" << html_escape(index2.description) << "
Old version:" << html_escape(old_desc) << "
New version:" << html_escape(new_desc) << "

\n"; } /*******************************************************************\ -Function: html_report_header - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void html_report_header( - std::ostream &out, - const indext &index, - const std::string &title) -{ - html_report_header(title, out); - - out << "\"DeltaCheck\n\n"; - - out << "

" << html_escape(title) << "

\n\n"; - - out << "

DeltaCheck version: " << DELTACHECK_VERSION << "

\n"; - - out << "

Software under analysis

\n"; - out << "

Single version: " << html_escape(index.file_name) - << " " << html_escape(index.description) << "

\n"; -} - -/*******************************************************************\ - Function: html_report_footer Inputs: @@ -129,7 +98,7 @@ void html_report_footer(std::ostream &out) out << "
\n" "\n" "
\n" - "DeltaCheck is © 2011–2013 Daniel Kroening, University of Oxford.\n" + "DeltaCheck is © 2011–2015 Daniel Kroening, University of Oxford.\n" "
\n" "\n" "\n" diff --git a/src/deltacheck/html_report.h b/src/deltacheck/html_report.h index c695fe4fb..226e4e3b6 100644 --- a/src/deltacheck/html_report.h +++ b/src/deltacheck/html_report.h @@ -11,17 +11,16 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../functions/index.h" +#include void html_report_header( std::ostream &out, - const indext &index1, const indext &index2, + const std::string &old_desc, const std::string &new_desc, const std::string &title); void html_report_header( - std::ostream &out, - const indext &index1, - const std::string &title); + const std::string &title, + std::ostream &out); void html_report_footer(std::ostream &out); diff --git a/src/deltacheck/report_header.html b/src/deltacheck/report_header.html index 2438025b4..cc5e08aea 100644 --- a/src/deltacheck/report_header.html +++ b/src/deltacheck/report_header.html @@ -157,13 +157,22 @@ } else if(what=='click') { - if(property_selected!=0) property_selected.bgColor='#ffffff'; + if(property_selected != 0) property_selected.bgColor='#ffffff'; row.bgColor='#31d1ff'; property_selected=row; var_values=ce; } } +var X_selected = 0; + +var X_click=function(X_span, ce) { + if(X_selected != 0) X_selected.style.color='#cc0000'; + X_span.style.color='#31d1ff'; + X_selected=X_span; + var_values=ce; +} + var var_tooltip=function(id, where) { var key=id+'@'+where; var value=var_values[key]; diff --git a/src/deltacheck/report_source_code.cpp b/src/deltacheck/report_source_code.cpp index 40e2aac6b..738db1301 100644 --- a/src/deltacheck/report_source_code.cpp +++ b/src/deltacheck/report_source_code.cpp @@ -11,10 +11,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "../html/html_escape.h" +#include "../html/syntax_highlighting.h" #include "report_source_code.h" #include "get_source.h" #include "source_diff.h" -#include "syntax_highlighting.h" /*******************************************************************\ @@ -198,12 +198,14 @@ void report_source_code( { syntax_highlightingt syntax_highlighting(out); + syntax_highlighting.identifier_tooltip=true; for(l_old_it=lines_old.begin(), l_it=lines_new.begin(); l_old_it!=lines_old.end() && l_it!=lines_new.end(); l_old_it++, l_it++) { - syntax_highlighting.different=(l_old_it->line!=l_it->line); + syntax_highlighting.strong_class= + (l_old_it->line!=l_it->line)?"different":""; syntax_highlighting.line_no=l_it->line_no; syntax_highlighting.id_suffix="@old"; syntax_highlighting(l_old_it->line); @@ -220,15 +222,39 @@ void report_source_code( for(std::list::const_iterator l_it=lines_new.begin(); l_it!=lines_new.end(); l_it++) { - std::string errors=get_errors(properties, *l_it); - if(!errors.empty()) + const linet &line=*l_it; + + unsigned count=0; + + for(propertiest::const_iterator + p_it=properties.begin(); p_it!=properties.end(); p_it++, count++) { - out << "" - << "✗" - << ""; + if(line.file==p_it->loc->source_location.get_file() && + i2string(line.line_no)==as_string(p_it->loc->source_location.get_line())) + { + if(p_it->status.is_false()) + { + irep_idt property=p_it->loc->source_location.get_property_class(); + irep_idt comment=p_it->loc->source_location.get_comment(); + + std::string msg; + + if(comment=="") + msg=as_string(property); + else + msg=as_string(comment); + + out << "" + << "✗" + << ""; + } + } } + out << "\n"; } @@ -250,12 +276,14 @@ void report_source_code( { syntax_highlightingt syntax_highlighting(out); + syntax_highlighting.identifier_tooltip=true; for(l_old_it=lines_old.begin(), l_it=lines_new.begin(); l_old_it!=lines_old.end() && l_it!=lines_new.end(); l_old_it++, l_it++) { - syntax_highlighting.different=(l_old_it->line!=l_it->line); + syntax_highlighting.strong_class= + (l_old_it->line!=l_it->line)?"different":""; syntax_highlighting.line_no=l_it->line_no; syntax_highlighting(l_it->line); } diff --git a/src/deltagit/Makefile b/src/deltagit/Makefile index b10153008..4c481facf 100644 --- a/src/deltagit/Makefile +++ b/src/deltagit/Makefile @@ -1,13 +1,14 @@ include ../config.inc CBMC ?= ../.. -SRC = deltagit_main.cpp deltagit_parseoptions.cpp show_jobs.cpp \ +SRC = deltagit_main.cpp deltagit_parse_options.cpp show_jobs.cpp \ shell_escape.cpp git_log.cpp git_branch.cpp job_status.cpp do_job.cpp \ deltagit_config.cpp revisions_report.cpp init.cpp reset.cpp \ - log_scale.cpp reanalyse.cpp + reanalyse.cpp OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ + $(CBMC)/src/json/json$(LIBEXT) \ $(CBMC)/src/big-int/big-int$(LIBEXT) \ ../html/html_escape$(OBJEXT) \ ../html/logo$(OBJEXT) diff --git a/src/deltagit/deltagit_main.cpp b/src/deltagit/deltagit_main.cpp index 919ba7eb7..2115a268c 100644 --- a/src/deltagit/deltagit_main.cpp +++ b/src/deltagit/deltagit_main.cpp @@ -8,7 +8,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "deltagit_parseoptions.h" +#include "deltagit_parse_options.h" /*******************************************************************\ @@ -26,13 +26,13 @@ Function: main int wmain(int argc, const wchar_t **argv_wide) { const char **argv=narrow_argv(argc, argv_wide); - deltagit_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + deltagit_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #else int main(int argc, const char **argv) { - deltagit_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + deltagit_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #endif diff --git a/src/deltagit/revisions_report.cpp b/src/deltagit/revisions_report.cpp index a67526de3..cb943234b 100644 --- a/src/deltagit/revisions_report.cpp +++ b/src/deltagit/revisions_report.cpp @@ -7,10 +7,10 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ #include -#include #include -#include +#include +#include #include "../html/html_escape.h" #include "../html/logo.h" @@ -182,7 +182,7 @@ void revisions_report( out << "\n" << "\n
\n" - << "\n" + << "\n" << "\n"; unsigned counter=0, number_of_jobs=jobs.size(); @@ -200,16 +200,13 @@ void revisions_report( unsigned passed=0, failed=0; { - std::string summary_file_name=j_it->get_wd()+"/deltacheck-stat.xml"; - xmlt deltacheck_summary; + std::string summary_file_name=j_it->get_wd()+"/deltacheck-stat.json"; + jsont deltacheck_summary; null_message_handlert null_message_handler; - parse_xml(summary_file_name, null_message_handler, deltacheck_summary); - xmlt::elementst::const_iterator properties=deltacheck_summary.find("properties"); - if(properties!=deltacheck_summary.elements.end()) - { - passed=atoi(properties->get_attribute("passed").c_str()); - failed=atoi(properties->get_attribute("failed").c_str()); - } + parse_json(summary_file_name, null_message_handler, deltacheck_summary); + const jsont &properties=deltacheck_summary["properties"]; + passed=unsafe_string2unsigned(properties["passed"].value); + failed=unsafe_string2unsigned(properties["failed"].value); } std::string tooltip= diff --git a/src/domains/Makefile b/src/domains/Makefile index acaf10c0d..bc6a7752c 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -2,21 +2,21 @@ include ../config.inc CBMC ?= ../.. SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ - tpolyhedra_domain.cpp equality_domain.cpp domain.cpp \ - ssa_analyzer.cpp util.cpp incremental_solver.cpp \ + tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ + ssa_analyzer.cpp disjunctive_analyzer.cpp util.cpp incremental_solver.cpp \ strategy_solver_base.cpp \ - linrank_domain.cpp lexlinrank_domain.cpp\ - ranking_solver_enumeration.cpp lexlinrank_solver_enumeration.cpp \ strategy_solver_enumeration.cpp strategy_solver_binsearch.cpp \ template_generator_base.cpp template_generator_summary.cpp \ - template_generator_callingcontext.cpp template_generator_ranking.cpp -# strategy_solver_binsearch2.cpp -#strategy_solver_binsearch3.cpp + template_generator_callingcontext.cpp \ + strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ + strategy_solver_predabs.cpp #solver_enumeration.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common +CP_CXXFLAGS += $(SUMMARIZERFLAGS) + INCLUDES= -I $(CBMC)/src CLEANFILES = diff --git a/src/domains/domain.h b/src/domains/domain.h index 1c5062686..fab89bb6a 100644 --- a/src/domains/domain.h +++ b/src/domains/domain.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -27,6 +28,7 @@ class domaint guardt pre_guard; guardt post_guard; vart var; + exprt aux_expr; //some auxiliary per-variable constraint kindt kind; } var_spect; @@ -34,41 +36,27 @@ class domaint class valuet { public: - typedef enum{TOP,BOTTOM,OTHER} basic_valuet; - valuet() : basic_value(OTHER) {} - virtual ~valuet() {} - - basic_valuet basic_value; + virtual ~valuet() {} }; - virtual void initialize(valuet &value) { value.basic_value = valuet::BOTTOM; } + virtual void initialize(valuet &value) { assert(false); } //returns true as long as further refinements are possible virtual void reset_refinements() { } virtual bool refine() { return false; } - virtual void join(valuet &value1, const valuet &value2) - { - if(value1.basic_value==value2.basic_value || - value1.basic_value==valuet::TOP || - (value1.basic_value==valuet::OTHER && - value2.basic_value==valuet::BOTTOM)) return; - value1.basic_value = value2.basic_value; - } + virtual void join(valuet &value1, const valuet &value2) { assert(false); } virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const { assert(false); } virtual void output_domain(std::ostream &out, const namespacet &ns) const { assert(false); } - virtual void project_on_vars(valuet &value, const var_sett &vars, - exprt &result) - //(not useful to make value const (e.g. union-find)) - { - if(value.basic_value==valuet::BOTTOM) result = false_exprt(); - else result = true_exprt(); - } + virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result) + { assert(false); } //(not useful to make value const (e.g. union-find)) + virtual bool is_spec_empty() const { assert(false); } + static kindt merge_kinds(kindt k1, kindt k2); static void output_var_specs(std::ostream &out, const var_specst &var_specs, @@ -78,7 +66,10 @@ class domaint unsigned domain_number; //serves as id for variables names replace_mapt &renaming_map; - inline void rename(exprt &expr) { replace_expr(renaming_map, expr); } + inline void rename(exprt &expr) + { + replace_expr(renaming_map, expr); + } inline void rename(exprt::operandst &operands) { for(unsigned i=0; i @@ -22,10 +23,6 @@ Function: equality_domaint::initialize void equality_domaint::initialize(valuet &value) { -#if 0 - if(templ.size()==0) return domaint::initialize(value); -#endif - equ_valuet &v = static_cast(value); v.equs.clear(); v.disequs.clear(); @@ -58,7 +55,9 @@ exprt equality_domaint::get_post_not_equ_constraint(unsigned index) const template_rowt &templ_row = templ[index]; if(templ_row.kind==IN) return true_exprt(); const var_pairt &vv = templ_row.var_pair; - exprt c = not_exprt(implies_exprt(templ_row.post_guard,equal_exprt(vv.first,vv.second))); + exprt c = and_exprt(templ_row.aux_expr, + not_exprt(implies_exprt(templ_row.post_guard, + equal_exprt(vv.first,vv.second)))); rename(c); return c; } @@ -78,12 +77,13 @@ exprt equality_domaint::get_post_not_disequ_constraint(unsigned index) const template_rowt &templ_row = templ[index]; if(templ_row.kind==IN) return true_exprt(); const var_pairt &vv = templ_row.var_pair; - exprt c = not_exprt(implies_exprt(templ_row.post_guard,notequal_exprt(vv.first,vv.second))); + exprt c = and_exprt(templ_row.aux_expr, + not_exprt(implies_exprt(templ_row.post_guard, + notequal_exprt(vv.first,vv.second)))); rename(c); return c; } - /*******************************************************************\ Function: template_domaint::project_on_vars @@ -99,10 +99,6 @@ Function: template_domaint::project_on_vars void equality_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) { -#if 0 - if(templ.size()==0) return domaint::project_on_vars(value,vars,result); -#endif - equ_valuet &v = static_cast(value); exprt::operandst c; @@ -110,10 +106,13 @@ void equality_domaint::project_on_vars(valuet &value, { const var_pairt &vv = templ[index].var_pair; +#if 0 + std::cout << vv.second << std::endl; +#endif if(vars.find(vv.first)==vars.end() || - vars.find(vv.second)==vars.end() && - !(vv.second.id()==ID_constant && - to_constant_expr(vv.second).get_value()=="NULL")) + (vars.find(vv.second)==vars.end() && + !(vv.second.id()==ID_constant && + to_constant_expr(vv.second).get_value()=="NULL"))) continue; if(v.equs.same_set(vv.first,vv.second)) @@ -132,9 +131,9 @@ void equality_domaint::project_on_vars(valuet &value, const var_pairt &vv = templ[*it].var_pair; if(vars.find(vv.first)==vars.end() || - vars.find(vv.second)==vars.end() && - !(vv.second.id()==ID_constant && - to_constant_expr(vv.second).get_value()=="NULL")) + (vars.find(vv.second)==vars.end() && + !(vv.second.id()==ID_constant && + to_constant_expr(vv.second).get_value()=="NULL"))) continue; if(templ[*it].kind==LOOP) @@ -263,7 +262,8 @@ void equality_domaint::output_domain(std::ostream &out, { case LOOP: out << "(LOOP) [ " << from_expr(ns,"",templ_row.pre_guard) << " | "; - out << from_expr(ns,"",templ_row.post_guard) + out << from_expr(ns,"",templ_row.post_guard) << " | "; + out << from_expr(ns,"",templ_row.aux_expr) << " ] ===> " << std::endl << " "; break; case IN: @@ -376,6 +376,7 @@ void equality_domaint::make_template( null_pointer_exprt(to_pointer_type(v1->var.type()))); templ_row.pre_guard = v1->pre_guard; templ_row.post_guard = v1->post_guard; + templ_row.aux_expr = v1->aux_expr; templ_row.kind = v1->kind; } @@ -385,10 +386,10 @@ void equality_domaint::make_template( kindt k = domaint::merge_kinds(v1->kind,v2->kind); //if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); + exprt pre_g, post_g, aux_expr; + merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); + merge_and(post_g, v1->post_guard, v2->post_guard, ns); + merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); exprt vv1 = v1->var; exprt vv2 = v2->var; @@ -399,6 +400,7 @@ void equality_domaint::make_template( templ_row.var_pair = var_pairt(vv1,vv2); templ_row.pre_guard = pre_g; templ_row.post_guard = post_g; + templ_row.aux_expr = aux_expr; templ_row.kind = k; } } diff --git a/src/domains/equality_domain.h b/src/domains/equality_domain.h index 8addae384..47ba3341d 100644 --- a/src/domains/equality_domain.h +++ b/src/domains/equality_domain.h @@ -36,6 +36,7 @@ class equality_domaint : public domaint guardt pre_guard; guardt post_guard; equality_domaint::var_pairt var_pair; + exprt aux_expr; kindt kind; } template_rowt; diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index 369c7001b..9001b2e41 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -7,66 +7,101 @@ #include "incremental_solver.h" -literalt incremental_solvert::new_context() +void incremental_solvert::new_context() { - literalt activation_literal = solver.convert( +#ifdef NON_INCREMENTAL + contexts.push_back(constraintst()); + +#if 0 + std::cerr << "new context: " << contexts.size() << std::endl; +#endif + +#else + + literalt activation_literal = solver->convert( symbol_exprt("goto_symex::\\act$"+ i2string(activation_literal_counter++), bool_typet())); -#ifdef DISPLAY_FORMULA +#ifdef DEBUG_OUTPUT debug() << "new context: " << activation_literal<< eom; #endif activation_literals.push_back(activation_literal); - solver.set_assumptions(activation_literals); - return !activation_literals.back(); + solver->set_assumptions(activation_literals); + +#if 0 + return !activation_literals.back(); //not to be used anymore +#endif +#endif } void incremental_solvert::pop_context() { +#ifdef NON_INCREMENTAL + assert(!contexts.empty()); + +#if 0 + std::cerr << "pop context: " << contexts.size() << std::endl; +#endif + + contexts.pop_back(); + +#else + assert(!activation_literals.empty()); literalt activation_literal = activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA - solver.set_to_false(literal_exprt(activation_literal)); + solver->set_to_false(literal_exprt(activation_literal)); #else formula.push_back(!activation_literal); #endif -#ifdef DISPLAY_FORMULA +#ifdef DEBUG_OUTPUT debug() << "pop context: " << activation_literal << eom; #endif - solver.set_assumptions(activation_literals); + solver->set_assumptions(activation_literals); +#endif } void incremental_solvert::make_context_permanent() { +#ifdef NON_INCREMENTAL + assert(contexts.size()>=2); + contextst::iterator c_it = contexts.end(); c_it--; c_it--; + c_it->insert(c_it->end(),contexts.back().begin(),contexts.back().end()); + contexts.pop_back(); +#else assert(!activation_literals.empty()); literalt activation_literal = activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA - solver.set_to_true(literal_exprt(activation_literal)); + solver->set_to_true(literal_exprt(activation_literal)); #else formula.push_back(activation_literal); #endif -#ifdef DISPLAY_FORMULA +#ifdef DEBUG_OUTPUT debug() << "make context permanent: " << activation_literal<< eom; #endif - solver.set_assumptions(activation_literals); + solver->set_assumptions(activation_literals); +#endif } void incremental_solvert::debug_add_to_formula(const exprt &expr) { - literalt l = solver.convert(expr); +#ifdef NON_INCREMENTAL + // no debug mode for non-incremental yet +#else + literalt l = solver->convert(expr); if(l.is_false()) { #ifdef DEBUG_OUTPUT debug() << "literal " << l << ": false = " << from_expr(ns,"",expr) <convert(symbol_exprt("goto_symex::\\dummy", bool_typet())); formula.push_back(dummy); formula.push_back(!dummy); @@ -82,4 +117,5 @@ void incremental_solvert::debug_add_to_formula(const exprt &expr) #endif formula.push_back(l); } +#endif } diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 2feba5e1e..e11c31149 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -13,11 +13,16 @@ Author: Peter Schrammel #include #include +#include #include #include "domain.h" #include "util.h" + +//#define NO_ARITH_REFINEMENT +//#define NON_INCREMENTAL // (experimental) + //#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT @@ -27,24 +32,33 @@ class incremental_solvert : public messaget public: typedef std::list constraintst; + typedef std::list contextst; explicit incremental_solvert( - const namespacet &_ns) : - sat_check(), - solver(_ns,sat_check), + const namespacet &_ns, bool _arith_refinement) : + sat_check(NULL), + solver(NULL), ns(_ns), activation_literal_counter(0), domain_number(0), + arith_refinement(_arith_refinement), solver_calls(0) { + allocate_solvers(_arith_refinement); + contexts.push_back(constraintst()); + } + + virtual ~incremental_solvert() + { + deallocate_solvers(); } virtual void set_message_handler(message_handlert &handler) { messaget::set_message_handler(handler); #if 0 - sat_check.set_message_handler(handler); - solver.set_message_handler(handler); + sat_check->set_message_handler(handler); + solver->set_message_handler(handler); #endif } @@ -52,30 +66,56 @@ class incremental_solvert : public messaget { solver_calls++; +#ifdef NON_INCREMENTAL + deallocate_solvers(); + allocate_solvers(arith_refinement); + unsigned context_no = 0; + for(contextst::const_iterator c_it = contexts.begin(); + c_it != contexts.end(); c_it++, context_no++) + { +#ifdef DISPLAY_FORMULA + std::cerr << "context: " << context_no << std::endl; +#endif + for(incremental_solvert::constraintst::const_iterator it = c_it->begin(); + it != c_it->end(); it++) + { +#ifdef DISPLAY_FORMULA + std::cerr << "actual add_to_solver: " << from_expr(ns,"",*it) << std::endl; +#endif + *solver << *it; + } + } +#else #ifdef DEBUG_FORMULA bvt whole_formula = formula; whole_formula.insert(whole_formula.end(),activation_literals.begin(), activation_literals.end()); - solver.set_assumptions(whole_formula); + solver->set_assumptions(whole_formula); +#endif #endif - return solver(); + return (*solver)(); } + exprt get(const exprt& expr) { return solver->get(expr); } + tvt l_get(literalt l) { return solver->l_get(l); } + literalt convert(const exprt& expr) { return solver->convert(expr); } + unsigned get_number_of_solver_calls() { return solver_calls; } unsigned next_domain_number() { return domain_number++; } - static incremental_solvert *allocate(const namespacet &_ns) + static incremental_solvert *allocate(const namespacet &_ns, + bool array_refinement) { - return new incremental_solvert(_ns); + return new incremental_solvert(_ns,array_refinement); } - satcheck_minisat_no_simplifiert sat_check; - bv_pointerst solver; + propt* sat_check; + prop_convt* solver; const namespacet &ns; - literalt new_context(); + void new_context(); void pop_context(); void make_context_permanent(); @@ -85,12 +125,39 @@ class incremental_solvert : public messaget //context assumption literals bvt activation_literals; - protected: + + //non-incremental solving + contextst contexts; + + protected: unsigned activation_literal_counter; unsigned domain_number; //ids for each domain instance to make symbols unique + bool arith_refinement; //statistics unsigned solver_calls; + + void allocate_solvers(bool arith_refinement) + { + sat_check = new satcheckt(); +#if 0 + sat_check = new satcheck_minisat_no_simplifiert(); +#endif +#ifdef NON_INCREMENTAL + solver = new bv_pointerst(ns,*sat_check); +#else + solver = new bv_refinementt(ns,*sat_check); + solver->set_all_frozen(); + ((bv_refinementt *)solver)->do_array_refinement = false; + ((bv_refinementt *)solver)->do_arithmetic_refinement = arith_refinement; +#endif + } + + void deallocate_solvers() + { + if(solver!=NULL) delete solver; + if(sat_check!=NULL) delete sat_check; + } }; static inline incremental_solvert & operator << ( @@ -99,23 +166,28 @@ static inline incremental_solvert & operator << ( { #ifdef DISPLAY_FORMULA if(!dest.activation_literals.empty()) - std::cout << "add_to_solver(" << !dest.activation_literals.back() << "): " + std::cerr << "add_to_solver(" << !dest.activation_literals.back() << "): " << from_expr(dest.ns,"",src) << std::endl; else - std::cout << "add_to_solver: " << from_expr(dest.ns,"",src) << std::endl; + std::cerr << "add_to_solver: " << from_expr(dest.ns,"",src) << std::endl; #endif + +#ifdef NON_INCREMENTAL + dest.contexts.back().push_back(src); +#else #ifndef DEBUG_FORMULA if(!dest.activation_literals.empty()) - dest.solver << or_exprt(src, + *dest.solver << or_exprt(src, literal_exprt(!dest.activation_literals.back())); else - dest.solver << src; + *dest.solver << src; #else if(!dest.activation_literals.empty()) dest.debug_add_to_formula( or_exprt(src,literal_exprt(!dest.activation_literals.back()))); else dest.debug_add_to_formula(src); +#endif #endif return dest; } @@ -124,15 +196,16 @@ static inline incremental_solvert& operator << ( incremental_solvert &dest, const incremental_solvert::constraintst &src) { +#ifdef NON_INCREMENTAL + dest.contexts.back().insert(dest.contexts.back().begin() + ,src.begin(),src.end()); +#else for(incremental_solvert::constraintst::const_iterator it = src.begin(); it != src.end(); it++) { -#ifndef DEBUG_FORMULA - dest.solver << *it; -#else - dest.debug_add_to_formula(*it); -#endif + dest << *it; } +#endif return dest; } diff --git a/src/domains/ssa_analyzer.cpp b/src/domains/ssa_analyzer.cpp index e74069825..87c485072 100644 --- a/src/domains/ssa_analyzer.cpp +++ b/src/domains/ssa_analyzer.cpp @@ -15,18 +15,12 @@ Author: Daniel Kroening, kroening@kroening.com #include "strategy_solver_enumeration.h" //#include "solver_enumeration.h" #include "strategy_solver_binsearch.h" -//#include "strategy_solver_binsearch2.h" -//#include "strategy_solver_binsearch3.h" +#include "strategy_solver_binsearch2.h" +#include "strategy_solver_binsearch3.h" #include "strategy_solver_equality.h" -#include "linrank_domain.h" -#include "lexlinrank_domain.h" -#include "ranking_solver_enumeration.h" -#include "lexlinrank_solver_enumeration.h" -#include "template_generator_ranking.h" - +#include "strategy_solver_predabs.h" #include "ssa_analyzer.h" - #include #include @@ -35,6 +29,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#define BINSEARCH_SOLVER strategy_solver_binsearcht(*static_cast(domain), solver, assertions_check, SSA.ns) +//#define BINSEARCH_SOLVER strategy_solver_binsearch2t(*static_cast(domain), solver, assertions_check, SSA.ns) +//#define BINSEARCH_SOLVER strategy_solver_binsearch3t(*static_cast(domain), solver, assertions_check, SSA, SSA.ns) + #ifdef DEBUG #include #endif @@ -45,19 +43,20 @@ Function: ssa_analyzert::operator() Inputs: - Outputs: - + Outputs: true if the computation was not aborted due to + assertion_checks that did not pass Purpose: \*******************************************************************/ -void ssa_analyzert::operator()(incremental_solvert &solver, +bool ssa_analyzert::operator()(incremental_solvert &solver, local_SSAt &SSA, const exprt &precondition, - template_generator_baset &template_generator) + template_generator_baset &template_generator, + bool check_assertions) { if(SSA.goto_function.body.instructions.empty()) - return; + return true; solver << SSA; SSA.mark_nodes(); @@ -70,49 +69,59 @@ void ssa_analyzert::operator()(incremental_solvert &solver, domain = template_generator.domain(); - // get strategy solver from options - strategy_solver_baset *strategy_solver; - if(template_generator.options.get_bool_option("compute-ranking-functions")) - { - if(template_generator.options.get_bool_option( - "monolithic-ranking-function")) + // get assertions if check_assertions is requested + literalt assertions_check = const_literal(false); + bvt assertion_literals; + if(check_assertions) + { + exprt::operandst ll; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - strategy_solver = new ranking_solver_enumerationt( - *static_cast(domain), solver, SSA.ns, - template_generator.options.get_unsigned_int_option( - "max-inner-ranking-iterations")); - result = new linrank_domaint::templ_valuet(); + assert(n_it->assertions.size()<=1); + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + { + literalt l = solver.solver->convert(*a_it); + assertion_literals.push_back(!l); + ll.push_back(literal_exprt(!l)); + nonpassed_assertions.push_back(n_it); + } } - else - { - strategy_solver = new lexlinrank_solver_enumerationt( - *static_cast(domain), solver, SSA.ns, - template_generator.options.get_unsigned_int_option( - "lexicographic-ranking-function"), - template_generator.options.get_unsigned_int_option( - "max-inner-ranking-iterations")); - result = new lexlinrank_domaint::templ_valuet(); - } - } - else if(template_generator.options.get_bool_option("equalities")) + assertions_check = solver.solver->convert(disjunction(ll)); + } + + // get strategy solver from options + strategy_solver_baset *strategy_solver; + if(template_generator.options.get_bool_option("equalities")) { strategy_solver = new strategy_solver_equalityt( - *static_cast(domain), solver, SSA.ns); + *static_cast(domain), solver, + assertions_check, SSA.ns); result = new equality_domaint::equ_valuet(); } else { - result = new tpolyhedra_domaint::templ_valuet(); if(template_generator.options.get_bool_option("enum-solver")) { + result = new tpolyhedra_domaint::templ_valuet(); strategy_solver = new strategy_solver_enumerationt( - *static_cast(domain), solver, SSA.ns); + *static_cast(domain), solver, + assertions_check, SSA.ns); + } + else if(template_generator.options.get_bool_option("predabs-solver")) + { + result = new predabs_domaint::templ_valuet(); + strategy_solver = new strategy_solver_predabst( + *static_cast(domain), solver, + assertions_check, SSA.ns); } else if(template_generator.options.get_bool_option("binsearch-solver")) { - strategy_solver = - new BINSEARCH_SOLVER( - *static_cast(domain), solver, SSA.ns); + result = new tpolyhedra_domaint::templ_valuet(); + strategy_solver = new BINSEARCH_SOLVER; } else assert(false); } @@ -121,48 +130,63 @@ void ssa_analyzert::operator()(incremental_solvert &solver, unsigned iteration_number=0; - // initialize inv + // initialize domain domain->initialize(*result); - bool change; + strategy_solver_baset::progresst status; do { iteration_number++; - #ifdef DEBUG +#ifdef DEBUG std::cout << "\n" << "******** Forward least fixed-point iteration #" << iteration_number << "\n"; - #endif +#endif - change = strategy_solver->iterate(*result); + status = strategy_solver->iterate(*result); - if(change) +#ifdef DEBUG + if(status == strategy_solver_baset::CHANGED) { - - #ifdef DEBUG std::cout << "Value after " << iteration_number << " iteration(s):\n"; domain->output_value(std::cout,*result,SSA.ns); - #endif } +#endif } - while(change); + while(status == strategy_solver_baset::CHANGED); - #ifdef DEBUG +#ifdef DEBUG std::cout << "Fixed-point after " << iteration_number << " iteration(s)\n"; domain->output_value(std::cout,*result,SSA.ns); - #endif +#endif + + //get status of assertions + if(!assertions_check.is_false() && + status == strategy_solver_baset::FAILED) + { + nonpassed_assertionst::iterator it = nonpassed_assertions.begin(); + for(unsigned i=0;il_get(assertion_literals[i]).is_true()) + nonpassed_assertions.erase(it++); + else ++it; + } + } + else nonpassed_assertions.clear(); solver.pop_context(); //statistics - solver_instances += strategy_solver->get_number_of_solver_instances(); solver_calls += strategy_solver->get_number_of_solver_calls(); + solver_instances += strategy_solver->get_number_of_solver_instances(); delete strategy_solver; + + return (status == strategy_solver_baset::CONVERGED); } /*******************************************************************\ diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index 665e84f83..3e45aedc7 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -15,8 +15,6 @@ Author: Daniel Kroening, kroening@kroening.com #include "strategy_solver_base.h" #include "template_generator_base.h" -#define BINSEARCH_SOLVER strategy_solver_binsearcht - class ssa_analyzert : public messaget { public: @@ -25,7 +23,6 @@ class ssa_analyzert : public messaget explicit ssa_analyzert() : - domain(NULL), result(NULL), solver_instances(0), solver_calls(0) @@ -37,19 +34,30 @@ class ssa_analyzert : public messaget if(result!=NULL) delete result; } - void operator()(incremental_solvert &solver, + // returns true if the computation was not aborted due to + // assertion_checks that did not pass + bool operator()(incremental_solvert &solver, local_SSAt &SSA, const exprt &precondition, - template_generator_baset &template_generator); + template_generator_baset &template_generator, + bool check_assertions=false); + //retrieve the result if operator() returned true void get_result(exprt &result, const domaint::var_sett &vars); + //retrieve the non-passed assertions if operator() returned false + typedef std::list + nonpassed_assertionst; + nonpassed_assertionst get_nonpassed_assertions() + { return nonpassed_assertions; } + unsigned get_number_of_solver_instances() { return solver_instances; } unsigned get_number_of_solver_calls() { return solver_calls; } protected: domaint *domain; //template generator is responsable for the domain object domaint::valuet *result; + nonpassed_assertionst nonpassed_assertions; //statistics unsigned solver_instances; diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index aacbbd7a3..7a071d569 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -10,9 +10,6 @@ #include "util.h" #include "../domains/incremental_solver.h" - -//#define DEBUG_FORMULA - class strategy_solver_baset : public messaget { @@ -20,23 +17,27 @@ class strategy_solver_baset : public messaget typedef std::list constraintst; typedef std::vector var_listt; typedef domaint::valuet invariantt; + typedef enum {CHANGED, CONVERGED, FAILED} progresst; explicit strategy_solver_baset( incremental_solvert &_solver, + literalt _assertion_check, const namespacet &_ns) : solver(_solver), + assertion_check(_assertion_check), ns(_ns), solver_instances(0), solver_calls(0) {} - virtual bool iterate(invariantt &inv) { assert(false); } + virtual progresst iterate(invariantt &inv) { assert(false); } unsigned get_number_of_solver_calls() { return solver_calls; } unsigned get_number_of_solver_instances() { return solver_instances; } protected: incremental_solvert &solver; + literalt assertion_check; const namespacet &ns; @@ -44,7 +45,7 @@ class strategy_solver_baset : public messaget bvt strategy_cond_literals; exprt::operandst strategy_value_exprs; - //statistics + //statistics for additional solvers unsigned solver_instances; unsigned solver_calls; }; diff --git a/src/domains/strategy_solver_binsearch.cpp b/src/domains/strategy_solver_binsearch.cpp index 77b0bda7a..f715d43b5 100644 --- a/src/domains/strategy_solver_binsearch.cpp +++ b/src/domains/strategy_solver_binsearch.cpp @@ -3,14 +3,13 @@ #include "strategy_solver_binsearch.h" #include "util.h" -// #define DEBUG_FORMULA - -bool strategy_solver_binsearcht::iterate(invariantt &_inv) +strategy_solver_baset::progresst +strategy_solver_binsearcht::iterate(invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv = static_cast(_inv); - bool improved = false; + progresst progress = CONVERGED; solver.new_context(); //for improvement check @@ -37,13 +36,16 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) #if 0 debug() << (i>0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.solver.convert(strategy_cond_exprs[i]); + strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); //solver.set_frozen(strategy_cond_literals[i]); strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); } +#if 0 debug() << eom; +#endif - solver << disjunction(strategy_cond_exprs); + solver << or_exprt(disjunction(strategy_cond_exprs), + literal_exprt(assertion_check)); #if 0 debug() << "solve(): "; @@ -51,10 +53,6 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) if(solver() == decision_proceduret::D_SATISFIABLE) //improvement check { -#if 0 - _inv.basic_value = domaint::valuet::OTHER; //formula is at least satisfiable -#endif - #if 0 debug() << "SAT" << eom; #endif @@ -63,22 +61,23 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) for(unsigned i=0; i improve_rows; improve_rows.insert(row); @@ -97,8 +103,7 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) tpolyhedra_domaint::row_valuet upper = tpolyhedra_domain.get_max_row_value(row); tpolyhedra_domaint::row_valuet lower = - // tpolyhedra_domain.get_min_row_value(row); - simplify_const(solver.solver.get(strategy_value_exprs[row])); + simplify_const(solver.get(strategy_value_exprs[row])); solver.pop_context(); //improvement check @@ -126,7 +131,7 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; // row_symb_value >= middle - exprt c = tpolyhedra_domain.get_row_symb_value_constraint(row,middle); + exprt c = tpolyhedra_domain.get_row_symb_value_constraint(row,middle,true); #if 0 debug() << "upper: " << from_expr(ns,"",upper) << eom; @@ -153,7 +158,7 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) { debug() << from_expr(ns, "", tpolyhedra_domain.get_row_symb_value(i)) << " " << - from_expr(ns, "", solver.solver.get(tpolyhedra_domain.get_row_symb_value(i))) + from_expr(ns, "", solver.get(tpolyhedra_domain.get_row_symb_value(i))) << eom; } #endif @@ -166,13 +171,14 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) { debug() << "replace_map (1st): " << from_expr(ns, "", it->first) << " " << - from_expr(ns, "", solver.solver.get(it->first)) << eom; + from_expr(ns, "", solver.get(it->first)) << eom; debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " << from_expr(ns, "", solver.get(it->second)) << eom; } #endif - lower = middle; + lower = simplify_const( + solver.get(tpolyhedra_domain.get_row_symb_value(row))); } else { @@ -183,7 +189,7 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) #if 0 for(unsigned i=0; iis_in_conflict(solver.formula[i])) debug() << "is_in_conflict: " << solver.formula[i] << eom; else debug() << "not_in_conflict: " << solver.formula[i] << eom; @@ -191,20 +197,17 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) #endif if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; - upper = middle; } solver.pop_context(); // binary search iteration } -#if 1 debug() << "update value: " << from_expr(ns,"",lower) << eom; -#endif solver.pop_context(); //symbolic value system tpolyhedra_domain.set_row_value(row,lower,inv); - improved = true; + progress = CHANGED; } else { @@ -212,9 +215,28 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) debug() << "UNSAT" << eom; #endif - solver.pop_context(); //improvement check +#ifdef DEBUG_FORMULA + for(unsigned i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } +#endif + + if(tpolyhedra_domain.refine()) + { + debug() << "refining..." << eom; + progress = CHANGED; //refinement possible + } + else + { + tpolyhedra_domain.reset_refinements(); + solver.pop_context(); //improvement check + } } - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch.h b/src/domains/strategy_solver_binsearch.h index 89d4b1041..13521f708 100644 --- a/src/domains/strategy_solver_binsearch.h +++ b/src/domains/strategy_solver_binsearch.h @@ -10,11 +10,12 @@ class strategy_solver_binsearcht : public strategy_solver_baset explicit strategy_solver_binsearcht( tpolyhedra_domaint &_tpolyhedra_domain, incremental_solvert &_solver, + literalt _assertion_check, const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), + strategy_solver_baset(_solver, _assertion_check, _ns), tpolyhedra_domain(_tpolyhedra_domain) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; diff --git a/src/domains/strategy_solver_binsearch2.cpp b/src/domains/strategy_solver_binsearch2.cpp index 93583e756..e6d90c18f 100644 --- a/src/domains/strategy_solver_binsearch2.cpp +++ b/src/domains/strategy_solver_binsearch2.cpp @@ -7,18 +7,19 @@ #include "util.h" -// #define DEBUG_FORMULA - #define SUM_BOUND_VAR "sum_bound#" -bool strategy_solver_binsearch2t::iterate(invariantt &_inv) +//TODO: implement assertion check + +strategy_solver_baset::progresst +strategy_solver_binsearch2t::iterate(invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv = static_cast(_inv); - bool improved = false; + progresst progress = CONVERGED; - literalt activation_literal0 = new_context(); //for improvement check + solver.new_context(); //for improvement check exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); @@ -27,26 +28,29 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; #endif - solver << or_exprt(inv_expr, literal_exprt(activation_literal0)); + solver << inv_expr; exprt::operandst strategy_cond_exprs; tpolyhedra_domain.make_not_post_constraints(inv, strategy_cond_exprs, strategy_value_exprs); strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 - debug() << "post-inv: " << from_expr(ns,"",disjunction(strategy_cond_exprs)) << eom; + debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); +#endif strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); + //solver.set_frozen(strategy_cond_literals[i]); strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); } + debug() << eom; - solver << or_exprt(disjunction(strategy_cond_exprs), - literal_exprt(activation_literal0)); + solver << disjunction(strategy_cond_exprs); #if 0 debug() << "solve(): "; @@ -58,42 +62,42 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) std::set improve_rows; bool improved_from_neginf = false; - while(solve() == decision_proceduret::D_SATISFIABLE) //improvement check + while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check { #if 0 debug() << "SAT" << eom; #endif - improved = true; + progress = CHANGED; unsigned row=0; for(;row::iterator it = symb_values.begin(); exprt _lower = lower_values[it->first]; -#if 0 +#if 1 debug() << "update row " << it->first << ": " << from_expr(ns,"",lower_values[it->first]) << eom; #endif @@ -120,7 +124,7 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) _upper = plus_exprt(_upper,tpolyhedra_domain.get_max_row_value(it->first)); _lower = plus_exprt(_lower,lower_values[it->first]); -#if 0 +#if 1 debug() << "update row " << it->first << ": " << from_expr(ns,"",lower_values[it->first]) << eom; #endif @@ -128,11 +132,11 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) } //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) return improved; + if(improved_from_neginf) return progress; - literalt activation_literal1 = new_context(); //symbolic value system - solver << or_exprt(pre_inv_expr, literal_exprt(activation_literal1)); - solver << or_exprt(post_inv_expr, literal_exprt(activation_literal1)); + solver.new_context(); //symbolic value system + solver << pre_inv_expr; + solver << post_inv_expr; extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); @@ -143,14 +147,14 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) assert(sum.type()==lower.type()); symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << or_exprt(equal_exprt(sum_bound,sum), - literal_exprt(activation_literal1)); + solver << equal_exprt(sum_bound,sum); + #if 0 debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; #endif while(tpolyhedra_domain.less_than(lower,upper)) - { + { tpolyhedra_domaint::row_valuet middle = tpolyhedra_domain.between(lower,upper); if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; @@ -165,15 +169,15 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) debug() << "lower: " << from_expr(ns,"",lower) << eom; #endif - literalt activation_literal2 = new_context(); // binary search iteration + solver.new_context(); // binary search iteration #if 0 debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << or_exprt(c,literal_exprt(activation_literal2)); + solver << c; - if(solve() == decision_proceduret::D_SATISFIABLE) + if(solver() == decision_proceduret::D_SATISFIABLE) { #if 0 debug() << "SAT" << eom; @@ -184,11 +188,13 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) for(std::map::iterator it = symb_values.begin(); it != symb_values.end(); it++) { -#if 0 - debug() << "update row " << it->first << " " << from_expr(ns,"",it->second) << ": "; +#if 1 + debug() << "update row " << it->first << " " + << from_expr(ns,"",it->second) << ": "; #endif - constant_exprt lower_row = simplify_const(solver.get(it->second)); -#if 0 + constant_exprt lower_row = + simplify_const(solver.get(it->second)); +#if 1 debug() << from_expr(ns,"",lower_row) << eom; #endif tpolyhedra_domain.set_row_value(it->first,lower_row,inv); @@ -204,11 +210,11 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) upper = middle; } - pop_context(); // binary search iteration + solver.pop_context(); // binary search iteration } - pop_context(); //symbolic value system + solver.pop_context(); //symbolic value system - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch2.h b/src/domains/strategy_solver_binsearch2.h index 7af18383b..94d6d06e4 100644 --- a/src/domains/strategy_solver_binsearch2.h +++ b/src/domains/strategy_solver_binsearch2.h @@ -1,20 +1,22 @@ #ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH2_H #define CPROVER_STRATEGY_SOLVER_BINSEARCH2_H - + #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" class strategy_solver_binsearch2t : public strategy_solver_baset { public: - explicit strategy_solver_binsearch2t(const constraintst &program, + explicit strategy_solver_binsearch2t( tpolyhedra_domaint &_tpolyhedra_domain, - bv_pointerst &_solver, const namespacet &_ns) : - strategy_solver_baset(program, _solver, _ns), + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns) : + strategy_solver_baset( _solver, _assertion_check, _ns), tpolyhedra_domain(_tpolyhedra_domain), sum_bound_counter(0) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; @@ -22,4 +24,5 @@ class strategy_solver_binsearch2t : public strategy_solver_baset }; + #endif diff --git a/src/domains/strategy_solver_binsearch3.cpp b/src/domains/strategy_solver_binsearch3.cpp index 85a3cfabd..f010932ea 100644 --- a/src/domains/strategy_solver_binsearch3.cpp +++ b/src/domains/strategy_solver_binsearch3.cpp @@ -7,18 +7,19 @@ #include "util.h" -// #define DEBUG_FORMULA - #define SUM_BOUND_VAR "sum_bound#" -bool strategy_solver_binsearch3t::iterate(invariantt &_inv) +//TODO: implement assertion check + +strategy_solver_baset::progresst +strategy_solver_binsearch3t::iterate(invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv = static_cast(_inv); - bool improved = false; + progresst progress = CONVERGED; - literalt activation_literal0 = new_context(); //for improvement check + solver.new_context(); //for improvement check exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); @@ -27,40 +28,45 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; #endif - solver << or_exprt(inv_expr, literal_exprt(activation_literal0)); + solver << inv_expr; exprt::operandst strategy_cond_exprs; tpolyhedra_domain.make_not_post_constraints(inv, strategy_cond_exprs, strategy_value_exprs); strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 - debug() << "post-inv: " << from_expr(ns,"",disjunction(strategy_cond_exprs)) << eom; + debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); +#endif strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); + //solver.set_frozen(strategy_cond_literals[i]); strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); } + debug() << eom; - solver << or_exprt(disjunction(strategy_cond_exprs), - literal_exprt(activation_literal0)); + solver << disjunction(strategy_cond_exprs); -#if 1 - debug() << "solve(): " << eom; +#if 0 + debug() << "solve(): "; #endif + std::set improve_rows; + std::map symb_values; + std::map lower_values; exprt::operandst blocking_constraint; - bool improved_from_neginf = false; - while(solve() == decision_proceduret::D_SATISFIABLE) //improvement check + while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check { #if 0 debug() << "SAT" << eom; #endif - improved = true; + progress = CHANGED; unsigned row=0; for(;row=1); std::map::iterator @@ -124,36 +124,74 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) _lower = plus_exprt(_lower,lower_values[it->first]); #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns,"",lower_values[it->first]) << eom; #endif tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); } //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) ; //return improved; + if(improved_from_neginf) return progress; - literalt activation_literal1 = new_context(); //symbolic value system - solver << or_exprt(pre_inv_expr, literal_exprt(activation_literal1)); - solver << or_exprt(post_inv_expr, literal_exprt(activation_literal1)); + solver.new_context(); //symbolic value system + solver << pre_inv_expr; + solver << post_inv_expr; + +#if 1 + debug() << "symbolic value system: " << eom; + debug() << "pre-inv: " << from_expr(ns,"",pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns,"",post_inv_expr) << eom; +#endif + +/* + //add renamed SSA for rows 1..n-1 + SSA.unmark_nodes(); + for(unsigned i=1; i program; + program << SSA; + for(std::list::iterator it = program.begin(); + it != program.end(); it++) + { +// tpolyhedra_domain.rename_for_row(*it,i); +#if 1 + debug() << "ssa " << i << ": " << from_expr(ns,"",*it) << eom; +#endif + } + solver << SSA; + } + SSA.mark_nodes(); +*/ extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); tpolyhedra_domaint::row_valuet upper = simplify_const(_upper); - //from_integer(mp_integer(512),_upper.type()); tpolyhedra_domaint::row_valuet lower = simplify_const(_lower); assert(sum.type()==upper.type()); assert(sum.type()==lower.type()); symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << or_exprt(equal_exprt(sum_bound,sum), - literal_exprt(activation_literal1)); -#if 0 + solver << equal_exprt(sum_bound,sum); + +#if 1 debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; #endif while(tpolyhedra_domain.less_than(lower,upper)) - { + { tpolyhedra_domaint::row_valuet middle = tpolyhedra_domain.between(lower,upper); if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; @@ -162,21 +200,21 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) assert(sum_bound.type()==middle.type()); exprt c = binary_relation_exprt(sum_bound,ID_ge,middle); -#if 0 +#if 1 debug() << "upper: " << from_expr(ns,"",upper) << eom; debug() << "middle: " << from_expr(ns,"",middle) << eom; debug() << "lower: " << from_expr(ns,"",lower) << eom; #endif - literalt activation_literal2 = new_context(); // binary search iteration + solver.new_context(); // binary search iteration -#if 0 +#if 1 debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << or_exprt(c,literal_exprt(activation_literal2)); + solver << c; - if(solve() == decision_proceduret::D_SATISFIABLE) + if(solver() == decision_proceduret::D_SATISFIABLE) { #if 0 debug() << "SAT" << eom; @@ -188,9 +226,11 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) it = symb_values.begin(); it != symb_values.end(); it++) { #if 1 - debug() << "update row " << it->first << " " << from_expr(ns,"",it->second) << ": "; + debug() << "update row " << it->first << " " + << from_expr(ns,"",it->second) << ": "; #endif - constant_exprt lower_row = simplify_const(solver.get(it->second)); + constant_exprt lower_row = + simplify_const(solver.get(it->second)); #if 1 debug() << from_expr(ns,"",lower_row) << eom; #endif @@ -207,11 +247,11 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) upper = middle; } - pop_context(); // binary search iteration + solver.pop_context(); // binary search iteration } - pop_context(); //symbolic value system + solver.pop_context(); //symbolic value system - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch3.h b/src/domains/strategy_solver_binsearch3.h index 260474539..cc233c0ac 100644 --- a/src/domains/strategy_solver_binsearch3.h +++ b/src/domains/strategy_solver_binsearch3.h @@ -1,28 +1,33 @@ #ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH3_H #define CPROVER_STRATEGY_SOLVER_BINSEARCH3_H +#include "../ssa/local_ssa.h" #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" class strategy_solver_binsearch3t : public strategy_solver_baset { public: - explicit strategy_solver_binsearch3t(const constraintst &program, + explicit strategy_solver_binsearch3t( tpolyhedra_domaint &_tpolyhedra_domain, - bv_pointerst &_solver, const namespacet &_ns) : - strategy_solver_baset(program, _solver, _ns), + incremental_solvert &_solver, + literalt _assertion_check, + local_SSAt& _SSA, + const namespacet &_ns) : + strategy_solver_baset( _solver, _assertion_check, _ns), + SSA(_SSA), tpolyhedra_domain(_tpolyhedra_domain), sum_bound_counter(0) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: - const constraintst &program; + local_SSAt &SSA; tpolyhedra_domaint &tpolyhedra_domain; unsigned sum_bound_counter; - std::set improve_rows; - std::map symb_values; - std::map lower_values; +// std::set improve_rows; +// std::map symb_values; +// std::map lower_values; }; diff --git a/src/domains/strategy_solver_enumeration.cpp b/src/domains/strategy_solver_enumeration.cpp index e9068d41a..710724651 100644 --- a/src/domains/strategy_solver_enumeration.cpp +++ b/src/domains/strategy_solver_enumeration.cpp @@ -6,17 +6,20 @@ #include "strategy_solver_enumeration.h" #include "util.h" -bool strategy_solver_enumerationt::iterate(invariantt &_inv) +strategy_solver_baset::progresst +strategy_solver_enumerationt::iterate(invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv = static_cast(_inv); - bool improved = false; + progresst progress = CONVERGED; solver.new_context(); exprt preinv_expr = tpolyhedra_domain.to_pre_constraints(inv); +#ifdef DEBUG_OUTPUT debug() << "pre-inv: " << from_expr(ns,"",preinv_expr) << eom; +#endif solver << preinv_expr; @@ -28,44 +31,56 @@ bool strategy_solver_enumerationt::iterate(invariantt &_inv) exprt postinv_expr = disjunction(strategy_cond_exprs); +#ifdef DEBUG_OUTPUT debug() << "post-inv: "; +#endif for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]) ; +#endif - strategy_cond_literals[i] = solver.solver.convert(strategy_cond_exprs[i]); + strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); //solver.set_frozen(strategy_cond_literals[i]); strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); } +#ifdef DEBUG_OUTPUT debug() << eom; +#endif - solver << disjunction(strategy_cond_exprs); + solver << or_exprt(disjunction(strategy_cond_exprs), + literal_exprt(assertion_check)); +#ifdef DEBUG_OUTPUT debug() << "solve(): "; +#endif if(solver() == decision_proceduret::D_SATISFIABLE) { +#ifdef DEBUG_OUTPUT debug() << "SAT" << eom; - -#if 0 - _inv.basic_value = domaint::valuet::OTHER; //formula is at least satisfiable #endif - #if 0 +#ifdef DEBUG_OUTPUT + for(unsigned i=0; iis_in_conflict(solver.formula[i])) debug() << "is_in_conflict: " << solver.formula[i] << eom; else debug() << "not_in_conflict: " << solver.formula[i] << eom; @@ -131,5 +152,5 @@ bool strategy_solver_enumerationt::iterate(invariantt &_inv) } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/strategy_solver_enumeration.h b/src/domains/strategy_solver_enumeration.h index 4bf6ebcc6..206a6b027 100644 --- a/src/domains/strategy_solver_enumeration.h +++ b/src/domains/strategy_solver_enumeration.h @@ -10,11 +10,12 @@ class strategy_solver_enumerationt : public strategy_solver_baset explicit strategy_solver_enumerationt( tpolyhedra_domaint &_tpolyhedra_domain, incremental_solvert &_solver, + literalt _assertion_check, const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), + strategy_solver_baset(_solver, _assertion_check, _ns), tpolyhedra_domain(_tpolyhedra_domain) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; diff --git a/src/domains/strategy_solver_equality.cpp b/src/domains/strategy_solver_equality.cpp index 582f0351f..a9869e437 100644 --- a/src/domains/strategy_solver_equality.cpp +++ b/src/domains/strategy_solver_equality.cpp @@ -3,7 +3,10 @@ #include #include "strategy_solver_equality.h" -bool strategy_solver_equalityt::iterate(invariantt &_inv) +//Comment: assertion check is not possible because this is a gfp solver + +strategy_solver_baset::progresst +strategy_solver_equalityt::iterate(invariantt &_inv) { equality_domaint::equ_valuet &inv = static_cast(_inv); @@ -18,7 +21,7 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) solver << pre_expr; exprt post_expr = equality_domain.get_post_not_equ_constraint(*e_it); - literalt cond_literal = solver.solver.convert(post_expr); + literalt cond_literal = solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -74,7 +77,7 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) else //check disequalities { e_it = todo_disequs.begin(); - if(e_it==todo_disequs.end()) return false; //done + if(e_it==todo_disequs.end()) return CONVERGED; //done solver.new_context(); @@ -83,7 +86,7 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) solver << pre_expr; exprt post_expr = equality_domain.get_post_not_disequ_constraint(*e_it); - literalt cond_literal = solver.solver.convert(post_expr); + literalt cond_literal = solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -113,5 +116,5 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) todo_disequs.erase(e_it); } - return true; + return CHANGED; } diff --git a/src/domains/strategy_solver_equality.h b/src/domains/strategy_solver_equality.h index 115ef7037..fadd18b1d 100644 --- a/src/domains/strategy_solver_equality.h +++ b/src/domains/strategy_solver_equality.h @@ -10,14 +10,15 @@ class strategy_solver_equalityt : public strategy_solver_baset explicit strategy_solver_equalityt( equality_domaint &_equality_domain, incremental_solvert &_solver, + literalt _assertion_check, const namespacet &_ns) : - strategy_solver_baset(_solver, _ns), + strategy_solver_baset(_solver, _assertion_check, _ns), equality_domain(_equality_domain) { equality_domain.get_index_set(todo_equs); } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: equality_domaint &equality_domain; diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index 91a5623e7..dee1cf723 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -9,6 +9,7 @@ Author: Peter Schrammel #include "template_generator_base.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" +#include "predabs_domain.h" #include #include @@ -20,6 +21,103 @@ Author: Peter Schrammel #include #endif +/*******************************************************************\ + +Function: template_generator_baset::get_pre_post_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::get_pre_post_guards(const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, exprt &post_guard) +{ + exprt lhguard = SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); + exprt lsguard = SSA.name(SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); + pre_guard = and_exprt(lhguard,lsguard); + + exprt pguard = SSA.guard_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); + exprt pcond = SSA.cond_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); + post_guard = and_exprt(pguard,pcond); +} + +/*******************************************************************\ + +Function: template_generator_baset::get_pre_var + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::get_pre_var(const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var) +{ + pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); + + symbol_exprt post_var = SSA.read_rhs(*o_it, n_it->location); + ssa_local_unwinder.unwinder_rename(post_var,*n_it,false); + post_renaming_map[pre_var] = post_var; + + rename_aux_post(post_var); + aux_renaming_map[pre_var]=post_var; +} + +/*******************************************************************\ + +Function: template_generator_baset::get_init_expr + + Inputs: + + Outputs: + + Purpose: supposes that loop head PHIs are of the form + xphi = gls?xlb:x0 + +\*******************************************************************/ + +void template_generator_baset::get_init_expr(const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr) +{ + symbol_exprt phi_var = SSA.name(*o_it, local_SSAt::PHI, + n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var,*n_it->loophead,true); + for (local_SSAt::nodet::equalitiest::const_iterator e_it = + n_it->loophead->equalities.begin(); + e_it != n_it->loophead->equalities.end(); e_it++) + { + if (e_it->rhs().id() == ID_if && + to_symbol_expr(e_it->lhs()).get_identifier()==phi_var.get_identifier()) + { + const if_exprt &if_expr = to_if_expr(e_it->rhs()); + init_expr = if_expr.false_case(); + //should already be renamed for inner loops + break; + } + } + + symbol_exprt pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); + init_renaming_map[pre_var]=init_expr; +} /*******************************************************************\ @@ -44,18 +142,9 @@ void template_generator_baset::collect_variables_loop(const local_SSAt &SSA,bool { if(n_it->loophead != SSA.nodes.end()) //we've found a loop { - exprt lhguard = SSA.guard_symbol(n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); - exprt lsguard = SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); - exprt pre_guard = and_exprt(lhguard,lsguard); - - exprt pguard = SSA.guard_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); - exprt pcond = SSA.cond_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); - exprt post_guard = and_exprt(pguard,pcond); - + exprt pre_guard, post_guard; + get_pre_post_guards(SSA,n_it,pre_guard, post_guard); + const ssa_domaint::phi_nodest &phi_nodes = SSA.ssa_analysis[n_it->loophead->location].phi_nodes; @@ -71,59 +160,19 @@ void template_generator_baset::collect_variables_loop(const local_SSAt &SSA,bool if(p_it==phi_nodes.end()) continue; // object not modified in this loop - symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - ssa_local_unwinder.unwinder_rename(in,*n_it,true); - symbol_exprt out=SSA.read_rhs(*o_it, n_it->location); - ssa_local_unwinder.unwinder_rename(out,*n_it,false); + symbol_exprt pre_var; + get_pre_var(SSA,o_it,n_it,pre_var); + exprt init_expr; + get_init_expr(SSA,o_it,n_it,init_expr); + add_var(pre_var,pre_guard,post_guard,domaint::LOOP,var_specs); - add_var(in,pre_guard,post_guard,domaint::LOOP,var_specs); - - pre_state_vars.push_back(in); - post_state_vars.push_back(out); - #ifdef DEBUG std::cout << "Adding " << from_expr(ns, "", in) << " " << from_expr(ns, "", out) << std::endl; #endif } - - /* - // local nondet variables - const ssa_domaint &ssa_domain=SSA.ssa_analysis[i_it->get_target()]; - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - ssa_domaint::def_mapt::const_iterator - d_it = ssa_domain.def_map.find(o_it->get_identifier()); - if(d_it!=ssa_domain.def_map.end()) - { - #if 1 - std::cout << "ssa_object " << o_it->get_identifier() << - ": " << d_it->second.def.is_input() << std::endl; - #endif - symbol_exprt in=SSA.name_input(*o_it); - exprt guard = SSA.guard_symbol(i_it->get_target()); - add_var(in,guard,guard,domaint::IN,var_specs); - - #if 1 - std::cout << "Adding " << from_expr(ns, "", in) << std::endl; - #endif - } - } - */ } } - - // building map for renaming from pre into post-state - assert(pre_state_vars.size()==post_state_vars.size()); - var_listt::const_iterator it1=pre_state_vars.begin(); - var_listt::const_iterator it2=post_state_vars.begin(); - for(; it1!=pre_state_vars.end(); ++it1, ++it2) - { - renaming_map[*it1]=*it2; - } } /*******************************************************************\ @@ -219,16 +268,29 @@ Function: template_generator_baset::add_vars void template_generator_baset::add_var(const domaint::vart &var, const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, + domaint::guardt post_guard, const domaint::kindt &kind, domaint::var_specst &var_specs) { + exprt aux_expr = true_exprt(); + if(std_invariants && pre_guard.id()==ID_and) + { + exprt init_guard = and_exprt(pre_guard.op0(),not_exprt(pre_guard.op1())); + exprt post_var = post_renaming_map[var]; + exprt aux_var = aux_renaming_map[var]; + aux_expr = and_exprt( + implies_exprt(and_exprt(post_guard, not_exprt(init_guard)), + equal_exprt(aux_var,post_var)), + implies_exprt(init_guard,equal_exprt(aux_var,init_renaming_map[var]))); + post_guard = or_exprt(post_guard,init_guard); + } if(var.type().id()!=ID_array) { var_specs.push_back(domaint::var_spect()); domaint::var_spect &var_spec = var_specs.back(); var_spec.pre_guard = pre_guard; var_spec.post_guard = post_guard; + var_spec.aux_expr = aux_expr; var_spec.kind = kind; var_spec.var = var; } @@ -246,6 +308,7 @@ void template_generator_baset::add_var(const domaint::vart &var, constant_exprt index = from_integer(i,array_type.size().type()); var_spec.pre_guard = pre_guard; var_spec.post_guard = post_guard; + var_spec.aux_expr = aux_expr; var_spec.kind = kind; var_spec.var = index_exprt(var,index); } @@ -300,6 +363,7 @@ Function: template_generator_baset::handle_special_functions void template_generator_baset::handle_special_functions(const local_SSAt &SSA) { const irep_idt &function_id = SSA.goto_function.body.instructions.front().function; + if(id2string(function_id) == "__CPROVER_initialize") { options.set_option("intervals",true); @@ -309,6 +373,211 @@ void template_generator_baset::handle_special_functions(const local_SSAt &SSA) /*******************************************************************\ +Function: template_generator_baset::build_custom_expr + + Inputs: + + Outputs: + + Purpose: rename custom template to correct SSA identifiers + +\*******************************************************************/ + +bool template_generator_baset::replace_post(replace_mapt replace_map, exprt &expr) +{ + bool replaced = false; + if(expr.id()==ID_function_application) + { + const function_application_exprt &f = to_function_application_expr(expr); + if(f.function().get(ID_identifier) == TEMPLATE_NEWVAR) + { + assert(f.arguments().size()==1); + if(f.arguments()[0].id()==ID_typecast) + expr = replace_map[f.arguments()[0].op0()]; + else + expr = replace_map[f.arguments()[0]]; + return true; + } + } + for(unsigned i=0; iloophead->location].phi_nodes; + + for(local_SSAt::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(o_it->get_identifier()); + + if(p_it!=phi_nodes.end()) //modified in loop + { + //rename to pre + replace_map[o_it->get_expr()] = + SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + + //rename to post + replace_post_map[o_it->get_expr()] = + SSA.read_rhs(*o_it, n_it->location); + //TODO: unwinding + } + else //not modified in loop + { + //rename to id valid at loop head + replace_map[o_it->get_expr()] = + SSA.read_rhs(*o_it,n_it->loophead->location); + //TODO: unwinding + } + } + + bool contains_newvar = replace_post(replace_post_map,expr); + replace_expr(replace_map,expr); + return contains_newvar; +} + +/*******************************************************************\ + +Function: template_generator_baset::instantiate_custom_templates + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool template_generator_baset::instantiate_custom_templates( + const local_SSAt &SSA) +{ + // used for renaming map + var_listt pre_state_vars, post_state_vars; + + bool found_poly = false, found_predabs = false; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead != SSA.nodes.end()) //we've found a loop + { + exprt pre_guard, post_guard, aux_expr; + get_pre_post_guards(SSA,n_it,pre_guard, post_guard); + aux_expr = true_exprt(); //TODO: change to "standard" invariant semantics + bool add_post_vars = false; + + //search for templates in the loop + for(local_SSAt::nodest::const_iterator nn_it=n_it->loophead; + nn_it!=n_it; nn_it++) + { + if(nn_it->templates.empty()) continue; + if(nn_it->templates.size()>1000) continue; //TODO: there is an unwinder-related bug + for(local_SSAt::nodet::templatest::const_iterator + t_it=nn_it->templates.begin(); + t_it!=nn_it->templates.end(); t_it++) + { + debug() << "Template expression: " + << from_expr(SSA.ns,"",*t_it) << eom; + + // check whether it is a template polyhedra or a pred abs + std::set symbols; + find_symbols(*t_it, symbols); + + bool predabs = true; + for(std::set::iterator it = symbols.begin(); + it != symbols.end(); it++) + { + std::size_t found_param = + id2string(it->get_identifier()).find(TEMPLATE_PARAM_PREFIX); + if (found_param != std::string::npos) + { + predabs = false; + break; + } + } + + //template polyhedra + if(!predabs && t_it->id()==ID_le) + { + debug() << "Custom template polyhedron found" << eom; + if(!found_poly) //create domain + { + domain_ptr = new tpolyhedra_domaint(domain_number, + post_renaming_map); //TODO: aux_renaming_map + found_poly = true; + } + exprt expr = t_it->op0(); + bool contains_new_var = build_custom_expr(SSA,n_it,expr); + if(contains_new_var) add_post_vars = true; + static_cast(domain_ptr)->add_template_row( + expr,pre_guard, + contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + } + // pred abs domain + else if (predabs) + { + options.set_option("predabs-solver",true); + + debug() << "Custom predicate template found" << eom; + if(!found_predabs) //create domain + { + domain_ptr = new predabs_domaint(domain_number, + post_renaming_map); //TODO: aux_renaming_map + found_predabs = true; + } + exprt expr = *t_it; + bool contains_new_var = build_custom_expr(SSA,n_it,expr); + if(contains_new_var) add_post_vars = true; + static_cast(domain_ptr)->add_template_row( + expr,pre_guard, + contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + + } + else // neither pred abs, nor polyhedra + warning() << "ignoring unsupported template " + << from_expr(SSA.ns,"",*t_it) << eom; + } + if(add_post_vars) //for result retrieval via all_vars() only + { + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(domaint::var_specst::const_iterator v = new_var_specs.begin(); + v!=new_var_specs.end(); v++) + { + var_specs.push_back(*v); + if(v->kind==domaint::LOOP) + { + var_specs.push_back(*v); + var_specs.back().kind = domaint::OUTL; + replace_expr(aux_renaming_map,var_specs.back().var); + } + } + } + } + } + } + + return (found_poly || found_predabs); +} + +/*******************************************************************\ + Function: template_generator_baset::instantiate_standard_domains Inputs: @@ -321,6 +590,9 @@ Function: template_generator_baset::instantiate_standard_domains void template_generator_baset::instantiate_standard_domains(const local_SSAt &SSA) { + replace_mapt &renaming_map = + std_invariants ? aux_renaming_map : post_renaming_map; + //get domain from command line options if(options.get_bool_option("equalities")) { @@ -341,7 +613,9 @@ void template_generator_baset::instantiate_standard_domains(const local_SSAt &SS domain_ptr = new tpolyhedra_domaint(domain_number, renaming_map); filter_template_domain(); - static_cast(domain_ptr)->add_zone_template( + static_cast(domain_ptr)->add_interval_template( + var_specs, SSA.ns); + static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); } else if(options.get_bool_option("octagons")) @@ -349,7 +623,21 @@ void template_generator_baset::instantiate_standard_domains(const local_SSAt &SS domain_ptr = new tpolyhedra_domaint(domain_number, renaming_map); filter_template_domain(); - static_cast(domain_ptr)->add_octagon_template( + static_cast(domain_ptr)->add_interval_template( + var_specs, SSA.ns); + static_cast(domain_ptr)->add_difference_template( + var_specs, SSA.ns); + static_cast(domain_ptr)->add_sum_template( + var_specs, SSA.ns); + } + else if(options.get_bool_option("qzones")) + { + domain_ptr = new tpolyhedra_domaint(domain_number, + renaming_map); + filter_template_domain(); + static_cast(domain_ptr)->add_difference_template( + var_specs, SSA.ns); + static_cast(domain_ptr)->add_quadratic_template( var_specs, SSA.ns); } } diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index de115b3f4..dc66379a5 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -16,18 +16,21 @@ Author: Peter Schrammel #include "../ssa/ssa_unwinder.h" #include "strategy_solver_base.h" +#define SHOW_TEMPLATE_VARIABLES +#define SHOW_TEMPLATE + class template_generator_baset : public messaget { public: typedef strategy_solver_baset::var_listt var_listt; - explicit template_generator_baset(optionst &_options, - ssa_dbt &_ssa_db, + explicit template_generator_baset(optionst &_options, ssa_dbt &_ssa_db, ssa_local_unwindert &_ssa_local_unwinder) : options(_options), ssa_db(_ssa_db), ssa_local_unwinder(_ssa_local_unwinder) { + std_invariants = options.get_bool_option("std-invariants"); } virtual ~template_generator_baset() @@ -43,11 +46,13 @@ class template_generator_baset : public messaget } virtual domaint::var_sett all_vars(); - + bool empty() { assert(domain_ptr!=NULL); return domain_ptr->is_spec_empty(); } domaint *domain() { assert(domain_ptr!=NULL); return domain_ptr; } domaint::var_specst var_specs; - replace_mapt renaming_map; + replace_mapt post_renaming_map; + replace_mapt init_renaming_map; + replace_mapt aux_renaming_map; unsigned domain_number; //serves as id for variables names optionst options; // copy: we may override options @@ -56,6 +61,7 @@ class template_generator_baset : public messaget const ssa_dbt &ssa_db; const ssa_local_unwindert &ssa_local_unwinder; domaint* domain_ptr; + bool std_invariants; //include value at loop entry virtual void collect_variables_loop(const local_SSAt &SSA, bool forward); @@ -65,7 +71,7 @@ class template_generator_baset : public messaget void add_var(const domaint::vart &var_to_add, const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, + domaint::guardt post_guard, const domaint::kindt &kind, domaint::var_specst &var_specs); void add_vars(const var_listt &vars_to_add, @@ -84,9 +90,32 @@ class template_generator_baset : public messaget const domaint::kindt &kind, domaint::var_specst &var_specs); + void get_pre_post_guards(const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, exprt &post_guard); + void get_pre_var(const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var); + + void get_init_expr(const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr); + + bool replace_post(replace_mapt replace_map, exprt &expr); + bool build_custom_expr(const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &expr); + virtual void handle_special_functions(const local_SSAt &SSA); void instantiate_standard_domains(const local_SSAt &SSA); + bool instantiate_custom_templates(const local_SSAt &SSA); + void rename_aux_post(symbol_exprt &expr) + { + expr.set_identifier(id2string(expr.get_identifier())+"'"); + } }; diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index da50ad100..75619d0f5 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -39,16 +39,20 @@ void template_generator_summaryt::operator()(unsigned _domain_number, collect_variables_loop(SSA,forward); - // do not compute summary for main - if(SSA.goto_function.body.instructions.front().function != ID_main || + // do not compute summary for entry-point + if(SSA.goto_function.body.instructions.front().function != ID__start || options.get_bool_option("preconditions")) collect_variables_inout(SSA,forward); - instantiate_standard_domains(SSA); + // either use standard templates or user-supplied ones + if(!instantiate_custom_templates(SSA)) + instantiate_standard_domains(SSA); -#if 1 +#ifdef SHOW_TEMPLATE_VARIABLES debug() << "Template variables: " << eom; domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; +#endif +#ifdef SHOW_TEMPLATE debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; #endif @@ -149,7 +153,8 @@ domaint::var_sett template_generator_summaryt::loop_vars() for(domaint::var_specst::const_iterator v = var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::LOOP) vars.insert(v->var); + if(v->kind==domaint::LOOP || v->kind==domaint::IN) + vars.insert(v->var); } return vars; } diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index f133d89ab..96bba8b1e 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -26,10 +26,6 @@ Function: tpolyhedra_domaint::initialize void tpolyhedra_domaint::initialize(valuet &value) { -#if 0 - if(templ.size()==0) return domaint::initialize(value); -#endif - templ_valuet &v = static_cast(value); v.resize(templ.size()); for(unsigned row = 0; row(value1); const templ_valuet &v2 = static_cast(value2); assert(v1.size()==templ.size()); assert(v1.size()==v2.size()); for(unsigned row = 0; row loop15 regression binary_relation_exprt(templ_row.expr,ID_le,get_row_symb_value(row))); } @@ -388,7 +404,7 @@ exprt tpolyhedra_domaint::get_row_symb_post_constraint(const rowt &row) exprt c = and_exprt(templ_row.post_guard, binary_relation_exprt(templ_row.expr,ID_ge,get_row_symb_value(row))); rename(c); - return c; + return and_exprt(templ_row.aux_expr,c); } @@ -428,7 +444,7 @@ Function: tpolyhedra_domaint::to_symb_pre_constraints \*******************************************************************/ exprt tpolyhedra_domaint::to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows) + const std::set &symb_rows) { assert(value.size()==templ.size()); exprt::operandst c; @@ -454,7 +470,8 @@ Function: tpolyhedra_domaint::to_symb_post_constraints \*******************************************************************/ -exprt tpolyhedra_domaint::to_symb_post_constraints(const std::set &symb_rows) +exprt tpolyhedra_domaint::to_symb_post_constraints( + const std::set &symb_rows) { exprt::operandst c; for(std::set::const_iterator it = symb_rows.begin(); @@ -473,17 +490,17 @@ Function: tpolyhedra_domaint::get_row_symb_value_constraint Outputs: - Purpose: row_value <= symb_row_value + Purpose: row_value_value <= symb_row \*******************************************************************/ exprt tpolyhedra_domaint::get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value) + const row_valuet &row_value, bool geq) { if(is_row_value_neginf(row_value)) return false_exprt(); if(is_row_value_inf(row_value)) return true_exprt(); - exprt c = binary_relation_exprt(row_value,ID_le,get_row_symb_value(row)); - rename(c); + exprt c = binary_relation_exprt(get_row_symb_value(row), + geq ? ID_ge : ID_le,row_value); return c; } @@ -523,10 +540,6 @@ Function: tpolyhedra_domaint::project_on_vars void tpolyhedra_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) { -#if 0 - if(templ.size()==0) return domaint::project_on_vars(value,vars,result); -#endif - const templ_valuet &v = static_cast(value); assert(v.size()==templ.size()); @@ -553,19 +566,20 @@ void tpolyhedra_domaint::project_on_vars(valuet &value, const row_valuet &row_v = v[row]; if(templ_row.kind==LOOP) { - if(is_row_value_neginf(row_v)) - c.push_back(implies_exprt(templ_row.pre_guard,false_exprt())); - else if(is_row_value_inf(row_v)) - c.push_back(implies_exprt(templ_row.pre_guard,true_exprt())); + if(is_row_value_neginf(value, row)) + //c.push_back(implies_exprt(templ_row.pre_guard,false_exprt())); + c.push_back(not_exprt(templ_row.pre_guard)); + else if(is_row_value_inf(value, row)) + ; //c.push_back(implies_exprt(templ_row.pre_guard,true_exprt())); else c.push_back(implies_exprt(templ_row.pre_guard, binary_relation_exprt(templ_row.expr,ID_le,row_v))); } else { - if(is_row_value_neginf(row_v)) + if(is_row_value_neginf(value, row)) c.push_back(false_exprt()); - else if(is_row_value_inf(row_v)) - c.push_back(true_exprt()); + else if(is_row_value_inf(value, row)) + ; //c.push_back(true_exprt()); else c.push_back(binary_relation_exprt(templ_row.expr,ID_le,row_v)); } } @@ -606,9 +620,9 @@ Function: tpolyhedra_domaint::get_row_max_value \*******************************************************************/ tpolyhedra_domaint::row_valuet tpolyhedra_domaint::get_max_row_value( - const tpolyhedra_domaint::rowt &row) + const tpolyhedra_domaint::rowt &row) const { - const template_rowt &templ_row = templ[row]; + const template_rowt &templ_row = templ.at(row); if(templ_row.expr.type().id()==ID_signedbv) { return to_signedbv_type(templ_row.expr.type()).largest_expr(); @@ -639,9 +653,9 @@ Function: tpolyhedra_domaint::get_row_max_value \*******************************************************************/ tpolyhedra_domaint::row_valuet tpolyhedra_domaint::get_min_row_value( - const tpolyhedra_domaint::rowt &row) + const tpolyhedra_domaint::rowt &row) const { - const template_rowt &templ_row = templ[row]; + const template_rowt &templ_row = templ.at(row); if(templ_row.expr.type().id()==ID_signedbv) { return to_signedbv_type(templ_row.expr.type()).smallest_expr(); @@ -682,7 +696,8 @@ void tpolyhedra_domaint::output_value(std::ostream &out, const valuet &value, { case LOOP: out << "(LOOP) [ " << from_expr(ns,"",templ_row.pre_guard) << " | "; - out << from_expr(ns,"",templ_row.post_guard) << " ] ===> " << std::endl << " "; + out << from_expr(ns,"",templ_row.post_guard) << " | "; + out << from_expr(ns,"",templ_row.aux_expr) << " ] ===> " << std::endl << " "; break; case IN: out << "(IN) "; break; case OUT: case OUTL: out << "(OUT) "; break; @@ -717,7 +732,8 @@ void tpolyhedra_domaint::output_domain(std::ostream &out, const namespacet &ns) { case LOOP: out << "(LOOP) [ " << from_expr(ns,"",templ_row.pre_guard) << " | "; - out << from_expr(ns,"",templ_row.post_guard) << " ] ===> " << std::endl << " "; + out << from_expr(ns,"",templ_row.post_guard) << " | "; + out << from_expr(ns,"",templ_row.aux_expr) << " ] ===> " << std::endl << " "; break; case IN: out << "(IN) "; @@ -762,12 +778,19 @@ Function: tpolyhedra_domaint::is_row_value_neginf Purpose: \*******************************************************************/ - -bool tpolyhedra_domaint::is_row_value_neginf(const row_valuet & row_value) const +bool tpolyhedra_domaint::is_row_value_neginf( + const row_valuet &row_value) const { return row_value.get(ID_value)==ID_false; } +bool tpolyhedra_domaint::is_row_value_neginf( + const valuet &value, const rowt &row) const +{ + const templ_valuet &v = static_cast(value); + return v.at(row).get(ID_value)==ID_false; +} + /*******************************************************************\ Function: tpolyhedra_domaint::is_row_value_inf @@ -780,15 +803,75 @@ Function: tpolyhedra_domaint::is_row_value_inf \*******************************************************************/ -bool tpolyhedra_domaint::is_row_value_inf(const row_valuet & row_value) const +bool tpolyhedra_domaint::is_row_value_inf( + const row_valuet &row_value) const { return row_value.get(ID_value)==ID_true; } +bool tpolyhedra_domaint::is_row_value_inf( + const valuet &value, const rowt &row) const +{ + const templ_valuet &v = static_cast(value); + const row_valuet &row_value = v.at(row); + if(row_value.get(ID_value)==ID_true) + return true; + if(row_value==get_max_row_value(row)) + return true; + const row_exprt &row_expr = templ[row].expr; + if(row_expr.id()==ID_unary_minus && + row_expr.op0().id()==ID_typecast) + { + mp_integer rvalue; + to_integer(row_value, rvalue); + const typet &inner_type = row_expr.op0().op0().type(); + mp_integer smallest; + if(inner_type.id()==ID_unsignedbv) + smallest = to_unsignedbv_type(inner_type).smallest(); + else if(inner_type.id()==ID_signedbv) + smallest = to_signedbv_type(inner_type).smallest(); + else + return false; + if(smallest == rvalue) + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: tpolyhedra_domaint::rename_for_row + + Inputs: + + Outputs: + + Purpose: add row suffix to non-symbolic-bound variables in expression + (required for strategy iteration (binsearch3)) + +\*******************************************************************/ + + +void tpolyhedra_domaint::rename_for_row(exprt &expr, const rowt &row) +{ + if(row==0) return; //do not rename + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) + { + const std::string &old_id = expr.get_string(ID_identifier); + if(old_id.find(SYMB_BOUND_VAR)==std::string::npos) + { + irep_idt id = old_id + "_" + i2string(row); + expr.set(ID_identifier,id); + } + } + for(unsigned i=0; i< expr.operands().size(); i++) + rename_for_row(expr.operands()[i],row); +} /*******************************************************************\ -Function: make_interval_template +Function: add_template_row Inputs: @@ -798,6 +881,37 @@ Function: make_interval_template \*******************************************************************/ +tpolyhedra_domaint::template_rowt &tpolyhedra_domaint::add_template_row( + const exprt& expr, + const exprt& pre_guard, + const exprt& post_guard, + const exprt& aux_expr, + kindt kind + ) +{ + templ.push_back(template_rowt()); + template_rowt &templ_row = templ.back(); + templ_row.expr = expr; + extend_expr_types(templ_row.expr); + templ_row.pre_guard = pre_guard; + templ_row.post_guard = post_guard; + templ_row.aux_expr = aux_expr; + templ_row.kind = kind; + return templ_row; +} + +/*******************************************************************\ + +Function: add_interval_template + + Inputs: + + Outputs: + + Purpose: +-x<=c + +\*******************************************************************/ + void tpolyhedra_domaint::add_interval_template(const var_specst &var_specs, const namespacet &ns) { @@ -810,75 +924,36 @@ void tpolyhedra_domaint::add_interval_template(const var_specst &var_specs, if(v->kind==IN) continue; // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v->var; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } + add_template_row(v->var,v->pre_guard,v->post_guard, + v->aux_expr, v->kind); // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v->var,v->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } + add_template_row(unary_minus_exprt(v->var,v->var.type()), + v->pre_guard,v->post_guard,v->aux_expr, v->kind); } } /*******************************************************************\ -Function: make_zone_template +Function: add_difference_template Inputs: Outputs: - Purpose: + Purpose: x+-y<=c \*******************************************************************/ -void tpolyhedra_domaint::add_zone_template(const var_specst &var_specs, +void tpolyhedra_domaint::add_difference_template(const var_specst &var_specs, const namespacet &ns) { - unsigned size = 2*var_specs.size()+var_specs.size()*(var_specs.size()-1); + unsigned size = var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); for(var_specst::const_iterator v1 = var_specs.begin(); v1!=var_specs.end(); v1++) { - if(v1->kind!=IN) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - var_specst::const_iterator v2 = v1; v2++; for(; v2!=var_specs.end(); v2++) { @@ -886,84 +961,73 @@ void tpolyhedra_domaint::add_zone_template(const var_specst &var_specs, if(k==IN) continue; if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); + exprt pre_g, post_g, aux_expr; + merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); + merge_and(post_g, v1->post_guard, v2->post_guard, ns); + merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } + add_template_row(minus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); // x2 - x1 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } + add_template_row(minus_exprt(v2->var,v1->var),pre_g,post_g,aux_expr,k); } } } /*******************************************************************\ -Function: make_octagon_template +Function: add_quadratic_template Inputs: Outputs: - Purpose: + Purpose: +-x^2<=c + +\*******************************************************************/ + +void tpolyhedra_domaint::add_quadratic_template(const var_specst &var_specs, + const namespacet &ns) +{ + unsigned size = 2*var_specs.size(); + templ.reserve(templ.size()+size); + + for(var_specst::const_iterator v = var_specs.begin(); + v!=var_specs.end(); v++) + { + if(v->kind==IN) continue; + + // x + add_template_row(mult_exprt(v->var,v->var), + v->pre_guard,v->post_guard,v->aux_expr,v->kind); + + // -x + add_template_row(unary_minus_exprt(mult_exprt(v->var,v->var),v->var.type()), + v->pre_guard,v->post_guard,v->aux_expr,v->kind); + }} + +/*******************************************************************\ + +Function: add_sum_template + + Inputs: + + Outputs: + + Purpose: x+y<=c, -x-y<=c \*******************************************************************/ -void tpolyhedra_domaint::add_octagon_template(const var_specst &var_specs, +void tpolyhedra_domaint::add_sum_template(const var_specst &var_specs, const namespacet &ns) { - unsigned size = 2*var_specs.size()+2*var_specs.size()*(var_specs.size()-1); + unsigned size = var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); for(var_specst::const_iterator v1 = var_specs.begin(); v1!=var_specs.end(); v1++) { - if(v1->kind!=IN) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - var_specst::const_iterator v2 = v1; v2++; for(; v2!=var_specs.end(); v2++) { @@ -971,59 +1035,71 @@ void tpolyhedra_domaint::add_octagon_template(const var_specst &var_specs, if(k==IN) continue; if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); - - // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // -x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } + exprt pre_g, post_g, aux_expr; + merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); + merge_and(post_g, v1->post_guard, v2->post_guard, ns); + merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); // -x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt p_expr(unary_minus_exprt(v1->var,v1->var.type()),v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } + add_template_row(minus_exprt(unary_minus_exprt(v1->var,v1->var.type()),v2->var), + pre_g,post_g,aux_expr,k); // x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - plus_exprt p_expr(v1->var,v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } + add_template_row(plus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); + } + } + +} + +/*******************************************************************\ + +Function: tpolyhedra_domaint::refine + + Inputs: + + Outputs: + + Purpose: non-monotone condition refinement + +\*******************************************************************/ + +void tpolyhedra_domaint::replace_comparison(exprt &expr, bool greater) +{ + //TODO +} + +bool tpolyhedra_domaint::refine() +{ + return false; + + //TODO + if(current_refinement==0) //initialise + { + if(refinement_exprs.size()==0) + { + max_refinements = 0; + return false; } + max_refinements = 3; + current_refinement = 1; + exprt::operandst c; + //TODO + current_refinement_expr = conjunction(c); + return true; + } + + if(current_refinement>max_refinements) + return false; + + if(current_refinement==1) + { + exprt::operandst c; + //TODO + current_refinement_expr = conjunction(c); } + else if(current_refinement==2) + current_refinement_expr = true_exprt(); + current_refinement++; + return true; } diff --git a/src/domains/tpolyhedra_domain.h b/src/domains/tpolyhedra_domain.h index a70c3e347..a25febd65 100644 --- a/src/domains/tpolyhedra_domain.h +++ b/src/domains/tpolyhedra_domain.h @@ -24,18 +24,23 @@ class tpolyhedra_domaint : public domaint guardt pre_guard; guardt post_guard; row_exprt expr; + exprt aux_expr; kindt kind; } template_rowt; typedef std::vector templatet; tpolyhedra_domaint(unsigned _domain_number, replace_mapt &_renaming_map) : - domaint(_domain_number,_renaming_map) + domaint(_domain_number,_renaming_map),current_refinement(0) {} // initialize value virtual void initialize(valuet &value); + virtual void reset_refinements() { current_refinement = 0; } + virtual bool refine(); //non-monotone condition refinement + std::vector &refinement_expressions() { return refinement_exprs; } + virtual void join(valuet &value1, const valuet &value2); // value -> constraints @@ -56,9 +61,9 @@ class tpolyhedra_domaint : public domaint const std::set &symb_rows); exprt to_symb_post_constraints(const std::set &symb_rows); exprt get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value); + const row_valuet &row_value, bool geq=false); exprt get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value); + const row_valuet &row_value); exprt get_row_symb_post_constraint(const rowt &row); @@ -67,12 +72,14 @@ class tpolyhedra_domaint : public domaint void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); // max, min, comparison - row_valuet get_max_row_value(const rowt &row); - row_valuet get_min_row_value(const rowt &row); + row_valuet get_max_row_value(const rowt &row) const; + row_valuet get_min_row_value(const rowt &row) const; row_valuet between(const row_valuet &lower, const row_valuet &upper); bool less_than(const row_valuet &v1, const row_valuet &v2); bool is_row_value_inf(const row_valuet & row_value) const; bool is_row_value_neginf(const row_valuet & row_value) const; + bool is_row_value_inf(const valuet &value, const rowt & row) const; + bool is_row_value_neginf(const valuet &value, const rowt & row) const; // printing virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; @@ -82,29 +89,42 @@ class tpolyhedra_domaint : public domaint virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); unsigned template_size(); + virtual bool is_spec_empty() const { return templ.size()==0; } // generating templates + template_rowt &add_template_row( + const exprt& expr, + const exprt& pre_guard, + const exprt& post_guard, + const exprt& aux_expr, + kindt kind + ); + void add_interval_template(const var_specst &var_specs, const namespacet &ns); - void add_zone_template(const var_specst &var_specs, + void add_difference_template(const var_specst &var_specs, const namespacet &ns); - void add_octagon_template(const var_specst &var_specs, + void add_sum_template(const var_specst &var_specs, + const namespacet &ns); + void add_quadratic_template(const var_specst &var_specs, const namespacet &ns); symbol_exprt get_row_symb_value(const rowt &row); + void rename_for_row(exprt &expr, const rowt &row); + protected: friend class strategy_solver_binsearcht; + friend class strategy_solver_enumerationt; templatet templ; - -}; - -void extend_expr_types(exprt &expr); -constant_exprt simplify_const(const exprt &expr); -ieee_floatt simplify_const_float(const exprt &expr); -mp_integer simplify_const_int(const exprt &expr); + //non-monotone condition refinement + std::vector refinement_exprs; + unsigned current_refinement, max_refinements; + exprt current_refinement_expr; + void replace_comparison(exprt &expr, bool greater); +}; #endif diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 90585c2c3..439136c6d 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -1,5 +1,6 @@ -#include "util.h" +#include +#include "util.h" /*******************************************************************\ @@ -21,7 +22,13 @@ unsigned get_bitvector_width(const exprt &expr) void extend_expr_types(exprt &expr) { // std::cerr << "expr: " << expr << std::endl; - if(expr.id()==ID_typecast) return; + if(expr.id()==ID_typecast) + { + exprt new_expr = expr.op0(); + extend_expr_types(new_expr); + expr = new_expr; + return; + } if(expr.id()==ID_constant) return; if(expr.id()==ID_symbol) return; if(expr.id()==ID_index) return; @@ -121,12 +128,39 @@ void extend_expr_types(exprt &expr) } else assert(false); } - std::cerr << "expr: " << expr << std::endl; + if(expr.id()==ID_div) + { + extend_expr_types(expr.op0()); + extend_expr_types(expr.op1()); + unsigned size0 = get_bitvector_width(expr.op0()); + unsigned size1 = get_bitvector_width(expr.op1()); + assert(size0>0); assert(size1>0); + if((expr.op0().type().id()==ID_unsignedbv || expr.op0().type().id()==ID_signedbv) && + (expr.op1().type().id()==ID_unsignedbv || expr.op1().type().id()==ID_signedbv)) + { + typet new_type; + if(expr.op0().type().id()==ID_unsignedbv && expr.op0().type().id()==ID_unsignedbv) + new_type = unsignedbv_typet(std::max(size0,size1)); + else if(expr.op0().type().id()==ID_signedbv && expr.op0().type().id()==ID_unsignedbv) + new_type = signedbv_typet(size0>size1 ? size0 : size1+1); + else new_type = signedbv_typet(size0>=size1 ? size0+1 : size1); + + expr = div_exprt(typecast_exprt(expr.op0(),new_type), + typecast_exprt(expr.op1(),new_type)); + return; + } + else if(expr.op0().type().id()==ID_floatbv || expr.op1().type().id()==ID_floatbv) + { + // TODO: shall we extend floats? + return; + } + else assert(false); + } + std::cerr << "failed expr: " << expr << std::endl; assert(false); } - /*******************************************************************\ Function: simplify_const @@ -154,13 +188,22 @@ mp_integer simplify_const_int(const exprt &expr) return simplify_const_int(op0); } if(expr.id()==ID_unary_minus) return -simplify_const_int(expr.op0()); - if(expr.id()==ID_plus) return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); - if(expr.id()==ID_minus) return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); - if(expr.id()==ID_mult) return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); + if(expr.id()==ID_plus) + return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); + if(expr.id()==ID_minus) + return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); + if(expr.id()==ID_mult) + return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); + if(expr.id()==ID_div) + { + mp_integer d = simplify_const_int(expr.op1()); + assert(d!=0); + return simplify_const_int(expr.op0())/d; + } if(expr.id()==ID_symbol) { #if 0 - std::cout << "substituting default value for " << expr << std::endl; + std::cerr << "substituting default value for " << expr << std::endl; #endif return 0; //default value if not substituted in expr } @@ -229,13 +272,22 @@ ieee_floatt simplify_const_float(const exprt &expr) v1 *= v2; return v1; } + if(expr.id()==ID_div) + { + ieee_floatt v1 = simplify_const_float(expr.op0()); + ieee_floatt v2 = simplify_const_float(expr.op1()); + v1 /= v2; + return v1; + } if(expr.id()==ID_symbol) //default value if not substituted in expr { ieee_floatt v; v.make_zero(); + #if 0 - std::cout << "substituting default value for " << expr << std::endl; + std::cerr << "substituting default value for " << expr << std::endl; #endif + return v; } if(expr.id()==ID_index) @@ -256,9 +308,7 @@ ieee_floatt simplify_const_float(const exprt &expr) constant_exprt simplify_const(const exprt &expr) { -// std::cerr << "const: " << expr << std::endl; if(expr.id()==ID_constant) return to_constant_expr(expr); - //TODO: handle "address_of" constants if(expr.id()==ID_index) { const index_exprt &index_expr = to_index_expr(expr); @@ -307,82 +357,23 @@ constant_exprt simplify_const(const exprt &expr) assert(false); //type not supported } -void remove_typecast(exprt& expr) -{ - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); ++it) - remove_typecast(*it); - - if (expr.id() == ID_typecast) - expr = expr.op0(); -} - /*******************************************************************\ -Function: pretty_print_termination_argument() +Function: merge_and Inputs: Outputs: - Purpose: print ranking argument expressions in a more readable format + Purpose: -\******************************************************************/ +\*******************************************************************/ -void pretty_print_termination_argument(std::ostream &out, const namespacet &ns, const exprt &_expr) +void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, + const namespacet &ns) { - exprt expr = _expr; - remove_typecast(expr); - - if(expr.id()==ID_and) - { - // should be of the form /\_i g_i => R_i - for(exprt::operandst::const_iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) - { - out << "\n"; - if(it == expr.operands().begin()) out << " "; - else out << "&& "; - if(it->id()==ID_implies) - { - out << from_expr(ns,"",it->op0()) << " ==> "; - if(it->op1().id()==ID_gt) - out << from_expr(ns,"",it->op1().op0()); - else if(it->op1().id()==ID_or) // needed for lexicographic ones - { - for(exprt::operandst::const_iterator it_lex = it->op1().operands().begin(); - it_lex != it->op1().operands().end(); ++it_lex) - { - if(it_lex->id() == ID_and) - { - if(it_lex == it->op1().operands().begin()) out << "("; - else out << "\n " << " " << ","; - out << from_expr(ns,"",it_lex->op0()); - } - else - { - out << "\n " << " " << "," - << from_expr(ns,"",*it_lex); - } - } - out << ")"; - } - else out << from_expr(ns,"",it->op1()); - } - else out << from_expr(ns,"",*it); - } - return; - } - else - { - if(expr.id()==ID_implies) - { - if(expr.op1().id()==ID_gt) - { - out << from_expr(ns,"",expr.op0()) << " ==> " << from_expr(ns,"",expr.op1().op0()); - return; - } - } - } - out << from_expr(ns,"",expr); + result = expr1; + if(expr1!=expr2) result = and_exprt(expr1,expr2); + simplify(result,ns); } + diff --git a/src/domains/util.h b/src/domains/util.h index 12578925f..bb99c6497 100644 --- a/src/domains/util.h +++ b/src/domains/util.h @@ -2,6 +2,7 @@ #define CPROVER_DOMAIN_UTIL_H #include +#include #include #include #include @@ -11,8 +12,8 @@ void extend_expr_types(exprt &expr); constant_exprt simplify_const(const exprt &expr); ieee_floatt simplify_const_float(const exprt &expr); mp_integer simplify_const_int(const exprt &expr); -void pretty_print_termination_argument(std::ostream &out, - const namespacet &ns, const exprt &expr); +void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, + const namespacet &ns); #endif diff --git a/src/functions/Makefile b/src/functions/Makefile index a904b3ec4..f73eb0b85 100644 --- a/src/functions/Makefile +++ b/src/functions/Makefile @@ -6,6 +6,9 @@ SRC = call_graph.cpp summary.cpp index.cpp get_function.cpp path_util.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common + +CP_CXXFLAGS += $(SUMMARIZERFLAGS) + INCLUDES= -I $(CBMC)/src CLEANFILES = diff --git a/src/functions/get_function.cpp b/src/functions/get_function.cpp index c87b57da8..39bbb593f 100644 --- a/src/functions/get_function.cpp +++ b/src/functions/get_function.cpp @@ -49,7 +49,7 @@ goto_functionst::goto_functiont * get_functiont::operator()(const irep_idt &id) current_file_name=index.full_path(file_name); - status() << "Reading \"" << current_file_name << "\"" << eom; + status() << "Reading \"" << id2string(current_file_name) << "\"" << eom; // read the file goto_model.clear(); diff --git a/src/functions/index.cpp b/src/functions/index.cpp index 5ca3b56f9..2de0dff1a 100644 --- a/src/functions/index.cpp +++ b/src/functions/index.cpp @@ -140,7 +140,7 @@ void indext::index_goto_binary(const irep_idt &file) f_it!=goto_model.goto_functions.function_map.end(); f_it++) { - if(f_it->second.body_available) + if(f_it->second.body_available()) { function_to_file[f_it->first].insert(file); file_to_function[file].insert(f_it->first); diff --git a/src/html/Makefile b/src/html/Makefile index ac3c81d60..51760b59b 100644 --- a/src/html/Makefile +++ b/src/html/Makefile @@ -1,7 +1,7 @@ include ../config.inc CBMC ?= ../.. -SRC = logo.cpp html_escape.cpp +SRC = logo.cpp html_escape.cpp syntax_highlighting.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common diff --git a/src/solver/Makefile b/src/solver/Makefile index e2f61aa74..bdbbed955 100644 --- a/src/solver/Makefile +++ b/src/solver/Makefile @@ -6,6 +6,8 @@ SRC = solver.cpp predicate.cpp fixed_point.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common +CP_CXXFLAGS += $(SUMMARIZERFLAGS) + INCLUDES= -I $(CBMC)/src CLEANFILES = diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 13d8f80e6..275a10380 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,15 +1,18 @@ include ../config.inc CBMC ?= ../.. -SRC = local_ssa.cpp ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ - guard_map.cpp ssa_object.cpp assignments.cpp ssa_aliasing.cpp \ - address_canonizer.cpp simplify_ssa.cpp ssa_build_goto_trace.cpp \ - ssa_inliner.cpp ssa_unwinder.cpp \ - split_loopheads.cpp +SRC = ssa_slicer.cpp local_ssa.cpp ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ + guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ + ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ + ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ + split_loopheads.cpp const_propagator.cpp ssa_const_propagator.cpp \ + replace_symbol_ext.cpp ssa_dependency_graph.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common +CP_CXXFLAGS += $(SUMMARIZERFLAGS) + INCLUDES= -I $(CBMC)/src CLEANFILES = diff --git a/src/ssa/assignments.cpp b/src/ssa/assignments.cpp index 674f4d205..bcae6e998 100644 --- a/src/ssa/assignments.cpp +++ b/src/ssa/assignments.cpp @@ -6,8 +6,10 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +#include + #include "assignments.h" -#include "ssa_aliasing.h" +#include "ssa_dereference.h" /*******************************************************************\ @@ -34,7 +36,8 @@ void assignmentst::build_assignment_map( if(it->is_assign()) { const code_assignt &code_assign=to_code_assign(it->code); - assign(code_assign.lhs(), it, ns); + exprt lhs_deref=dereference(code_assign.lhs(), ssa_value_ai[it], "", ns); + assign(lhs_deref, it, ns); } else if(it->is_decl()) { @@ -61,7 +64,10 @@ void assignmentst::build_assignment_map( // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) - assign(code_function_call.lhs(), it, ns); + { + exprt lhs_deref=dereference(code_function_call.lhs(), ssa_value_ai[it], "", ns); + assign(lhs_deref, it, ns); + } } else if(it->is_dead()) { @@ -83,10 +89,10 @@ Function: assignmentst::assign void assignmentst::assign( const exprt &lhs, - goto_programt::const_targett loc, + locationt loc, const namespacet &ns) { - if(is_symbol_or_deref_struct_member(lhs, ns)) + if(is_symbol_struct_member(lhs, ns)) { const typet &lhs_type=ns.follow(lhs.type()); @@ -110,15 +116,13 @@ void assignmentst::assign( return; // done } - // this might alias all sorts of stuff - for(std::set::const_iterator - o_it=ssa_objects.objects.begin(); - o_it!=ssa_objects.objects.end(); - o_it++) + // object? + ssa_objectt ssa_object(lhs, ns); + + if(ssa_object) { - if(ssa_may_alias(o_it->get_expr(), lhs, ns)) - assign(*o_it, loc, ns); - } + assign(ssa_object, loc, ns); + } return; // done } @@ -142,6 +146,13 @@ void assignmentst::assign( const member_exprt &member_expr=to_member_expr(lhs); assign(member_expr.struct_op(), loc, ns); } + else if(lhs.id()==ID_byte_extract_little_endian || + lhs.id()==ID_byte_extract_big_endian) + { + const byte_extract_exprt &byte_extract_expr=to_byte_extract_expr(lhs); + + assign(byte_extract_expr.op(), loc, ns); + } else if(lhs.id()==ID_complex_real || lhs.id()==ID_complex_imag) { assert(lhs.operands().size()==1); @@ -163,7 +174,7 @@ Function: assignmentst::assign void assignmentst::assign( const ssa_objectt &lhs, - goto_programt::const_targett loc, + locationt loc, const namespacet &) { assignment_map[loc].insert(lhs); diff --git a/src/ssa/assignments.h b/src/ssa/assignments.h index 4e0873f8f..e21462cfe 100644 --- a/src/ssa/assignments.h +++ b/src/ssa/assignments.h @@ -12,25 +12,29 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "ssa_object.h" +#include "ssa_value_set.h" class assignmentst { public: + typedef goto_programt::const_targett locationt; + const ssa_objectst &ssa_objects; + const ssa_value_ait &ssa_value_ai; typedef ssa_objectst::objectst objectst; - typedef std::map assignment_mapt; + typedef std::map assignment_mapt; assignment_mapt assignment_map; - bool assigns(goto_programt::const_targett loc, const ssa_objectt &object) const + bool assigns(locationt loc, const ssa_objectt &object) const { assignment_mapt::const_iterator it=assignment_map.find(loc); if(it==assignment_map.end()) return false; return it->second.find(object)!=it->second.end(); } - inline const objectst &get(goto_programt::const_targett loc) const + inline const objectst &get(locationt loc) const { assignment_mapt::const_iterator it=assignment_map.find(loc); assert(it!=assignment_map.end()); @@ -40,8 +44,10 @@ class assignmentst explicit assignmentst( const goto_programt &_goto_program, const namespacet &_ns, - const ssa_objectst &_ssa_objects): - ssa_objects(_ssa_objects) + const ssa_objectst &_ssa_objects, + const ssa_value_ait &_ssa_value_ai): + ssa_objects(_ssa_objects), + ssa_value_ai(_ssa_value_ai) { build_assignment_map(_goto_program, _ns); } @@ -55,11 +61,11 @@ class assignmentst void build_assignment_map(const goto_programt &, const namespacet &); void assign( - const exprt &lhs, goto_programt::const_targett, + const exprt &lhs, locationt, const namespacet &ns); void assign( - const ssa_objectt &lhs, goto_programt::const_targett, + const ssa_objectt &lhs, locationt, const namespacet &ns); }; diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index fdaccd2d0..03e73b237 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -6,10 +6,11 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#define DEBUG +//#define DEBUG #ifdef DEBUG #include +#include #endif #include @@ -22,10 +23,11 @@ Author: Daniel Kroening, kroening@kroening.com #include +#include "ssa_slicer.h" #include "local_ssa.h" #include "malloc_ssa.h" +#include "ssa_dereference.h" #include "address_canonizer.h" -#include "ssa_aliasing.h" /*******************************************************************\ @@ -58,11 +60,20 @@ void local_SSAt::build_SSA() build_cond(i_it); build_guard(i_it); build_assertions(i_it); + build_assumptions(i_it); build_function_call(i_it); } + // collect custom templates in loop heads + collect_custom_templates(); + // entry and exit variables get_entry_exit_vars(); + +#ifdef ASSERTION_HOISTING + // collect assertions after loop exit (for k-induction assertion hoisting) + assertions_after_loop(); +#endif } /*******************************************************************\ @@ -79,6 +90,8 @@ Function: local_SSAt::get_entry_exit_vars void local_SSAt::get_entry_exit_vars() { + goto_programt::const_targett first = goto_function.body.instructions.begin(); + //get parameters const code_typet::parameterst ¶meter_types = goto_function.type.parameters(); @@ -88,16 +101,24 @@ void local_SSAt::get_entry_exit_vars() const code_typet::parametert ¶meter=*it; const irep_idt &identifier=parameter.get_identifier(); - // const symbolt &symbol=ns.lookup(identifier); - const symbolt *symbol; if(ns.lookup(identifier,symbol)) continue; - params.push_back(symbol->symbol_expr()); + if(ns.follow(symbol->type).id()==ID_struct) + { + exprt param = read_rhs(symbol->symbol_expr(), first); +#if 0 + std::cout << "param: " + << from_expr(ns, "", param) << std::endl; +#endif + forall_operands(it, param) + params.push_back(to_symbol_expr(*it)); + } + else + params.push_back(symbol->symbol_expr()); } //get globals in - goto_programt::const_targett first = goto_function.body.instructions.begin(); get_globals(first,globals_in,true,false); //filters out #return_value //get globals out (includes return value) @@ -144,14 +165,54 @@ void local_SSAt::get_globals(locationt loc, std::set &globals, id2string(returns_for_function)+"#return_value")==std::string::npos) continue; - if(rhs_value) - globals.insert(to_symbol_expr(read_rhs(it->get_expr(),loc))); + if(rhs_value) + { + const exprt &expr = read_rhs(it->get_expr(),loc); + globals.insert(to_symbol_expr(expr)); + } else - globals.insert(to_symbol_expr(read_lhs(it->get_expr(),loc))); + { + const exprt &expr = read_lhs(it->get_expr(),loc); + globals.insert(to_symbol_expr(expr)); + } } } } + +/*******************************************************************\ + +Function: local_SSAt::collect_custom_templates + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::collect_custom_templates() +{ + for(local_SSAt::nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + { + if(n_it->loophead != nodes.end()) //we've found a loop + { + //search for templates in the loop + for(local_SSAt::nodest::iterator nn_it=n_it->loophead; + nn_it!=n_it; nn_it++) + { + if(nn_it->templates.empty()) continue; + n_it->loophead->templates.insert(n_it->loophead->templates.end(), + nn_it->templates.begin(), + nn_it->templates.end()); + nn_it->templates.clear(); + } + } + } +} + /*******************************************************************\ Function: local_SSAt::find_node @@ -208,8 +269,7 @@ Function: local_SSAt::find_nodes \*******************************************************************/ -void local_SSAt::find_nodes(locationt loc, - std::list &_nodes) const +void local_SSAt::find_nodes(locationt loc, std::list &_nodes) const { nodest::const_iterator n_it = nodes.begin(); for(; n_it != nodes.end(); n_it++) @@ -220,6 +280,28 @@ void local_SSAt::find_nodes(locationt loc, /*******************************************************************\ +Function: local_SSAt::find_location_by_number + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +local_SSAt::locationt local_SSAt::find_location_by_number(unsigned location_number) const +{ + local_SSAt::nodest::const_iterator n_it =nodes.begin(); + for(; n_it != nodes.end(); n_it++) + { + if(n_it->location->location_number == location_number) break; + } + return n_it->location; +} + +/*******************************************************************\ + Function: local_SSAt::edge_guard Inputs: @@ -269,12 +351,20 @@ void local_SSAt::build_phi_nodes(locationt loc) o_it=ssa_objects.objects.begin(); o_it!=ssa_objects.objects.end(); o_it++) { - // phi-node here? + // phi-node for this object here? ssa_domaint::phi_nodest::const_iterator p_it= phi_nodes.find(o_it->get_identifier()); if(p_it==phi_nodes.end()) continue; // none + + #ifdef DEBUG + std::cout << "PHI " << o_it->get_identifier() << "\n"; + #endif + //ignore custom template variables + if(id2string(o_it->get_identifier()). + find(TEMPLATE_PREFIX)!=std::string::npos) continue; + // Yes. Get the source -> def map. const std::map &incoming=p_it->second; @@ -330,6 +420,25 @@ void local_SSAt::build_phi_nodes(locationt loc) /*******************************************************************\ +Function: local_SSAt::dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt local_SSAt::dereference(const exprt &src, locationt loc) const +{ + const ssa_value_domaint &ssa_value_domain=ssa_value_ai[loc]; + const std::string nondet_prefix="deref#"+i2string(loc->location_number); + return ::dereference(src, ssa_value_domain, nondet_prefix, ns); +} + +/*******************************************************************\ + Function: local_SSAt::build_transfer Inputs: @@ -345,13 +454,35 @@ void local_SSAt::build_transfer(locationt loc) if(loc->is_assign()) { const code_assignt &code_assign=to_code_assign(loc->code); - assign_rec(code_assign.lhs(), code_assign.rhs(), loc); + + // template declarations + if(code_assign.lhs().id()==ID_symbol && + id2string(code_assign.lhs().get(ID_identifier)). + find("return_value_" TEMPLATE_NEWVAR) != std::string::npos) + { + //propagate equalities through replace map + exprt lhs = code_assign.lhs(); + template_newvars[lhs] = template_newvars[template_last_newvar]; + template_last_newvar = lhs; + return; + } + if(code_assign.lhs().id()==ID_symbol && + id2string(code_assign.lhs().get(ID_identifier)). + find(TEMPLATE_PREFIX)!=std::string::npos) return; + if(code_assign.rhs().id()==ID_symbol && + id2string(code_assign.rhs().get(ID_identifier)). + find(TEMPLATE_PREFIX)!=std::string::npos) return; + + exprt deref_lhs=dereference(code_assign.lhs(), loc); + exprt deref_rhs=dereference(code_assign.rhs(), loc); + + assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); } } /*******************************************************************\ -Function: local_SSAt::build_transfer +Function: local_SSAt::build_function_call Inputs: @@ -368,29 +499,85 @@ void local_SSAt::build_function_call(locationt loc) const code_function_callt &code_function_call= to_code_function_call(loc->code); + const exprt &lhs=code_function_call.lhs(); + + if(lhs.is_not_nil()) + { + exprt deref_lhs=dereference(lhs, loc); + + // generate a symbol for rhs + irep_idt identifier="ssa::return_value"+i2string(loc->location_number); + symbol_exprt rhs(identifier, code_function_call.lhs().type()); + + assign_rec(deref_lhs, rhs, true_exprt(), loc); + } + + nodest::iterator n_it = --nodes.end(); + + //template declarations + if(code_function_call.function().id()==ID_symbol && + has_prefix(TEMPLATE_DECL, + id2string(code_function_call.function().get(ID_identifier)))) + { + assert(code_function_call.arguments().size()==1); + n_it->templates.push_back(code_function_call.arguments()[0]); + + // replace "new" vars + replace_expr(template_newvars,n_it->templates.back()); + +#if 0 + std::cout << "found template declaration: " + << from_expr(ns,"",code_function_call.arguments()[0]) + << std::endl; +#endif + template_newvars.clear(); + return; + } + + //turn function call into expression function_application_exprt f; f.function() = code_function_call.function(); f.type() = code_function_call.lhs().type(); f.arguments() = code_function_call.arguments(); - f = to_function_application_expr(read_rhs(f, loc)); - nodest::iterator n_it = --nodes.end(); + //access to "new" value in template declarations + if(code_function_call.function().id()==ID_symbol && + has_prefix(TEMPLATE_NEWVAR, + id2string(code_function_call.function().get(ID_identifier)))) + { + assert(code_function_call.arguments().size()==1); + template_last_newvar = f; + template_newvars[template_last_newvar] = template_last_newvar; + return; + } + + f = to_function_application_expr(read_rhs(f, loc)); assert(f.function().id()==ID_symbol); //no function pointers irep_idt fname = to_symbol_expr(f.function()).get_identifier(); - //add equalities for arguments unsigned i=0; for(exprt::operandst::iterator it = f.arguments().begin(); it != f.arguments().end(); it++, i++) { symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i),it->type()); - n_it->equalities.push_back(equal_exprt(*it,arg)); + "#arg"+i2string(i),it->type()); + const typet &argtype = ns.follow(it->type()); + if(argtype.id()==ID_struct) + { + exprt lhs = read_rhs(arg, loc); + for(int j=0; jequalities.push_back(equal_exprt(lhs.operands()[j], + it->operands()[j])); + } + } + else + { + n_it->equalities.push_back(equal_exprt(arg,*it)); + } *it = arg; } - - n_it->function_calls.push_back( - to_function_application_expr(f)); + n_it->function_calls.push_back(to_function_application_expr(f)); } } @@ -419,8 +606,7 @@ void local_SSAt::build_cond(locationt loc) { equal_exprt equality(cond_symbol(loc), true_exprt()); (--nodes.end())->equalities.push_back(equality); - } -} + }} /*******************************************************************\ @@ -530,22 +716,24 @@ void local_SSAt::build_assertions(locationt loc) /*******************************************************************\ -Function: local_SSAt::assertions +Function: local_SSAt::build_assumptions Inputs: Outputs: - Purpose: returns assertions for a given location + Purpose: collect assumptions (required for backwards analysis) \*******************************************************************/ -/*local_SSAt::nodet::assertionst local_SSAt::assertions(locationt loc) const +void local_SSAt::build_assumptions(locationt loc) { - nodest::const_iterator n_it=nodes.find(loc); - if(n_it==nodes.end()) return nodet::assertionst(); - return n_it->second.assertions; - }*/ + if(loc->is_assume()) + { + exprt c=read_rhs(loc->guard, loc); + (--nodes.end())->assumptions.push_back(c); + } +} /*******************************************************************\ @@ -573,6 +761,64 @@ void local_SSAt::assertions_to_constraints() /*******************************************************************\ +Function: local_SSAt::assertions_after_loop + + Inputs: + + Outputs: + + Purpose: find assertions after loop exit + +\*******************************************************************/ + +void local_SSAt::assertions_after_loop() +{ + if(nodes.empty()) + return; + std::map assertion_map; + std::list::iterator> loopheads; + loopheads.push_back(nodes.begin()); + nodest::iterator n_it=nodes.end(); + while(n_it!=nodes.begin()) //collect assertions backwards + { + n_it--; + +#if 0 + std::cout << "location: " << n_it->location->location_number << std::endl; + std::cout << "loophead: " << loopheads.back()->location->location_number << std::endl; +#endif + + if(n_it==loopheads.back()) //assign parent-level assertions at loophead + { + loopheads.pop_back(); + if(!loopheads.empty()) + { + const exprt::operandst &assertions = + assertion_map[loopheads.back()->location]; + n_it->assertions_after_loop.insert(n_it->assertions_after_loop.end(), + assertions.begin(),assertions.end()); + assertion_map[loopheads.back()->location].clear(); + //do not consider assertions after another loop + } + else // we should have reached the beginning + assert(n_it==nodes.begin()); + } + if(n_it->loophead!=nodes.end()) + { + assert(!loopheads.empty()); + loopheads.push_back(n_it->loophead); + } + if(!n_it->assertions.empty() && !loopheads.empty()) + { + exprt::operandst &a = assertion_map[loopheads.back()->location]; + a.insert(a.end(),n_it->assertions.begin(),n_it->assertions.end()); + } + //TODO: could also add assertions that are on a direct path within a loop + } +} + +/*******************************************************************\ + Function: local_SSAt::cond_symbol Inputs: @@ -654,12 +900,24 @@ exprt local_SSAt::read_lhs( const exprt &expr, locationt loc) const { - ssa_objectt object(expr, ns); + // dereference first + exprt tmp1=dereference(expr, loc); + +#ifdef DEBUG + std::cout << "read_lhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; +#endif + + ssa_objectt object(tmp1, ns); // is this an object we track? if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { + +#ifdef DEBUG + std::cout << from_expr(ns, "", tmp1) << "is_object" << '\n'; +#endif + // yes, it is if(assignments.assigns(loc, object)) return name(object, OUT, loc); @@ -667,7 +925,7 @@ exprt local_SSAt::read_lhs( return read_rhs(object, loc); } else - return read_rhs(expr, loc); + return read_rhs(tmp1, loc); } /*******************************************************************\ @@ -740,12 +998,27 @@ Function: local_SSAt::read_rhs exprt local_SSAt::read_rhs(const exprt &expr, locationt loc) const { - exprt tmp=expr; - adjust_float_expressions(tmp, ns); + exprt tmp1=expr; + adjust_float_expressions(tmp1, ns); + unsigned counter=0; - replace_side_effects_rec(tmp, loc, counter); + replace_side_effects_rec(tmp1, loc, counter); + + #ifdef DEBUG + std::cout << "read_rhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; + #endif + + exprt tmp2=dereference(tmp1, loc); + + #ifdef DEBUG + std::cout << "read_rhs tmp2: " << from_expr(ns, "", tmp2) << '\n'; + #endif - exprt result=read_rhs_rec(tmp, loc); + exprt result=read_rhs_rec(tmp2, loc); + + #ifdef DEBUG + std::cout << "read_rhs result: " << from_expr(ns, "", result) << '\n'; + #endif return result; } @@ -812,50 +1085,7 @@ Function: local_SSAt::read_rhs_rec exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { - if(is_deref_struct_member(expr, ns)) - { - // Stuff like (*ptr).m1.m2 or simply *ptr. - // This might alias with whatnot. - - // We use the identifier produced by - // local_SSAt::replace_side_effects_rec - exprt result=symbol_exprt(expr.get(ID_C_identifier), expr.type()); - - // case split for aliasing - for(objectst::const_iterator - o_it=ssa_objects.objects.begin(); - o_it!=ssa_objects.objects.end(); o_it++) - { - if(ssa_may_alias(expr, o_it->get_expr(), ns)) - { - exprt guard=ssa_alias_guard(expr, o_it->get_expr(), ns); - exprt value=ssa_alias_value(expr, read_rhs(*o_it, loc), ns); - guard=read_rhs_rec(guard, loc); - value=read_rhs_rec(value, loc); - - result=if_exprt(guard, value, result); - } - } - - // may alias literals - for(ssa_objectst::literalst::const_iterator - o_it=ssa_objects.literals.begin(); - o_it!=ssa_objects.literals.end(); o_it++) - { - if(ssa_may_alias(expr, *o_it, ns)) - { - exprt guard=ssa_alias_guard(expr, *o_it, ns); - exprt value=ssa_alias_value(expr, read_rhs(*o_it, loc), ns); - guard=read_rhs_rec(guard, loc); - value=read_rhs_rec(value, loc); - - result=if_exprt(guard, value, result); - } - } - - return result; - } - else if(expr.id()==ID_side_effect) + if(expr.id()==ID_side_effect) { // ignore @@ -869,7 +1099,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const } else if(expr.id()==ID_dereference) { - // caught by case above + throw "unexpected dereference in read_rhs_rec"; } else if(expr.id()==ID_index) { @@ -968,11 +1198,12 @@ void local_SSAt::replace_side_effects_rec( } else if(statement==ID_malloc) { - counter++; + assert(false); +/* counter++; std::string tmp_suffix= i2string(loc->location_number)+ "."+i2string(counter)+suffix; - expr=malloc_ssa(side_effect_expr, tmp_suffix, ns); + expr=malloc_ssa(side_effect_expr, tmp_suffix, ns);*/ } else { @@ -980,17 +1211,6 @@ void local_SSAt::replace_side_effects_rec( // ignore } } - else if(is_deref_struct_member(expr, ns)) - { - // We generate a symbol identifier in case dereferencing turns - // out to need one. - counter++; - const irep_idt identifier= - "ssa::deref"+ - i2string(loc->location_number)+ - "."+i2string(counter)+suffix; - expr.set(ID_C_identifier, identifier); - } } /*******************************************************************\ @@ -1022,6 +1242,10 @@ symbol_exprt local_SSAt::name( i2string(cnt)+ (kind==LOOP_SELECT?std::string(""):suffix); +#ifdef DEBUG + std::cout << "name " << kind << ": " << new_id << '\n'; +#endif + new_symbol_expr.set_identifier(new_id); if(object.get_expr().source_location().is_not_nil()) @@ -1070,10 +1294,10 @@ symbol_exprt local_SSAt::name_input(const ssa_objectt &object) const { symbol_exprt new_symbol_expr(object.get_expr().type()); // copy const irep_idt old_id=object.get_identifier(); - irep_idt new_id=id2string(old_id)+suffix; + irep_idt new_id=id2string(old_id)+suffix; //+"#in" new_symbol_expr.set_identifier(new_id); - if(object.get_expr().location().is_not_nil()) + if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); return new_symbol_expr; @@ -1094,15 +1318,12 @@ Function: local_SSAt::assign_rec void local_SSAt::assign_rec( const exprt &lhs, const exprt &rhs, + const exprt &guard, locationt loc) { - - bool flag_symbol=is_symbol_struct_member(lhs, ns); - bool flag_deref=is_symbol_or_deref_struct_member(lhs, ns); - const typet &type=ns.follow(lhs.type()); - - if(flag_symbol || flag_deref) + + if(is_symbol_struct_member(lhs, ns)) { if(type.id()==ID_struct) { @@ -1118,7 +1339,7 @@ void local_SSAt::assign_rec( { member_exprt new_lhs(lhs, it->get_name(), it->type()); member_exprt new_rhs(rhs, it->get_name(), it->type()); - assign_rec(new_lhs, new_rhs, loc); + assign_rec(new_lhs, new_rhs, guard, loc); } return; @@ -1126,59 +1347,14 @@ void local_SSAt::assign_rec( ssa_objectt lhs_object(lhs, ns); - exprt rhs_read=read_rhs(rhs, loc); - const std::set &assigned= assignments.get(loc); - for(std::set::const_iterator - a_it=assigned.begin(); - a_it!=assigned.end(); - a_it++) + if(assigned.find(lhs_object)!=assigned.end()) { - const symbol_exprt ssa_symbol=name(*a_it, OUT, loc); - exprt ssa_rhs; - - if(lhs_object==*a_it) - { - ssa_rhs=rhs_read; - } - else if(lhs.id()==ID_dereference) // might alias stuff - { - const dereference_exprt &dereference_expr= - to_dereference_expr(lhs); - - if(!ssa_may_alias(dereference_expr, a_it->get_expr(), ns)) - continue; - - exprt guard=ssa_alias_guard(dereference_expr, a_it->get_expr(), ns); - exprt value=ssa_alias_value(dereference_expr, read_rhs(*a_it, loc), ns); - - exprt final_rhs=nil_exprt(); - - // read the value and the rhs - value=read_rhs(value, loc); - - // merge rhs into value - if(value.id()==ID_symbol) - final_rhs=rhs_read; - else if(value.id()==ID_byte_extract_little_endian) - final_rhs=byte_update_little_endian_exprt( - value.op0(), value.op1(), rhs_read); - else if(value.id()==ID_byte_extract_big_endian) - final_rhs=byte_update_big_endian_exprt( - value.op0(), value.op1(), rhs_read); - - if(final_rhs.is_nil()) - ssa_rhs=read_rhs(*a_it, loc); - else - ssa_rhs=if_exprt( - read_rhs(guard, loc), - final_rhs, // read_rhs done above - read_rhs(*a_it, loc)); - } - else - continue; + exprt ssa_rhs=read_rhs(rhs, loc); + + const symbol_exprt ssa_symbol=name(lhs_object, OUT, loc); equal_exprt equality(ssa_symbol, ssa_rhs); (--nodes.end())->equalities.push_back(equality); @@ -1187,8 +1363,9 @@ void local_SSAt::assign_rec( else if(lhs.id()==ID_index) { const index_exprt &index_expr=to_index_expr(lhs); - exprt new_rhs=with_exprt(index_expr.array(), index_expr.index(), rhs); - assign_rec(index_expr.array(), new_rhs, loc); + exprt ssa_array=index_expr.array(); + exprt new_rhs=with_exprt(ssa_array, index_expr.index(), rhs); + assign_rec(index_expr.array(), new_rhs, guard, loc); } else if(lhs.id()==ID_member) { @@ -1200,14 +1377,14 @@ void local_SSAt::assign_rec( if(compound_type.id()==ID_union) { union_exprt new_rhs(member_expr.get_component_name(), rhs, compound.type()); - assign_rec(member_expr.struct_op(), new_rhs, loc); + assign_rec(member_expr.struct_op(), new_rhs, guard, loc); } else if(compound_type.id()==ID_struct) { exprt member_name(ID_member_name); member_name.set(ID_component_name, member_expr.get_component_name()); with_exprt new_rhs(compound, member_name, rhs); - assign_rec(compound, new_rhs, loc); + assign_rec(compound, new_rhs, guard, loc); } } else if(lhs.id()==ID_complex_real) @@ -1217,7 +1394,7 @@ void local_SSAt::assign_rec( const complex_typet &complex_type=to_complex_type(op.type()); exprt imag_op=unary_exprt(ID_complex_imag, op, complex_type.subtype()); complex_exprt new_rhs(rhs, imag_op, complex_type); - assign_rec(op, new_rhs, loc); + assign_rec(op, new_rhs, guard, loc); } else if(lhs.id()==ID_complex_imag) { @@ -1226,7 +1403,25 @@ void local_SSAt::assign_rec( const complex_typet &complex_type=to_complex_type(op.type()); exprt real_op=unary_exprt(ID_complex_real, op, complex_type.subtype()); complex_exprt new_rhs(real_op, rhs, complex_type); - assign_rec(op, new_rhs, loc); + assign_rec(op, new_rhs, guard, loc); + } + else if(lhs.id()==ID_if) + { + const if_exprt &if_expr=to_if_expr(lhs); + assign_rec(if_expr.true_case(), rhs, and_exprt(guard, if_expr.cond()), loc); + assign_rec(if_expr.false_case(), rhs, and_exprt(guard, not_exprt(if_expr.cond())), loc); + } + else if(lhs.id()==ID_byte_extract_little_endian || + lhs.id()==ID_byte_extract_big_endian) + { + const byte_extract_exprt &byte_extract_expr=to_byte_extract_expr(lhs); + + exprt new_lhs=byte_extract_expr.op(); + + exprt new_rhs=byte_extract_exprt( + byte_extract_expr.id(), rhs, byte_extract_expr.offset(), new_lhs.type()); + + assign_rec(new_lhs, new_rhs, guard, loc); } else throw "UNKNOWN LHS: "+lhs.id_string(); @@ -1251,12 +1446,36 @@ void local_SSAt::output(std::ostream &out) const n_it != nodes.end(); n_it++) { if(n_it->empty()) continue; + n_it->output(out, ns); + out << '\n'; + } +} +/*******************************************************************\ + +Function: local_SSAt::output_verbose + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::output_verbose(std::ostream &out) const +{ + for(nodest::const_iterator + n_it = nodes.begin(); + n_it != nodes.end(); n_it++) + { + if(n_it->empty()) continue; out << "*** " << n_it->location->location_number << " " << n_it->location->source_location << "\n"; n_it->output(out, ns); if(n_it->loophead!=nodes.end()) - out << "loop back to location " << n_it->loophead->location->location_number << "\n"; + out << "loop back to location " + << n_it->loophead->location->location_number << "\n"; out << "\n"; } out << "(enable) " << from_expr(ns, "", get_enabling_exprs()) << "\n\n"; @@ -1278,8 +1497,12 @@ void local_SSAt::nodet::output( std::ostream &out, const namespacet &ns) const { + if(!enabling_expr.is_true()) + out << "(enable) " << from_expr(ns, "", enabling_expr) << "\n"; +#if 0 if(!marked) out << "(not marked)" << "\n"; +#endif for(equalitiest::const_iterator e_it=equalities.begin(); e_it!=equalities.end(); @@ -1303,6 +1526,12 @@ void local_SSAt::nodet::output( f_it!=function_calls.end(); f_it++) out << "(F) " << from_expr(ns, "", *f_it) << "\n"; + +#if 0 + if(!assertions_after_loop.empty()) + out << "(assertions-after-loop) " + << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; +#endif } /*******************************************************************\ @@ -1364,10 +1593,14 @@ Function: local_SSAt::operator << \*******************************************************************/ -std::list & operator << ( - std::list &dest, +std::vector & operator << ( + std::vector &dest, const local_SSAt &src) { +#ifdef SLICING + ssa_slicert ssa_slicer; + ssa_slicer(dest,src); +#else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); n_it != src.nodes.end(); n_it++) { @@ -1377,7 +1610,10 @@ std::list & operator << ( e_it!=n_it->equalities.end(); e_it++) { - dest.push_back(*e_it); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + else + dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator @@ -1385,9 +1621,13 @@ std::list & operator << ( c_it!=n_it->constraints.end(); c_it++) { - dest.push_back(*c_it); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + else + dest.push_back(*c_it); } } +#endif return dest; } @@ -1404,10 +1644,14 @@ Function: local_SSAt::operator << \*******************************************************************/ -decision_proceduret & operator << ( - decision_proceduret &dest, +std::list & operator << ( + std::list &dest, const local_SSAt &src) { +#ifdef SLICING + ssa_slicert ssa_slicer; + ssa_slicer(dest,src); +#else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); n_it != src.nodes.end(); n_it++) { @@ -1417,7 +1661,10 @@ decision_proceduret & operator << ( e_it!=n_it->equalities.end(); e_it++) { - dest << *e_it; + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + else + dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator @@ -1425,9 +1672,13 @@ decision_proceduret & operator << ( c_it!=n_it->constraints.end(); c_it++) { - dest << *c_it; + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + else + dest.push_back(*c_it); } } +#endif return dest; } @@ -1444,10 +1695,17 @@ Function: local_SSAt::operator << \*******************************************************************/ -incremental_solvert & operator << ( - incremental_solvert &dest, +decision_proceduret & operator << ( + decision_proceduret &dest, const local_SSAt &src) { +#ifdef SLICING + std::list tmp; + tmp << src; + for(std::list::const_iterator it = tmp.begin(); + it != tmp.end(); it++) + dest << *it; +#else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); n_it != src.nodes.end(); n_it++) { @@ -1474,13 +1732,13 @@ incremental_solvert & operator << ( dest << *c_it; } } - +#endif return dest; } /*******************************************************************\ -Function: local_SSAt::get_enabling_expr +Function: local_SSAt::operator << Inputs: @@ -1490,23 +1748,50 @@ Function: local_SSAt::get_enabling_expr \*******************************************************************/ -exprt local_SSAt::get_enabling_exprs() const +incremental_solvert & operator << ( + incremental_solvert &dest, + const local_SSAt &src) { - exprt::operandst result; - result.reserve(enabling_exprs.size()); - for(std::list::const_iterator it = enabling_exprs.begin(); - it != enabling_exprs.end(); ++it) +#ifdef SLICING + std::list tmp; + tmp << src; + for(std::list::const_iterator it = tmp.begin(); + it != tmp.end(); it++) + dest << *it; +#else + for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); + n_it != src.nodes.end(); n_it++) { - std::list::const_iterator lh = it; lh++; - if(lh != enabling_exprs.end()) result.push_back(not_exprt(*it)); - else result.push_back(*it); + if(n_it->marked) continue; + for(local_SSAt::nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + { + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr,*e_it); + else + dest << *e_it; + } + + for(local_SSAt::nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + { + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr,*c_it); + else + dest << *c_it; + } } - return conjunction(result); +#endif + return dest; } /*******************************************************************\ -Function: local_SSAt::has_function_calls +Function: local_SSAt::get_enabling_expr Inputs: @@ -1516,18 +1801,16 @@ Function: local_SSAt::has_function_calls \*******************************************************************/ -bool local_SSAt::has_function_calls() const +exprt local_SSAt::get_enabling_exprs() const { - bool found = false; - for(local_SSAt::nodest::const_iterator n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + exprt::operandst result; + result.reserve(enabling_exprs.size()); + for(std::list::const_iterator it = enabling_exprs.begin(); + it != enabling_exprs.end(); ++it) { - if(!n_it->function_calls.empty()) - { - found = true; - break; - } + std::list::const_iterator lh = it; lh++; + if(lh != enabling_exprs.end()) result.push_back(not_exprt(*it)); + else result.push_back(*it); } - return found; + return conjunction(result); } - diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 7ba3d70f4..5b9a064ca 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -19,6 +19,11 @@ Author: Daniel Kroening, kroening@kroening.com #include "guard_map.h" #include "ssa_object.h" +#define TEMPLATE_PREFIX "__CPROVER_template" +#define TEMPLATE_DECL TEMPLATE_PREFIX +#define TEMPLATE_NEWVAR TEMPLATE_PREFIX "_newvar" +#define TEMPLATE_PARAM_PREFIX TEMPLATE_PREFIX "_param" + class local_SSAt { public: @@ -31,7 +36,8 @@ class local_SSAt const std::string &_suffix=""): ns(_ns), goto_function(_goto_function), ssa_objects(_goto_function, ns), - assignments(_goto_function.body, ns, ssa_objects), + ssa_value_ai(_goto_function, ns), + assignments(_goto_function.body, ns, ssa_objects, ssa_value_ai), guard_map(_goto_function.body), ssa_analysis(assignments), suffix(_suffix) @@ -40,6 +46,7 @@ class local_SSAt } void output(std::ostream &) const; + void output_verbose(std::ostream &) const; // the SSA node for a location class nodet @@ -53,8 +60,13 @@ class local_SSAt marked(false), location(_location), loophead(_loophead) - { } - + { + } + + exprt enabling_expr; //for incremental unwinding + + bool marked; //for incremental solving + typedef std::vector equalitiest; equalitiest equalities; @@ -63,24 +75,27 @@ class local_SSAt typedef std::vector assertionst; assertionst assertions; + exprt::operandst assertions_after_loop; //for k-induction assertion hoisting + + typedef std::vector assumptionst; + assertionst assumptions; typedef std::vector function_callst; function_callst function_calls; - exprt enabling_expr; //for incremental unwinding - - bool marked; //for incremental unwinding + //custom invariant templates + typedef std::vector templatest; + templatest templates; locationt location; //link to goto instruction std::list::iterator loophead; //link to loop head node - // otherwise points to nodes.end() void output(std::ostream &, const namespacet &) const; inline bool empty() const { return equalities.empty() && constraints.empty() && - assertions.empty() && function_calls.empty(); + assertions.empty() && function_calls.empty(); } }; @@ -96,6 +111,11 @@ class local_SSAt for(nodest::iterator n_it=nodes.begin(); n_it!=nodes.end(); n_it++) n_it->marked = true; } + void unmark_nodes() + { + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) n_it->marked = false; + } // for incremental unwinding std::list enabling_exprs; @@ -107,8 +127,6 @@ class local_SSAt var_listt params; var_sett globals_in, globals_out; - bool has_function_calls() const; - const namespacet &ns; const goto_functiont &goto_function; @@ -135,15 +153,18 @@ class local_SSAt exprt read_rhs_rec(const exprt &, locationt loc) const; symbol_exprt read_rhs(const ssa_objectt &, locationt loc) const; exprt read_node_in(const ssa_objectt &, locationt loc) const; - void assign_rec(const exprt &lhs, const exprt &rhs, locationt loc); + void assign_rec(const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); void get_entry_exit_vars(); bool has_static_lifetime(const ssa_objectt &) const; bool has_static_lifetime(const exprt &) const; + + exprt dereference(const exprt &expr, locationt loc) const; ssa_objectst ssa_objects; typedef ssa_objectst::objectst objectst; + ssa_value_ait ssa_value_ai; assignmentst assignments; //protected: @@ -160,7 +181,8 @@ class local_SSAt nodest::iterator find_node(locationt loc); nodest::const_iterator find_node(locationt loc) const; void find_nodes(locationt loc, std::list &_nodes) const; - + locationt find_location_by_number(unsigned location_number) const; + protected: // build the SSA formulas void build_SSA(); @@ -172,8 +194,18 @@ class local_SSAt void build_guard(locationt loc); void build_function_call(locationt loc); void build_assertions(locationt loc); + void build_assumptions(locationt loc); + void assertions_after_loop(); + + // custom templates + void collect_custom_templates(); + replace_mapt template_newvars; + exprt template_last_newvar; }; +std::vector & operator << + (std::vector &dest, const local_SSAt &src); + std::list & operator << (std::list &dest, const local_SSAt &src); diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index d8d026b6d..583f425f0 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -6,11 +6,14 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +#include + #include #include #include #include #include +#include #include #include @@ -64,11 +67,12 @@ Function: malloc_ssa exprt malloc_ssa( const side_effect_exprt &code, const std::string &suffix, - const namespacet &ns) + symbol_tablet &symbol_table) { if(code.operands().size()!=1) throw "malloc expected to have one operand"; - + + namespacet ns(symbol_table); exprt size=code.op0(); typet object_type=nil_typet(); @@ -82,6 +86,14 @@ exprt malloc_ssa( c_sizeof_type_rec(size.op0()), size.op1()); } + else if(size.id()==ID_mult && + size.operands().size()==2 && + size.op1().find(ID_C_c_sizeof_type).is_not_nil()) + { + object_type=array_typet( + c_sizeof_type_rec(size.op1()), + size.op0()); + } else { typet tmp_type=c_sizeof_type_rec(size); @@ -89,7 +101,7 @@ exprt malloc_ssa( if(tmp_type.is_not_nil()) { // Did the size get multiplied? - mp_integer elem_size=pointer_offset_size(ns, tmp_type); + mp_integer elem_size=pointer_offset_size(tmp_type, ns); mp_integer alloc_size; if(elem_size<0 || to_integer(size, alloc_size)) { @@ -113,16 +125,21 @@ exprt malloc_ssa( if(object_type.is_nil()) object_type=array_typet(unsigned_char_type(), size); } + +#if 0 + std::cout << "OBJECT_TYPE: " << from_type(ns, "", object_type) << std::endl; +#endif // value symbolt value_symbol; - + value_symbol.base_name="dynamic_object"+suffix; value_symbol.name="ssa::"+id2string(value_symbol.base_name); value_symbol.is_lvalue=true; value_symbol.type=object_type; value_symbol.type.set("#dynamic", true); value_symbol.mode=ID_C; + symbol_table.add(value_symbol); address_of_exprt address_of; @@ -148,3 +165,52 @@ exprt malloc_ssa( return result; } + +static void replace_malloc_rec(exprt &expr, + const std::string &suffix, + symbol_tablet &symbol_table, + const exprt &malloc_size, + unsigned &counter) +{ + if(expr.id()==ID_side_effect && + to_side_effect_expr(expr).get_statement()==ID_malloc) + { + assert(!malloc_size.is_nil()); + expr.op0() = malloc_size; + + expr = malloc_ssa(to_side_effect_expr(expr),"$"+i2string(counter++)+suffix,symbol_table); + } + else + Forall_operands(it,expr) + replace_malloc_rec(*it,suffix,symbol_table,malloc_size,counter); +} + +void replace_malloc(goto_modelt &goto_model, + const std::string &suffix) +{ + unsigned counter = 0; + Forall_goto_functions(f_it, goto_model.goto_functions) + { + exprt malloc_size = nil_exprt(); + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assign()) + { + code_assignt &code_assign = to_code_assign(i_it->code); + if(code_assign.lhs().id()==ID_symbol) + { + // we have to propagate the malloc size + // in order to get the object type + // TODO: this only works with inlining + const irep_idt &lhs_id = + to_symbol_expr(code_assign.lhs()).get_identifier(); + if(lhs_id == "malloc::malloc_size") + malloc_size = code_assign.rhs(); + } + replace_malloc_rec(code_assign.rhs(),suffix, + goto_model.symbol_table,malloc_size,counter); + } + } + } +} + diff --git a/src/ssa/malloc_ssa.h b/src/ssa/malloc_ssa.h index f9b66a170..d9ef8d6b1 100644 --- a/src/ssa/malloc_ssa.h +++ b/src/ssa/malloc_ssa.h @@ -10,10 +10,17 @@ Author: Daniel Kroening, kroening@kroening.com #define CPROVER_MALLOC_SSA_H #include +#include exprt malloc_ssa( const side_effect_exprt &, const std::string &suffix, - const namespacet &); + symbol_tablet &); + + +#if 1 +void replace_malloc(goto_modelt &goto_model, + const std::string &suffix); +#endif #endif diff --git a/src/ssa/ssa_domain.cpp b/src/ssa/ssa_domain.cpp index 3e0ff7dd2..ed852105a 100644 --- a/src/ssa/ssa_domain.cpp +++ b/src/ssa/ssa_domain.cpp @@ -135,7 +135,7 @@ bool ssa_domaint::merge( d_it_b++) { const irep_idt &id=d_it_b->first; - + // check if we have a phi node for 'id' phi_nodest::iterator p_it=phi_nodes.find(id); diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 13a54781e..3f03f818a 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -13,6 +13,93 @@ Author: Peter Schrammel /*******************************************************************\ +Function: ssa_inlinert::get_guard_binding + + Inputs: + + Outputs: + + Purpose: get guard binding for function call + +\*******************************************************************/ + +void ssa_inlinert::get_guard_binding( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter) +{ + exprt callee_guard, caller_guard; + callee_guard = fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); + rename(callee_guard, counter); + caller_guard = SSA.guard_symbol(n_it->location); + + guard_binding = equal_exprt(callee_guard, caller_guard); +} + +/*******************************************************************\ + +Function: ssa_inlinert::get_bindings + + Inputs: + + Outputs: + + Purpose: get bindings for function call + +\*******************************************************************/ + +void ssa_inlinert::get_bindings( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter) +{ + //getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc = n_it->location; + + SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_out,false); + +#if 0 + std::cout << "cs_globals_in: "; + for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); + it != cs_globals_in.end(); it++) + std::cout << from_expr(SSA.ns,"",*it) << " "; + std::cout << std::endl; + + std::cout << "cs_globals_out: "; + for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); + it != cs_globals_out.end(); it++) + std::cout << from_expr(SSA.ns,"",*it) << " "; + std::cout << std::endl; +#endif + + //equalities for arguments + //bindings_in.push_back(get_replace_params(SSA.params,*f_it)); + get_replace_params(SSA,fSSA.params,n_it,*f_it,bindings_in,counter); + + //equalities for globals_in + //bindings_in.push_back(get_replace_globals_in(SSA.globals_in,cs_globals_in)); + + //get_replace_globals_in(fSSA.globals_in,cs_globals_in,bindings_in,counter); + get_replace_globals_in(fSSA.globals_in,*f_it,cs_globals_in,bindings_in,counter); + + //equalities for globals out (including unmodified globals) + //bindings_out.push_back(get_replace_globals_out(SSA.globals_out,cs_globals_in,cs_globals_out)); + + //get_replace_globals_out(fSSA.globals_out,cs_globals_in,cs_globals_out,bindings_out,counter); + get_replace_globals_out(fSSA.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings_out,counter); + +} + +/*******************************************************************\ + Function: ssa_inlinert::get_summary Inputs: @@ -30,10 +117,10 @@ void ssa_inlinert::get_summary( const summaryt &summary, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings) + exprt::operandst &bindings, + int counter, + bool error_summ) { - counter++; - //getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc = n_it->location; @@ -63,37 +150,57 @@ void ssa_inlinert::get_summary( #endif //equalities for arguments - bindings.push_back(get_replace_params(summary.params,*f_it)); + get_replace_params(SSA,summary.params,n_it,*f_it,bindings,counter); //equalities for globals_in - if(forward) - bindings.push_back(get_replace_globals_in(summary.globals_in, - cs_globals_in)); - else - bindings.push_back(get_replace_globals_in(summary.globals_out, - cs_globals_out)); + if(forward){ + //get_replace_globals_in(summary.globals_in,cs_globals_in,bindings,counter); + get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); + } + else{ + //get_replace_globals_in(summary.globals_out,cs_globals_out,bindings,counter); + get_replace_globals_in(summary.globals_out,*f_it,cs_globals_out,bindings,counter); + } //constraints for transformer + exprt transformer; - if(forward) - transformer = summary.fw_transformer.is_nil() ? true_exprt() : - summary.fw_transformer; - else - { - transformer = summary.bw_transformer.is_nil() ? true_exprt() : - summary.bw_transformer; - } - rename(transformer); + + if(error_summ) + { + // update transformer using the error_summaries map + summaryt::call_sitet call_site(loc); + summaryt::error_summariest::const_iterator e_it = + summary.error_summaries.find(call_site); + if(e_it != summary.error_summaries.end() && + !e_it->second.is_nil()) + transformer = e_it->second; + else + transformer = true_exprt(); + } + else + { + if(forward) + transformer = summary.fw_transformer.is_nil() ? true_exprt() : + summary.fw_transformer; + else + transformer = summary.bw_transformer.is_nil() ? true_exprt() : + summary.bw_transformer; + } + + rename(transformer,counter); summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), transformer)); //equalities for globals out (including unmodified globals) - if(forward) - bindings.push_back(get_replace_globals_out(summary.globals_out, - cs_globals_in,cs_globals_out)); - else - bindings.push_back(get_replace_globals_out(summary.globals_in, - cs_globals_out,cs_globals_in)); + if(forward){ + //get_replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,bindings,counter); + get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); + } + else{ + //get_replace_globals_out(summary.globals_in,cs_globals_out,cs_globals_in,bindings,counter); + get_replace_globals_out(summary.globals_in,*f_it,cs_globals_out,cs_globals_in,bindings,counter); + } } /*******************************************************************\ @@ -116,9 +223,9 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) } void ssa_inlinert::get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings) { for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) @@ -132,13 +239,57 @@ void ssa_inlinert::get_summaries(const local_SSAt &SSA, if(summary_db.exists(fname)) { + counter++; get_summary(SSA,n_it,f_it,summary_db.get(fname), - forward,summaries,bindings); + forward,summaries,bindings,counter); } } } } +bool ssa_inlinert::get_summaries(const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings) +{ + bool assertion_flag = false; + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it = + n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) + { + //do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site == this_call_site) + continue; + + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries + if(summary_db.exists(fname)) + { + summaryt summary = summary_db.get(fname); + if(summary.has_assertion == true){ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); + assertion_flag = true; + } + else{ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); + } + } + } + } + return assertion_flag; +} + + /*******************************************************************\ Function: ssa_inlinert::replace @@ -155,7 +306,8 @@ Function: ssa_inlinert::replace void ssa_inlinert::replace(local_SSAt &SSA, bool forward, - bool preconditions_as_assertions) + bool preconditions_as_assertions, + int counter) { for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) @@ -181,7 +333,7 @@ void ssa_inlinert::replace(local_SSAt &SSA, //replace replace(SSA,n_it,f_it,cs_globals_in,cs_globals_out,summary, - forward,preconditions_as_assertions); + forward,preconditions_as_assertions,counter); //remove function_call rm_function_calls.insert(f_it); @@ -207,58 +359,64 @@ Function: ssa_inlinert::replace \*******************************************************************/ +/* +void ssa_inlinert::replace(local_SSAt &SSA, + const ssa_dbt &ssa_db, + bool recursive, bool rename) +*/ void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - bool recursive, bool rename) + const ssa_dbt &ssa_db, + int counter, + bool recursive, bool rename) { for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(ssa_db.exists(fname)) - { - status() << "Inlining function " << fname << eom; - local_SSAt fSSA = ssa_db.get(fname); //copy - - if(rename) - { - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - - if(recursive) - { - replace(fSSA,ssa_db,true); - } - - //replace - replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA); - } - else // just add to nodes + for(local_SSAt::nodet::function_callst::iterator + f_it = n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) { - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++) - { - debug() << "new node: "; fn_it->output(debug(),fSSA.ns); - debug() << eom; - - new_nodes.push_back(*fn_it); - } + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + if(ssa_db.exists(fname)) + { + status() << "Inlining function " << fname << eom; + local_SSAt fSSA = ssa_db.get(fname); //copy + + if(rename) + { + //getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc = n_it->location; + SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_out,false); + + if(recursive) + { + replace(fSSA,ssa_db,true,counter); + } + + //replace + replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); + } + else // just add to nodes + { + for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++) + { + debug() << "new node: "; fn_it->output(debug(),fSSA.ns); + debug() << eom; + + new_nodes.push_back(*fn_it); + } + } + } + else debug() << "No body available for function " << fname << eom; + commit_node(n_it); } - } - else debug() << "No body available for function " << fname << eom; - commit_node(n_it); + commit_nodes(SSA.nodes,n_it); } - commit_nodes(SSA.nodes,n_it); - } } /*******************************************************************\ @@ -274,52 +432,51 @@ Function: ssa_inlinert::replace() \*******************************************************************/ void ssa_inlinert::replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const summaryt &summary, - bool forward, - bool preconditions_as_assertions) + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + bool forward, + bool preconditions_as_assertions, + int counter) { - counter++; - //equalities for arguments - replace_params(summary.params,*f_it); + replace_params(summary.params,*f_it,counter); //equalities for globals_in - replace_globals_in(summary.globals_in,cs_globals_in); + replace_globals_in(summary.globals_in,cs_globals_in,counter); //constraints for precondition and transformer exprt precondition; if(forward) precondition = summary.fw_precondition; else precondition = summary.bw_precondition; if(!preconditions_as_assertions) - { - rename(precondition); - node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); - } + { + rename(precondition,counter); + node->constraints.push_back( + implies_exprt(SSA.guard_symbol(node->location), + precondition)); + } else - { - rename(precondition); - node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); - } + { + rename(precondition,counter); + node->assertions.push_back( + implies_exprt(SSA.guard_symbol(node->location), + precondition)); + } exprt transformer; if(forward) transformer = summary.fw_transformer; else transformer = summary.bw_transformer; node->constraints.push_back(transformer); //copy exprt &_transformer = node->constraints.back(); - rename(_transformer); + rename(_transformer,counter); //remove function call rm_function_calls.insert(f_it); //equalities for globals out (including unmodified globals) - replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out); + replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,counter); } /*******************************************************************\ @@ -339,34 +496,33 @@ void ssa_inlinert::replace(local_SSAt &SSA, \*******************************************************************/ void ssa_inlinert::replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const local_SSAt &function) + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &function, + int counter) { - counter++; - //equalities for arguments - replace_params(function.params,*f_it); + replace_params(function.params,*f_it,counter); //equalities for globals_in - replace_globals_in(function.globals_in,cs_globals_in); - + replace_globals_in(function.globals_in,cs_globals_in,counter); + //add function body for(local_SSAt::nodest::const_iterator n_it = function.nodes.begin(); n_it != function.nodes.end(); n_it++) - { + { local_SSAt::nodet n = *n_it; //copy - rename(n); + rename(n,counter); new_nodes.push_back(n); } //remove function call rm_function_calls.insert(f_it); - + //equalities for globals out (including unmodified globals) - replace_globals_out(function.globals_out,cs_globals_in,cs_globals_out); + replace_globals_out(function.globals_out,cs_globals_in,cs_globals_out,counter); } /*******************************************************************\ @@ -381,48 +537,55 @@ Function: ssa_inlinert::replace_globals_in() \*******************************************************************/ -exprt ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals) +void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter) { + std::string suffix = id2string(funapp_expr.get(ID_suffix)); + //equalities for globals_in - exprt::operandst c; for(summaryt::var_sett::const_iterator it = globals_in.begin(); it != globals_in.end(); it++) - { - symbol_exprt lhs = *it; //copy - rename(lhs); - symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) { - debug() << "binding: " << lhs.get_identifier() << " == " - << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs,rhs)); - } + symbol_exprt lhs = *it; //copy + rename(lhs,counter); + symbol_exprt rhs; + if(find_corresponding_symbol(*it,globals,rhs)) + { + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + c.push_back(equal_exprt(lhs,rhs)); + } #if 0 - else - warning() << "'" << it->get_identifier() - << "' not bound in caller" << eom; + else + warning() << "'" << it->get_identifier() + << "' not bound in caller" << eom; #endif - } - return conjunction(c); + } + //return conjunction(c); } void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals) + const local_SSAt::var_sett &globals, + int counter) { //equalities for globals_in for(summaryt::var_sett::const_iterator it = globals_in.begin(); it != globals_in.end(); it++) { symbol_exprt lhs = *it; //copy - rename(lhs); + rename(lhs,counter); symbol_exprt rhs; if(find_corresponding_symbol(*it,globals,rhs)) - { - debug() << "binding: " << lhs.get_identifier() << " == " - << rhs.get_identifier() << eom; - new_equs.push_back(equal_exprt(lhs,rhs)); - } + { + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + new_equs.push_back(equal_exprt(lhs,rhs)); + } #if 0 else warning() << "'" << it->get_identifier() @@ -443,50 +606,88 @@ Function: ssa_inlinert::replace_params() \*******************************************************************/ -exprt ssa_inlinert::get_replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) +void ssa_inlinert::get_replace_params(const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter) { + //std::string suffix = id2string(funapp_expr.get(ID_suffix)); + //equalities for arguments - exprt::operandst c; local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); it != funapp_expr.arguments().end(); it++, p_it++) - { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; - break; +#if 0 + std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*it) << std::endl; +#endif + +#if 0 + local_SSAt::var_listt::const_iterator next_p_it = p_it; + if(funapp_expr.arguments().size() != params.size() && + ++next_p_it==params.end()) //TODO: handle ellipsis + { + warning() << "ignoring excess function arguments" << eom; + break; + } +#endif + + if(SSA.ns.follow(it->type()).id()==ID_struct) + { + exprt rhs = SSA.read_rhs(*it, n_it->location); //copy +#if 0 + std::cout << "split param " << from_expr(SSA.ns,"",*it) + << " into " << from_expr(SSA.ns,"",rhs) << std::endl; +#endif + forall_operands(o_it, rhs) + { + assert(p_it!=params.end()); + exprt lhs = *p_it; //copy + rename(lhs,counter); +#if 0 + std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; +#endif + c.push_back(equal_exprt(lhs,*o_it)); + ++p_it; + } + } + else + { + exprt lhs = *p_it; //copy + rename(lhs,counter); + c.push_back(equal_exprt(lhs,*it)); + //symbol_exprt sexpr = to_symbol_expr(*it); + //sexpr.set_identifier(id2string(sexpr.get_identifier())+suffix); + //c.push_back(equal_exprt(lhs,sexpr)); + } } - - exprt lhs = *p_it; //copy - rename(lhs); - c.push_back(equal_exprt(lhs,*it)); - } - return conjunction(c); } void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr) + const function_application_exprt &funapp_expr, + int counter) { //equalities for arguments local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); it != funapp_expr.arguments().end(); it++, p_it++) - { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; - break; + local_SSAt::var_listt::const_iterator next_p_it = p_it; + if(funapp_expr.arguments().size() != params.size() && + ++next_p_it==params.end()) //TODO: handle ellipsis + { + warning() << "ignoring excess function arguments" << eom; + break; + } + + exprt lhs = *p_it; //copy + rename(lhs,counter); + new_equs.push_back(equal_exprt(lhs,*it)); } - - exprt lhs = *p_it; //copy - rename(lhs); - new_equs.push_back(equal_exprt(lhs,*it)); - } } /*******************************************************************\ @@ -501,44 +702,52 @@ Function: ssa_inlinert::replace_globals_out() \*******************************************************************/ -exprt ssa_inlinert::get_replace_globals_out( - const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) +void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter) { + std::string suffix = id2string(funapp_expr.get(ID_suffix)); + //equalities for globals_out - exprt::operandst c; for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) - { - symbol_exprt rhs = *it; //copy - symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs); - else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - c.push_back(equal_exprt(lhs,rhs)); - } - return conjunction (c); + { + symbol_exprt lhs = *it; //copy + + lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + + symbol_exprt rhs; + if(find_corresponding_symbol(*it,globals_out,rhs)) + rename(rhs,counter); + else{ + bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); + assert(found); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + } + c.push_back(equal_exprt(lhs,rhs)); + } } -void ssa_inlinert::replace_globals_out( - const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out) +void ssa_inlinert::replace_globals_out(const local_SSAt::var_sett &globals_out, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter) { //equalities for globals_out for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) - { - symbol_exprt rhs = *it; //copy - symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs); - else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - new_equs.push_back(equal_exprt(lhs,rhs)); - } + { + symbol_exprt rhs = *it; //copy + symbol_exprt lhs; + if(find_corresponding_symbol(*it,globals_out,lhs)) + rename(lhs,counter); + else + assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); + new_equs.push_back(equal_exprt(lhs,rhs)); + } } /*******************************************************************\ @@ -572,19 +781,90 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(exprt &expr) +irep_idt ssa_inlinert::rename(irep_idt &id, int counter) { - if(expr.id()==ID_symbol) - { - symbol_exprt &sexpr = to_symbol_expr(expr); - irep_idt id = id2string(sexpr.get_identifier())+"@"+i2string(counter); - sexpr.set_identifier(id); + return id2string(id)+"@"+i2string(counter); +} + + +/*******************************************************************\ + +Function: ssa_inlinert::rename() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ + + std::string id_str = id2string(id); + + if(attach == false){ + //find first @ where afterwards there are no letters + size_t pos = std::string::npos; + for(size_t i=0;iarguments().size()); @@ -710,36 +990,36 @@ void ssa_inlinert::rename_to_callee( local_SSAt::var_listt::const_iterator next_p_it = p_it; if(f_it->arguments().size() != params.size() && ++next_p_it==params.end()) //TODO: handle ellipsis - { - warning() << "ignoring excess function arguments" << eom; - break; - } + { + warning() << "ignoring excess function arguments" << eom; + break; + } replace_map[*it] = *p_it; } -/* replace_expr(replace_map,expr); + /* replace_expr(replace_map,expr); replace_map.clear(); //arguments might contain globals, // thus, we have to replace them separately */ for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); it != cs_globals_in.end(); it++) - { - symbol_exprt cg; - if(find_corresponding_symbol(*it,globals_in,cg)) - replace_map[*it] = cg; - else { + symbol_exprt cg; + if(find_corresponding_symbol(*it,globals_in,cg)) + replace_map[*it] = cg; + else + { #if 0 - warning() << "'" << it->get_identifier() - << "' not bound in caller" << eom; + warning() << "'" << it->get_identifier() + << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + replace_map[*it] = + symbol_exprt(id2string(it->get_identifier())+ + "@"+i2string(++counter),it->type()); + } } - } - + replace_expr(replace_map,expr); } @@ -814,16 +1094,16 @@ bool ssa_inlinert::find_corresponding_symbol(const symbol_exprt &s, const irep_idt &s_orig_id = get_original_identifier(s); for(local_SSAt::var_sett::const_iterator it = globals.begin(); it != globals.end(); it++) - { + { #if 0 - std::cout << s_orig_id << " =?= " << get_original_identifier(*it) << std::endl; + std::cout << s_orig_id << " =?= " << get_original_identifier(*it) << std::endl; #endif - if(s_orig_id == get_original_identifier(*it)) - { - s_found = *it; - return true; + if(s_orig_id == get_original_identifier(*it)) + { + s_found = *it; + return true; + } } - } return false; } diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index e9a5634f1..e588c3354 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -16,57 +16,83 @@ Author: Peter Schrammel #include "../ssa/local_ssa.h" class summary_dbt; +class ssa_dbt; class ssa_inlinert : public messaget { public: explicit ssa_inlinert(summary_dbt &_summary_db) : - counter(0), + counter(0), summary_db(_summary_db) {} + void get_guard_binding(const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter); + void get_bindings(const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter); void get_summary(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const summaryt &summary, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + int counter, + bool error_summ = false); void get_summaries(const local_SSAt &SSA, bool forward, exprt::operandst &summaries, - exprt::operandst &bindings); - exprt get_summaries(const local_SSAt &SSA); + exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + bool get_summaries(const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + exprt get_summaries(const local_SSAt &SSA); //TODO: need to explicitly pass the correct counter + void replace(local_SSAt &SSA, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site + //incoming globals at call site const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site + //outgoing globals at call site const summaryt &summary, bool forward, - bool preconditions_as_assertions); + bool preconditions_as_assertions, + int counter); void replace(local_SSAt &SSA, bool forward, - bool preconditions_as_assertions); + bool preconditions_as_assertions, + int counter); void replace(local_SSAt::nodest &nodes, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site + //incoming globals at call site const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site - const local_SSAt &function); + //outgoing globals at call site + const local_SSAt &function, + int counter); void replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, + const ssa_dbt &ssa_db, + int counter, bool recursive=false, bool rename=true); - + void havoc(local_SSAt::nodet &node, local_SSAt::nodet::function_callst::iterator f_it); @@ -77,22 +103,34 @@ class ssa_inlinert : public messaget //functions for renaming preconditions to calling context void rename_to_caller(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); void rename_to_callee(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); - + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); + static bool find_corresponding_symbol(const symbol_exprt &s, const local_SSAt::var_sett &globals, symbol_exprt &s_found); static irep_idt get_original_identifier(const symbol_exprt &s); + + irep_idt rename(irep_idt &id, int counter); + + irep_idt rename(irep_idt &id, int counter, bool attach); + int get_rename_counter(){ + counter++; + return counter; + } + + // moved from protected to public, for use in summarizer_bw_cex_complete + void rename(exprt &expr, int counter); + protected: unsigned counter; summary_dbt &summary_db; @@ -102,23 +140,35 @@ class ssa_inlinert : public messaget std::set rm_function_calls; void replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals); + const local_SSAt::var_sett &globals, + int counter); void replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); + const function_application_exprt &funapp_expr, + int counter); void replace_globals_out(const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); - - exprt get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals); - exprt get_replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr); - exprt get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out); - - void rename(exprt &expr); - void rename(local_SSAt::nodet &node); + const local_SSAt::var_sett &cs_globals_out, + int counter); + + void get_replace_globals_in(const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter); + void get_replace_params(const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter); + void get_replace_globals_out(const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter); + + void rename(local_SSAt::nodet &node, int counter); }; diff --git a/src/ssa/ssa_object.cpp b/src/ssa/ssa_object.cpp index 585fe623f..834db068a 100644 --- a/src/ssa/ssa_object.cpp +++ b/src/ssa/ssa_object.cpp @@ -6,12 +6,39 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +//#define DEBUG + +#ifdef DEBUG +#include +#include +#endif + +#include + #include #include "ssa_object.h" /*******************************************************************\ +Function: is_ptr_object + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool is_ptr_object(const exprt &src) +{ + return src.id()==ID_symbol && + src.get(ID_ptr_object)!=irep_idt(); +} + +/*******************************************************************\ + Function: collect_objects_rec Inputs: @@ -34,6 +61,10 @@ void collect_objects_address_of_rec( std::set &objects, std::set &literals) { +#ifdef DEBUG + std::cout << "COLLECT ADDRESS OF " << from_expr(ns,"",src) << "\n"; +#endif + if(src.id()==ID_index) { collect_objects_address_of_rec( @@ -56,6 +87,11 @@ void collect_objects_address_of_rec( { literals.insert(src); } + else if(src.id()==ID_symbol) + { + collect_objects_rec( + src, ns, objects, literals); + } } /*******************************************************************\ @@ -76,6 +112,11 @@ void collect_objects_rec( std::set &objects, std::set &literals) { + + #ifdef DEBUG + std::cout << "COLLECT " << from_expr(ns,"",src) << "\n"; + #endif + if(src.id()==ID_code) { forall_operands(it, src) @@ -96,7 +137,7 @@ void collect_objects_rec( return; ssa_objectt ssa_object(src, ns); - + if(ssa_object) { if(type.id()==ID_struct) @@ -114,11 +155,16 @@ void collect_objects_rec( member_exprt new_src(src, it->get_name(), it->type()); collect_objects_rec(new_src, ns, objects, literals); // recursive call } - - return; // done } - - objects.insert(ssa_object); + else + { + + #ifdef DEBUG + std::cout << "OBJECT " << ssa_object.get_identifier() << "\n"; + #endif + + objects.insert(ssa_object); + } } else { @@ -143,6 +189,17 @@ void ssa_objectst::collect_objects( const goto_functionst::goto_functiont &src, const namespacet &ns) { + // Add objects for parameters. + for(goto_functionst::goto_functiont::parameter_identifierst:: + const_iterator it=src.parameter_identifiers.begin(); + it!=src.parameter_identifiers.end(); + it++) + { + symbol_exprt symbol=ns.lookup(*it).symbol_expr(); + collect_objects_rec(symbol, ns, objects, literals); + } + + // Rummage through body. forall_goto_program_instructions(it, src.body) { collect_objects_rec(it->guard, ns, objects, literals); @@ -152,6 +209,51 @@ void ssa_objectst::collect_objects( /*******************************************************************\ +Function: ssa_objectst::add_ptr_objects + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_objectst::add_ptr_objects( + const namespacet &ns) +{ + objectst tmp; + + for(objectst::const_iterator o_it=objects.begin(); + o_it!=objects.end(); + o_it++) + { + exprt root_object=o_it->get_root_object(); + if(root_object.id()==ID_symbol) + { + if(o_it->type().id()==ID_pointer) + { + const symbolt &symbol=ns.lookup(root_object); + if(symbol.is_parameter) + tmp.insert(*o_it); + } + } + } + + for(objectst::const_iterator o_it=tmp.begin(); + o_it!=tmp.end(); + o_it++) + { + typet type=o_it->type().subtype(); + irep_idt identifier=id2string(o_it->get_identifier())+"'obj"; + symbol_exprt ptr_object(identifier, type); + ptr_object.set(ID_ptr_object, o_it->get_identifier()); + collect_objects_rec(ptr_object, ns, objects, literals); + } +} + +/*******************************************************************\ + Function: ssa_objectst::categorize_objects Inputs: @@ -173,18 +275,29 @@ void ssa_objectst::categorize_objects( o_it++) { exprt root_object=o_it->get_root_object(); + +#ifdef DEBUG + std::cout << "CATEGORIZE " << from_expr(ns,"",root_object) << "\n"; +#endif + if(root_object.id()==ID_symbol) { - const symbolt &symbol=ns.lookup(root_object); - if(symbol.is_procedure_local()) + if(is_ptr_object(root_object)) { - if(dirty(symbol.name)) - dirty_locals.insert(*o_it); - else - clean_locals.insert(*o_it); } else - globals.insert(*o_it); + { + const symbolt &symbol=ns.lookup(root_object); + if(symbol.is_procedure_local()) + { + if(dirty(symbol.name)) + dirty_locals.insert(*o_it); + else + clean_locals.insert(*o_it); + } + else + globals.insert(*o_it); + } } } } @@ -233,13 +346,13 @@ Function: ssa_objectt::object_id_rec \*******************************************************************/ -irep_idt ssa_objectt::object_id_rec( +ssa_objectt::identifiert ssa_objectt::object_id_rec( const exprt &src, const namespacet &ns) { if(src.id()==ID_symbol) { - return to_symbol_expr(src).get_identifier(); + return identifiert(to_symbol_expr(src).get_identifier()); } else if(src.id()==ID_member) { @@ -250,37 +363,29 @@ irep_idt ssa_objectt::object_id_rec( if(is_struct_member(member_expr, ns)) { irep_idt compound_object=object_id_rec(compound_op, ns); - if(compound_object==irep_idt()) return irep_idt(); + if(compound_object==irep_idt()) return identifiert(); - return id2string(compound_object)+ - "."+id2string(member_expr.get_component_name()); + return identifiert( + id2string(compound_object)+ + "."+id2string(member_expr.get_component_name())); } else - return irep_idt(); + return identifiert(); } else if(src.id()==ID_index) { - #if 0 - const index_exprt &index_expr=to_index_expr(src); - return id2string(object_id_rec(index_expr.array()))+ - "["+"]"; - #else - return irep_idt(); - #endif + return identifiert(); } else if(src.id()==ID_dereference) { - #if 0 - const dereference_exprt &dereference_expr=to_dereference_expr(src); - irep_idt pointer_object=object_id_rec(dereference_expr.pointer(), ns); - if(pointer_object==irep_idt()) return irep_idt(); - return id2string(pointer_object)+"'obj"; - #else - return irep_idt(); - #endif + return identifiert(); + } + else if(src.id()==ID_ptr_object) + { + return identifiert(id2string(src.get(ID_identifier))+"'obj"); } else - return irep_idt(); + return identifiert(); } /*******************************************************************\ diff --git a/src/ssa/ssa_object.h b/src/ssa/ssa_object.h index 04192b4da..27d1df1e8 100644 --- a/src/ssa/ssa_object.h +++ b/src/ssa/ssa_object.h @@ -14,24 +14,43 @@ Author: Daniel Kroening, kroening@kroening.com class ssa_objectt { public: + // type specialisation for object identifiers + class identifiert:public irep_idt + { + public: + inline explicit identifiert(const irep_idt &_src):irep_idt(_src) + { + } + + inline identifiert() + { + } + }; + inline explicit ssa_objectt(const exprt &_expr, const namespacet &_ns): expr(_expr), identifier(object_id_rec(expr, _ns)) { } + inline const typet &type() const + { + return expr.type(); + } + inline const exprt &get_expr() const { return expr; } - inline irep_idt get_identifier() const + inline identifiert get_identifier() const { return identifier; } // The identifier is unique, so ordering and comparison - // can be done on the identifier. + // can be done on the identifier, which in turn is + // an integer. inline bool operator<(const ssa_objectt &other) const { return identifier objectst; - objectst objects, dirty_locals, clean_locals, globals; + objectst objects, dirty_locals, clean_locals, globals, ptr_objects; // literals whose address is taken typedef std::set literalst; @@ -84,6 +103,7 @@ class ssa_objectst const namespacet &ns) { collect_objects(goto_function, ns); + add_ptr_objects(ns); categorize_objects(goto_function, ns); } @@ -95,8 +115,13 @@ class ssa_objectst void categorize_objects( const goto_functionst::goto_functiont &, const namespacet &); + + void add_ptr_objects( + const namespacet &); }; +bool is_ptr_object(const exprt &); + // Returns true if the member expression is a struct member // expression. bool is_struct_member(const member_exprt &, const namespacet &); @@ -105,12 +130,4 @@ bool is_struct_member(const member_exprt &, const namespacet &); // all members are struct members. bool is_symbol_struct_member(const exprt &, const namespacet &); -// Returns true for ((*ptr)|symbol)(.member)*, where -// all members are struct members. -bool is_symbol_or_deref_struct_member(const exprt &, const namespacet &); - -// Returns true for (*ptr)(.member)*, where -// all members are struct members. -bool is_deref_struct_member(const exprt &, const namespacet &); - #endif diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index e8840960f..b00417102 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -1,7 +1,7 @@ /******************************************************************* Module: SSA Unwinder - Author: Peter Schrammel + Author: Saurabh Joshi \*******************************************************************/ @@ -10,9 +10,52 @@ #include #include #include +#include #include "ssa_unwinder.h" + +void ssa_local_unwindert::set_return_var(const irep_idt& id) +{ + return_var="c::"+id2string(id)+"#return_value"; +} +std::string ssa_local_unwindert::keep_first_two_hash(const std::string& str) const +{ + std::size_t pos = str.find('#'); + if(pos==std::string::npos) return str; + pos=str.find('#',pos+1); + if(pos==std::string::npos) return str; + return str.substr(0,pos); +} +void ssa_local_unwindert::dissect_loop_suffix(const irep_idt& id, + irep_idt& before_suffix,std::list& iterations,bool baseonly) const +{ + std::string s = id2string(id); + + std::size_t pos=s.find_first_of("%"); + if(pos==std::string::npos) { return; } + + before_suffix=s.substr(0,pos); + if(baseonly) return; + std::size_t pos1; + + do + { + pos1=s.find_first_of("%",pos+1); + if(pos1==std::string::npos) continue; + // if(pos1==pos+1) {assert(false&& "Ill formed loop suffix");} + //TODO use safe string to unsigned + + iterations.push_back(string2integer(s.substr(pos+1,pos1-(pos+1)),10).to_ulong()); + pos=pos1; + + }while(pos1!=std::string::npos); + + //if(pos==(s.length()-1)) {assert(false && "Ill-formed loop suffix");} + + iterations.push_back(string2integer(s.substr(pos+1)).to_ulong()); + +} /***************************************************************************** * * Function : ssa_local_unwindert::get_base_name @@ -31,6 +74,17 @@ irep_idt ssa_local_unwindert::get_base_name(const irep_idt& id) std::string s1=s.substr(0,pos); return irep_idt(s1); } +void ssa_local_unwindert::is_void_func() +{ + for (local_SSAt::objectst::const_iterator o_it = + SSA.ssa_objects.objects.begin(); + o_it != SSA.ssa_objects.objects.end(); o_it++) { + + if(as_string(o_it->get_identifier()).find(RETVAR)){ isvoid=false; return;} + } + isvoid=true; + +} /*****************************************************************************\ * * Function : ssa_local_unwindert::ssa_local_unwindert @@ -47,8 +101,10 @@ irep_idt ssa_local_unwindert::get_base_name(const irep_idt& id) * *****************************************************************************/ ssa_local_unwindert::ssa_local_unwindert(local_SSAt& _SSA,bool k_induct, - bool _ibmc): SSA(_SSA), - current_unwinding(0),is_initialized(false),is_kinduction(k_induct),is_ibmc(_ibmc){ } + bool _ibmc):isvoid(true), SSA(_SSA), + current_unwinding(0),prev_unwinding(std::numeric_limits::max()), + is_initialized(false),is_kinduction(k_induct), + is_ibmc(_ibmc){ } /******************************************************************* Struct: compare_node_iterators @@ -62,12 +118,14 @@ ssa_local_unwindert::ssa_local_unwindert(local_SSAt& _SSA,bool k_induct, unsigned long \*******************************************************************/ -struct compare_node_iteratorst { - bool operator()(const local_SSAt::nodest::iterator& a, - const local_SSAt::nodest::iterator& b) const { - return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); - } -}; +bool compare_node_iteratorst::operator()(const local_SSAt::nodest::iterator& a, + const local_SSAt::nodest::iterator& b) const { + return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); +} +bool compare_node_iteratorst::operator()(const local_SSAt::nodest::const_iterator& a, + const local_SSAt::nodest::const_iterator& b) const { + return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); +} /*****************************************************************************\ * Function : ssa_local_unwindert::init * @@ -158,6 +216,8 @@ void ssa_local_unwindert::init() { //ASSUME : the last node in the body_nodes // of a loop is always the back-edge node + //copy assertions after the loop for assertion hoisting + current_node->assertions_after_loop = n_it->loophead->assertions_after_loop; current_node->body_nodes.push_back(*n_it); current_node->body_nodes.back().marked = false; @@ -225,6 +285,14 @@ void ssa_local_unwindert::init() { SSA.nodes.insert(SSA.nodes.begin(), root_node.body_nodes.begin(), root_node.body_nodes.end()); + populate_parents(); + is_void_func(); + + if(!isvoid) + { + populate_return_val_mod(); + } + //populate connector for every loop for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); lit!=root_node.loop_nodes.end();lit++) @@ -232,6 +300,8 @@ void ssa_local_unwindert::init() { populate_connectors(*lit); } + put_varmod_in_parent(); +#if 0 //pointers of tree_loopnodet are stable now and they must not be //modified beyond this point. Now populate "parent" field of //every node @@ -259,11 +329,110 @@ void ssa_local_unwindert::init() { } - +#endif is_initialized=true; } +void ssa_local_unwindert::propagate_varmod_to_ancestors(const irep_idt& id,tree_loopnodet* current_loop) +{ + current_loop->vars_modified.insert(id); + if(current_loop->parent!=NULL && current_loop->parent!=&root_node) + { + propagate_varmod_to_ancestors(id,current_loop->parent); + } +} +void ssa_local_unwindert::populate_parents() +{ + std::list worklist; + worklist.push_back(&root_node); + while(!worklist.empty()) + { + tree_loopnodet* current_node = worklist.back(); + worklist.pop_back(); + + if(current_node->loop_nodes.empty()) continue; + for(loop_nodest::iterator it=current_node->loop_nodes.begin(); + it!=current_node->loop_nodes.end();it++) + { + it->parent = current_node; + + worklist.push_back(&(*it)); + } + + } +} +void ssa_local_unwindert::put_varmod_in_parent() +{ + std::list worklist; + worklist.push_back(&root_node); + while(!worklist.empty()) + { + tree_loopnodet* current_node = worklist.back(); + worklist.pop_back(); + + if(current_node->loop_nodes.empty()) continue; + for(loop_nodest::iterator it=current_node->loop_nodes.begin(); + it!=current_node->loop_nodes.end();it++) + { + + + // if a variable is modified in child loop then consider it modified + //for the current loop for the purpose of renaming + //NOTE : this code only looks at the child. Not sure if you should look at + // all the descendents for the purpose of renaming + current_node->vars_modified.insert(it->vars_modified.begin(),it->vars_modified.end()); + worklist.push_back(&(*it)); + } + + } +} +void ssa_local_unwindert::populate_return_val_mod() +{ + std::list worklist; + for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); + lit!=root_node.loop_nodes.end();lit++) + { + worklist.push_back(&(*lit)); + } + +while(!worklist.empty()) +{ + tree_loopnodet* current_loop=worklist.back(); + worklist.pop_back(); + for(local_SSAt::nodest::iterator nit=current_loop->body_nodes.begin(); + nit!=current_loop->body_nodes.end();nit++) + { + for(local_SSAt::nodet::equalitiest::iterator eit=nit->equalities.begin(); + eit!=nit->equalities.end();eit++) + { + + if(eit->lhs().id()==ID_symbol) + { + + symbol_exprt sym_e=to_symbol_expr(eit->lhs()); + irep_idt sym_id=sym_e.get_identifier(); + std::string s = as_string(sym_id); + + std::size_t pos=s.find(RETVAR); + if(pos!=std::string::npos) + { + + irep_idt id= keep_first_two_hash(s); + propagate_varmod_to_ancestors(id,current_loop); + current_loop->return_nodes.insert(nit); + } + } + } + } + for(loop_nodest::iterator lit=current_loop->loop_nodes.begin(); + lit!=current_loop->loop_nodes.end();lit++) + { + worklist.push_back(&(*lit)); + } +} + +} void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) { typedef std::map varobj_mapt; @@ -272,8 +441,11 @@ void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) body_nodest::const_reverse_iterator lit = current_loop.body_nodes.rbegin(); + std::vector exit_conditions; + unsigned int end_location = lit->location->location_number; + for(local_SSAt::nodet::equalitiest::const_iterator eqit=lit->equalities.begin(); eqit!=lit->equalities.end();eqit++) { @@ -287,6 +459,28 @@ void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) break; } } + exprt cond_e; + exprt guard_e; + exprt loop_continue_e; + if(!current_loop.is_dowhile) + { + cond_e = SSA.cond_symbol(it->location); + guard_e=SSA.guard_symbol(it->location); + exit_conditions.push_back(cond_e); + //either you reached head of the loop and exit + //or you have not reached the end of the loop + //this will happen only for while. What about dowhile? + loop_continue_e = and_exprt(cond_e,guard_e); + loop_continue_e=or_exprt(loop_continue_e,not_exprt(SSA.guard_symbol(lit->location))); + } + else + { + cond_e = SSA.cond_symbol(lit->location); + guard_e=SSA.guard_symbol(lit->location); + exprt not_cond_e=not_exprt(cond_e); + exit_conditions.push_back(not_cond_e); + loop_continue_e=and_exprt(not_cond_e,guard_e); + } for (local_SSAt::objectst::const_iterator o_it = SSA.ssa_objects.objects.begin(); @@ -296,26 +490,33 @@ void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) if(fit==current_loop.vars_modified.end()) continue; varobj_map[*fit]=o_it; + //though #return_value is added into vars_modified + //we don't want PHI connectors for them + if(as_string(*fit).find(RETVAR)!=std::string::npos) continue; + if(!current_loop.is_dowhile) { - current_loop.connectors.insert(SSA.name(*o_it,local_SSAt::PHI,it->location)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.name(*o_it,local_SSAt::PHI,it->location),loop_continue_e)); } else { - current_loop.connectors.insert(SSA.read_rhs(*o_it,lit->location)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*o_it,lit->location),loop_continue_e)); } } if(!current_loop.is_dowhile) { - current_loop.connectors.insert(SSA.guard_symbol(it->location)); - current_loop.connectors.insert(SSA.cond_symbol(it->location)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); } else { - current_loop.connectors.insert(SSA.guard_symbol(lit->location)); - current_loop.connectors.insert(SSA.cond_symbol(lit->location)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(lit->location),loop_continue_e)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(lit->location),loop_continue_e)); } +//since loophead is processed we can probably go on? +it++; + for(;it!=current_loop.body_nodes.end();it++) { @@ -323,16 +524,32 @@ for(;it!=current_loop.body_nodes.end();it++) if(next_node==current_loop.body_nodes.end()) break; if(!is_break_node(*it,end_location)) continue; + //no separete treatment for return nodes required as break nodes + // are all nodes with jump out of the loop which include the return + //nodes + exprt break_cond_e=SSA.cond_symbol(it->location); + //NOTE : do we check if the end of the guard has reached in loop_continue_e? +loop_continue_e = and_exprt(break_cond_e,SSA.guard_symbol(it->location)); +if(!is_return_node(current_loop,it)) +{ +exit_conditions.push_back(break_cond_e); +} for(varobj_mapt::iterator vit=varobj_map.begin();vit!=varobj_map.end();vit++) { - current_loop.connectors.insert(SSA.read_rhs(*(vit->second),it->location)); - current_loop.connectors.insert(SSA.guard_symbol(it->location)); - current_loop.connectors.insert(SSA.cond_symbol(it->location)); + if(it==current_loop.body_nodes.begin() && + (as_string(vit->first).find(RETVAR)!=std::string::npos)) continue; + + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*(vit->second),it->location),loop_continue_e)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); + current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); } + } +current_loop.exit_condition=disjunction(exit_conditions); + for(loop_nodest::iterator loopit=current_loop.loop_nodes.begin(); loopit!=current_loop.loop_nodes.end();loopit++) { @@ -342,7 +559,7 @@ for(loop_nodest::iterator loopit=current_loop.loop_nodes.begin(); } bool ssa_local_unwindert::is_break_node(const local_SSAt::nodet& node, - const unsigned int end_location) + const unsigned int end_location) const { local_SSAt::locationt instr = node.location; if(!instr->is_goto()) return false; @@ -350,10 +567,17 @@ bool ssa_local_unwindert::is_break_node(const local_SSAt::nodet& node, // a break should have only one target if(instr->targets.size()>1) return false; - if(instr->targets.front()->location_number < end_location ) return false; + if(instr->targets.front()->location_number <= end_location ) return false; return true; } + +bool ssa_local_unwindert::is_return_node(const tree_loopnodet& current_loop, + const local_SSAt::nodest::const_iterator& node) const +{ + return_nodest::const_iterator it=current_loop.return_nodes.find(node); + return (it!=current_loop.return_nodes.end()); +} /*****************************************************************************\ * Function : ssa_local_unwindert::unwind * @@ -372,8 +596,17 @@ void ssa_local_unwindert::unwind(const irep_idt& fname,unsigned int k) { return; if (k <= current_unwinding) assert(false && "unwind depth smaller than previous unwinding!!"); + +//watch out for border-line cases + //parameter to unwind must never be 0 + prev_unwinding=current_unwinding; + local_SSAt::nodest new_nodes; - irep_idt func_name = "unwind:"+as_string(fname)+"enable_"+i2string(k); + irep_idt func_name = "unwind:"+as_string(fname)+":enable_"+i2string(k); + /* if(return_var.empty()) + { + return_var=as_string(fname)+"#return_value"; + }*/ symbol_exprt new_sym(func_name,bool_typet()); SSA.enabling_exprs.push_back(new_sym); for (loop_nodest::iterator it = root_node.loop_nodes.begin(); @@ -438,7 +671,7 @@ unsigned int ssa_local_unwindert::get_last_iteration(std::string& suffix, bool& std::size_t pos = suffix.find_last_of("%"); if(pos==std::string::npos) {result=false; return 0;} unsigned int val = safe_string2unsigned(suffix.substr(pos+1)); - if(val >= std::numeric_limits::max()) assert(false); + assert(val < std::numeric_limits::max()); suffix=suffix.substr(0,pos); result = true; return val; @@ -467,35 +700,46 @@ unsigned int ssa_local_unwindert::get_last_iteration(std::string& suffix, bool& *****************************************************************************/ void ssa_local_unwindert::rename(exprt &expr, std::string suffix, const int iteration,tree_loopnodet& current_loop) { - if (expr.id() == ID_symbol) { + if (expr.id() == ID_function_application) { + expr.set(ID_suffix,suffix + "%" + i2string(iteration)); + } + else if (expr.id() == ID_symbol) { symbol_exprt &sexpr = to_symbol_expr(expr); irep_idt vid=sexpr.get_identifier(); irep_idt base_id = get_base_name(vid); - + bool isreturnvar=(as_string(vid).find(RETVAR)!=std::string::npos); + isreturnvar= isreturnvar||(as_string(vid).find(RETVAR1)!=std::string::npos); int mylevel; if(iteration<0) { irep_idt id = id2string(vid) + suffix; sexpr.set_identifier(id); + sexpr.set(ID_suffix,suffix); return; } std::string s = id2string(base_id); if(s.find("$guard")!=std::string::npos || s.find("$cond")!=std::string::npos - || (mylevel = need_renaming(current_loop,base_id)) ==0) + || id2string(vid).find("#arg")!=std::string::npos + || isreturnvar + || (mylevel = need_renaming(current_loop,base_id))==0) { irep_idt id=id2string(vid) + suffix + "%" + i2string(iteration); sexpr.set_identifier(id); + sexpr.set(ID_suffix,suffix + "%" + i2string(iteration)); + return; + } + if(mylevel<0) + { return; } - if(mylevel<0) return; std::string fsuffix = suffix; std::size_t pos; - for(unsigned int i=1;i0) - || (!current_loop.is_dowhile && i>1))) + if(is_kinduction && !new_node.assertions.empty() &&( + (current_loop.is_dowhile && i>0) + || (!current_loop.is_dowhile && i>1))) { //convert all assert to assumes for k-induction //except the bottom most iteration @@ -687,8 +937,10 @@ void ssa_local_unwindert::unwind(tree_loopnodet& current_loop, #if 1 exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.begin()->location); + local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); rename(guard_select,suffix,i,current_loop); + + for(local_SSAt::nodet::assertionst::iterator ait=new_node.assertions.begin(); ait!=new_node.assertions.end();ait++) { @@ -737,6 +989,17 @@ void ssa_local_unwindert::unwind(tree_loopnodet& current_loop, //store the end of this loop, its "loophead" field will be //pointed to the topmost loophead node current_loop.loopends_map[suffix] = le_it; + + { + //add all the loop continuation expressions for the bottom most iterations %0 + //this is requested by peter + //TODO : later document why this is needed + exprt loopend_guard = SSA.guard_symbol(current_loop.body_nodes.rbegin()->location); + exprt loopend_cond = SSA.cond_symbol(current_loop.body_nodes.rbegin()->location); + rename(loopend_guard,suffix,i,current_loop); + rename(loopend_cond,suffix,i,current_loop); + current_loop.loop_continuation_exprs.push_back(and_exprt(loopend_guard,loopend_cond)); + } } } @@ -820,201 +1083,44 @@ void ssa_local_unwindert::unwind(tree_loopnodet& current_loop, } add_connector_node(current_loop,suffix,unwind_depth,new_sym,new_nodes); -#if 0 - //now the connector node - { - //copy the original loop head - - local_SSAt::nodet node = current_loop.body_nodes.front(); - node.marked = false; - bool is_do_while=false; - exprt guard_e; //= SSA.guard_symbol(node.location); - exprt cond_e ;//= SSA.cond_symbol(node.location); - - local_SSAt::nodest::const_reverse_iterator loopend_node = current_loop.body_nodes.rbegin(); - for(local_SSAt::nodet::equalitiest::const_iterator eqit=loopend_node->equalities.begin(); - eqit!=loopend_node->equalities.end();eqit++) - { - if(eqit->lhs()==SSA.cond_symbol(loopend_node->location)) - { - if(!eqit->rhs().is_true()) - { - is_do_while=true; - - } - break; - } - } - if(!is_do_while) - { - //if while loop, exit condition is in the loop head - cond_e = SSA.cond_symbol(node.location); - guard_e=SSA.guard_symbol(node.location); - - } - else - { - //if do while loop, condition is the loop end - exprt e = SSA.cond_symbol(loopend_node->location); - cond_e=not_exprt(e); - guard_e=SSA.guard_symbol(loopend_node->location); - node.equalities.push_back(equal_exprt(e,true_exprt())); - - } - bool prev_elem_erased=false; - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - exprt e; - - //= e_it->lhs(); - if(prev_elem_erased) - { - e_it--; - prev_elem_erased=false; - } - - if(is_do_while && (e_it->rhs().id()!=ID_if && SSA.guard_symbol(node.location) != e_it->lhs() - && SSA.cond_symbol(loopend_node->location)!=e_it->lhs())) - { - e_it = node.equalities.erase(e_it); - prev_elem_erased=true; - continue; - } - else if(is_do_while && e_it->rhs().id()==ID_if) - { - if_exprt ife1 = to_if_expr(e_it->rhs()); - e = current_loop.pre_post_exprs[ife1.true_case()]; - } - else if(is_do_while && e_it->lhs()==SSA.guard_symbol(node.location)) - { - e = SSA.guard_symbol(loopend_node->location); - } - else - { - e = e_it->lhs(); - } - exprt re = e; - rename(re, suffix, 0,current_loop); - for (unsigned int i = 1; i < unwind_depth; i++) { - exprt ce = cond_e; - rename(ce, suffix, i,current_loop); - exprt ge = guard_e; - rename(ge, suffix, i,current_loop); - exprt cond_expr = and_exprt(ce, ge); - exprt true_expr = e; - rename(true_expr, suffix,i,current_loop); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); - } - - e_it->rhs() = re; - e_it->lhs()=e; - rename(e_it->lhs(),suffix,-1,current_loop); - - node.enabling_expr = new_sym; - //exprt ie = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(ie); - - } - //node.equalities.clear(); - new_nodes.push_back(node); - } -#endif } -#if 0 + void ssa_local_unwindert::add_connector_node(tree_loopnodet& current_loop, std::string suffix, const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes) { //copy the original loop head - local_SSAt::nodet node = current_loop.body_nodes.front(); + local_SSAt::nodet node=current_loop.body_nodes.front(); node.marked = false; - bool is_do_while=false; - exprt guard_e; //= SSA.guard_symbol(node.location); - exprt cond_e ;//= SSA.cond_symbol(node.location); + node.equalities.clear(); + node.assertions.clear(); + node.constraints.clear(); + node.templates.clear(); - local_SSAt::nodest::const_reverse_iterator loopend_node = current_loop.body_nodes.rbegin(); - for(local_SSAt::nodet::equalitiest::const_iterator eqit=loopend_node->equalities.begin(); - eqit!=loopend_node->equalities.end();eqit++) - { - if(eqit->lhs()==SSA.cond_symbol(loopend_node->location)) - { - if(!eqit->rhs().is_true()) - { - is_do_while=true; - } - break; - } - } - if(!is_do_while) - { - //if while loop, exit condition is in the loop head - cond_e = SSA.cond_symbol(node.location); - guard_e=SSA.guard_symbol(node.location); - - } - else - { - //if do while loop, condition is the loop end - exprt e = SSA.cond_symbol(loopend_node->location); - cond_e=not_exprt(e); - guard_e=SSA.guard_symbol(loopend_node->location); - node.equalities.push_back(equal_exprt(e,true_exprt())); - - } - bool prev_elem_erased=false; - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - exprt e; - - //= e_it->lhs(); - if(prev_elem_erased) - { - e_it--; - prev_elem_erased=false; - } - - if(is_do_while && (e_it->rhs().id()!=ID_if && SSA.guard_symbol(node.location) != e_it->lhs() - && SSA.cond_symbol(loopend_node->location)!=e_it->lhs())) - { - e_it = node.equalities.erase(e_it); - prev_elem_erased=true; - continue; - } - else if(is_do_while && e_it->rhs().id()==ID_if) - { - if_exprt ife1 = to_if_expr(e_it->rhs()); - e = current_loop.pre_post_exprs[ife1.true_case()]; - } - else if(is_do_while && e_it->lhs()==SSA.guard_symbol(node.location)) - { - e = SSA.guard_symbol(loopend_node->location); - } - else - { - e = e_it->lhs(); - } + for(expr_break_mapt::iterator e_it=current_loop.connectors.begin(); + e_it!=current_loop.connectors.end();e_it++) + { + exprt e = e_it->first; exprt re = e; rename(re, suffix, 0,current_loop); for (unsigned int i = 1; i < unwind_depth; i++) { - exprt ce = cond_e; - rename(ce, suffix, i,current_loop); - exprt ge = guard_e; - rename(ge, suffix, i,current_loop); - exprt cond_expr = and_exprt(ce, ge); + + exprt cond_expr = e_it->second; + rename(cond_expr,suffix,i,current_loop); exprt true_expr = e; rename(true_expr, suffix,i,current_loop); exprt false_expr = re; re = if_exprt(cond_expr, true_expr, false_expr); } + exprt rhs = re; + exprt lhs=e; - e_it->rhs() = re; - e_it->lhs()=e; - rename(e_it->lhs(),suffix,-1,current_loop); + rename(lhs,suffix,-1,current_loop); + node.equalities.push_back(equal_exprt(lhs,rhs)); node.enabling_expr = new_sym; //exprt ie = implies_exprt(new_sym, *e_it); @@ -1025,74 +1131,97 @@ void ssa_local_unwindert::add_connector_node(tree_loopnodet& current_loop, new_nodes.push_back(node); } -#else -void ssa_local_unwindert::add_connector_node(tree_loopnodet& current_loop, - std::string suffix, - const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes) + +void ssa_local_unwindert::loop_continuation_conditions( + const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const { - //copy the original loop head + loop_cont_e.insert(loop_cont_e.end(), + current_loop.loop_continuation_exprs.begin(), + current_loop.loop_continuation_exprs.end()); + for(loop_nodest::const_iterator it=current_loop.loop_nodes.begin(); + it!=current_loop.loop_nodes.end();it++) + { + loop_continuation_conditions(*it,loop_cont_e); + } +} - local_SSAt::nodet node=current_loop.body_nodes.front(); - node.marked = false; - exprt guard_e; //= SSA.guard_symbol(node.location); - exprt cond_e ;//= SSA.cond_symbol(node.location); - if(current_loop.is_dowhile) - { - body_nodest::const_reverse_iterator bit = current_loop.body_nodes.rbegin(); - exprt e = SSA.cond_symbol(bit->location); - cond_e=not_exprt(e); - guard_e=SSA.guard_symbol(bit->location); - node= *bit; - node.equalities.clear(); +void ssa_local_unwindert::loop_continuation_conditions( + exprt::operandst& loop_cont_e) const +{ + loop_continuation_conditions(root_node,loop_cont_e); +} + +void ssa_local_unwindert::assertion_hoisting(tree_loopnodet& current_loop, + const local_SSAt::nodet& tmp_node, + const std::string& suffix, const bool is_kinduction, + const unsigned int unwind_depth, + symbol_exprt& new_sym, local_SSAt::nodest& new_nodes) + +{ + + if(suffix=="" && is_kinduction) + { + unsigned lower_bound = current_loop.is_dowhile? 1 : 2; + exprt assertion_hoist_e = conjunction(current_loop.assertions_after_loop); + exprt guard_select = SSA.name(SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); + rename(guard_select,suffix,unwind_depth-1,current_loop); + for(unsigned int i=lower_bound;i0) + // || (!current_loop.is_dowhile && (i-1)>1)))) + // { + local_SSAt::nodet node= tmp_node; + node.marked=false; node.assertions.clear(); + node.equalities.clear(); node.constraints.clear(); + node.templates.clear(); + node.assertions_after_loop.clear(); - } - else - { - body_nodest::const_iterator bit = current_loop.body_nodes.begin(); - cond_e=SSA.cond_symbol(bit->location); - guard_e=SSA.guard_symbol(bit->location); - node=*bit; - node.equalities.clear(); - node.assertions.clear(); - node.constraints.clear(); + // if(is_kinduction &&( + // (current_loop.is_dowhile && (i-1)>0) + // || (!current_loop.is_dowhile && (i-1)>1))) + // { //convert all assert to assumes for k-induction + //except the bottom most iteration - } + //assertions should be converted to assume only if you are in the step case + //of k-induction and not the base case. that means + // you want guardls=> assume and \not guardls => assert + // as of now this conflicts with checking spurious examples + //so just removing the assertion if it is NOT the bottom most iteration. + // Unless you have checked it for all unwinding less than k, this will + // lead to unsoundness (a bug may not be found if the assertion can fail in iterations + //other than the last - for(exprst::iterator e_it=current_loop.connectors.begin(); - e_it!=current_loop.connectors.end();e_it++) - { - exprt e = *e_it; - exprt re = e; - rename(re, suffix, 0,current_loop); - for (unsigned int i = 1; i < unwind_depth; i++) { - exprt ce = cond_e; - rename(ce, suffix, i,current_loop); - exprt ge = guard_e; - rename(ge, suffix, i,current_loop); - exprt cond_expr = and_exprt(ce, ge); - exprt true_expr = e; - rename(true_expr, suffix,i,current_loop); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); - } - exprt rhs = re; - exprt lhs=e; - rename(lhs,suffix,-1,current_loop); - node.equalities.push_back(equal_exprt(lhs,rhs)); - node.enabling_expr = new_sym; - //exprt ie = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(ie); + //if outermost loop, do the assertion hoisting. + //for innerloop assertion hoisting is not necessary because assertions are + //assumed in the parent context anyway - } - //node.equalities.clear(); + exprt exit_cond_e=current_loop.exit_condition; + if(!assertion_hoist_e.is_true()&& !exit_cond_e.is_false()) + { + //rename(assertion_hoist_e,suffix,-1,current_loop); + + rename(exit_cond_e,suffix,i,current_loop); + + exprt hoist_cond_e = and_exprt(guard_select,exit_cond_e); + + node.constraints.push_back(implies_exprt(hoist_cond_e,assertion_hoist_e)); + node.enabling_expr = new_sym; new_nodes.push_back(node); } -#endif + + // } + } + } + +} /*****************************************************************************\ * * Function : ssa_local_unwindert::unwinder_rename @@ -1152,6 +1281,152 @@ void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, std::cout << "new id: " << var.get_identifier() << std::endl; #endif } + + +unsigned ssa_local_unwindert::rename_required(const exprt& e, + const unsigned prev_unwinding) const +{ + if(e.id()==ID_symbol) + { + const symbol_exprt& sym=to_symbol_expr(e); + irep_idt id = sym.get_identifier(); + + std::list iterations; + irep_idt basename; + dissect_loop_suffix(id,basename,iterations,false); + bool rename_required=true; + for(std::list::iterator it=iterations.begin(); + it!=iterations.end();it++) + { + if(*it!=(prev_unwinding-1)) rename_required=false; + } + //if(iterations.back()==(prev_unwinding-1)) return iterations.size(); + if(rename_required) return iterations.size(); + + + } + else + { + if(!e.operands().empty()) + { + for(exprt::operandst::const_iterator e_it=e.operands().begin(); + e_it!=e.operands().end();e_it++) + { + unsigned depth=rename_required(*e_it,prev_unwinding); + if(depth) return depth; + } + } + } + + return 0; + +} + + +void ssa_local_unwindert::rename_invariant(exprt& e,const irep_idt& suffix) const +{ + if(e.id()==ID_symbol) + { + symbol_exprt& sym=to_symbol_expr(e); + irep_idt id = sym.get_identifier(); + + std::list iterations; + irep_idt basename; + dissect_loop_suffix(id,basename,iterations,true); + + + sym.set_identifier(id2string(basename)+id2string(suffix)); + } + else + { + if(!e.operands().empty()) + { + for(exprt::operandst::iterator e_it=e.operands().begin(); + e_it!=e.operands().end();e_it++) + { + rename_invariant(*e_it,suffix); + } + } + } + +} +/***************************************************************************** + * + * Function : ssa_local_unwindert::rename_invariant + * + * Input : inv_in - list of input invariants that is to be renamed for reuse + * + * Output : inv_out - list of renamed invariants + * + * Purpose : For the purpose of reuse of invariant, rename all + * + * + *****************************************************************************/ +void ssa_local_unwindert::rename_invariant(const exprt::operandst& inv_in, + std::vector& inv_out,const unsigned prev_unwinding) const +{ + + if(prev_unwinding==0 || prev_unwinding==std::numeric_limits::max()) + { + return; + } + for(std::vector::const_iterator e_it=inv_in.begin(); + e_it!=inv_in.end();e_it++) + { + unsigned depth=rename_required(*e_it,prev_unwinding); + if(depth==0) continue; + + std::vector iter_vector(depth-1,current_unwinding-1); + + do + { + + irep_idt suffix; + + for(std::vector::const_iterator vit=iter_vector.begin(); + vit!=iter_vector.end();vit++) + { + + suffix = id2string(suffix)+"%"+i2string(*vit); + } + suffix = id2string(suffix)+"%"+i2string(current_unwinding-1); + inv_out.push_back(*e_it); + exprt& e = inv_out.back(); + rename_invariant(e,suffix); + } while(odometer_increment(iter_vector,current_unwinding)); + } +} + +exprt ssa_local_unwindert::rename_invariant(const exprt& inv_in) const +{ + if(inv_in.is_true()) return inv_in; + + exprt::operandst inv_in_operands; + if(inv_in.id()!=ID_and) inv_in_operands.push_back(inv_in); + else inv_in_operands = inv_in.operands(); + + std::vector new_inv; + rename_invariant(inv_in_operands,new_inv,prev_unwinding); + + return conjunction(new_inv); +} + + +bool ssa_local_unwindert::odometer_increment(std::vector& odometer, + unsigned base) const +{ + if(odometer.empty()) return false; + unsigned i=odometer.size()-1; + while(true) + { + if(odometer[i] < base-1) {odometer[i]++; return true;} + odometer[i]=0; + if(i==0) return false; //overflow + i--; + + } +return false; +} /*****************************************************************************\ * * Function : ssa_unwindert::ssa_unwindert @@ -1234,7 +1509,7 @@ void ssa_unwindert::output(std::ostream & out) { if(!is_initialized) return; for (unwinder_mapt::iterator it = unwinder_map.begin(); it != unwinder_map.end(); it++) { - out << "Unwinding for function" << it->first << std::endl; + out << "Unwinding for function " << it->first << std::endl; it->second.output(out); } } @@ -1270,6 +1545,7 @@ void ssa_unwindert::init_localunwinders() for(unwinder_mapt::iterator it=unwinder_map.begin(); it!=unwinder_map.end();it++) { + it->second.set_return_var(it->first); it->second.init(); } is_initialized=true; diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index e1268a01b..df01fa8a5 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -14,24 +14,47 @@ Author: Saurabh Joshi #include "../ssa/local_ssa.h" #include "../summarizer/ssa_db.h" +#define RETVAR "#return_value" +#define RETVAR1 "return_value___VERIFIER_nondet" + +struct compare_node_iteratorst { + bool operator()(const local_SSAt::nodest::iterator& a, + const local_SSAt::nodest::iterator& b) const; + bool operator()(const local_SSAt::nodest::const_iterator& a, + const local_SSAt::nodest::const_iterator& b) const; +}; + class ssa_local_unwindert : public messaget { + irep_idt return_var; + bool isvoid; local_SSAt& SSA; unsigned int current_unwinding; + unsigned int prev_unwinding; class tree_loopnodet; typedef std::list loop_nodest; typedef std::map loopends_mapt; typedef std::map modvar_levelt; typedef std::set exprst; + typedef exprt cond_et; + typedef exprt guard_et; + typedef std::map expr_break_mapt; + typedef std::pair exp_guard_cond_pairt; + typedef std::set return_nodest; typedef local_SSAt::nodest body_nodest; bool loopless; + class tree_loopnodet { public: - exprst connectors; + return_nodest return_nodes; + //exprst connectors; + exprt::operandst assertions_after_loop; + exprt::operandst loop_continuation_exprs; + exprt exit_condition; + expr_break_mapt connectors; tree_loopnodet* parent; local_SSAt::nodest body_nodes; - //local_SSAt::nodet::iterator loophead_node; std::map pre_post_exprs; modvar_levelt modvar_level; std::set vars_modified; @@ -67,11 +90,23 @@ class ssa_local_unwindert : public messaget } }; tree_loopnodet root_node; - bool is_break_node(const local_SSAt::nodet& node,const unsigned int end_location); + std::string keep_first_two_hash(const std::string& str) const; + void put_varmod_in_parent(); + void populate_parents(); + void propagate_varmod_to_ancestors(const irep_idt& id, + tree_loopnodet* current_loop); + void populate_return_val_mod(); + void is_void_func(); + bool is_break_node(const local_SSAt::nodet& node, + const unsigned int end_location) const; + bool is_return_node(const tree_loopnodet& current_loop, + const local_SSAt::nodest::const_iterator& node) const; + void populate_connectors(tree_loopnodet& current_loop); void unwind(tree_loopnodet& current_loop, std::string suffix,bool full, - const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes); + const unsigned int unwind_depth,symbol_exprt& new_sym, + local_SSAt::nodest& new_nodes); void rename(local_SSAt::nodet& node,std::string suffix, const int iteration,tree_loopnodet& current_loop); void rename(exprt &expr, std::string suffix, @@ -80,22 +115,40 @@ class ssa_local_unwindert : public messaget const irep_idt& id); unsigned int get_last_iteration(std::string& suffix, bool& result); irep_idt get_base_name(const irep_idt& id); - + unsigned rename_required(const exprt& e, + const unsigned prev_unwinding) const; + void rename_invariant(exprt& e,const irep_idt& suffix) const; void add_connector_node(tree_loopnodet& current_loop, std::string suffix, - const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes); - // void init(); + const unsigned int unwind_depth, + symbol_exprt& new_sym, + local_SSAt::nodest& new_nodes); + void loop_continuation_conditions( + const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const; + void assertion_hoisting(tree_loopnodet& current_loop, + const local_SSAt::nodet& tmp_node, + const std::string& suffix, const bool is_kinduction, + const unsigned int unwind_depth, + symbol_exprt& new_sym, local_SSAt::nodest& new_nodes); bool is_initialized; public : + void set_return_var(const irep_idt& id); + void dissect_loop_suffix(const irep_idt& id, + irep_idt& before_suffix, + std::list& iterations, bool baseonly) const; + void rename_invariant(const std::vector& inv_in, + std::vector& inv_out,const unsigned prev_unwinding) const; + exprt rename_invariant(const exprt& inv_in) const; + void loop_continuation_conditions(exprt::operandst& loop_cont_e) const; + bool odometer_increment(std::vector& odometer, + unsigned base) const; bool is_kinduction; bool is_ibmc; void init(); void output(std::ostream& out) { - //root_node.output(out,SSA.ns); SSA.output(out); } - //std::list enabling_exprs; ssa_local_unwindert(local_SSAt& _SSA, bool k_induct, bool _ibmc); void unwind(const irep_idt& fname,unsigned int k); @@ -122,6 +175,7 @@ class ssa_unwindert : public messaget ssa_local_unwindert &get(const irep_idt& fname) { return unwinder_map.at(fname); } void output(std::ostream & out); + protected: ssa_dbt& ssa_db; bool is_initialized; diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index b71617234..bcc3b454b 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -2,12 +2,19 @@ include ../config.inc SRC = summarizer_base.cpp \ summarizer_fw.cpp summarizer_bw.cpp \ - summarizer_fw_term.cpp summarizer_bw_term.cpp \ - summarizer_main.cpp summarizer_parseoptions.cpp \ - show.cpp summary_checker.cpp \ - summary_db.cpp cover_goals_ext.cpp \ - summary.cpp ssa_db.cpp \ - array_abstraction.cpp + summarizer_bw_cex.cpp summarizer_bw_cex_complete.cpp \ + summarizer_bw_cex_concrete.cpp \ + summarizer_bw_cex_wp.cpp \ + summarizer_bw_cex_ai.cpp summarizer_bw_cex_all.cpp \ + summarizer_fw_contexts.cpp \ + summarizer_main.cpp summarizer_parse_options.cpp \ + show.cpp summary_checker_base.cpp \ + summary_checker_ai.cpp summary_checker_bmc.cpp \ + summary_checker_kind.cpp \ + cover_goals_ext.cpp \ + summary_db.cpp summary.cpp ssa_db.cpp \ + array_abstraction.cpp preprocessing_util.cpp \ + instrument_goto.cpp function_signature.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ @@ -19,24 +26,31 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ $(CBMC)/src/langapi/langapi$(LIBEXT) \ $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ + $(CBMC)/src/json/json$(LIBEXT) \ $(CBMC)/src/solvers/solvers$(LIBEXT) \ $(CBMC)/src/util/util$(LIBEXT) \ DELTA_OBJ+=\ - ../ssa/local_ssa$(OBJEXT) \ + $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ + ../ssa/ssa_slicer$(OBJEXT) \ ../ssa/malloc_ssa$(OBJEXT) \ ../ssa/ssa_domain$(OBJEXT) \ ../ssa/assignments$(OBJEXT) \ ../ssa/guard_map$(OBJEXT) \ ../ssa/ssa_object$(OBJEXT) \ ../ssa/address_canonizer$(OBJEXT) \ - ../ssa/ssa_aliasing$(OBJEXT) \ + ../ssa/ssa_dereference$(OBJEXT) \ ../ssa/simplify_ssa$(OBJEXT) \ ../ssa/ssa_build_goto_trace$(OBJEXT) \ ../ssa/split_loopheads$(OBJEXT)\ ../ssa/ssa_inliner$(OBJEXT)\ ../ssa/ssa_unwinder$(OBJEXT)\ + ../ssa/ssa_value_set$(OBJEXT) \ + ../ssa/const_propagator$(OBJEXT) \ + ../ssa/replace_symbol_ext$(OBJEXT) \ + ../ssa/ssa_const_propagator$(OBJEXT) \ + ../ssa/ssa_dependency_graph$(OBJEXT) \ ../functions/summary$(OBJEXT) \ ../functions/get_function$(OBJEXT) \ ../functions/path_util$(OBJEXT) \ @@ -45,26 +59,25 @@ DELTA_OBJ+=\ ../domains/fixed_point$(OBJEXT) \ ../domains/ssa_fixed_point$(OBJEXT) \ ../domains/tpolyhedra_domain$(OBJEXT) \ + ../domains/predabs_domain$(OBJEXT) \ ../domains/equality_domain$(OBJEXT) \ ../domains/domain$(OBJEXT) \ ../domains/util$(OBJEXT) \ ../domains/incremental_solver$(OBJEXT) \ - ../domains/strategy_solver_equality$(OBJEXT) \ ../domains/strategy_solver_base$(OBJEXT) \ + ../domains/strategy_solver_predabs$(OBJEXT) \ + ../domains/strategy_solver_equality$(OBJEXT) \ ../domains/strategy_solver_enumeration$(OBJEXT) \ ../domains/strategy_solver_binsearch$(OBJEXT) \ + ../domains/strategy_solver_binsearch2$(OBJEXT) \ + ../domains/strategy_solver_binsearch3$(OBJEXT) \ ../domains/template_generator_base$(OBJEXT) \ ../domains/template_generator_summary$(OBJEXT) \ ../domains/template_generator_callingcontext$(OBJEXT) \ ../domains/ssa_analyzer$(OBJEXT) \ - ../domains/predicate$(OBJEXT) \ - ../domains/template_generator_ranking$(OBJEXT) \ - ../domains/ranking_solver_enumeration$(OBJEXT) \ - ../domains/linrank_domain$(OBJEXT) \ - ../domains/lexlinrank_solver_enumeration$(OBJEXT) \ - ../domains/lexlinrank_domain$(OBJEXT) - -# ../domains/strategy_solver_binsearch2$(OBJEXT) + ../domains/disjunctive_analyzer$(OBJEXT) \ + ../domains/predicate$(OBJEXT) +# ../domains/solver_enumeration$(OBJEXT) OBJ+=$(DELTA_OBJ) @@ -72,6 +85,8 @@ OBJ+=$(DELTA_OBJ) include $(CBMC)/src/config.inc include $(CBMC)/src/common +CP_CXXFLAGS+= $(SUMMARIZERFLAGS) + INCLUDES= -I $(CBMC)/src # \ # -I $(CUDD)/cudd -I $(CUDD)/obj -I $(CUDD)/mtr -I $(CUDD)/epd @@ -80,13 +95,13 @@ LIBS = #LIBS = $(CUDD)/cudd/libcudd.a $(CUDD)/mtr/libmtr.a \ # $(CUDD)/st/libst.a $(CUDD)/epd/libepd.a $(CUDD)/util/libutil.a -CP_CXXFLAGS +=-g +#CP_CXXFLAGS +=-g -DSHOW_CALLING_CONTEXTS # $(CUDD)/obj/libobj.a CLEANFILES = summarizer$(EXEEXT) $(DELTA_OBJ) -all: 2ls$(EXEEXT) +all: summarizer$(EXEEXT) ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) @@ -100,6 +115,6 @@ endif ############################################################################### -2ls$(EXEEXT): $(OBJ) +summarizer$(EXEEXT): $(OBJ) $(LINKBIN) diff --git a/src/summarizer/array_abstraction.cpp b/src/summarizer/array_abstraction.cpp index 810c00d7a..438c59ac2 100644 --- a/src/summarizer/array_abstraction.cpp +++ b/src/summarizer/array_abstraction.cpp @@ -330,7 +330,7 @@ void array_abstractiont::add_argument( symbolt new_symbol; new_symbol.type=type; new_symbol.value.make_nil(); - new_symbol.location=str_args.back().location(); + new_symbol.location=str_args.back().source_location(); new_symbol.name=str_args.back().get_identifier(); new_symbol.module=fct_symbol.module; new_symbol.base_name=str_args.back().get_base_name(); @@ -940,7 +940,7 @@ Function: array_abstractiont::replace_string_macros void array_abstractiont::replace_string_macros( exprt &expr, bool lhs, - const locationt &location) + const source_locationt &location) { if(expr.id()=="is_zero_string") { @@ -981,7 +981,7 @@ exprt array_abstractiont::build( const exprt &pointer, whatt what, bool write, - const locationt &location) + const source_locationt &location) { // take care of pointer typecasts now if(pointer.id()==ID_typecast) diff --git a/src/summarizer/array_abstraction.h b/src/summarizer/array_abstraction.h index 5831544bd..c1c203d44 100644 --- a/src/summarizer/array_abstraction.h +++ b/src/summarizer/array_abstraction.h @@ -51,7 +51,7 @@ class array_abstractiont:public message_streamt void replace_string_macros( exprt &expr, bool lhs, - const locationt &location); + const source_locationt &location); void move_lhs_arithmetic(exprt &lhs, exprt &rhs); @@ -118,7 +118,7 @@ class array_abstractiont:public message_streamt const exprt &pointer, whatt what, bool write, - const locationt &location); + const source_locationt &location); bool build(const exprt &object, exprt &dest, bool write); bool build_wrap(const exprt &object, exprt &dest, bool write); diff --git a/src/summarizer/cover_goals_ext.cpp b/src/summarizer/cover_goals_ext.cpp index 2773dd886..c2e42f74c 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/summarizer/cover_goals_ext.cpp @@ -1,5 +1,6 @@ /*******************************************************************\ + Module: Cover a set of goals incrementally Author: Daniel Kroening, kroening@kroening.com @@ -13,7 +14,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: colver_goals_extt::~colver_goals_extt +Function: cover_goals_extt::~cover_goals_extt Inputs: @@ -46,7 +47,7 @@ void cover_goals_extt::mark() g_it!=goals.end(); g_it++) if(!g_it->covered && - solver.solver.l_get(g_it->condition).is_true()) + solver.l_get(g_it->condition).is_true()) { g_it->covered=true; _number_covered++; @@ -99,7 +100,7 @@ void cover_goals_extt::freeze_goal_variables() g_it!=goals.end(); g_it++) if(!g_it->condition.is_constant()) - solver.solver.set_frozen(g_it->condition); + solver.solver->set_frozen(g_it->condition); } /*******************************************************************\ @@ -144,6 +145,8 @@ void cover_goals_extt::operator()() // notify assignment(); + + if(!all_properties) return; //exit on first failure if requested break; default: @@ -169,65 +172,53 @@ Function: cover_goals_extt::assignment void cover_goals_extt::assignment() { - if(!spurious_check) return; - - //check loop head choices in model - bool invariants_involved = false; - for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); - l_it != loophead_selects.end(); l_it++) - { - if(solver.solver.get(l_it->op0()).is_true()) - { - invariants_involved = true; - break; - } - } - if(!invariants_involved) - { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) - { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.solver.l_get(g_it->condition).is_true()) - { - property_map[it->first].result = property_checkert::FAIL; - } - } - return; - } - - solver.new_context(); - // force avoiding paths going through invariants - - solver << conjunction(loophead_selects); - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) + std::list::const_iterator g_it=goals.begin(); + for(goal_mapt::const_iterator it=goal_map.begin(); + it!=goal_map.end(); it++, g_it++) { if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.solver.l_get(g_it->condition).is_true()) - { - property_map[it->first].result = property_checkert::FAIL; - //show_error_trace(it->first,SSA,solver,debug(),get_message_handler()); - } + solver.l_get(g_it->condition).is_true()) + { + if(spurious_check) + { + assert((g_it->cond_expression).id() == ID_not); + exprt conjunct_expr = (g_it->cond_expression).op0(); + + if(conjunct_expr.id() != ID_and) + { + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); + } + else + { + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it = + conjunct_expr.operands().begin(); + c_it != conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal = solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_false()) + failed_exprs.push_back(*c_it); + } + solver.pop_context(); //otherwise this would interfere with necessary preconditions + for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); + if(property_map[it->first].result == + property_checkert::FAIL) + break; + } + solver.new_context(); + } + } + else + property_map[it->first].result = property_checkert::FAIL; + } } - break; - } - case decision_proceduret::D_UNSATISFIABLE: - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } - - solver.pop_context(); - + _iterations++; //statistics } diff --git a/src/summarizer/cover_goals_ext.h b/src/summarizer/cover_goals_ext.h index 2d5e30af4..7b670f0b7 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/summarizer/cover_goals_ext.h @@ -13,6 +13,8 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "../domains/incremental_solver.h" +#include "summarizer_bw_cex.h" +#include "summarizer_bw_cex_complete.h" /*******************************************************************\ @@ -48,11 +50,14 @@ class cover_goals_extt:public messaget explicit inline cover_goals_extt(incremental_solvert &_solver, const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, - bool _spurious_check): - solver(_solver), + bool _spurious_check, bool _all_properties, + summarizer_bw_cex_baset &_summarizer_bw_cex): + solver(_solver), property_map(_property_map), spurious_check(_spurious_check), - loophead_selects(_loophead_selects) + all_properties(_all_properties), + loophead_selects(_loophead_selects), + summarizer_bw_cex(_summarizer_bw_cex) {} virtual ~cover_goals_extt(); @@ -65,6 +70,7 @@ class cover_goals_extt:public messaget { literalt condition; bool covered; + exprt cond_expression; cover_goalt():covered(false) { @@ -102,12 +108,20 @@ class cover_goals_extt:public messaget goals.back().condition=condition; } + inline void add(const exprt cond_expression) + { + goals.push_back(cover_goalt()); + goals.back().condition=!solver.convert(not_exprt(cond_expression)); + goals.back().cond_expression=cond_expression; + } + protected: unsigned _number_covered, _iterations; incremental_solvert &solver; property_checkert::property_mapt &property_map; - bool spurious_check; + bool spurious_check, all_properties; exprt::operandst loophead_selects; + summarizer_bw_cex_baset &summarizer_bw_cex; // this method is called for each satisfying assignment virtual void assignment(); diff --git a/src/summarizer/show.cpp b/src/summarizer/show.cpp index c24a60185..88751bfd0 100644 --- a/src/summarizer/show.cpp +++ b/src/summarizer/show.cpp @@ -7,10 +7,10 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ #include -#include #include #include #include +#include #include #include @@ -21,6 +21,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "../ssa/guard_map.h" #include "../ssa/local_ssa.h" #include "../ssa/simplify_ssa.h" +#include "../ssa/ssa_value_set.h" #include "../domains/ssa_fixed_point.h" @@ -44,7 +45,8 @@ void show_assignments( std::ostream &out) { ssa_objectst ssa_objects(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects); + ssa_value_ait ssa_value_ai(goto_function, ns); + assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); assignments.output(ns, goto_function.body, out); } @@ -62,18 +64,31 @@ Function: show_assignments void show_assignments( const goto_modelt &goto_model, + const irep_idt &function, std::ostream &out, message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - - forall_goto_functions(f_it, goto_model.goto_functions) + + if(!function.empty()) { - out << ">>>> Function " << f_it->first << "\n"; - - show_assignments(f_it->second, ns, out); - - out << "\n"; + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_assignments(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_assignments(f_it->second, ns, out); + + out << "\n"; + } } } @@ -95,7 +110,8 @@ void show_defs( std::ostream &out) { ssa_objectst ssa_objects(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects); + ssa_value_ait ssa_value_ai(goto_function, ns); + assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); ssa_ait ssa_analysis(assignments); ssa_analysis(goto_function, ns); ssa_analysis.output(ns, goto_function.body, out); @@ -115,18 +131,31 @@ Function: show_defs void show_defs( const goto_modelt &goto_model, + const irep_idt &function, std::ostream &out, message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - forall_goto_functions(f_it, goto_model.goto_functions) + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_defs(f_it->second, ns, out); + } + else { - out << ">>>> Function " << f_it->first << "\n"; + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; - show_defs(f_it->second, ns, out); + show_defs(f_it->second, ns, out); - out << "\n"; + out << "\n"; + } } } @@ -165,18 +194,31 @@ Function: show_guards void show_guards( const goto_modelt &goto_model, + const irep_idt &function, std::ostream &out, message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - forall_goto_functions(f_it, goto_model.goto_functions) + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_guards(f_it->second, ns, out); + } + else { - out << ">>>> Function " << f_it->first << "\n"; + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; - show_guards(f_it->second, ns, out); + show_guards(f_it->second, ns, out); - out << "\n"; + out << "\n"; + } } } @@ -200,7 +242,7 @@ void show_ssa( { local_SSAt local_SSA(goto_function, ns); if(simplify) ::simplify(local_SSA, ns); - local_SSA.output(out); + local_SSA.output_verbose(out); } /*******************************************************************\ @@ -217,22 +259,36 @@ Function: show_ssa void show_ssa( const goto_modelt &goto_model, + const irep_idt &function, bool simplify, std::ostream &out, message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - forall_goto_functions(f_it, goto_model.goto_functions) + if(!function.empty()) { - if(f_it->first=="assert") continue; - if(f_it->first=="__CPROVER_assume") continue; - - out << ">>>> Function " << f_it->first << "\n"; + out << ">>>> Function " << function << "\n"; + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_ssa(f_it->second, simplify, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(f_it->first=="assert") continue; + if(f_it->first=="__CPROVER_assume") continue; + + out << ">>>> Function " << f_it->first << "\n"; - show_ssa(f_it->second, simplify, ns, out); + show_ssa(f_it->second, simplify, ns, out); - out << "\n"; + out << "\n"; + } } } @@ -274,25 +330,38 @@ Function: show_fixed_points void show_fixed_points( const goto_modelt &goto_model, + const irep_idt &function, bool simplify, std::ostream &out, message_handlert &message_handler) { const namespacet ns(goto_model.symbol_table); - forall_goto_functions(f_it, goto_model.goto_functions) + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_fixed_point(f_it->second, simplify, ns, out); + } + else { - out << ">>>> Function " << f_it->first << "\n"; + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; - show_fixed_point(f_it->second, simplify, ns, out); + show_fixed_point(f_it->second, simplify, ns, out); - out << "\n"; + out << "\n"; + } } } /*******************************************************************\ -Function: show_trace +Function: show_error_trace Inputs: @@ -302,6 +371,24 @@ Function: show_trace \*******************************************************************/ +void print_symbol_values(const local_SSAt &SSA, + prop_convt &solver, + std::ostream &out, + const exprt &expr) +{ +// if(expr.id()==ID_symbol) + { + out << from_expr(SSA.ns, "",expr) << " == " << + from_expr(SSA.ns, "", solver.get(expr)) << "\n"; + // return; + } + for(exprt::operandst::const_iterator it = expr.operands().begin(); + it != expr.operands().end(); it++) + { + print_symbol_values(SSA,solver,out,*it); + } +} + void show_error_trace(const irep_idt &property_id, const local_SSAt &SSA, prop_convt &solver, @@ -309,15 +396,26 @@ void show_error_trace(const irep_idt &property_id, message_handlert &message_handler) { - out << "\n*** Abstract error trace for property " << property_id << "\n"; + out << "\n*** Error trace for property " << property_id << "\n"; for (local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { for (local_SSAt::nodet::equalitiest::const_iterator e_it = n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) { - out << from_expr(SSA.ns, "", e_it->op0()) << " == " << - from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; + print_symbol_values(SSA,solver,out,*e_it); + // out << from_expr(SSA.ns, "", e_it->op0()) << " == " << + // from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; + } + for (local_SSAt::nodet::constraintst::const_iterator c_it = + n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) + { + print_symbol_values(SSA,solver,out,*c_it); + } + for (local_SSAt::nodet::assertionst::const_iterator a_it = + n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) + { + print_symbol_values(SSA,solver,out,*a_it); } } out << "\n"; @@ -342,13 +440,7 @@ local_SSAt::locationt find_loc_by_guard(const local_SSAt &SSA, unsigned pos1 = gstr.find("#")+1; unsigned pos2 = gstr.find("%",pos1); unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); - local_SSAt::nodest::const_iterator n_it =SSA.nodes.begin(); - for(; n_it != SSA.nodes.end(); n_it++) - { - if(n_it->location->location_number == n) break; - } - return n_it->location; - + return SSA.find_location_by_number(n); } void purify_identifiers(exprt &expr) @@ -410,3 +502,117 @@ void show_invariants(const local_SSAt &SSA, } else assert(false); } + + +/*******************************************************************\ + +Function: show_ssa_symbols + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_ssa_symbols( + const local_SSAt &SSA, + std::ostream & out) +{ + std::set symbols; + out << "\n*** SSA Symbols " << "\n"; + for (local_SSAt::nodest::const_iterator n_it = + SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) + { + for (local_SSAt::nodet::equalitiest::const_iterator e_it = + n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) + { + find_symbols(*e_it,symbols); + } + for (local_SSAt::nodet::constraintst::const_iterator c_it = + n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) + { + find_symbols(*c_it,symbols); + } + for (local_SSAt::nodet::assertionst::const_iterator a_it = + n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) + { + find_symbols(*a_it,symbols); + } + find_symbols(n_it->enabling_expr,symbols); + } + + for(std::set::const_iterator it = symbols.begin(); + it != symbols.end(); it++) + { + out << from_type(SSA.ns, "",it->type()) << " " << + from_expr(SSA.ns, "", *it) << ";\n"; + } + out << "\n"; +} + +/*******************************************************************\ + +Function: show_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_set( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + std::ostream &out) +{ + ssa_objectst ssa_objects(goto_function, ns); + ssa_value_ait ssa_value_ai(goto_function, ns); + ssa_value_ai.output(ns, goto_function, out); +} + +/*******************************************************************\ + +Function: show_value_sets + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_sets( + const goto_modelt &goto_model, + const irep_idt &function, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_value_set(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_value_set(f_it->second, ns, out); + + out << "\n"; + } + } +} + diff --git a/src/summarizer/show.h b/src/summarizer/show.h index 0d567efd4..7546da5ad 100644 --- a/src/summarizer/show.h +++ b/src/summarizer/show.h @@ -16,31 +16,44 @@ Author: Daniel Kroening, kroening@kroening.com #include +#include "summary.h" + class message_handlert; void show_ssa( const goto_modelt &, + const irep_idt &function, bool simplify, std::ostream &, message_handlert &); void show_defs( const goto_modelt &, + const irep_idt &function, + std::ostream &, + message_handlert &); + +void show_value_sets( + const goto_modelt &, + const irep_idt &function, std::ostream &, message_handlert &); void show_assignments( const goto_modelt &, + const irep_idt &function, std::ostream &, message_handlert &); void show_guards( const goto_modelt &, + const irep_idt &function, std::ostream &, message_handlert &); void show_fixed_points( const goto_modelt &, + const irep_idt &function, bool simplify, std::ostream &, message_handlert &); @@ -58,4 +71,8 @@ void show_invariants( const summaryt &summary, std::ostream &out); +void show_ssa_symbols( + const local_SSAt &SSA, + std::ostream &out); + #endif diff --git a/src/summarizer/ssa_db.cpp b/src/summarizer/ssa_db.cpp index 144d8fd6c..8e77f0cf3 100644 --- a/src/summarizer/ssa_db.cpp +++ b/src/summarizer/ssa_db.cpp @@ -7,3 +7,12 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ #include "ssa_db.h" + +//void ssa_dbt::depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner) +void ssa_dbt::depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner, bool entry) +{ + depgraph_store[function_name] = new ssa_dependency_grapht(*this,ns); + const local_SSAt &SSA=this->get(function_name); + depgraph_store[function_name]->create(SSA, ssa_inliner, entry); +} + diff --git a/src/summarizer/ssa_db.h b/src/summarizer/ssa_db.h index 41010dd6c..22642b260 100644 --- a/src/summarizer/ssa_db.h +++ b/src/summarizer/ssa_db.h @@ -9,17 +9,29 @@ Author: Daniel Kroening, kroening@kroening.com #ifndef CPROVER_LOCAL_SSA_DB_H #define CPROVER_LOCAL_SSA_DB_H +#include + #include "../ssa/local_ssa.h" +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_dependency_graph.h" #include "../domains/incremental_solver.h" #include +class ssa_inlinert; +class ssa_dependency_grapht; + class ssa_dbt { -public: + public: typedef irep_idt function_namet; typedef std::map functionst; + typedef std::map depgrapht; typedef std::map solverst; + explicit ssa_dbt(const optionst &_options) + : options(_options) + { } + ~ssa_dbt() { for(functionst::iterator it = store.begin(); @@ -33,12 +45,16 @@ class ssa_dbt local_SSAt &get(const function_namet &function_name) const { return *store.at(function_name); } + ssa_dependency_grapht &get_depgraph(const function_namet &function_name) const + { return *depgraph_store.at(function_name); } + incremental_solvert &get_solver(const function_namet &function_name) { solverst::iterator it = the_solvers.find(function_name); if(it!=the_solvers.end()) return *(it->second); the_solvers[function_name] = - incremental_solvert::allocate(store.at(function_name)->ns); + incremental_solvert::allocate(store.at(function_name)->ns, + options.get_bool_option("refine")); return *the_solvers.at(function_name); } @@ -55,8 +71,12 @@ class ssa_dbt store[function_name] = new local_SSAt(goto_function,ns); } + void depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner, bool entry); + protected: + const optionst &options; functionst store; + depgrapht depgraph_store; solverst the_solvers; }; diff --git a/src/summarizer/summarizer_base.cpp b/src/summarizer/summarizer_base.cpp index 4b878fa3e..480df89dc 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/summarizer/summarizer_base.cpp @@ -20,12 +20,10 @@ Author: Peter Schrammel #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" #include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" #include "../ssa/local_ssa.h" #include "../ssa/simplify_ssa.h" -#define PRECISE_JOIN /*******************************************************************\ @@ -46,7 +44,8 @@ void summarizer_baset::summarize() it!=ssa_db.functions().end(); it++) { status() << "\nSummarizing function " << it->first << eom; - if(!summary_db.exists(it->first)) + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) compute_summary_rec(it->first,precondition,false); else status() << "Summary for function " << it->first << " exists already" << eom; @@ -70,7 +69,8 @@ void summarizer_baset::summarize(const function_namet &function_name) exprt precondition = true_exprt(); //initial calling context status() << "\nSummarizing function " << function_name << eom; - if(!summary_db.exists(function_name)) + if(!summary_db.exists(function_name) || + summary_db.get(function_name).mark_recompute) { compute_summary_rec(function_name,precondition,true); } @@ -278,6 +278,7 @@ bool summarizer_baset::check_precondition( if(summary_db.exists(fname)) { summaryt summary = summary_db.get(fname); + if(summary.mark_recompute) return false; if(!context_sensitive || summary.fw_precondition.is_true()) //precondition trivially holds { @@ -400,3 +401,45 @@ bool summarizer_baset::check_end_reachable( return result; } + +/*******************************************************************\ + +Function: summarizer_baset::get_loophead_selects + + Inputs: + + Outputs: + + Purpose: returns the select guards at the loop heads + e.g. in order to check whether a countermodel is spurious + +\*******************************************************************/ + +exprt::operandst summarizer_baset::get_loophead_selects( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver) +{ + exprt::operandst loophead_selects; + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) continue; + symbol_exprt lsguard = SSA.name(SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(lsguard,*n_it,true); + loophead_selects.push_back(not_exprt(lsguard)); + solver.set_frozen(solver.convert(lsguard)); + } + literalt loophead_selects_literal = solver.convert(conjunction(loophead_selects)); + if(!loophead_selects_literal.is_constant()) + solver.set_frozen(loophead_selects_literal); + +#if 0 + std::cout << "loophead_selects: " + << from_expr(SSA.ns,"",conjunction(loophead_selects)) + << std::endl; +#endif + + return loophead_selects; +} diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h index 4b82dc99f..be8d80ae3 100644 --- a/src/summarizer/summarizer_base.h +++ b/src/summarizer/summarizer_base.h @@ -31,6 +31,7 @@ class summarizer_baset : public messaget ssa_db(_ssa_db), ssa_unwinder(_ssa_unwinder), ssa_inliner(_ssa_inliner), + nonpassed_assertions(false), solver_instances(0), solver_calls(0), summaries_used(0) @@ -50,12 +51,18 @@ class summarizer_baset : public messaget unsigned get_number_of_solver_calls() { return solver_calls; } unsigned get_number_of_summaries_used() { return summaries_used; } + static exprt::operandst get_loophead_selects( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver); + protected: optionst &options; summary_dbt &summary_db; ssa_dbt &ssa_db; ssa_unwindert &ssa_unwinder; ssa_inlinert &ssa_inliner; + bool nonpassed_assertions; virtual void compute_summary_rec(const function_namet &function_name, const exprt &precondition, diff --git a/src/summarizer/summarizer_bw.cpp b/src/summarizer/summarizer_bw.cpp index d6f7bd1d0..cfaf5449e 100644 --- a/src/summarizer/summarizer_bw.cpp +++ b/src/summarizer/summarizer_bw.cpp @@ -20,7 +20,6 @@ Author: Peter Schrammel #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" #include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" #include "../ssa/local_ssa.h" #include "../ssa/simplify_ssa.h" diff --git a/src/summarizer/summarizer_bw.h b/src/summarizer/summarizer_bw.h index 3d2dfcd81..48600ede1 100644 --- a/src/summarizer/summarizer_bw.h +++ b/src/summarizer/summarizer_bw.h @@ -31,7 +31,7 @@ class summarizer_bwt : public summarizer_baset summarizer_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) {} - virtual void summarize(); + virtual void summarize(); virtual void summarize(const function_namet &entry_function); @@ -41,7 +41,7 @@ class summarizer_bwt : public summarizer_baset const exprt &postcondition, bool context_sensitive); - void inline_summaries(const function_namet &function_name, + virtual void inline_summaries(const function_namet &function_name, local_SSAt &SSA, const summaryt &old_summary, const exprt &postcondition, diff --git a/src/summarizer/summarizer_fw.cpp b/src/summarizer/summarizer_fw.cpp index 5a780f90f..dcf437019 100644 --- a/src/summarizer/summarizer_fw.cpp +++ b/src/summarizer/summarizer_fw.cpp @@ -20,11 +20,11 @@ Author: Peter Schrammel #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" #include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" #include "../ssa/local_ssa.h" #include "../ssa/simplify_ssa.h" +//#define SHOW_WHOLE_RESULT /*******************************************************************\ @@ -77,6 +77,7 @@ void summarizer_fwt::compute_summary_rec(const function_namet &function_name, do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); } + if(!options.get_bool_option("competition-mode")) { std::ostringstream out; out << std::endl << "Summary for function " << function_name << std::endl; @@ -121,20 +122,58 @@ void summarizer_fwt::do_summary(const function_namet &function_name, template_generator.set_message_handler(get_message_handler()); template_generator(solver.next_domain_number(),SSA,true); - cond = and_exprt(cond, - and_exprt(summary.fw_precondition, - ssa_inliner.get_summaries(SSA))); + exprt::operandst conds; + conds.reserve(5); + conds.push_back(cond); + conds.push_back(summary.fw_precondition); + conds.push_back(ssa_inliner.get_summaries(SSA)); - analyzer(solver,SSA,cond,template_generator); - analyzer.get_result(summary.fw_transformer,template_generator.inout_vars()); - analyzer.get_result(summary.fw_invariant,template_generator.loop_vars()); +#ifdef REUSE_INVARIANTS + if(summary_db.exists(function_name)) //reuse existing invariants + { + const exprt &old_inv = summary_db.get(function_name).fw_invariant; + exprt inv = ssa_unwinder.get(function_name).rename_invariant(old_inv); + conds.push_back(inv); + +#if 0 + std::ostringstream out; + out << "(original inv)" << from_expr(SSA.ns,"",old_inv) << "\n"; + debug() << out.str() << eom; + out << "(renamed inv)" << from_expr(SSA.ns,"",inv)<<"\n"; + debug() << out.str() << eom; +#endif + } +#endif + + cond = conjunction(conds); + + bool assertions_check = false; //options.get_bool_option("k-induction"); + bool assertions_hold = analyzer(solver,SSA,cond,template_generator, + assertions_check); + if(assertions_hold) + { + analyzer.get_result(summary.fw_transformer,template_generator.inout_vars()); + analyzer.get_result(summary.fw_invariant,template_generator.loop_vars()); + +#ifdef SHOW_WHOLE_RESULT + // to see all the custom template values + exprt whole_result; + analyzer.get_result(whole_result,template_generator.all_vars()); + debug() << "whole result: " << from_expr(SSA.ns,"",whole_result) << eom; +#endif - if(context_sensitive && !summary.fw_precondition.is_true()) + if(context_sensitive && !summary.fw_precondition.is_true()) + { + summary.fw_transformer = + implies_exprt(summary.fw_precondition,summary.fw_transformer); + summary.fw_invariant = + implies_exprt(summary.fw_precondition,summary.fw_invariant); + } + } + else //!assertions_hold { - summary.fw_transformer = - implies_exprt(summary.fw_precondition,summary.fw_transformer); - summary.fw_invariant = - implies_exprt(summary.fw_precondition,summary.fw_invariant); + nonpassed_assertions = true; + summary.nonpassed_assertions = analyzer.get_nonpassed_assertions(); } solver_instances += analyzer.get_number_of_solver_instances(); diff --git a/src/summarizer/summarizer_fw.h b/src/summarizer/summarizer_fw.h index 221ab3023..054ee113d 100644 --- a/src/summarizer/summarizer_fw.h +++ b/src/summarizer/summarizer_fw.h @@ -38,7 +38,7 @@ class summarizer_fwt : public summarizer_baset const exprt &precondition, bool context_sensitive); - void inline_summaries(const function_namet &function_name, + virtual void inline_summaries(const function_namet &function_name, local_SSAt &SSA, const exprt &precondition, bool context_sensitive); diff --git a/src/summarizer/summarizer_main.cpp b/src/summarizer/summarizer_main.cpp index 6be559b00..6bafefc6a 100644 --- a/src/summarizer/summarizer_main.cpp +++ b/src/summarizer/summarizer_main.cpp @@ -8,7 +8,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "summarizer_parseoptions.h" +#include "summarizer_parse_options.h" /*******************************************************************\ @@ -26,13 +26,13 @@ Function: main int wmain(int argc, const wchar_t **argv_wide) { const char **argv=narrow_argv(argc, argv_wide); - summarizer_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + summarizer_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #else int main(int argc, const char **argv) { - summarizer_parseoptionst parseoptions(argc, argv); - return parseoptions.main(); + summarizer_parse_optionst parse_options(argc, argv); + return parse_options.main(); } #endif diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp index 4107f5014..6904fa878 100644 --- a/src/summarizer/summary.cpp +++ b/src/summarizer/summary.cpp @@ -11,7 +11,7 @@ Author: Peter Schrammel #include -//#define PRETTY_PRINT +const summaryt::call_sitet summaryt::entry_call_site; /*******************************************************************\ @@ -63,16 +63,18 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const out << "backward invariant: " << (bw_invariant.is_nil() ? "not computed" : from_expr(ns,"",bw_invariant)) << std::endl; - out << "termination argument: "; - if(termination_argument.is_nil()) out << "not computed"; - else -#if PRETTY_PRINT - pretty_print_termination_argument(out,ns,termination_argument); -#else - out << from_expr(ns,"",termination_argument) << std::endl; -#endif - out << std::endl; - out << "terminates: " << threeval2string(terminates) << std::endl; + for(error_summariest::const_iterator + it = error_summaries.begin(); + it != error_summaries.end(); it++) + { + out << "error summary for "; + if(it->first == entry_call_site) + out << "entry point"; + else + out << "location " << it->first.location_number; + out << ": " << std::endl + << " " << from_expr(ns,"",it->second) << std::endl; + } } /*******************************************************************\ @@ -125,39 +127,5 @@ void summaryt::join(const summaryt &new_summary) combine_or(bw_postcondition,new_summary.bw_postcondition); combine_and(bw_transformer,new_summary.bw_transformer); combine_and(bw_invariant,new_summary.bw_invariant); - combine_and(termination_argument,new_summary.termination_argument); - switch(new_summary.terminates) - { - case YES: - break; - case NO: terminates=NO; - break; - case UNKNOWN: - if(terminates!=NO) terminates=UNKNOWN; - break; - default: assert(false); - } } -/*******************************************************************\ - -Function: threeval2string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string threeval2string(threevalt v) -{ - switch(v) - { - case YES: return "yes"; - case NO: return "no"; - case UNKNOWN: return "unknown"; - } - assert(false); -} diff --git a/src/summarizer/summary.h b/src/summarizer/summary.h index 66f98c032..ef0e739f1 100644 --- a/src/summarizer/summary.h +++ b/src/summarizer/summary.h @@ -9,12 +9,12 @@ Author: Daniel Kroening, kroening@kroening.com #ifndef CPROVER_DELTACHECK_SUMMARY_H #define CPROVER_DELTACHECK_SUMMARY_H +#include #include #include #include - -typedef enum{YES, NO, UNKNOWN} threevalt; +#include "../ssa/local_ssa.h" class summaryt { @@ -31,9 +31,9 @@ class summaryt bw_precondition(nil_exprt()), bw_postcondition(nil_exprt()), bw_transformer(nil_exprt()), - bw_invariant(nil_exprt()), - termination_argument(nil_exprt()), - terminates(UNKNOWN) {} + bw_invariant(nil_exprt()), + mark_recompute(false), + has_assertion(false) {} var_listt params; var_sett globals_in, globals_out; @@ -48,8 +48,33 @@ class summaryt predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) - predicatet termination_argument; - threevalt terminates; + bool mark_recompute; //to force recomputation of the summary + // (used for invariant reuse in k-induction) + + //-------------- + // the following is for generating interprocedural counterexample + + bool has_assertion; + + std::list nonpassed_assertions; + + struct call_sitet { //TODO: we also need unwinding information here + call_sitet() + : location_number(UINT_MAX) {} + explicit call_sitet(local_SSAt::locationt loc) + : location_number(loc->location_number) {} + unsigned location_number; + + bool operator<(const call_sitet &other) const + { return (location_number < other.location_number); } + bool operator==(const call_sitet &other) const + { return (location_number == other.location_number); } + }; + + const static call_sitet entry_call_site; + typedef std::map error_summariest; + error_summariest error_summaries; + //-------------- void output(std::ostream &out, const namespacet &ns) const; @@ -62,7 +87,6 @@ class summaryt }; -std::string threeval2string(threevalt v); #endif diff --git a/src/summarizer/summary_db.cpp b/src/summarizer/summary_db.cpp index b80c01556..4e7dde39e 100644 --- a/src/summarizer/summary_db.cpp +++ b/src/summarizer/summary_db.cpp @@ -6,4 +6,81 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +#include + +#include + #include "summary_db.h" + +void summary_dbt::put(const function_namet &function_name, + const summaryt &summary) +{ + if(store.find(function_name)==store.end() || + store[function_name].mark_recompute) + store[function_name] = summary; + else + store[function_name].join(summary); +} + +void summary_dbt::mark_recompute_all() +{ + for(std::map::iterator it = store.begin(); + it != store.end(); it++) + it->second.mark_recompute = true; +} + +/*******************************************************************\ + +Function: summary_dbt::file_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string summary_dbt::file_name(const std::string &id) +{ + return "summary."+id; +} + +/*******************************************************************\ + +Function: summary_dbt::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::read(const std::string &id) +{ + current=id; + + summary=jsont::json_object(); + + parse_json(file_name(id), get_message_handler(), summary); +} + +/*******************************************************************\ + +Function: summary_dbt::write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::write() +{ + std::ofstream out(file_name(current).c_str()); + out << summary << '\n'; +} diff --git a/src/summarizer/summary_db.h b/src/summarizer/summary_db.h index 0cc7fae8d..3dfe0341f 100644 --- a/src/summarizer/summary_db.h +++ b/src/summarizer/summary_db.h @@ -10,30 +10,36 @@ Author: Daniel Kroening, kroening@kroening.com #define CPROVER_SUMMARIZER_SUMMARY_DB_H #include "summary.h" +#include +#include -class summary_dbt +class summary_dbt:public messaget { public: typedef irep_idt function_namet; - void load() const {} - void save() const {} + // retrieve a summary for function with given identifier + void read(const std::string &); + void write(); void clear() { store.clear(); } summaryt get(const function_namet &function_name) const { return store.at(function_name); } + void set(const function_namet &function_name, const summaryt &summary) + { store[function_name] = summary; } bool exists(const function_namet &function_name) const { return store.find(function_name)!=store.end(); } - void put(const function_namet &function_name, const summaryt &summary) - { - if(store.find(function_name)==store.end()) - store[function_name] = summary; - else - store[function_name].join(summary); - } - - protected: + void put(const function_namet &function_name, const summaryt &summary); + + void mark_recompute_all(); + + jsont summary; + +protected: std::map store; + + std::string current; + std::string file_name(const std::string &); }; #endif diff --git a/src/summarizer/version.h b/src/summarizer/version.h index 9df566712..401faf272 100644 --- a/src/summarizer/version.h +++ b/src/summarizer/version.h @@ -1 +1 @@ -#define SUMMARIZER_VERSION "0.2.0" +#define SUMMARIZER_VERSION "0.1.0" From 74d5022d75fa9b6feebb7f9c144af0a36dcc027a Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 23 Apr 2016 16:31:04 +0100 Subject: [PATCH 02/90] all commits of old branch --- src/deltacheck/deltacheck_parse_options.cpp | 347 +++++ src/deltacheck/deltacheck_parse_options.h | 52 + src/deltagit/deltagit_parse_options.cpp | 206 +++ src/deltagit/deltagit_parse_options.h | 30 + src/domains/disjunctive_analyzer.cpp | 302 ++++ src/domains/disjunctive_analyzer.h | 51 + src/domains/domain_refinement.h | 39 + src/domains/domain_refinement_variables.cpp | 20 + src/domains/predabs_domain.cpp | 386 +++++ src/domains/predabs_domain.h | 81 + src/domains/strategy_solver_predabs.cpp | 81 + src/domains/strategy_solver_predabs.h | 33 + src/html/syntax_highlighting.cpp | 306 ++++ src/html/syntax_highlighting.h | 35 + src/ssa/const_propagator.cpp | 467 ++++++ src/ssa/const_propagator.h | 101 ++ src/ssa/replace_symbol_ext.cpp | 82 + src/ssa/replace_symbol_ext.h | 20 + src/ssa/ssa_const_propagator.cpp | 305 ++++ src/ssa/ssa_const_propagator.h | 80 + src/ssa/ssa_dependency_graph.cpp | 385 +++++ src/ssa/ssa_dependency_graph.h | 62 + src/ssa/ssa_dereference.cpp | 405 +++++ src/ssa/ssa_dereference.h | 27 + src/ssa/ssa_slicer.cpp | 181 +++ src/ssa/ssa_slicer.h | 44 + src/ssa/ssa_value_set.cpp | 483 ++++++ src/ssa/ssa_value_set.h | 105 ++ src/storefront/data.cpp | 84 + src/storefront/data.h | 40 + src/storefront/file_view.cpp | 147 ++ src/storefront/file_view.h | 14 + src/storefront/property_view.cpp | 30 + src/storefront/property_view.h | 14 + src/storefront/storefront_main.cpp | 38 + src/storefront/storefront_parse_options.cpp | 121 ++ src/storefront/storefront_parse_options.h | 29 + src/storefront/trace_view.cpp | 29 + src/storefront/trace_view.h | 14 + src/summarizer/function_signature.cpp | 78 + src/summarizer/function_signature.h | 17 + src/summarizer/instrument_goto.cpp | 180 +++ src/summarizer/instrument_goto.h | 55 + src/summarizer/preprocessing_util.cpp | 265 ++++ src/summarizer/summarizer.cpp | 125 ++ src/summarizer/summarizer.h | 45 + src/summarizer/summarizer_bw_cex.cpp | 100 ++ src/summarizer/summarizer_bw_cex.h | 51 + src/summarizer/summarizer_bw_cex_ai.cpp | 511 ++++++ src/summarizer/summarizer_bw_cex_ai.h | 76 + src/summarizer/summarizer_bw_cex_all.cpp | 75 + src/summarizer/summarizer_bw_cex_all.h | 73 + src/summarizer/summarizer_bw_cex_complete.cpp | 525 +++++++ src/summarizer/summarizer_bw_cex_complete.h | 64 + src/summarizer/summarizer_bw_cex_concrete.cpp | 614 ++++++++ src/summarizer/summarizer_bw_cex_concrete.h | 77 + src/summarizer/summarizer_bw_cex_wp.cpp | 645 ++++++++ src/summarizer/summarizer_bw_cex_wp.h | 76 + src/summarizer/summarizer_fw_contexts.cpp | 174 +++ src/summarizer/summarizer_fw_contexts.h | 59 + src/summarizer/summarizer_parse_options.cpp | 1380 +++++++++++++++++ src/summarizer/summarizer_parse_options.h | 142 ++ src/summarizer/summary_checker_ai.cpp | 145 ++ src/summarizer/summary_checker_ai.h | 29 + src/summarizer/summary_checker_base.cpp | 684 ++++++++ src/summarizer/summary_checker_base.h | 102 ++ src/summarizer/summary_checker_bmc.cpp | 63 + src/summarizer/summary_checker_bmc.h | 26 + src/summarizer/summary_checker_kind.cpp | 76 + src/summarizer/summary_checker_kind.h | 26 + 70 files changed, 11834 insertions(+) create mode 100644 src/deltacheck/deltacheck_parse_options.cpp create mode 100644 src/deltacheck/deltacheck_parse_options.h create mode 100644 src/deltagit/deltagit_parse_options.cpp create mode 100644 src/deltagit/deltagit_parse_options.h create mode 100644 src/domains/disjunctive_analyzer.cpp create mode 100644 src/domains/disjunctive_analyzer.h create mode 100644 src/domains/domain_refinement.h create mode 100644 src/domains/domain_refinement_variables.cpp create mode 100644 src/domains/predabs_domain.cpp create mode 100644 src/domains/predabs_domain.h create mode 100644 src/domains/strategy_solver_predabs.cpp create mode 100644 src/domains/strategy_solver_predabs.h create mode 100644 src/html/syntax_highlighting.cpp create mode 100644 src/html/syntax_highlighting.h create mode 100644 src/ssa/const_propagator.cpp create mode 100644 src/ssa/const_propagator.h create mode 100644 src/ssa/replace_symbol_ext.cpp create mode 100644 src/ssa/replace_symbol_ext.h create mode 100644 src/ssa/ssa_const_propagator.cpp create mode 100644 src/ssa/ssa_const_propagator.h create mode 100644 src/ssa/ssa_dependency_graph.cpp create mode 100644 src/ssa/ssa_dependency_graph.h create mode 100644 src/ssa/ssa_dereference.cpp create mode 100644 src/ssa/ssa_dereference.h create mode 100644 src/ssa/ssa_slicer.cpp create mode 100644 src/ssa/ssa_slicer.h create mode 100644 src/ssa/ssa_value_set.cpp create mode 100644 src/ssa/ssa_value_set.h create mode 100644 src/storefront/data.cpp create mode 100644 src/storefront/data.h create mode 100644 src/storefront/file_view.cpp create mode 100644 src/storefront/file_view.h create mode 100644 src/storefront/property_view.cpp create mode 100644 src/storefront/property_view.h create mode 100644 src/storefront/storefront_main.cpp create mode 100644 src/storefront/storefront_parse_options.cpp create mode 100644 src/storefront/storefront_parse_options.h create mode 100644 src/storefront/trace_view.cpp create mode 100644 src/storefront/trace_view.h create mode 100644 src/summarizer/function_signature.cpp create mode 100644 src/summarizer/function_signature.h create mode 100644 src/summarizer/instrument_goto.cpp create mode 100644 src/summarizer/instrument_goto.h create mode 100644 src/summarizer/preprocessing_util.cpp create mode 100644 src/summarizer/summarizer.cpp create mode 100644 src/summarizer/summarizer.h create mode 100644 src/summarizer/summarizer_bw_cex.cpp create mode 100644 src/summarizer/summarizer_bw_cex.h create mode 100644 src/summarizer/summarizer_bw_cex_ai.cpp create mode 100644 src/summarizer/summarizer_bw_cex_ai.h create mode 100644 src/summarizer/summarizer_bw_cex_all.cpp create mode 100644 src/summarizer/summarizer_bw_cex_all.h create mode 100644 src/summarizer/summarizer_bw_cex_complete.cpp create mode 100644 src/summarizer/summarizer_bw_cex_complete.h create mode 100644 src/summarizer/summarizer_bw_cex_concrete.cpp create mode 100644 src/summarizer/summarizer_bw_cex_concrete.h create mode 100644 src/summarizer/summarizer_bw_cex_wp.cpp create mode 100644 src/summarizer/summarizer_bw_cex_wp.h create mode 100644 src/summarizer/summarizer_fw_contexts.cpp create mode 100644 src/summarizer/summarizer_fw_contexts.h create mode 100644 src/summarizer/summarizer_parse_options.cpp create mode 100644 src/summarizer/summarizer_parse_options.h create mode 100644 src/summarizer/summary_checker_ai.cpp create mode 100644 src/summarizer/summary_checker_ai.h create mode 100644 src/summarizer/summary_checker_base.cpp create mode 100644 src/summarizer/summary_checker_base.h create mode 100644 src/summarizer/summary_checker_bmc.cpp create mode 100644 src/summarizer/summary_checker_bmc.h create mode 100644 src/summarizer/summary_checker_kind.cpp create mode 100644 src/summarizer/summary_checker_kind.h diff --git a/src/deltacheck/deltacheck_parse_options.cpp b/src/deltacheck/deltacheck_parse_options.cpp new file mode 100644 index 000000000..4938e4300 --- /dev/null +++ b/src/deltacheck/deltacheck_parse_options.cpp @@ -0,0 +1,347 @@ +/*******************************************************************\ + +Module: Command Line Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "deltacheck_parse_options.h" +#include "version.h" +#include "analyzer.h" +#include "change_impact.h" +#include "../functions/path_util.h" + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::deltacheck_parse_optionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +deltacheck_parse_optionst::deltacheck_parse_optionst( + int argc, const char **argv): + parse_options_baset(DELTACHECK_OPTIONS, argc, argv), + xml_interfacet(cmdline), + ui_message_handler( + cmdline.isset("xml-ui")?ui_message_handlert::XML_UI:ui_message_handlert::PLAIN, + "DeltaCheck " DELTACHECK_VERSION) +{ +} + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::eval_verbosity + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void deltacheck_parse_optionst::eval_verbosity() +{ + // our default verbosity + int v=messaget::M_STATISTICS; + + if(cmdline.isset("verbosity")) + { + v=unsafe_string2int(cmdline.get_value("verbosity")); + if(v<0) + v=0; + else if(v>10) + v=10; + } + + ui_message_handler.set_verbosity(v); +} + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::get_command_line_options + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void deltacheck_parse_optionst::get_command_line_options(optionst &options) +{ + if(config.set(cmdline)) + { + usage_error(); + exit(1); + } + + if(cmdline.isset("debug-level")) + options.set_option("debug-level", cmdline.get_value("debug-level")); + + // check array bounds + if(cmdline.isset("bounds-check")) + options.set_option("bounds-check", true); + else + options.set_option("bounds-check", false); + + // check division by zero + if(cmdline.isset("div-by-zero-check")) + options.set_option("div-by-zero-check", true); + else + options.set_option("div-by-zero-check", false); + + // check overflow/underflow + if(cmdline.isset("signed-overflow-check")) + options.set_option("signed-overflow-check", true); + else + options.set_option("signed-overflow-check", false); + + // check overflow/underflow + if(cmdline.isset("unsigned-overflow-check")) + options.set_option("unsigned-overflow-check", true); + else + options.set_option("unsigned-overflow-check", false); + + // check for NaN (not a number) + if(cmdline.isset("nan-check")) + options.set_option("nan-check", true); + else + options.set_option("nan-check", false); + + // check pointers + if(cmdline.isset("pointer-check")) + options.set_option("pointer-check", true); + else + options.set_option("pointer-check", false); + + // do we do inlining? + if(cmdline.isset("no-inlining")) + options.set_option("partial-inlining", false); + else + options.set_option("partial-inlining", true); + + // check assertions + options.set_option("assertions", true); + + // use assumptions + options.set_option("assumptions", true); +} + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::register_langauges + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void deltacheck_parse_optionst::register_languages() +{ + register_language(new_ansi_c_language); + register_language(new_cpp_language); +} + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int deltacheck_parse_optionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << DELTACHECK_VERSION << std::endl; + return 0; + } + + register_languages(); + + // command line options + + optionst options; + get_command_line_options(options); + set_message_handler(ui_message_handler); + eval_verbosity(); + + try + { + options.set_option("simplify", true); + options.set_option("assertions", true); + options.set_option("assumptions", true); + + if(cmdline.isset("function")) + options.set_option("function", cmdline.get_value("function")); + + if(cmdline.args.size()!=2) + { + usage_error(); + return 10; + } + + if(cmdline.isset("description-old")) + options.set_option("description-old", cmdline.get_value("description-old")); + else + options.set_option("description-old", cmdline.args[0]); + + if(cmdline.isset("description-new")) + options.set_option("description-new", cmdline.get_value("description-new")); + else + options.set_option("description-new", cmdline.args[1]); + + status() << "Reading first GOTO program from file" << eom; + + goto_modelt goto_model1; + + if(read_goto_binary(cmdline.args[0], + goto_model1, get_message_handler())) + return 10; + + status() << "Reading second GOTO program from file" << eom; + + goto_modelt goto_model2; + + if(read_goto_binary(cmdline.args[1], + goto_model2, get_message_handler())) + return 10; + + if(cmdline.isset("show-diff")) + { + change_impactt change_impact; + change_impact.set_message_handler(get_message_handler()); + + change_impact.diff(goto_model1, goto_model2); + change_impact.output_diff(std::cout); + } + else if(cmdline.isset("show-change-impact")) + { + change_impactt change_impact; + change_impact.set_message_handler(get_message_handler()); + + status() << "Computing syntactic difference" << eom; + change_impact.diff(goto_model1, goto_model2); + + status() << "Change-impact analysis" << eom; + change_impact.change_impact(goto_model2); + + change_impact.output_change_impact(std::cout); + } + else + { + std::string path1=get_directory(cmdline.args[0]); + std::string path2=get_directory(cmdline.args[1]); + + deltacheck_analyzer( + path1, goto_model1, + path2, goto_model2, + options, get_message_handler()); + } + + return 0; + } + + catch(const char *e) + { + error() << e << eom; + return 13; + } + + catch(const std::string &e) + { + error() << e << eom; + return 13; + } + + catch(int) + { + return 13; + } + + catch(std::bad_alloc) + { + error() << "Out of memory" << eom; + return 14; + } + + return 0; +} + +/*******************************************************************\ + +Function: deltacheck_parse_optionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void deltacheck_parse_optionst::help() +{ + std::cout << + "\n" + "* * DELTACHECK " DELTACHECK_VERSION " - Copyright (C) 2011-2015 * *\n" + "* * based on CBMC " CBMC_VERSION " * *\n" + "* * Daniel Kroening * *\n" + "* * Oxford University, Computer Science Department * *\n" + "* * kroening@kroening.com * *\n" + "\n" + "Usage: Purpose:\n" + "\n" + " deltacheck [-?] [-h] [--help] show help\n" + " deltacheck prog1 prog2 delta check two programs\n" + "\n" + "Delta checking options:\n" + " --show-change-impact show syntactic change-impact\n" + " --description-old text description of old version\n" + " --description-new text description of new version\n" + "\n" + "Safety checks:\n" + " --bounds-check add array bounds checks\n" + " --div-by-zero-check add division by zero checks\n" + " --pointer-check add pointer checks\n" + " --signed-overflow-check add arithmetic over- and underflow checks\n" + " --unsigned-overflow-check add arithmetic over- and underflow checks\n" + " --nan-check add floating-point NaN checks\n" + "\n" + "Other options:\n" + " --version show version and exit\n" + " --xml-ui use XML-formatted output\n" + " --xml-interface stdio-XML interface\n" + "\n"; +} diff --git a/src/deltacheck/deltacheck_parse_options.h b/src/deltacheck/deltacheck_parse_options.h new file mode 100644 index 000000000..dbeb2fa6d --- /dev/null +++ b/src/deltacheck/deltacheck_parse_options.h @@ -0,0 +1,52 @@ +/*******************************************************************\ + +Module: Command Line Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H +#define CPROVER_DELTACHECK_PARSE_OPTIONS_H + +#include +#include +#include + +#include +#include + +#define DELTACHECK_OPTIONS \ + "(function):" \ + "(debug-level):" \ + "(xml-ui)(xml-interface)" \ + "(verbosity):(version)(index):(description-old):(description-new):" \ + "(bounds-check)(pointer-check)(div-by-zero-check)" \ + "(signed-overflow-check)(unsigned-overflow-check)(nan-check)" \ + "(show-ssa)(show-defs)(show-guards)(show-fixed-points)" \ + "(show-properties)(show-change-impact)(show-diff)" \ + "(no-inline)(sat)" + +class deltacheck_parse_optionst: + public parse_options_baset, + public xml_interfacet, + public messaget +{ +public: + virtual int doit(); + virtual void help(); + + deltacheck_parse_optionst( + int argc, const char **argv); + +protected: + virtual void register_languages(); + + virtual void get_command_line_options(optionst &options); + + void eval_verbosity(); + + ui_message_handlert ui_message_handler; +}; + +#endif diff --git a/src/deltagit/deltagit_parse_options.cpp b/src/deltagit/deltagit_parse_options.cpp new file mode 100644 index 000000000..bbd8f4e1d --- /dev/null +++ b/src/deltagit/deltagit_parse_options.cpp @@ -0,0 +1,206 @@ +/*******************************************************************\ + +Module: Command Line Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include + +#include "../deltacheck/version.h" + +#include "show_jobs.h" +#include "do_job.h" +#include "init.h" +#include "reset.h" +#include "reanalyse.h" +#include "deltagit_parse_options.h" +#include "revisions_report.h" + +/*******************************************************************\ + +Function: deltagit_parse_optionst::deltagit_parse_optionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +deltagit_parse_optionst::deltagit_parse_optionst( + int argc, const char **argv): + parse_options_baset(DELTACHECK_OPTIONS, argc, argv) +{ +} + +/*******************************************************************\ + +Function: deltagit_parse_optionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int deltagit_parse_optionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << DELTACHECK_VERSION << std::endl; + return 0; + } + + try + { + if(cmdline.args.size()==0) + { + usage_error(); + return 10; + } + + const std::string command=cmdline.args[0]; + + if(command=="jobs") + { + show_jobs(std::cout); + } + else if(command=="init") + { + if(cmdline.args.size()==1) + { + init(0); + } + else if(cmdline.args.size()==2) + { + init(atoi(cmdline.args[1].c_str())); + } + else + { + usage_error(); + return 10; + } + } + else if(command=="do") + { + if(cmdline.args.size()==2) + { + do_job(cmdline.args[1]); + } + else if(cmdline.args.size()==1) + { + do_job(); + } + else + { + usage_error(); + return 10; + } + } + else if(command=="reset") + { + if(cmdline.args.size()==2) + reset(cmdline.args[1]); + else if(cmdline.args.size()==1) + reset(); + else + { + usage_error(); + return 10; + } + } + else if(command=="reanalyse") + { + if(cmdline.args.size()==2) + reanalyse(cmdline.args[1]); + else if(cmdline.args.size()==1) + reanalyse(); + else + { + usage_error(); + return 10; + } + } + else if(command=="report") + { + bool partial_html=cmdline.isset("partial-html"); + unsigned max_revs=0; + std::string rel_path; + if(cmdline.isset("max-revs")) + max_revs=unsafe_string2unsigned(cmdline.get_value("max-revs")); + if(partial_html) + rel_path=cmdline.get_value("partial-html"); + revisions_report(partial_html, rel_path, max_revs); + } + else + { + usage_error(); + return 10; + } + } + + catch(const std::string &e) + { + std::cerr << e << std::endl; + return 13; + } + + catch(std::bad_alloc) + { + std::cerr << "Out of memory" << std::endl; + return 14; + } + + return 0; +} + +/*******************************************************************\ + +Function: deltagit_parse_optionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void deltagit_parse_optionst::help() +{ + std::cout << + "\n" + "* * DELTAGIT " DELTACHECK_VERSION " - Copyright (C) 2012-2013 * *\n" + "* * Daniel Kroening * *\n" + "* * Oxford University, Computer Science Department * *\n" + "* * kroening@kroening.com * *\n" + "\n" + "Usage: Purpose:\n" + "\n" + " deltagit [-?] [-h] [--help] show help\n" + " deltagit init set up the jobs\n" + " deltagit jobs list the jobs\n" + " deltagit do do given job\n" + " deltagit do do a job that needs work\n" + " deltagit reset clear failure bit on all jobs\n" + " deltagit reanalyse redo analysis\n" + " deltagit report generate top-level report\n" + "\n" + "Reporting options:\n" + " --partial-html generate a partial HTML file \"include.html\"\n" + " --max-revs report on the last revisions\n" + "\n" + "Other options:\n" + " --version show version and exit\n" + " --xml-ui use XML-formatted output\n" + "\n"; +} diff --git a/src/deltagit/deltagit_parse_options.h b/src/deltagit/deltagit_parse_options.h new file mode 100644 index 000000000..357f563ab --- /dev/null +++ b/src/deltagit/deltagit_parse_options.h @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Command Line Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H +#define CPROVER_DELTACHECK_PARSE_OPTIONS_H + +#include + +#define DELTACHECK_OPTIONS \ + "(verbosity):(version)(description):" \ + "(max-revs):(partial-html):" + +class deltagit_parse_optionst:public parse_options_baset +{ +public: + virtual int doit(); + virtual void help(); + + deltagit_parse_optionst( + int argc, const char **argv); + +protected: +}; + +#endif diff --git a/src/domains/disjunctive_analyzer.cpp b/src/domains/disjunctive_analyzer.cpp new file mode 100644 index 000000000..b82e3cfa0 --- /dev/null +++ b/src/domains/disjunctive_analyzer.cpp @@ -0,0 +1,302 @@ +/*******************************************************************\ + +Module: Data Flow Analysis + +Author: Peter Schrammel, Kumar Madhukar + +\*******************************************************************/ + +#include +#include + +#include "ssa_analyzer.h" +#include "disjunctive_analyzer.h" + + +/*******************************************************************\ + +Function: disjunctive_analyzert::eliminate_implication() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::eliminate_implication(exprt &formula) +{ + if(formula.id() == ID_implies) + formula = or_exprt(not_exprt(formula.op0()), formula.op1()); + + Forall_operands(it, formula) + eliminate_implication(*it); + + return; +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::push_negation_to_atoms() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::push_negation_to_atoms(exprt &formula) +{ + if(formula.id() == ID_not){ + if((formula.op0()).id() == ID_not){ + formula = (formula.op0()).op0(); + } + else{ + exprt::operandst operands; + Forall_operands(it, formula.op0()) + operands.push_back(not_exprt(*it)); + + if((formula.op0()).id() == ID_and){ + formula = disjunction(operands); + } + else{ + if((formula.op0()).id() == ID_or){ + formula = conjunction(operands); + } + } + } + } + + Forall_operands(it, formula) + push_negation_to_atoms(*it); + + return; +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::convert_to_dnf() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::convert_to_dnf(exprt &formula) +{ + this->eliminate_implication(formula); + this->push_negation_to_atoms(formula); + this->convert_to_dnf_rec(formula); + + return; +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::convert_to_dnf_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void disjunctive_analyzert::convert_to_dnf_rec(exprt &formula) +{ + if(formula.id() == ID_or){ + Forall_operands(it, formula) + convert_to_dnf_rec(*it); + } + else{ + if(formula.id() == ID_and){ + + Forall_operands(it, formula) + convert_to_dnf_rec(*it); + + while((formula.operands()).size() > 1){ + exprt::operandst first_operands, second_operands, combination; + + if(((formula.operands()).back()).id() == ID_or) + first_operands = ((formula.operands()).back()).operands(); + else + first_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + if(((formula.operands()).back()).id() == ID_or) + second_operands = ((formula.operands()).back()).operands(); + else + second_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + for(exprt::operandst::iterator f_it=first_operands.begin();f_it!=first_operands.end();f_it++){ + for(exprt::operandst::iterator s_it=second_operands.begin();s_it!=second_operands.end();s_it++){ + combination.push_back(and_exprt(*f_it,*s_it)); + } + } + + (formula.operands()).push_back(disjunction(combination)); + } + formula = formula.op0(); + } + } + + return; +} + +/*******************************************************************\ + +Function: disjunctive_analyzert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool disjunctive_analyzert::operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars + ) +{ + bool response = true; + exprt::operandst result; + + //std::cout << "unsimplified disjunctive cond: " << from_expr(SSA.ns, "", disjunctive_conditions) << "\n\n"; + exprt simple_disjunctive_conditions = simplify_expr(disjunctive_conditions, SSA.ns); //disjunctive_conditions; + //std::cout << "simplified disjunctive cond: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n"; + + //converting simple_disjunctive_conditions into DNF + //std::cout << "before conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; + this->convert_to_dnf(simple_disjunctive_conditions); + //std::cout << "after conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; + + if(simple_disjunctive_conditions.id() == ID_or){ + + exprt::operandst processed_disjuncts; + + exprt::operandst disjuncts=simple_disjunctive_conditions.operands(); + for(exprt::operandst::iterator d_it=disjuncts.begin();d_it!=disjuncts.end();d_it++){ + if((*d_it).id() == ID_not){ + exprt::operandst ops = (*d_it).operands(); + for(exprt::operandst::iterator o_it=ops.begin();o_it!=ops.end();o_it++){ + if((*o_it).id() == ID_equal){ + exprt::operandst ops_equality = (*o_it).operands(); + + equal_exprt equal_expr_in_not = to_equal_expr(*o_it); + + bool constant_comparison = false; + for(exprt::operandst::iterator oe_it=ops_equality.begin();oe_it!=ops_equality.end();oe_it++){ + if((*oe_it).id() == ID_constant) + constant_comparison = true; + } + if(constant_comparison){ + processed_disjuncts.push_back(binary_relation_exprt(equal_expr_in_not.rhs(),ID_gt,equal_expr_in_not.lhs())); + processed_disjuncts.push_back(binary_relation_exprt(equal_expr_in_not.rhs(),ID_lt,equal_expr_in_not.lhs())); + } + else{ + processed_disjuncts.push_back(*d_it); + } + } + } + } + else{ + processed_disjuncts.push_back(*d_it); + } + } + + for(exprt::operandst::iterator it=processed_disjuncts.begin();it!=processed_disjuncts.end();it++){ + + //std::cout << "disjunct: " << from_expr(SSA.ns, "", *it) << "\n"; + + std::set disjunct_symbols; + find_symbols(*it,disjunct_symbols); + + //TODO: decompose into convex regions for all variables + //assert(disjunct_symbols.size() == 1); + + symbol_exprt var; + for(std::set::const_iterator ds_it=disjunct_symbols.begin(); + ds_it!=disjunct_symbols.end(); ds_it++){ + var = *ds_it; + } + //std::cout << "symbol expr in disjunct: " << var << "\n\n"; + + exprt::operandst split_disjuncts; + + if((var.type().id() == ID_signedbv) || (var.type().id() == ID_unsignedbv)){ + + exprt smallest; + if(var.type().id()==ID_signedbv) + smallest = to_signedbv_type(var.type()).smallest_expr(); + if(var.type().id()==ID_unsignedbv) + smallest = to_unsignedbv_type(var.type()).smallest_expr(); + + split_disjuncts.push_back + (and_exprt(*it,binary_relation_exprt(var,ID_ge,plus_exprt(smallest,from_integer(mp_integer(1),var.type()))))); + + split_disjuncts.push_back(and_exprt(*it,binary_relation_exprt(var,ID_equal,smallest))); + } + else{ + split_disjuncts.push_back(*it); + } + + for(exprt::operandst::iterator s_it=split_disjuncts.begin();s_it!=split_disjuncts.end();s_it++){ + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + exprt cc = simplify_expr((and_exprt(side_conditions, *s_it)), SSA.ns); + response = response && (analyzer(solver,SSA,cc,template_generator)); + + exprt res; + analyzer.get_result(res, vars); + result.push_back(res); + + //std::cout << "disjunct passed: " << from_expr(SSA.ns, "", cc) << "\n"; + //std::cout << "disjunct's result: " << from_expr(SSA.ns, "", res) << "\n"; + + //statistics + solver_instances += analyzer.get_number_of_solver_instances(); + solver_calls += analyzer.get_number_of_solver_calls(); + } + } + } + else{ + //for the complete disjunctive_conditions at once + //std::cout << "disjunction not at top-level" << "\n"; + + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + exprt cc = simplify_expr((and_exprt(side_conditions, simple_disjunctive_conditions)), SSA.ns); + + response = analyzer(solver,SSA,cc,template_generator); + + exprt res; + analyzer.get_result(res, vars); + result.push_back(res); + + //std::cout << "disjunct passed: " << from_expr(SSA.ns, "", cc) << "\n"; + //std::cout << "disjunct's result: " << from_expr(SSA.ns, "", res) << "\n"; + + //statistics + solver_instances += analyzer.get_number_of_solver_instances(); + solver_calls += analyzer.get_number_of_solver_calls(); + } + + result_expr = disjunction(result); + return response; +} + diff --git a/src/domains/disjunctive_analyzer.h b/src/domains/disjunctive_analyzer.h new file mode 100644 index 000000000..322d4a0a6 --- /dev/null +++ b/src/domains/disjunctive_analyzer.h @@ -0,0 +1,51 @@ +/*******************************************************************\ + +Module: Data Flow Analysis + +Author: Peter Schrammel, Kumar Madhukar + +\*******************************************************************/ + +#ifndef DELTACHECK_DISJUNCTIVE_ANALYZER_H +#define DELTACHECK_DISJUNCTIVE_ANALYZER_H + +class disjunctive_analyzert : public messaget +{ + public: + explicit disjunctive_analyzert() + : + solver_instances(0), + solver_calls(0) + { + } + + ~disjunctive_analyzert() + { + } + + void eliminate_implication(exprt &formula); + void push_negation_to_atoms(exprt &formula); + void convert_to_dnf(exprt &formula); + void convert_to_dnf_rec(exprt &formula); + + bool operator()(incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars + ); + + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + +protected: + //statistics + unsigned solver_instances; + unsigned solver_calls; +}; + + +#endif + diff --git a/src/domains/domain_refinement.h b/src/domains/domain_refinement.h new file mode 100644 index 000000000..58d53cf9b --- /dev/null +++ b/src/domains/domain_refinement.h @@ -0,0 +1,39 @@ +/*******************************************************************\ + +Module: Domain Refinement Interface + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_DOMAIN_REFINEMENT_H +#define CPROVER_DOMAIN_REFINEMENT_H + +#include "incremental_solver.h" +#include "template_generator_base.h" +#include "../ssa/local_SSA.h" + +class domain_refinementt +{ +public: + explicit domain_refinementt( + const local_SSAt &_SSA, + incremental_solvert &_solver) + : + SSA(_SSA), + solver(_solver) + {} + + //refine, returns true if there are no more refinements + virtual bool operator()() { return true; } + + //template generators associated with this SSA and solver + typedef std::map template_generatorst; + template_generatorst template_generators; + +protected: + const local_SSAt &SSA; + incremental_solvert &solver; +}; + +#endif diff --git a/src/domains/domain_refinement_variables.cpp b/src/domains/domain_refinement_variables.cpp new file mode 100644 index 000000000..f8e313ea4 --- /dev/null +++ b/src/domains/domain_refinement_variables.cpp @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Domain Refinement: Choice of Variables + +Author: Peter Schrammel + +\*******************************************************************/ + +bool domain_refinementt::operator()() +{ + //TODO: analyze counterexample + for(template_generatorst::iterator it = template_generators.begin(); + it != template_generators.end(); ++it) + { + //TODO: select refinement to be performed + + //TODO: update template and domain + //it->add_template(); + } +} diff --git a/src/domains/predabs_domain.cpp b/src/domains/predabs_domain.cpp new file mode 100644 index 000000000..ea898d5c0 --- /dev/null +++ b/src/domains/predabs_domain.cpp @@ -0,0 +1,386 @@ +#include "predabs_domain.h" +#include "util.h" + +#include + +#include +#include +#include +#include +#include + +#define SYMB_COEFF_VAR "symb_coeff#" +#define COMPLEXITY_COUNTER_PREFIX "__CPROVER_CPLX_CNT_" + +/*******************************************************************\ + +Function: predabs_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void predabs_domaint::initialize(valuet &value) +{ + templ_valuet &v = static_cast(value); + v.resize(templ.size()); + for(unsigned row = 0; row (row_value => row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_constraint(const rowt &row, + const row_valuet &row_value) +{ + assert(row (row_value => row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_post_constraint(const rowt &row, + const row_valuet &row_value) +{ + assert(row (row_value => row_expr) ) + +\*******************************************************************/ + +exprt predabs_domaint::to_pre_constraints(const templ_valuet &value) +{ + assert(value.size()==templ.size()); + exprt::operandst c; + for(unsigned row = 0; row (row_value => row_expr) ) + to be connected disjunctively + +\*******************************************************************/ + +void predabs_domaint::make_not_post_constraints(const templ_valuet &value, + exprt::operandst &cond_exprs) +{ + assert(value.size()==templ.size()); + cond_exprs.resize(templ.size()); + + exprt::operandst c; + for(unsigned row = 0; row(value); + + assert(v.size()==templ.size()); + exprt::operandst c; + for(unsigned row = 0; row symbols; + find_symbols(templ_row.expr,symbols); + + bool pure = true; + for(std::set::iterator it = symbols.begin(); + it != symbols.end(); it++) + { + if(vars.find(*it)==vars.end()) + { + pure = false; + break; + } + } + if(!pure) continue; + + const row_valuet &row_v = v[row]; + if(templ_row.kind==LOOP) + { + c.push_back(implies_exprt(templ_row.pre_guard, + implies_exprt(row_v,templ_row.expr))); + } + else + { + c.push_back(implies_exprt(row_v,templ_row.expr)); + } + } + result = conjunction(c); +} + +/*******************************************************************\ + +Function: predabs_domaint::set_row_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void predabs_domaint::set_row_value( + const rowt &row, const predabs_domaint::row_valuet &row_value, templ_valuet &value) +{ + assert(row(value); + for(unsigned row = 0; row " << std::endl << " "; + break; + case IN: out << "(IN) "; break; + case OUT: case OUTL: out << "(OUT) "; break; + default: assert(false); + } + out << "( " << from_expr(ns,"",v[row]) << " ==> " << + from_expr(ns,"",templ_row.expr) << " )" << std::endl; + } +} + +/*******************************************************************\ + +Function: predabs_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void predabs_domaint::output_domain(std::ostream &out, const namespacet &ns) const +{ + for(unsigned row = 0; row " << std::endl << " "; + break; + case IN: + out << "(IN) "; + out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; + break; + case OUT: case OUTL: + out << "(OUT) "; + out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; + break; + default: assert(false); + } + out << "( " << + from_expr(ns,"",templ_row.expr) << ")" << std::endl; + } +} + +/*******************************************************************\ + +Function: predabs_domaint::template_size + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned predabs_domaint::template_size() +{ + return templ.size(); +} + +/*******************************************************************\ + +Function: add_template_row + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +predabs_domaint::template_rowt &predabs_domaint::add_template_row( + const exprt& expr, + const exprt& pre_guard, + const exprt& post_guard, + const exprt& aux_expr, + kindt kind + ) +{ + templ.push_back(template_rowt()); + template_rowt &templ_row = templ.back(); + templ_row.expr = expr; + //extend_expr_types(templ_row.expr); + templ_row.pre_guard = pre_guard; + templ_row.post_guard = post_guard; + templ_row.aux_expr = aux_expr; + templ_row.kind = kind; + return templ_row; +} + +/*******************************************************************\ + +Function: equality_domaint::get_var_pairs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void predabs_domaint::get_row_set(std::set &rows) +{ + for(unsigned i=0;i +#include +#include +#include + +class predabs_domaint : public domaint +{ +public: + typedef unsigned rowt; + typedef exprt row_exprt; //predicate + typedef constant_exprt row_valuet; //true/false + typedef std::set row_sett; + + class templ_valuet : public domaint::valuet, public std::vector + { + }; + + typedef struct + { + guardt pre_guard; + guardt post_guard; + row_exprt expr; + exprt aux_expr; + kindt kind; + } template_rowt; + + typedef std::vector templatet; + + predabs_domaint(unsigned _domain_number, replace_mapt &_renaming_map) : + domaint(_domain_number, _renaming_map) + {} + + // initialize value + virtual void initialize(valuet &value); + + // value -> constraints + exprt get_row_constraint(const rowt &row, const row_valuet &row_value); + exprt get_row_pre_constraint(const rowt &row, const row_valuet &row_value); + exprt get_row_post_constraint(const rowt &row, const row_valuet &row_value); + exprt get_row_pre_constraint(const rowt &row, const templ_valuet &value); + exprt get_row_post_constraint(const rowt &row, const templ_valuet &value); + + exprt to_pre_constraints(const templ_valuet &value); + void make_not_post_constraints(const templ_valuet &value, + exprt::operandst &cond_exprs); + + // set, get value + row_valuet get_row_value(const rowt &row, const templ_valuet &value); + void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + + // printing + virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; + virtual void output_domain(std::ostream &out, const namespacet &ns) const; + + // projection + virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + + unsigned template_size(); + + // generating templates + template_rowt &add_template_row( + const exprt& expr, + const exprt& pre_guard, + const exprt& post_guard, + const exprt& aux_expr, + kindt kind + ); + + void get_row_set(row_sett &rows); + +protected: + templatet templ; + +}; + +#endif diff --git a/src/domains/strategy_solver_predabs.cpp b/src/domains/strategy_solver_predabs.cpp new file mode 100644 index 000000000..f49e090d0 --- /dev/null +++ b/src/domains/strategy_solver_predabs.cpp @@ -0,0 +1,81 @@ +#include + +#include +#include "strategy_solver_predabs.h" + +//Comment: assertion check is not possible because this is a gfp solver + +strategy_solver_baset::progresst +strategy_solver_predabst::iterate(invariantt &_inv) +{ + predabs_domaint::templ_valuet &inv = + static_cast(_inv); + + worklistt::iterator e_it = todo_preds.begin(); + if(e_it != todo_preds.end()) //check positive preds + { + solver.new_context(); + exprt preinv_expr = predabs_domain.get_row_pre_constraint(*e_it, true_exprt()); + +#ifdef DEBUG_OUTPUT + debug() << "pre-pred: " << from_expr(ns,"",preinv_expr) << eom; +#endif + + solver << preinv_expr; + + exprt strategy_cond_expr; + strategy_cond_expr = predabs_domain.get_row_post_constraint(*e_it, true_exprt()); + + literalt cond_literal = solver.convert(not_exprt(strategy_cond_expr)); + solver << literal_exprt(cond_literal); + +#ifdef DEBUG_OUTPUT + debug() << "post-pred: " << from_expr(ns,"",not_exprt(strategy_cond_expr)) << eom; +#endif + + + if(solver() == decision_proceduret::D_SATISFIABLE) + { + debug() << "SAT" << eom; + +#if 0 + for(replace_mapt::const_iterator + it=predabs_domain.renaming_map.begin(); + it!=predabs_domain.renaming_map.end(); + ++it) + { + debug() << "replace_map (1st): " << + from_expr(ns, "", it->first) << " " << + from_expr(ns, "", solver.get(it->first)) << eom; + debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " + << from_expr(ns, "", solver.get(it->second)) << eom; + } +#endif + todo_notpreds.insert(*e_it); + + solver.pop_context(); + + } + else + { + + debug() << "UNSAT" << eom; + + predabs_domain.set_row_value(*e_it, true_exprt(), inv); + + solver.pop_context(); + + solver << preinv_expr; //make permanent + + //due to transitivity, we would like to recheck predicates that did not hold + todo_preds.insert(todo_notpreds.begin(),todo_notpreds.end()); + todo_notpreds.clear(); + } + + todo_preds.erase(e_it); + + return CHANGED; + } + + return CONVERGED; +} diff --git a/src/domains/strategy_solver_predabs.h b/src/domains/strategy_solver_predabs.h new file mode 100644 index 000000000..e31b547f8 --- /dev/null +++ b/src/domains/strategy_solver_predabs.h @@ -0,0 +1,33 @@ +#ifndef CPROVER_STRATEGY_SOLVER_PREDABS_H +#define CPROVER_STRATEGY_SOLVER_PREDABS_H + +#include "strategy_solver_base.h" +#include "predabs_domain.h" + +class strategy_solver_predabst : public strategy_solver_baset +{ + public: + explicit strategy_solver_predabst( + predabs_domaint &_predabs_domain, + incremental_solvert &_solver, + literalt _assertion_check, + const namespacet &_ns) : + strategy_solver_baset(_solver, _assertion_check, _ns), + predabs_domain(_predabs_domain) + { + predabs_domain.get_row_set(todo_preds); + } + + virtual progresst iterate(invariantt &inv); + + protected: + predabs_domaint &predabs_domain; + + typedef std::set worklistt; + worklistt todo_preds; + worklistt todo_notpreds; + + +}; + +#endif diff --git a/src/html/syntax_highlighting.cpp b/src/html/syntax_highlighting.cpp new file mode 100644 index 000000000..5c6e078d0 --- /dev/null +++ b/src/html/syntax_highlighting.cpp @@ -0,0 +1,306 @@ +/*******************************************************************\ + +Module: Syntax Highlighting + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +// may wish to try http://www.gnu.org/software/src-highlite/ + +#include +#include +#include +#include + +#include "../html/html_escape.h" +#include "syntax_highlighting.h" + +/*******************************************************************\ + +Function: is_keyword + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +const char *keywords[]= +{ + "auto", "_Bool", "break", "case", "char", "_Complex", "const", "continue", + "default", "do", "double", "else", "enum", "extern", "float", "for", + "goto", "if", "inline", "int", "long", "register", "restrict", "return", + "short", "signed", "sizeof", "static", "struct", "switch", "typedef", + "union", "unsigned", "void", "volatile", "while", "__float128", + "__int128", "__int8", "__int16", "__int32", "__int64", "__ptr32", + "__ptr64", "__complex__", "__complex", "__real__" , "__real", "__imag__" , + "__imag", "offsetof", "__asm", "asm", "__asm__", "bool", "catch", "class", + "constexpr", "delete", "decltype", "explicit", "friend", "mutable", + "namespace", "new", "nullptr", "operator", "private", "protected", + "public", "static_assert", "template", "this", "thread_local", "throw", + "typeid", "typename", "using", "virtual", "wchar_t", "typeof", NULL +}; + +bool is_keyword(const std::string &token) +{ + for(unsigned i=0; keywords[i]!=NULL; i++) + { + if(strcmp(keywords[i], token.c_str())==0) + return true; + } + + return false; +} + +/*******************************************************************\ + + Class: tokenizert + + Purpose: + +\*******************************************************************/ + +const char *tokens[]= +{ "++", "+=", "--", "-=", "&&", "&=", "||", "|=", "/*", + "*/", "//", "%=", "/=", "<<", ">>", "<<=", ">>=", "==", + "!=", "<=", ">=", "::", "->", "##", ".*", "->*", NULL }; + +class tokenizert +{ +public: + explicit tokenizert(const std::string &_buf):buf(_buf) + { + } + + std::string get(); + std::string peek(); + std::string buf; + bool eol() const { return buf.empty(); } + + char get_char() + { + if(buf.empty()) return 0; + char result=buf[0]; + buf.erase(0, 1); + return result; + } + + static inline bool is_identifier_char(char ch) + { + return isalnum(ch) || ch=='_'; + } +}; + +/*******************************************************************\ + +Function: tokenizert::peek + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string tokenizert::peek() +{ + if(buf.empty()) return buf; + + char first=buf[0]; + + unsigned pos=1; + + if(is_identifier_char(first)) + { + // identifier or keyword or number + for(pos=1; pos"; + + if(comment) out << ""; + + std::string token; + + std::map var_count; + + while(!tokenizer.eol()) + { + if(comment) + { + std::string buf; + bool end_of_comment=false; + + while(!end_of_comment) + { + char ch=tokenizer.get_char(); + if(ch==0) break; + buf+=ch; + if(buf.size()>=2 && buf[buf.size()-2]=='*' && buf[buf.size()-1]=='/') + end_of_comment=true; + } + + out << html_escape(buf); + + if(end_of_comment) + { + out << ""; + comment=false; + } + } + else + { + token=tokenizer.get(); + assert(!token.empty()); + + if(isdigit(token[0])) // numeral + { + out << html_escape(token); + } + else if(isalpha(token[0])) + { + if(is_keyword(token)) + out << "" << html_escape(token) << ""; + else + { + if(identifier_tooltip) + out << ""; + else + out << ""; + + out << html_escape(token); + + out << ""; + } + } + else if(token=="/*") + { + comment=true; + out << "" << token; + } + else if(token=="//") + { + out << "" << token; + while(!(token=tokenizer.get()).empty()) + out << html_escape(token); + out << ""; + } + else if(token[0]=='"' || token[0]=='\'') + { + out << "" << html_escape(token) << ""; + } + else + { + // hack to distinguish lhs from rhs without parsing + #if 0 + if(token=="+=" || token=="=" || + token=="-=" || token=="<<=" || + token==">>=" || token=="&=" || + token=="^=" || token=="|=") + lhs=false; + else if(token==";") + lhs=true; + #endif + + out << html_escape(token); + } + } + } + + // close tags + if(comment) out << ""; + if(!strong_class.empty()) out << ""; + + out << "\n"; +} + diff --git a/src/html/syntax_highlighting.h b/src/html/syntax_highlighting.h new file mode 100644 index 000000000..159abcc28 --- /dev/null +++ b/src/html/syntax_highlighting.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Syntax Highlighting + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SYNTAX_HIGHLIGHTING_H +#define CPROVER_SYNTAX_HIGHLIGHTING_H + +#include +#include + +class syntax_highlightingt +{ +public: + explicit syntax_highlightingt(std::ostream &_out): + line_no(0), identifier_tooltip(false), + out(_out), comment(false) { } + + std::string strong_class; + unsigned line_no; + std::string id_suffix; + + bool identifier_tooltip; + + void operator()(const std::string &line); + +protected: + std::ostream &out; + bool comment; +}; + +#endif diff --git a/src/ssa/const_propagator.cpp b/src/ssa/const_propagator.cpp new file mode 100644 index 000000000..747f2af93 --- /dev/null +++ b/src/ssa/const_propagator.cpp @@ -0,0 +1,467 @@ +/*******************************************************************\ + +Module: Constant Propagation + +Author: Peter Schrammel + +\*******************************************************************/ + +//#define DEBUG + +#include + +#include +#include + +#include "const_propagator.h" + +/*******************************************************************\ + +Function: const_propagator_domaint::assign_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::assign_rec(const exprt &lhs, const exprt &rhs, + const namespacet &ns) +{ + const typet & rhs_type = ns.follow(rhs.type()); + +#ifdef DEBUG + std::cout << "assign: " << from_expr(ns, "", lhs) + << " := " << from_type(ns, "", rhs_type) << std::endl; +#endif + + if(lhs.id()==ID_symbol && rhs_type.id()!=ID_array + && rhs_type.id()!=ID_struct + && rhs_type.id()!=ID_union) + { + if(!values.maps_to_top(rhs)) + assign(values,lhs,rhs,ns); + else + values.set_to_top(lhs); + } +#if 0 + else //TODO: could make field or array element-sensitive + { + } +#endif +} + +/*******************************************************************\ + +Function: const_propagator_domaint::transform + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::transform( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) +{ +#ifdef DEBUG + std::cout << from->location_number << " --> " + << to->location_number << std::endl; +#endif + + if(from->is_decl()) + { + values.set_to_top(to_code_decl(from->code).symbol()); + } + else if(from->is_assign()) + { + const code_assignt &assignment=to_code_assign(from->code); + const exprt &lhs = assignment.lhs(); + const exprt &rhs = assignment.rhs(); + assign_rec(lhs,rhs,ns); + } + else if(from->is_goto()) + { + if(from->guard.id()==ID_equal && from->get_target()==to) + { + const exprt &lhs = from->guard.op0(); + const exprt &rhs = from->guard.op1(); + + assign_rec(lhs,rhs,ns); + assign_rec(rhs,lhs,ns); + } + } + else if(from->is_dead()) + { + const code_deadt &code_dead=to_code_dead(from->code); + values.set_to_top(code_dead.symbol()); + } + else if(from->is_function_call()) + { + values.set_all_to_top(); + } + +#ifdef DEBUG + output(std::cout,ai,ns); +#endif +} + +/*******************************************************************\ + +Function: const_propagator_domaint::assign + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const +{ +#ifdef DEBUG + std::cout << "assign: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + values.replace_const(rhs); + + //this is to remove casts in constants propagated into the size of array types + bool valid = true; + exprt rhs_val = evaluate_casts_in_constants(rhs,lhs.type(),valid); + if(valid) + dest.set_to(lhs,rhs_val); +} + +/*******************************************************************\ + +Function: const_propagator_domaint::valuest::maps_to_top + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool const_propagator_domaint::valuest::maps_to_top(const exprt &expr) const +{ + if(expr.id()==ID_side_effect && + to_side_effect_expr(expr).get_statement()==ID_nondet) + return true; + if(expr.id()==ID_symbol) + if(replace_const.expr_map.find(expr.get(ID_identifier)) + == replace_const.expr_map.end()) + return true; + forall_operands(it,expr) + { + if(maps_to_top(*it)) + return true; + } + return false; +} + +/*******************************************************************\ + +Function: const_propagator_domaint::valuest::set_to_top + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool const_propagator_domaint::valuest::set_to_top(const irep_idt &id) +{ + bool result = false; + replace_symbolt::expr_mapt::iterator r_it = + replace_const.expr_map.find(id); + if(r_it != replace_const.expr_map.end()) + { + replace_const.expr_map.erase(r_it); + result = true; + } + if(top_ids.find(id)==top_ids.end()) + { + top_ids.insert(id); + result = true; + } + return result; +} + +bool const_propagator_domaint::valuest::set_to_top(const exprt &expr) +{ + return set_to_top(to_symbol_expr(expr).get_identifier()); +} + +void const_propagator_domaint::valuest::set_all_to_top() +{ + for(replace_symbolt::expr_mapt::iterator it = + replace_const.expr_map.begin(); + it != replace_const.expr_map.end(); ++it) + top_ids.insert(it->first); + replace_const.expr_map.clear(); +} + + +/*******************************************************************\ + +Function: const_propagator_domaint::valuest::add + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::valuest::set_to(const irep_idt &lhs_id, + const exprt &rhs_val) +{ + replace_const.expr_map[lhs_id] = rhs_val; + std::set::iterator it = top_ids.find(lhs_id); + if(it!=top_ids.end()) top_ids.erase(it); +} + +void const_propagator_domaint::valuest::set_to(const exprt &lhs, + const exprt &rhs_val) +{ + const irep_idt &lhs_id = to_symbol_expr(lhs).get_identifier(); + set_to(lhs_id,rhs_val); +} + +/*******************************************************************\ + +Function: const_propagator_domaint::valuest::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::valuest::output( + std::ostream &out, + const namespacet &ns) const +{ + out << "const map: " << std::endl; + for(replace_symbolt::expr_mapt::const_iterator + it=replace_const.expr_map.begin(); + it!=replace_const.expr_map.end(); + ++it) + out << ' ' << it->first << "=" << + from_expr(ns, "", it->second) << std::endl; + out << "top ids: " << std::endl; + for(std::set::const_iterator + it=top_ids.begin(); + it!=top_ids.end(); + ++it) + out << ' ' << *it << std::endl; +} + +/*******************************************************************\ + +Function: const_propagator_domaint::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_domaint::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + values.output(out,ns); +} + +/*******************************************************************\ + +Function: const_propagator_domaint::valuest::merge + + Inputs: + + Outputs: Return true if "this" has changed. + + Purpose: + +\*******************************************************************/ + +bool const_propagator_domaint::valuest::merge(const valuest &src) +{ + bool changed = false; + for(replace_symbolt::expr_mapt::const_iterator + it=src.replace_const.expr_map.begin(); + it!=src.replace_const.expr_map.end(); ++it) + { + replace_symbolt::expr_mapt::iterator + c_it = replace_const.expr_map.find(it->first); + if(c_it != replace_const.expr_map.end()) + { + if(c_it->second != it->second) + { + set_to_top(it->first); + changed = true; + } + } + else if(top_ids.find(it->first)==top_ids.end()) + { + set_to(it->first,it->second); + changed = true; + } + } + for(std::set::const_iterator it=src.top_ids.begin(); + it!=src.top_ids.end(); ++it) + { + bool c = set_to_top(*it); + changed = changed || c; + } + + return changed; +} + +/*******************************************************************\ + +Function: const_propagator_domaint::merge + + Inputs: + + Outputs: Return true if "this" has changed. + + Purpose: + +\*******************************************************************/ + +bool const_propagator_domaint::merge( + const const_propagator_domaint &other, + locationt from, + locationt to) +{ + return values.merge(other.values); +} + +/*******************************************************************\ + +Function: const_propagator_domaint::evaluate_casts_in_constants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt const_propagator_domaint::evaluate_casts_in_constants(exprt expr, + const typet& parent_type, bool &valid) const +{ + if(expr.id()==ID_side_effect) + { + valid = false; + return expr; + } + if(expr.type().id()!=ID_signedbv && expr.type().id()!=ID_unsignedbv) + return expr; + if(expr.id()==ID_typecast) + expr = evaluate_casts_in_constants(expr.op0(),expr.type(),valid); + if(expr.id()!=ID_constant) + { + if(expr.type()!=parent_type) + return typecast_exprt(expr,parent_type); + else + return expr; + } + //TODO: could be improved to resolve float casts as well... + if(expr.type().id()!=ID_signedbv && expr.type().id()!=ID_unsignedbv) + return expr; + mp_integer v; + to_integer(to_constant_expr(expr), v); + return from_integer(v,parent_type); +} + +/*******************************************************************\ + +Function: const_propagator_ait::replace + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_ait::replace( + goto_functionst::goto_functiont &goto_function, + const namespacet &ns) +{ + Forall_goto_program_instructions(it, goto_function.body) + { + state_mapt::iterator s_it = state_map.find(it); + if(s_it == state_map.end()) + continue; + replace_types_rec(s_it->second.values.replace_const, it->code); + replace_types_rec(s_it->second.values.replace_const, it->guard); + if(it->is_goto() || it->is_assume() || it->is_assert()) + { + s_it->second.values.replace_const(it->guard); + } + else if(it->is_assign()) + { + exprt &rhs = to_code_assign(it->code).rhs(); + s_it->second.values.replace_const(rhs); + } + else if(it->is_function_call()) + { + exprt::operandst &args = + to_code_function_call(it->code).arguments(); + for(exprt::operandst::iterator o_it = args.begin(); + o_it != args.end(); ++o_it) + s_it->second.values.replace_const(*o_it); + } + } +} + +/*******************************************************************\ + +Function: const_propagator_ait::replace_types_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void const_propagator_ait::replace_types_rec( + const replace_symbolt &replace_const, + exprt &expr) +{ + replace_const(expr.type()); + Forall_operands(it,expr) + replace_types_rec(replace_const,*it); +} + diff --git a/src/ssa/const_propagator.h b/src/ssa/const_propagator.h new file mode 100644 index 000000000..6d017f8b9 --- /dev/null +++ b/src/ssa/const_propagator.h @@ -0,0 +1,101 @@ +/*******************************************************************\ + +Module: Constant propagation + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_CONST_PROPAGATOR_H +#define CPROVER_CONST_PROPAGATOR_H + +#include + +#include +#include "replace_symbol_ext.h" + +class const_propagator_domaint:public ai_domain_baset +{ +public: + virtual void transform(locationt, locationt, ai_baset &, const namespacet &); + virtual void output(std::ostream &, const ai_baset &, const namespacet &) const; + bool merge(const const_propagator_domaint &, locationt, locationt); + + struct valuest + { + public: + // maps variables to constants + replace_symbol_extt replace_const; + std::set top_ids; + + void output(std::ostream &, const namespacet &) const; + + bool merge(const valuest &src); + + inline void clear() + { + replace_const.expr_map.clear(); + replace_const.type_map.clear(); + top_ids.clear(); + } + + bool empty() const + { + return replace_const.expr_map.empty() && + replace_const.type_map.empty() && + top_ids.empty(); + } + + void set_to(const exprt &lhs, const exprt &rhs_val); + void set_to(const irep_idt &lhs_id, const exprt &rhs_val); + + bool maps_to_top(const exprt &expr) const; + bool set_to_top(const exprt &expr); + bool set_to_top(const irep_idt &id); + void set_all_to_top(); + }; + + valuest values; + +protected: + void assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const; + + void assign_rec(const exprt &lhs, const exprt &rhs, + const namespacet &ns); + + exprt evaluate_casts_in_constants( + exprt expr, + const typet& parent_type, + bool &valid) const; + +}; + +class const_propagator_ait:public ait +{ +public: + const_propagator_ait( + goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + operator()(goto_function, ns); +// output(ns,goto_function.body,"",std::cout); + replace(goto_function, ns); + } + +protected: + friend class const_propagator_domaint; + +void replace( + goto_functionst::goto_functiont &goto_function, + const namespacet &ns); + +void replace_types_rec( + const replace_symbolt &replace_const, + exprt &expr); +}; + +#endif diff --git a/src/ssa/replace_symbol_ext.cpp b/src/ssa/replace_symbol_ext.cpp new file mode 100644 index 000000000..7d77633aa --- /dev/null +++ b/src/ssa/replace_symbol_ext.cpp @@ -0,0 +1,82 @@ +/*******************************************************************\ + +Module: Modified expression replacement for constant propagator + +Author: Peter Schrammel + +\*******************************************************************/ + +#include +#include + +#include "replace_symbol_ext.h" + +/*******************************************************************\ + +Function: replace_symbol_extt::replace + + Inputs: + + Outputs: + + Purpose: does not replace object in address_of expressions + +\*******************************************************************/ + +bool replace_symbol_extt::replace(exprt &dest) const +{ + bool result=true; + + // first look at type + + if(have_to_replace(dest.type())) + if(!replace_symbolt::replace(dest.type())) + result=false; + + // now do expression itself + + if(!have_to_replace(dest)) + return result; + + if(dest.id()==ID_address_of) + { + const exprt &object = to_address_of_expr(dest).object(); + if(object.id()==ID_symbol) + { + expr_mapt::const_iterator it= + expr_map.find(object.get(ID_identifier)); + + if(it!=expr_map.end()) + return false; + } + } + else if(dest.id()==ID_symbol) + { + expr_mapt::const_iterator it= + expr_map.find(dest.get(ID_identifier)); + + if(it!=expr_map.end()) + { + dest=it->second; + return false; + } + } + + Forall_operands(it, dest) + if(!replace(*it)) + result=false; + + const irept &c_sizeof_type=dest.find(ID_C_c_sizeof_type); + + if(c_sizeof_type.is_not_nil() && + !replace_symbolt::replace(static_cast(dest.add(ID_C_c_sizeof_type)))) + result=false; + + const irept &va_arg_type=dest.find(ID_C_va_arg_type); + + if(va_arg_type.is_not_nil() && + !replace_symbolt::replace(static_cast(dest.add(ID_C_va_arg_type)))) + result=false; + + return result; +} diff --git a/src/ssa/replace_symbol_ext.h b/src/ssa/replace_symbol_ext.h new file mode 100644 index 000000000..2aac30957 --- /dev/null +++ b/src/ssa/replace_symbol_ext.h @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Modified expression replacement for constant propagator + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_REPLACE_SYMBOL_EXT_H +#define CPROVER_REPLACE_SYMBOL_EXT_H + +#include + +class replace_symbol_extt : public replace_symbolt +{ +public: + virtual bool replace(exprt &dest) const; +}; + +#endif diff --git a/src/ssa/ssa_const_propagator.cpp b/src/ssa/ssa_const_propagator.cpp new file mode 100644 index 000000000..4ca296238 --- /dev/null +++ b/src/ssa/ssa_const_propagator.cpp @@ -0,0 +1,305 @@ +/*******************************************************************\ + +Module: SSA Constant Propagator + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +//#define DEBUG + +#ifdef DEBUG +#include +#include +#endif + +#include +#include +#include + +#include "ssa_const_propagator.h" + + +//bool iterate = true; + + +/*******************************************************************\ + +Function: ssa_const_propagatort::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_const_propagatort::operator()(std::list &dest, + const local_SSAt &src) +{ + values.iterate = true; + while(values.iterate){ + values.iterate = false; + for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); + n_it != src.nodes.end(); n_it++) + { + const local_SSAt::nodet &node=*n_it; + + // check if node is active; 'continue' if not active + if(!node.enabling_expr.is_true()) + continue; + + // if src.enabling_exprs does not have a true in the end, then also continue + if(src.enabling_exprs.size() > 0) + if((src.enabling_exprs.back()).is_true()) + continue; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it=node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) + { + equal_exprt e = to_equal_expr(*e_it); + const exprt &lhs = e.lhs(); + exprt &temprhs = e.rhs(); + + // resolving conditional expressions + while(temprhs.id() == ID_if){ + exprt simp_guard; +#ifdef DEBUG + std::cout << "guard: " << from_expr(src.ns, "", temprhs.op0()) << std::endl; +#endif + simp_guard = simplify_expr(temprhs.op0(), src.ns); +#ifdef DEBUG + std::cout << "simplified: " << from_expr(src.ns, "", simp_guard) << std::endl; +#endif + if(simp_guard.is_true()) + temprhs = temprhs.op1(); + else if(simp_guard.is_false()) + temprhs = temprhs.op2(); + else + break; + } + + const exprt &rhs = temprhs; + valuest copy_values = values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values,lhs,rhs,src.ns); + else + copy_values.set_to_top(lhs); + +#ifdef DEBUG + copy_values.output(std::cout,src.ns); +#endif + + if(rhs.id() == ID_symbol){ + if(!values.maps_to_top(lhs)) + assign(values,rhs,lhs,src.ns); + else + values.set_to_top(rhs); + } + +#ifdef DEBUG + values.output(std::cout,src.ns); +#endif + + values.merge(copy_values); + +#ifdef DEBUG + values.output(std::cout,src.ns); +#endif + } + + for(local_SSAt::nodet::constraintst::const_iterator c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); c_it++) + { + if(c_it->id()!=ID_equal) + continue; + + const equal_exprt e = to_equal_expr(*c_it); + const exprt &lhs = e.lhs(); + const exprt &rhs = e.rhs(); + + // if lhs is a variable and rhs is a constant expression + + valuest copy_values = values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values,lhs,rhs,src.ns); + else + copy_values.set_to_top(lhs); + +#ifdef DEBUG + copy_values.output(std::cout,src.ns); +#endif + + // if rhs is a variable and lhs is a constant expression + + if(!values.maps_to_top(lhs)) + assign(values,rhs,lhs,src.ns); + else + values.set_to_top(rhs); + +#ifdef DEBUG + values.output(std::cout,src.ns); +#endif + + values.merge(copy_values); + +#ifdef DEBUG + values.output(std::cout,src.ns); +#endif + + } + } + } + +#ifdef DEBUG + values.output(std::cout,src.ns); +#endif + + // iterate over values and get all equalities + for(replace_symbolt::expr_mapt::const_iterator + it=values.replace_const.expr_map.begin(); + it!=values.replace_const.expr_map.end(); + ++it){ + + //std::cout << ' ' << it->first << " = " << + // from_expr(src.ns, "", it->second) << std::endl; + + dest.push_back(equal_exprt(symbol_exprt(it->first,it->second.type()),it->second)); + + } +} + +bool ssa_const_propagatort::valuest::maps_to_top(const exprt &expr) const +{ + find_symbols_sett symbols; + find_symbols(expr,symbols); + for(find_symbols_sett::const_iterator it = symbols.begin(); + it != symbols.end(); ++it) + { + if(replace_const.expr_map.find(*it) + == replace_const.expr_map.end()) + return true; + } + return false; +} + + +void ssa_const_propagatort::assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const +{ +#ifdef DEBUG + std::cout << "assign: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + values.replace_const(rhs); + +#ifdef DEBUG + std::cout << "replace: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + rhs = simplify_expr(rhs,ns); + +#ifdef DEBUG + std::cout << "simplified: " << from_expr(ns, "", lhs) + << " := " << from_expr(ns, "", rhs) << std::endl; +#endif + + dest.set_to(lhs,rhs); +} + +bool ssa_const_propagatort::valuest::set_to_top(const irep_idt &id) +{ + bool result = false; + replace_symbolt::expr_mapt::iterator r_it = + replace_const.expr_map.find(id); + if(r_it != replace_const.expr_map.end()) + { + replace_const.expr_map.erase(r_it); + result = true; + } + if(top_ids.find(id)==top_ids.end()) + { + top_ids.insert(id); + result = true; + } + return result; +} + +bool ssa_const_propagatort::valuest::set_to_top(const exprt &expr) +{ + return set_to_top(to_symbol_expr(expr).get_identifier()); +} + +void ssa_const_propagatort::valuest::set_to(const irep_idt &lhs_id, + const exprt &rhs_val) +{ + + if(replace_const.expr_map[lhs_id] != rhs_val){ + replace_const.expr_map[lhs_id] = rhs_val; + iterate = true; + } + std::set::iterator it = top_ids.find(lhs_id); + if(it!=top_ids.end()) top_ids.erase(it); +} + +void ssa_const_propagatort::valuest::set_to(const exprt &lhs, + const exprt &rhs_val) +{ + const irep_idt &lhs_id = to_symbol_expr(lhs).get_identifier(); + set_to(lhs_id,rhs_val); +} + +void ssa_const_propagatort::valuest::output( + std::ostream &out, + const namespacet &ns) const +{ + out << "const map: " << std::endl; + for(replace_symbolt::expr_mapt::const_iterator + it=replace_const.expr_map.begin(); + it!=replace_const.expr_map.end(); + ++it) + out << ' ' << it->first << "=" << + from_expr(ns, "", it->second) << std::endl; + out << "top ids: " << std::endl; + for(std::set::const_iterator + it=top_ids.begin(); + it!=top_ids.end(); + ++it) + out << ' ' << *it << std::endl; +} + +bool ssa_const_propagatort::valuest::merge(const valuest &src) +{ + bool changed = false; + for(replace_symbolt::expr_mapt::const_iterator + it=src.replace_const.expr_map.begin(); + it!=src.replace_const.expr_map.end(); ++it) + { + replace_symbolt::expr_mapt::iterator + c_it = replace_const.expr_map.find(it->first); + if(c_it != replace_const.expr_map.end()) + { + if(c_it->second != it->second) + { + set_to_top(it->first); + changed = true; + } + } + else if(top_ids.find(it->first)==top_ids.end()) + { + set_to(it->first,it->second); + changed = true; + } + } + + return changed; +} + diff --git a/src/ssa/ssa_const_propagator.h b/src/ssa/ssa_const_propagator.h new file mode 100644 index 000000000..ee89071db --- /dev/null +++ b/src/ssa/ssa_const_propagator.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: SSA Constant Propagator + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SSA_CONST_PROPAGATOR_H +#define CPROVER_SSA_CONST_PROPAGATOR_H + +#include +#include + +#include "local_ssa.h" + +class ssa_const_propagatort : public messaget +{ + public: + + void operator()(std::list &dest, + const local_SSAt &src); + + struct valuest + { + public: + // maps variables to constants + replace_symbolt replace_const; + std::set top_ids; + + void output(std::ostream &, const namespacet &) const; + + bool merge(const valuest &src); + + + inline void clear() + { + replace_const.expr_map.clear(); + replace_const.type_map.clear(); + top_ids.clear(); + } + + bool empty() const + { + return replace_const.expr_map.empty() && + replace_const.type_map.empty() && + top_ids.empty(); + } + + void set_to(const exprt &lhs, const exprt &rhs_val); + void set_to(const irep_idt &lhs_id, const exprt &rhs_val); + + bool maps_to_top(const exprt &expr) const; + bool set_to_top(const exprt &expr); + bool set_to_top(const irep_idt &id); + + bool iterate; + + }; + + valuest values; + + protected: + + void assign( + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const; + + exprt evaluate_casts_in_constants( + exprt expr, + const typet& parent_type, + bool &valid) const; + + + +}; + +#endif diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp new file mode 100644 index 000000000..26bf05fba --- /dev/null +++ b/src/ssa/ssa_dependency_graph.cpp @@ -0,0 +1,385 @@ + +#include +#include +#include + +#include "ssa_dependency_graph.h" + +void ssa_dependency_grapht::output(std::ostream &out) const +{ + for(unsigned index = 0; index < depnodes_map.size(); index++) + { + out << "Node#" << index << "; info: " << from_expr(ns, "", depnodes_map[index].node_info) << "\n"; + out << "Node#" << index << "; -> Used Symbols: "; + for(find_symbols_sett::const_iterator u_it=depnodes_map[index].used_symbols.begin(); + u_it!=depnodes_map[index].used_symbols.end(); u_it++){ + out << *u_it << " "; + } + out << "\n"; + + out << "Node#" << index << "; -> Modified Symbols: "; + for(find_symbols_sett::const_iterator m_it=depnodes_map[index].modified_symbols.begin(); + m_it!=depnodes_map[index].modified_symbols.end(); m_it++){ + out << *m_it << " "; + } + out << "\n"; + + out << "Successors: "; + for(unsigned i = 0; i < depnodes_map[index].successors.size(); i++){ + out << depnodes_map[index].successors[i] << " "; + } + out << "\n"; + + out << "Predecessors:\n"; + for(ssa_dependency_grapht::annotated_predecessorst::const_iterator p_it = depnodes_map[index].predecessors.begin(); + p_it != depnodes_map[index].predecessors.end(); p_it++){ + annotated_predecessort pred = *p_it; + int pred_index = pred.predecessor_node_index; + find_symbols_sett pred_annotation = pred.annotation; + out << " " << "Predecessor Node#" << pred_index << "; Annotation: "; + for(find_symbols_sett::const_iterator s_it=pred_annotation.begin(); + s_it!=pred_annotation.end(); s_it++){ out << *s_it << " "; } + out << "\n"; } out << "\n"; + } + out << "\n\n"; +} + +void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inliner, bool entry) +{ + depnodet sink_node; + sink_node.is_assertion = false; + sink_node.is_function_call = false; + + // globals_out is the used_symbol of the sink_node + for(local_SSAt::var_sett::const_iterator g_it = SSA.globals_out.begin(); + g_it != SSA.globals_out.end(); g_it++){ + irep_idt id = (*g_it).get(ID_identifier); + sink_node.used_symbols.insert(id); + } + + exprt function_guard = SSA.guard_symbol(SSA.goto_function.body.instructions.begin()); + //std::cout << "function guard: " << from_expr(ns, "", function_guard) << "\n"; + + irep_idt id = function_guard.get(ID_identifier); + //std::cout << "guard identifier: " << id << "\n"; + sink_node.used_symbols.insert(id); + + depnodes_map.push_back(sink_node); + + // collect all the symbols to iterate over it + find_symbols_sett all_ssa_symbols; + + bool first_node = true; + bool ignore_equality = false; + bool ignore_equality_done = false; + + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++){ + + const local_SSAt::nodet &node=*n_it; + + // collecting symbols from equalities and populating dependency graph nodes + for(local_SSAt::nodet::equalitiest::const_iterator e_it=node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) + { + + find_symbols(*e_it,all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = *e_it; + temp_node.location = n_it->location; + + //temp_node.trivial_guard = true; + + equal_exprt e = to_equal_expr(*e_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + + find_symbols(rhs,temp_node.used_symbols); + find_symbols(lhs,temp_node.modified_symbols); + + if(!ignore_equality_done){ + std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true())){ + ignore_equality = true; + ignore_equality_done = true; + } + } + + if(first_node && ignore_equality){ + if(entry){ + depnodes_map.push_back(temp_node); + //std::cout << "created equality node, with info: " << from_expr(ns, "", *e_it) << "\n"; + } + ignore_equality = false; + } + else{ + depnodes_map.push_back(temp_node); + //std::cout << "created equality node, with info: " << from_expr(ns, "", *e_it) << "\n"; + } + + } + + // collecting symbols from constraints and populating dependency graph nodes + for(local_SSAt::nodet::constraintst::const_iterator c_it=node.constraints.begin(); + c_it!=node.constraints.end(); c_it++) + { + find_symbols(*c_it,all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = *c_it; + temp_node.location = n_it->location; + find_symbols(*c_it,temp_node.used_symbols); + find_symbols(*c_it,temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created constraint node, with info: " << from_expr(ns, "", *c_it) << "\n"; + } + + // collecting symbols from assertionst and populating dependency graph nodes + for(local_SSAt::nodet::assertionst::const_iterator a_it=node.assertions.begin(); + a_it!=node.assertions.end(); a_it++) + { + find_symbols(*a_it,all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion = true; + temp_node.is_function_call = false; + temp_node.node_info = *a_it; + temp_node.location = n_it->location; + find_symbols(*a_it,temp_node.used_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created assertion node, with info: " << from_expr(ns, "", *a_it) << "\n"; + } + + /* + // collecting symbols from assumptionst and populating dependency graph nodes + for(local_SSAt::nodet::assumptionst::const_iterator a_it=node.assumptions.begin(); + a_it!=node.assumptions.end(); a_it++) + { + find_symbols(*a_it,all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = *a_it; + find_symbols(*a_it,temp_node.used_symbols); + find_symbols(*a_it,temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created assumption node, with info: " << from_expr(ns, "", *a_it) << "\n"; + } + */ + + // collecting symbols from function_callst and populating dependency graph nodes + for(local_SSAt::nodet::function_callst::const_iterator f_it=node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + //find_symbols(*f_it,all_ssa_symbols); + + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + if(ssa_db.exists(fname)) + { + const local_SSAt &fSSA = ssa_db.get(fname); + + /******************************************************************/ + /******* additional nodes needed to fix the dependency tree *******/ + + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; + int counter = ssa_inliner.get_rename_counter(); + + /**/ + ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); + //std::cout << "guard binding: " << from_expr(ns, "", guard_binding) << "\n"; + /* + { + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = guard_binding; + + equal_exprt e = to_equal_expr(guard_binding); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + + find_symbols(rhs,temp_node.used_symbols); + find_symbols(lhs,temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created guard binding node, with info: " << from_expr(ns, "", guard_binding) << "\n"; + } + */ + /**/ + + ssa_inliner.get_bindings(SSA,fSSA,n_it,f_it,bindings_in,bindings_out,counter); + + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++){ + + //std::cout << "binding: " << from_expr(ns, "", *b_it) << "\n"; + + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = *b_it; + temp_node.location = n_it->location; + + equal_exprt e = to_equal_expr(*b_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + + find_symbols(rhs,temp_node.used_symbols); + find_symbols(lhs,temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created binding in node, with info: " << from_expr(ns, "", *b_it) << "\n"; + } + + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++){ + + //std::cout << "binding: " << from_expr(ns, "", *b_it) << "\n"; + + depnodet temp_node; + temp_node.is_assertion = false; + temp_node.is_function_call = false; + temp_node.node_info = *b_it; + temp_node.location = n_it->location; + + equal_exprt e = to_equal_expr(*b_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + + find_symbols(rhs,temp_node.used_symbols); + find_symbols(lhs,temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + //std::cout << "created binding out node, with info: " << from_expr(ns, "", *b_it) << "\n"; + } + + /******************************************************************/ + + depnodet temp_node; + temp_node.guard = guard_binding; + temp_node.is_assertion = false; + temp_node.is_function_call = true; + temp_node.node_info = *f_it; + temp_node.rename_counter = counter; + temp_node.location = n_it->location; + + find_symbols(guard_binding,temp_node.used_symbols); + + for(local_SSAt::var_listt::const_iterator p_it = fSSA.params.begin(); + p_it != fSSA.params.end(); p_it++){ + irep_idt id = (*p_it).get(ID_identifier); + irep_idt sym = ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(sym); + temp_node.used_symbols.insert(sym); + } + + for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_in.begin(); + g_it != fSSA.globals_in.end(); g_it++){ + irep_idt id = (*g_it).get(ID_identifier); + irep_idt sym = ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(sym); + temp_node.used_symbols.insert(sym); + } + + for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_out.begin(); + g_it != fSSA.globals_out.end(); g_it++){ + irep_idt id = (*g_it).get(ID_identifier); + irep_idt sym = ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(sym); + temp_node.modified_symbols.insert(sym); + } + + depnodes_map.push_back(temp_node); + //std::cout << "created function node, with info: " << from_expr(ns, "", *f_it) << "\n"; + } + } + } + first_node = false; + + depnodet source_node; + source_node.is_assertion = false; + source_node.is_function_call = false; + + // params and globals_in are the modified_symbols at source_node + + for(local_SSAt::var_listt::const_iterator p_it = SSA.params.begin(); + p_it != SSA.params.end(); p_it++){ + irep_idt id = (*p_it).get(ID_identifier); + source_node.modified_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it = SSA.globals_in.begin(); + g_it != SSA.globals_in.end(); g_it++){ + irep_idt id = (*g_it).get(ID_identifier); + source_node.modified_symbols.insert(id); + } + + depnodes_map.push_back(source_node); // source_node + //std::cout << "created source node, without any info" << "\n"; + + top_node_index = depnodes_map.size() - 1; + + for(find_symbols_sett::const_iterator + s_it=all_ssa_symbols.begin(); s_it!=all_ssa_symbols.end(); s_it++){ + + for(unsigned m_index = 0; m_index < depnodes_map.size(); m_index++){ + if(depnodes_map[m_index].modified_symbols.find(*s_it) != depnodes_map[m_index].modified_symbols.end()){ + + for(unsigned u_index = 0; u_index < depnodes_map.size(); u_index++){ + + if(m_index != u_index){ + if(depnodes_map[u_index].used_symbols.find(*s_it) != depnodes_map[u_index].used_symbols.end()){ + annotated_predecessort temp_pred; + temp_pred.predecessor_node_index = m_index; + temp_pred.annotation.insert(*s_it); + depnodes_map[u_index].predecessors.push_back(temp_pred); + depnodes_map[m_index].successors.push_back(u_index); + } + } + } + } + } + } + + if(depnodes_map.size() == 2){ + + //sink_node's successor is the source_node + annotated_predecessort def_pred; // successors of the default predecessors + def_pred.predecessor_node_index = top_node_index; + def_pred.annotation = depnodes_map[0].used_symbols; + depnodes_map[0].predecessors.push_back(def_pred); + + //source_node's successor is the sink_node + depnodes_map[top_node_index].successors.push_back(0); + + } + else{ + for(unsigned index = 1; index < depnodes_map.size() - 1; index++) + { + // if node does not have a predecessor + if(depnodes_map[index].predecessors.empty()){ + //its predecessor is the source_node + annotated_predecessort def_pred; // successors of the default predecessors + def_pred.predecessor_node_index = top_node_index; + def_pred.annotation = depnodes_map[index].used_symbols; + depnodes_map[index].predecessors.push_back(def_pred); + + //source_node's successor is this node + depnodes_map[top_node_index].successors.push_back(index); + } + + // if node does not have a successor + if(depnodes_map[index].successors.empty()){ + //its successor is the sink_node + depnodes_map[index].successors.push_back(0); + + //sink_node's predecessor is that node + annotated_predecessort pred_def; // predecessors of the default successor + pred_def.predecessor_node_index = index; + pred_def.annotation = depnodes_map[index].modified_symbols; + depnodes_map[0].predecessors.push_back(pred_def); + } + } + } + +} diff --git a/src/ssa/ssa_dependency_graph.h b/src/ssa/ssa_dependency_graph.h new file mode 100644 index 000000000..7d360f218 --- /dev/null +++ b/src/ssa/ssa_dependency_graph.h @@ -0,0 +1,62 @@ + +#ifndef CPROVER_DELTACHECK_SSA_DEPENDENCY_GRAPH_H +#define CPROVER_DELTACHECK_SSA_DEPENDENCY_GRAPH_H + +#include +#include + +#include "../summarizer/ssa_db.h" +#include "ssa_inliner.h" +#include "local_ssa.h" + +class ssa_inlinert; +class ssa_dbt; + +class ssa_dependency_grapht{ + public: + + inline ssa_dependency_grapht(ssa_dbt &_db, const namespacet &_ns): + ssa_db(_db), + ns(_ns) + {}; + + struct annotated_predecessort{ + int predecessor_node_index; + find_symbols_sett annotation; + }; + + typedef std::list annotated_predecessorst; + + struct depnodet{ + exprt node_info; + exprt guard; + bool is_assertion; + bool is_function_call; + //bool trivial_guard; + int rename_counter; + find_symbols_sett used_symbols; + find_symbols_sett modified_symbols; + annotated_predecessorst predecessors; + std::vector successors; + local_SSAt::locationt location; + }; + + //typedef std::map depnodest; + typedef std::vector depnodest; + depnodest depnodes_map; + + int top_node_index; + + //special source_node and sink_node + //depnodet source_node = depnodes_map[top_node_index]; + //depnodet sink_node = depnodes_map[0]; + + void create(const local_SSAt &SSA, ssa_inlinert &ssa_inliner, bool entry); + void output(std::ostream &) const; + + protected: + ssa_dbt &ssa_db; + const namespacet &ns; +}; + +#endif diff --git a/src/ssa/ssa_dereference.cpp b/src/ssa/ssa_dereference.cpp new file mode 100644 index 000000000..27db94362 --- /dev/null +++ b/src/ssa/ssa_dereference.cpp @@ -0,0 +1,405 @@ +/*******************************************************************\ + +Module: Aliasing Decision + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +//#define DEBUG + +#ifdef DEBUG +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ssa_dereference.h" +#include "address_canonizer.h" + +/*******************************************************************\ + +Function: lift_if + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt lift_if(const exprt &src) +{ + if(src.operands().size()==1 && src.op0().id()==ID_if) + { + // push operator into ?: + if_exprt if_expr=to_if_expr(src.op0()); + if_expr.type()=src.type(); + + if(if_expr.true_case().is_not_nil()) + { + exprt previous=if_expr.true_case(); + if_expr.true_case()=src; + if_expr.true_case().op0()=previous; + } + + if(if_expr.false_case().is_not_nil()) + { + exprt previous=if_expr.false_case(); + if_expr.false_case()=src; + if_expr.false_case().op0()=previous; + } + + return if_expr; + } + else + return src; +} + +/*******************************************************************\ + +Function: ssa_may_alias + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_may_alias( + const exprt &e1, + const exprt &e2, + const namespacet &ns) +{ + #ifdef DEBUG + std::cout << "MAY ALIAS1 " << from_expr(ns, "", e1) << " " + << from_expr(ns, "", e2) << "\n"; + #endif + + // The same? + if(e1==e2) + return true; + + // Both symbol? + if(e1.id()==ID_symbol && + e2.id()==ID_symbol) + { + return to_symbol_expr(e1).get_identifier()== + to_symbol_expr(e2).get_identifier(); + } + + // __CPROVER symbols + if(e1.id()==ID_symbol && + has_prefix(id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) + return false; + + if(e2.id()==ID_symbol && + has_prefix(id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) + return false; + + if(e1.id()==ID_symbol && + has_suffix(id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) + return false; + + if(e2.id()==ID_symbol && + has_suffix(id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) + return false; + + // Both member? + if(e1.id()==ID_member && + e2.id()==ID_member) + { + const member_exprt &m1=to_member_expr(e1); + const member_exprt &m2=to_member_expr(e2); + + // same component? + if(m1.get_component_name()!=m2.get_component_name()) + return false; + + return ssa_may_alias(m1.struct_op(), m2.struct_op(), ns); + } + + // Both index? + if(e1.id()==ID_index && + e2.id()==ID_index) + { + const index_exprt &i1=to_index_expr(e1); + const index_exprt &i2=to_index_expr(e2); + + return ssa_may_alias(i1.array(), i2.array(), ns); + } + + const typet &t1=ns.follow(e1.type()); + const typet &t2=ns.follow(e2.type()); + + // If one is an array and the other not, consider the elements + if(t1.id()==ID_array && t2.id()!=ID_array) + if(ssa_may_alias(index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) + return true; + + if(t2.id()==ID_array && t2.id()!=ID_array) + if(ssa_may_alias(e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) + return true; + + // Pointers only alias with other pointers, + // which is a restriction. + if(t1.id()==ID_pointer) + return t2.id()==ID_pointer; + + if(t2.id()==ID_pointer) + return t1.id()==ID_pointer; + + // Is one a scalar pointer? + if(e1.id()==ID_dereference && + (t1.id()==ID_signedbv || t1.id()==ID_unsignedbv || t1.id()==ID_floatbv)) + return true; + + if(e2.id()==ID_dereference && + (t2.id()==ID_signedbv || t2.id()==ID_unsignedbv || t1.id()==ID_floatbv)) + return true; + + // Is one a pointer? + if(e1.id()==ID_dereference || + e2.id()==ID_dereference) + { + // look at the types + + // same type? + if(base_type_eq(t1, t2, ns)) + { + return true; + } + + // should consider further options, e.g., struct prefixes + return false; + } + + return false; // both different objects +} + +/*******************************************************************\ + +Function: ssa_alias_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_alias_guard( + const exprt &e1, + const exprt &e2, + const namespacet &ns) +{ + exprt a1=address_canonizer(address_of_exprt(e1), ns); + //TODO: We should compare 'base' pointers here because + // we have a higher chance that there was no pointer arithmetic + // on the base pointer than that the result of the pointer + // arithmetic points to a base pointer. + // The following hack does that: + if(a1.id()==ID_plus) a1 = a1.op0(); + + exprt a2=address_canonizer(address_of_exprt(e2), ns); + + // in some cases, we can use plain address equality, + // as we assume well-aligned-ness + mp_integer size1=pointer_offset_size(e1.type(), ns); + mp_integer size2=pointer_offset_size(e2.type(), ns); + + if(size1>=size2) + { + exprt lhs=a1; + exprt rhs=a2; + if(ns.follow(rhs.type())!=ns.follow(lhs.type())) + rhs=typecast_exprt(rhs, lhs.type()); + + return equal_exprt(lhs, rhs); + } + + return same_object(a1, a2); +} + +/*******************************************************************\ + +Function: ssa_alias_value + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_alias_value( + const exprt &e1, + const exprt &e2, + const namespacet &ns) +{ + const typet &e1_type=ns.follow(e1.type()); + const typet &e2_type=ns.follow(e2.type()); + + // type matches? + if(e1_type==e2_type) + return e2; + + exprt a1=address_canonizer(address_of_exprt(e1), ns); + exprt a2=address_canonizer(address_of_exprt(e2), ns); + + exprt offset1=pointer_offset(a1); + + // array index possible? + if(e2_type.id()==ID_array && + e1_type==ns.follow(e2_type.subtype())) + { + // this assumes well-alignedness + + mp_integer element_size=pointer_offset_size(e2_type.subtype(), ns); + + if(element_size==1) + return index_exprt(e2, offset1, e1.type()); + else if(element_size>1) + { + exprt index=div_exprt(offset1, from_integer(element_size, offset1.type())); + return index_exprt(e2, index, e1.type()); + } + } + + byte_extract_exprt byte_extract(byte_extract_id(), e1.type()); + byte_extract.op()=e2; + byte_extract.offset()=offset1; + + return byte_extract; +} + +/*******************************************************************\ + +Function: dereference_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt dereference_rec( + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) +{ + if(src.id()==ID_dereference) + { + const exprt &pointer=to_dereference_expr(src).pointer(); + exprt pointer_deref=dereference(pointer, ssa_value_domain, nondet_prefix, ns); + + // We use the identifier produced by + // local_SSAt::replace_side_effects_rec + exprt result=symbol_exprt(nondet_prefix, src.type()); + + // query the value sets + const ssa_value_domaint::valuest values= + ssa_value_domain(pointer, ns); + + for(ssa_value_domaint::valuest::value_sett::const_iterator + it=values.value_set.begin(); + it!=values.value_set.end(); + it++) + { + exprt guard=ssa_alias_guard(src, it->get_expr(), ns); + exprt value=ssa_alias_value(src, it->get_expr(), ns); + result=if_exprt(guard, value, result); + } + + return result; + } + else if(src.id()==ID_member) + { + member_exprt tmp=to_member_expr(src); + tmp.struct_op()=dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); + + #ifdef DEBUG + std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; + #endif + + if(tmp.struct_op().is_nil()) + return nil_exprt(); + + return lift_if(tmp); + } + else if(src.id()==ID_address_of) + { + address_of_exprt tmp=to_address_of_expr(src); + tmp.object()=dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); + + if(tmp.object().is_nil()) + return nil_exprt(); + + return lift_if(tmp); + } + else + { + exprt tmp=src; + Forall_operands(it, tmp) + *it=dereference_rec(*it, ssa_value_domain, nondet_prefix, ns); + return tmp; + } +} + +/*******************************************************************\ + +Function: dereference + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt dereference( + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) +{ + #ifdef DEBUG + std::cout << "dereference src: " << from_expr(ns, "", src) << '\n'; + #endif + + exprt tmp1=dereference_rec(src, ssa_value_domain, nondet_prefix, ns); + + #ifdef DEBUG + std::cout << "dereference tmp1: " << from_expr(ns, "", tmp1) << '\n'; + #endif + + exprt tmp2=simplify_expr(tmp1, ns); + + #ifdef DEBUG + std::cout << "dereference tmp2: " << from_expr(ns, "", tmp2) << '\n'; + #endif + + return tmp2; +} diff --git a/src/ssa/ssa_dereference.h b/src/ssa/ssa_dereference.h new file mode 100644 index 000000000..2494d5973 --- /dev/null +++ b/src/ssa/ssa_dereference.h @@ -0,0 +1,27 @@ +/*******************************************************************\ + +Module: Aliasing Decision + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SSA_ALIASING_H +#define CPROVER_SSA_ALIASING_H + +#include +#include + +#include "ssa_value_set.h" + +//bool ssa_may_alias(const exprt &, const exprt &, const namespacet &); +//exprt ssa_alias_guard(const exprt &, const exprt &, const namespacet &); +//exprt ssa_alias_value(const exprt &, const exprt &, const namespacet &); + +exprt dereference( + const exprt &, + const ssa_value_domaint &, + const std::string &nondet_prefix, + const namespacet &); + +#endif diff --git a/src/ssa/ssa_slicer.cpp b/src/ssa/ssa_slicer.cpp new file mode 100644 index 000000000..d1eafcb13 --- /dev/null +++ b/src/ssa/ssa_slicer.cpp @@ -0,0 +1,181 @@ +#include + +#include +#include + +#include "local_ssa.cpp" + +void print_symbols(std::string msg, const find_symbols_sett &symbols) +{ + std::cout << msg << ": " << std::endl; + for(find_symbols_sett::const_iterator + s_it=symbols.begin(); s_it!=symbols.end(); s_it++) + std::cout << " " << *s_it << std::endl; + +} + +void ssa_slicert::operator()(std::list &dest, + const local_SSAt &src) +{ + //collect symbols in assertions + find_symbols_sett new_symbols; + for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); + n_it != src.nodes.end(); n_it++) + { + if(n_it->marked) continue; + if(n_it->assertions.empty()) continue; + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + { + find_symbols(*a_it,new_symbols); + } + } +#ifdef DEBUG + print_symbols("symbols in assertions",new_symbols); +#endif + if(new_symbols.empty()) return; + + //build map symbol -> (definition, constraint set) + symbol_mapt symbol_map; + sliced_equalities = 0; + sliced_constraints = 0; + + for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); + n_it != src.nodes.end(); n_it++) + { + for(local_SSAt::nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + { + find_symbols_sett e_symbols; + find_symbols(e_it->lhs(),e_symbols); + + for(find_symbols_sett::const_iterator + s_it=e_symbols.begin(); s_it!=e_symbols.end(); s_it++) + { + symbol_map[*s_it]; + symbol_map[*s_it].def = e_it; + symbol_map[*s_it].def_node = n_it; + } + } + for(local_SSAt::nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + { + find_symbols_sett c_symbols; + find_symbols(*c_it,c_symbols); + for(find_symbols_sett::const_iterator + s_it=c_symbols.begin(); s_it!=c_symbols.end(); s_it++) + { + if(symbol_map.find(*s_it)==symbol_map.end()) continue; + symbol_map[*s_it].constraints.push_back(constr_infot()); + constr_infot &constr_info = symbol_map[*s_it].constraints.back(); + constr_info.constr = c_it; + constr_info.node = n_it; + } + } + //statistics + if(!n_it->marked) + { + sliced_equalities += n_it->equalities.size(); + sliced_constraints += n_it->constraints.size(); + } + } + //statistics + std::cout << "Total equalities: " << sliced_equalities + << ", total constraints: " << sliced_constraints << std::endl; + + //compute backwards dependencies and add to formula on-the-fly + find_symbols_sett symbols_seen; + while(!new_symbols.empty()) + { + find_symbols_sett old_symbols = new_symbols; + +#ifdef DEBUG + print_symbols("new symbols",new_symbols); +#endif + + new_symbols.clear(); + for(find_symbols_sett::const_iterator + s_it=old_symbols.begin(); s_it!=old_symbols.end(); s_it++) + { + irep_idt sym = *s_it; + if(symbols_seen.find(sym)!=symbols_seen.end()) continue; + if(symbol_map.find(sym)==symbol_map.end()) + { + //handle loopback variables + //here it's getting a bit ugly + std::string sym_str = id2string(sym); + size_t pos1 = sym_str.find("#lb"); + if(pos1==std::string::npos) continue; + size_t pos2 = sym_str.find("%",pos1); + irep_idt basename = sym_str.substr(0,pos1); + std::string unwinder_suffix = ""; + if(pos2 + +#include "local_ssa.h" + +class ssa_slicert : public messaget +{ + public: + + void operator()(std::list &dest, + const local_SSAt &src); + + //statistics + unsigned sliced_equalities; + unsigned sliced_constraints; + + protected: + typedef struct + { + local_SSAt::nodet::constraintst::const_iterator constr; + local_SSAt::nodest::const_iterator node; + } constr_infot; + typedef std::list constraint_sett; + typedef struct + { + local_SSAt::nodet::equalitiest::const_iterator def; + local_SSAt::nodest::const_iterator def_node; + constraint_sett constraints; + } symbol_infot; + typedef hash_map_cont symbol_mapt; + +}; + +#endif diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp new file mode 100644 index 000000000..8c65a8951 --- /dev/null +++ b/src/ssa/ssa_value_set.cpp @@ -0,0 +1,483 @@ +/*******************************************************************\ + +Module: A flow-insensitive value set analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "ssa_value_set.h" +#include "ssa_dereference.h" + +/*******************************************************************\ + +Function: ssa_value_domaint::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::transform( + locationt from, + locationt to, + ai_baset &ai, + const namespacet &ns) +{ + if(from->is_assign()) + { + const code_assignt &assignment=to_code_assign(from->code); + exprt lhs_deref=dereference(assignment.lhs(), *this, "", ns); + exprt rhs_deref=dereference(assignment.rhs(), *this, "", ns); + assign_lhs_rec(lhs_deref, rhs_deref, ns); + } + else if(from->is_goto()) + { + // Perhaps look at condition, for stuff like + // p!=0 or the like. + //exprt cond_deref=dereference(from->guard, *this, "", ns); + } + else if(from->is_decl()) + { + const code_declt &code_decl=to_code_decl(from->code); + assign_lhs_rec(code_decl.symbol(), nil_exprt(), ns); + } + else if(from->is_function_call()) + { + const code_function_callt &code_function_call= + to_code_function_call(from->code); + + // functions may alter state almost arbitrarily: + // * any global-scoped variables + // * any dirty locals + + #if 0 + for(objectst::const_iterator + o_it=ssa_objects.dirty_locals.begin(); + o_it!=ssa_objects.dirty_locals.end(); o_it++) + assign(*o_it, it, ns); + + for(objectst::const_iterator + o_it=ssa_objects.globals.begin(); + o_it!=ssa_objects.globals.end(); o_it++) + assign(*o_it, it, ns); + #endif + + // the call might come with an assignment + if(code_function_call.lhs().is_not_nil()) + { + exprt lhs_deref=dereference(code_function_call.lhs(), *this, "", ns); + assign_lhs_rec(lhs_deref, nil_exprt(), ns); + } + } + else if(from->is_dead()) + { + const code_deadt &code_dead=to_code_dead(from->code); + assign_lhs_rec(code_dead.symbol(), nil_exprt(), ns); + } +} + +/*******************************************************************\ + +Function: ssa_value_domaint::assign_lhs_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::assign_lhs_rec( + const exprt &lhs, + const exprt &rhs, + const namespacet &ns, + bool add) +{ + #if 0 + std::cout << "assign_lhs_rec lhs: " << from_expr(ns, "", lhs) << '\n'; + std::cout << "assign_lhs_rec rhs: " << from_expr(ns, "", rhs) << '\n'; + #endif + + // is the lhs an object? + if(is_symbol_struct_member(lhs, ns)) + { + const typet &lhs_type=ns.follow(lhs.type()); + + if(lhs_type.id()==ID_struct) + { + // Are we assigning an entire struct? + // If so, need to split into pieces, recursively. + + const struct_typet &struct_type=to_struct_type(lhs_type); + const struct_typet::componentst &components=struct_type.components(); + + for(struct_typet::componentst::const_iterator + it=components.begin(); + it!=components.end(); + it++) + { + member_exprt new_lhs(lhs, it->get_name(), it->type()); + member_exprt new_rhs(rhs, it->get_name(), it->type()); + assign_lhs_rec(new_lhs, new_rhs, ns, add); // recursive call + } + + return; // done + } + + // object? + ssa_objectt ssa_object(lhs, ns); + + if(ssa_object) + { + valuest tmp_values; + assign_rhs_rec(tmp_values, rhs, ns, false, 0); + + valuest &lhs_values=value_map[ssa_object]; + + if(add) + lhs_values.merge(tmp_values); + else + lhs_values=tmp_values; + +#if 0 + std::cout << "value_set: "; lhs_values.output(std::cout,ns); std::cout << std::endl; +#endif + + if(lhs_values.empty()) + value_map.erase(ssa_object); + } + + return; // done + } + else if(lhs.id()==ID_typecast) + { + assign_lhs_rec(to_typecast_expr(lhs).op(), rhs, ns, add); + } + else if(lhs.id()==ID_if) + { + assign_lhs_rec(to_if_expr(lhs).true_case(), rhs, ns, true); + assign_lhs_rec(to_if_expr(lhs).false_case(), rhs, ns, true); + } + else if(lhs.id()==ID_index) + { + assign_lhs_rec(to_index_expr(lhs).array(), rhs, ns, true); + } + else if(lhs.id()==ID_dereference) + { + assert(false); // should have been removed + } + else if(lhs.id()==ID_member) + { + #if 0 + // non-flattened struct or union member + const member_exprt &member_expr=to_member_expr(lhs); + assign(member_expr.struct_op(), loc, ns); + #endif + } + else if(lhs.id()==ID_complex_real || lhs.id()==ID_complex_imag) + { + #if 0 + assert(lhs.operands().size()==1); + assign(lhs.op0(), loc, ns); + #endif + } +} + +/*******************************************************************\ + +Function: ssa_value_domaint::assign_rhs_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::assign_rhs_rec( + valuest &dest, + const exprt &rhs, + const namespacet &ns, + bool offset, + unsigned alignment) const +{ +#if 0 + std::cout << "assign_rhs_rec: " << from_expr(ns, "", rhs) << '\n'; +#endif + + if(rhs.id()==ID_address_of) + { + const exprt &op=to_address_of_expr(rhs).object(); + assign_rhs_rec_address_of(dest, op, ns, offset, alignment); + } + else if(rhs.id()==ID_constant) + { + if(to_constant_expr(rhs).get_value()==ID_NULL) + { + dest.null=true; + } + } + else if(rhs.id()==ID_if) + { + assign_rhs_rec(dest, to_if_expr(rhs).true_case(), ns, offset, alignment); + assign_rhs_rec(dest, to_if_expr(rhs).false_case(), ns, offset, alignment); + } + else if(rhs.id()==ID_typecast) + { + assign_rhs_rec(dest, to_typecast_expr(rhs).op(), ns, offset, alignment); + } + else if(rhs.id()==ID_plus) + { + forall_operands(it, rhs) + { + if(it->type().id()==ID_pointer) + { + mp_integer pointer_offset=pointer_offset_size(it->type().subtype(), ns); + if(pointer_offset<1) pointer_offset=1; + unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + assign_rhs_rec(dest, *it, ns, true, a); + } + } + } + else if(rhs.id()==ID_minus) + { + assert(rhs.operands().size()==2); + if(rhs.type().id()==ID_pointer) + { + mp_integer pointer_offset=pointer_offset_size(rhs.type().subtype(), ns); + if(pointer_offset<1) pointer_offset=1; + unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + assign_rhs_rec(dest, rhs.op0(), ns, true, a); + } + } + else if(rhs.id()==ID_dereference) + { + std::cout << rhs.pretty() << std::endl; + assert(false); // should have been removed + } + else + { + // object? + + ssa_objectt ssa_object(rhs, ns); + + if(ssa_object) + { + value_mapt::const_iterator m_it=value_map.find(ssa_object); + + if(m_it!=value_map.end()) + { + valuest tmp_values=m_it->second; + if(offset) tmp_values.offset=true; + tmp_values.alignment=merge_alignment(tmp_values.alignment, alignment); + dest.merge(tmp_values); + } + } + else + { + forall_operands(it, rhs) + assign_rhs_rec(dest, *it, ns, true, 1); + } + } +} + +/*******************************************************************\ + +Function: ssa_value_domaint::assign_rhs_rec_address_of + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::assign_rhs_rec_address_of( + valuest &dest, + const exprt &rhs, + const namespacet &ns, + bool offset, + unsigned alignment) const +{ + ssa_objectt ssa_object(rhs, ns); + + if(ssa_object) + { + dest.value_set.insert(ssa_object); + if(offset) dest.offset=true; + } + else if(rhs.id()==ID_if) + { + assign_rhs_rec_address_of(dest, to_if_expr(rhs).true_case(), ns, offset, alignment); + assign_rhs_rec_address_of(dest, to_if_expr(rhs).false_case(), ns, offset, alignment); + } + else if(rhs.id()==ID_index) + { + unsigned a=alignment; + + if(!to_index_expr(rhs).index().is_zero()) + { + offset=true; + mp_integer pointer_offset=pointer_offset_size(rhs.type(), ns); + if(pointer_offset<1) pointer_offset=1; + a=merge_alignment(a, integer2long(pointer_offset)); + } + + assign_rhs_rec_address_of(dest, to_index_expr(rhs).array(), ns, offset, a); + } +} + +/*******************************************************************\ + +Function: ssa_value_domaint::valuest::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::valuest::output( + std::ostream &out, + const namespacet &ns) const +{ + if(offset) + { + out << " offset"; + if(alignment!=0) out << "*" << alignment; + } + + if(null) out << " null"; + if(unknown) out << " unknown"; + if(integer_address) out << " integer_address"; + + for(value_sett::const_iterator it=value_set.begin(); + it!=value_set.end(); + it++) + out << ' ' << '&' << it->get_identifier(); +} + +/*******************************************************************\ + +Function: ssa_value_domaint::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_value_domaint::output( + std::ostream &out, + const ai_baset &ai, + const namespacet &ns) const +{ + for(value_mapt::const_iterator + v_it=value_map.begin(); + v_it!=value_map.end(); + v_it++) + { + out << v_it->first.get_identifier() << ':'; + v_it->second.output(out, ns); + out << '\n'; + } +} + +/*******************************************************************\ + +Function: ssa_value_domaint::valuest::merge + + Inputs: + + Outputs: Return true if "this" has changed. + + Purpose: + +\*******************************************************************/ + +bool ssa_value_domaint::valuest::merge(const valuest &src) +{ + bool result=false; + + // bits + if(src.offset && !offset) { offset=true; result=true; } + if(src.null && !null) { null=true; result=true; } + if(src.unknown && !unknown) { unknown=true; result=true; } + if(src.integer_address && !integer_address) { integer_address=true; result=true; } + + // value set + unsigned old_size=value_set.size(); + value_set.insert(src.value_set.begin(), src.value_set.end()); + if(old_size!=value_set.size()) result=true; + + // alignment + alignment=merge_alignment(alignment, src.alignment); + + return result; +} + +/*******************************************************************\ + +Function: ssa_value_domaint::merge + + Inputs: + + Outputs: Return true if "this" has changed. + + Purpose: + +\*******************************************************************/ + +bool ssa_value_domaint::merge( + const ssa_value_domaint &other, + locationt from, + locationt to) +{ + value_mapt::iterator v_it=value_map.begin(); + const value_mapt &new_value_map=other.value_map; + bool result=false; + + for(value_mapt::const_iterator + it=new_value_map.begin(); + it!=new_value_map.end(); + ) // no it++ + { + if(v_it==value_map.end() || it->firstfirst) + { + value_map.insert(v_it, *it); + result=true; + it++; + continue; + } + else if(v_it->firstfirst) + { + v_it++; + continue; + } + + assert(v_it->first==it->first); + + if(v_it->second.merge(it->second)) + result=true; + + v_it++; + it++; + } + + return result; +} diff --git a/src/ssa/ssa_value_set.h b/src/ssa/ssa_value_set.h new file mode 100644 index 000000000..719ba6308 --- /dev/null +++ b/src/ssa/ssa_value_set.h @@ -0,0 +1,105 @@ +/*******************************************************************\ + +Module: A flow-insensitive value set analysis + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SSA_VALUE_SET_H +#define CPROVER_SSA_VALUE_SET_H + +#include + +#include "ssa_object.h" + +class ssa_value_domaint:public ai_domain_baset +{ +public: + virtual void transform(locationt, locationt, ai_baset &, const namespacet &); + virtual void output(std::ostream &, const ai_baset &, const namespacet &) const; + bool merge(const ssa_value_domaint &, locationt, locationt); + + struct valuest + { + public: + typedef std::set value_sett; + value_sett value_set; + bool offset, null, unknown, integer_address; + unsigned alignment; + + inline valuest(): + offset(false), null(false), unknown(false), integer_address(false), + alignment(0) + { + } + + void output(std::ostream &, const namespacet &) const; + + bool merge(const valuest &src); + + inline void clear() + { + *this=valuest(); + } + + bool empty() const + { + return value_set.empty() && !null && !unknown && !integer_address; + } + }; + + // maps objects to values + typedef std::map value_mapt; + value_mapt value_map; + + const valuest operator()(const exprt &src, const namespacet &ns) const + { + valuest tmp; + assign_rhs_rec(tmp, src, ns, false, 0); + return tmp; + } + +protected: + void assign_lhs_rec( + const exprt &lhs, const exprt &rhs, + const namespacet &, + bool add=false); + + void assign_rhs_rec( + valuest &lhs, const exprt &rhs, + const namespacet &, + bool offset, + unsigned alignment) const; + + void assign_rhs_rec_address_of( + valuest &lhs, const exprt &rhs, + const namespacet &, + bool offset, + unsigned alignment) const; + + static unsigned merge_alignment(unsigned a, unsigned b) + { + // could use lcm here + if(a==b) return a; + if(a==0) return b; + if(b==0) return a; + return 1; + } +}; + +class ssa_value_ait:public ait +{ +public: + ssa_value_ait( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + operator()(goto_function, ns); + } + +protected: + friend class ssa_value_domaint; +}; + +#endif diff --git a/src/storefront/data.cpp b/src/storefront/data.cpp new file mode 100644 index 000000000..391a40991 --- /dev/null +++ b/src/storefront/data.cpp @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: Trace View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "data.h" + +/*******************************************************************\ + +Function: datat::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void datat::read(const std::string &file) +{ + xmlt xml; + + console_message_handlert message_handler; + parse_xml(file, message_handler, xml); + + read(xml); +} + +/*******************************************************************\ + +Function: datat::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void datat::read(const xmlt &xml) +{ + for(xmlt::elementst::const_iterator + it=xml.elements.begin(); + it!=xml.elements.end(); + it++) + { + if(it->name=="property") + { + propertyt property; + + for(xmlt::elementst::const_iterator + e_it=it->elements.begin(); + e_it!=it->elements.end(); + e_it++) + { + if(e_it->name=="file") + property.file=e_it->data; + else if(e_it->name=="line") + property.line=unsafe_string2unsigned(e_it->data); + else if(e_it->name=="category") + property.category=e_it->data; + else if(e_it->name=="message") + property.message=e_it->data; + } + + properties.push_back(property); + } + else if(it->name=="description") + { + description=it->data; + } + } +} + diff --git a/src/storefront/data.h b/src/storefront/data.h new file mode 100644 index 000000000..046c58ceb --- /dev/null +++ b/src/storefront/data.h @@ -0,0 +1,40 @@ +/*******************************************************************\ + +Module: Data + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_DATA_H +#define CPROVER_DELTACHECK_DATA_H + +#include + +class datat +{ +public: + class propertyt + { + public: + irep_idt file; + unsigned line; + irep_idt category; + std::string message; + }; + + typedef std::vector propertiest; + propertiest properties; + + std::string description; + + inline void add(const propertyt &e) + { + properties.push_back(e); + } + + void read(const std::string &file); + void read(const class xmlt &); +}; + +#endif diff --git a/src/storefront/file_view.cpp b/src/storefront/file_view.cpp new file mode 100644 index 000000000..a20041d77 --- /dev/null +++ b/src/storefront/file_view.cpp @@ -0,0 +1,147 @@ +/*******************************************************************\ + +Module: Trace View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include + +#include + +#include "../html/html_escape.h" +#include "../html/syntax_highlighting.h" + +#include "data.h" + +/*******************************************************************\ + +Function: file_view + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void print_file(const datat &data, irep_idt file, std::ostream &out) +{ + out << "
\n"; + out << "
" << html_escape(file) << "
\n"; + out << "
\n"; + + std::ifstream in(file.c_str()); + if(!in) + { + } + else + { + // line to property number + std::map > line_map; + + for(datat::propertiest::const_iterator + e_it=data.properties.begin(); + e_it!=data.properties.end(); + e_it++) + if(e_it->file==file) + { + line_map[e_it->line].push_back(e_it-data.properties.begin()); + } + + syntax_highlightingt syntax_highlighting(out); + + unsigned line_no=1; + + std::string line; + while(std::getline(in, line)) + { + syntax_highlighting.strong_class=""; + syntax_highlighting.line_no=line_no; + + std::vector &properties=line_map[line_no]; + + if(!properties.empty()) + { + syntax_highlighting.strong_class="alarm"; + } + + syntax_highlighting(line); + + line_no++; + } + } + + out << "
\n\n"; +} + +/*******************************************************************\ + +Function: file_view + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void file_view(const datat &data) +{ + std::ofstream out("file_view.html"); + + out << "\n\n"; + out << "" << html_escape(data.description) << "\n"; + + out << "\n"; + out << "\n"; + + out << "\n"; + + out << "
\n"; + + out << "
\n"; + out << html_escape(data.description) << "\n"; + out << "
\n"; + + std::set files; + + for(datat::propertiest::const_iterator + e_it=data.properties.begin(); + e_it!=data.properties.end(); + e_it++) + files.insert(e_it->file); + + for(std::set::const_iterator + f_it=files.begin(); + f_it!=files.end(); + f_it++) + { + if(has_prefix(id2string(*f_it), "/usr/include/")) + continue; + + if(has_prefix(id2string(*f_it), "")) + continue; + + print_file(data, *f_it, out); + } + + out << "\n\n"; +} diff --git a/src/storefront/file_view.h b/src/storefront/file_view.h new file mode 100644 index 000000000..e0c5ca8ee --- /dev/null +++ b/src/storefront/file_view.h @@ -0,0 +1,14 @@ +/*******************************************************************\ + +Module: File View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_FILE_VIEW_H +#define CPROVER_DELTACHECK_FILE_VIEW_H + +void file_view(const class datat &); + +#endif diff --git a/src/storefront/property_view.cpp b/src/storefront/property_view.cpp new file mode 100644 index 000000000..21c5b31e8 --- /dev/null +++ b/src/storefront/property_view.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: Property View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "data.h" + +/*******************************************************************\ + +Function: property_view + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void property_view(const class datat &) +{ + +} + + + + diff --git a/src/storefront/property_view.h b/src/storefront/property_view.h new file mode 100644 index 000000000..83f1eae61 --- /dev/null +++ b/src/storefront/property_view.h @@ -0,0 +1,14 @@ +/*******************************************************************\ + +Module: Property View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_PROPERTY_VIEW_H +#define CPROVER_DELTACHECK_PROPERTY_VIEW_H + +void property_view(const class datat &); + +#endif diff --git a/src/storefront/storefront_main.cpp b/src/storefront/storefront_main.cpp new file mode 100644 index 000000000..3914e4dbd --- /dev/null +++ b/src/storefront/storefront_main.cpp @@ -0,0 +1,38 @@ +/*******************************************************************\ + +Module: Main Module + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "storefront_parse_options.h" + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +#ifdef _MSC_VER +int wmain(int argc, const wchar_t **argv_wide) +{ + const char **argv=narrow_argv(argc, argv_wide); + storefront_parse_optionst parse_options(argc, argv); + return parse_options.main(); +} +#else +int main(int argc, const char **argv) +{ + storefront_parse_optionst parse_options(argc, argv); + return parse_options.main(); +} +#endif diff --git a/src/storefront/storefront_parse_options.cpp b/src/storefront/storefront_parse_options.cpp new file mode 100644 index 000000000..2cf35655c --- /dev/null +++ b/src/storefront/storefront_parse_options.cpp @@ -0,0 +1,121 @@ +/*******************************************************************\ + +Module: Command Line Interface + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "../deltacheck/version.h" + +#include "storefront_parse_options.h" +#include "data.h" +#include "file_view.h" +#include "trace_view.h" +#include "property_view.h" + +/*******************************************************************\ + +Function: storefront_parse_optionst::storefront_parse_optionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +storefront_parse_optionst::storefront_parse_optionst( + int argc, const char **argv): + parse_options_baset(STOREFRONT_OPTIONS, argc, argv) +{ +} + +/*******************************************************************\ + +Function: storefront_parse_optionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int storefront_parse_optionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << DELTACHECK_VERSION << std::endl; + return 0; + } + + try + { + if(cmdline.args.empty()) + { + usage_error(); + return 10; + } + + // read config + datat data; + + for(unsigned i=0; i + +#define STOREFRONT_OPTIONS \ + "(verbosity):(version)" + +class storefront_parse_optionst:public parse_options_baset +{ +public: + virtual int doit(); + virtual void help(); + + storefront_parse_optionst( + int argc, const char **argv); + +protected: +}; + +#endif diff --git a/src/storefront/trace_view.cpp b/src/storefront/trace_view.cpp new file mode 100644 index 000000000..73c2a30b3 --- /dev/null +++ b/src/storefront/trace_view.cpp @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: Trace View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "data.h" + +/*******************************************************************\ + +Function: trace_view + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void trace_view(const class datat &) +{ + +} + + + diff --git a/src/storefront/trace_view.h b/src/storefront/trace_view.h new file mode 100644 index 000000000..fcaaf7a4f --- /dev/null +++ b/src/storefront/trace_view.h @@ -0,0 +1,14 @@ +/*******************************************************************\ + +Module: Trace View + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_DELTACHECK_TRACE_VIEW_H +#define CPROVER_DELTACHECK_TRACE_VIEW_H + +void trace_view(const class datat &); + +#endif diff --git a/src/summarizer/function_signature.cpp b/src/summarizer/function_signature.cpp new file mode 100644 index 000000000..df464a847 --- /dev/null +++ b/src/summarizer/function_signature.cpp @@ -0,0 +1,78 @@ +/*******************************************************************\ + +Module: Signature of a Function + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include "../ssa/local_ssa.h" +#include "function_signature.h" + +/*******************************************************************\ + +Function: update_function_signature + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void update_function_signature( + const local_SSAt &SSA, + class jsont &dest) +{ + jsont &j_signature=dest["signature"]; + jsont &j_reads=j_signature["reads"]; + jsont &j_modifies=j_signature["modifies"]; + + j_signature.kind=jsont::J_OBJECT; + j_reads=jsont::json_array(); + j_modifies=jsont::json_array(); + + std::set modifies; + std::set reads; + + for(assignmentst::assignment_mapt::const_iterator + a_it=SSA.assignments.assignment_map.begin(); + a_it!=SSA.assignments.assignment_map.end(); + a_it++) + { + for(assignmentst::objectst::const_iterator + o_it=a_it->second.begin(); + o_it!=a_it->second.end(); + o_it++) + { + modifies.insert(o_it->get_identifier()); + } + } + + for(ssa_objectst::objectst::const_iterator + o_it=SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); + o_it++) + { + reads.insert(o_it->get_identifier()); + } + + for(std::set::const_iterator it=reads.begin(); + it!=reads.end(); + it++) + { + j_reads.array.push_back(jsont::json_string(id2string(*it))); + } + + for(std::set::const_iterator it=modifies.begin(); + it!=modifies.end(); + it++) + { + j_modifies.array.push_back(jsont::json_string(id2string(*it))); + } + +} + diff --git a/src/summarizer/function_signature.h b/src/summarizer/function_signature.h new file mode 100644 index 000000000..16a7eaf09 --- /dev/null +++ b/src/summarizer/function_signature.h @@ -0,0 +1,17 @@ +/*******************************************************************\ + +Module: Signature of a Function + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H +#define CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H + +#include + +void update_function_signature(const class local_SSAt &, jsont &); + +#endif + diff --git a/src/summarizer/instrument_goto.cpp b/src/summarizer/instrument_goto.cpp new file mode 100644 index 000000000..7cd4b4f3f --- /dev/null +++ b/src/summarizer/instrument_goto.cpp @@ -0,0 +1,180 @@ +/*******************************************************************\ + +Module: Instrument Goto Program with Inferred Information + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#define DEBUG + +#ifdef DEBUG +#include +#endif + +#include "instrument_goto.h" + + + +local_SSAt::locationt find_loop_by_guard(const local_SSAt &SSA, + const symbol_exprt &guard) +{ + std::string gstr = id2string(guard.get_identifier()); + unsigned pos1 = gstr.find("#")+1; + unsigned pos2 = gstr.find("%",pos1); + unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); + + local_SSAt::nodest::const_iterator n_it =SSA.nodes.begin(); + + for(; n_it != SSA.nodes.end(); n_it++) + { + if(n_it->location->location_number == n) { + // find end of loop + break; + } + } + + if(n_it->loophead==SSA.nodes.end()) + return n_it->location; + else + return n_it->loophead->location; +} + + +void instrument_gotot::instrument_instruction( + const exprt &expr, + goto_programt &dest, + goto_programt::targett &target) +{ + + goto_programt::targett where=target; + + std::cout << "target " << target->type << " : " << target->source_location << std::endl; + + for(;;++where) + { + if(where->is_goto() && where->get_target()==target) + break; + } + + + goto_programt tmp; + + goto_programt::targett assumption=tmp.add_instruction(); + assumption->make_assumption(expr); + assumption->source_location=target->source_location; + assumption->source_location.set_comment("invariant generated by 2LS"); + + dest.insert_before_swap(where, tmp); + + #ifdef DEBUG + std::cout << "instrumenting instruction " << std::endl; + #endif + + + //dest.update(); +} + +extern +void purify_identifiers(exprt &expr); + + +void instrument_gotot::instrument_body( + const local_SSAt &SSA, + const exprt &expr, + goto_functionst::goto_functiont &function +) +{ + //expected format (/\_j g_j) => inv + const exprt &impl = expr.op0(); + exprt inv = expr.op1(); //copy + + std::cout << "Invariant " << from_expr(inv) << std::endl; + + purify_identifiers(inv); + + local_SSAt::locationt loc; + if(impl.id()==ID_symbol) + { + loc = find_loop_by_guard(SSA,to_symbol_expr(impl)); + } + else if(impl.id()==ID_and) + { + assert(impl.op0().id()==ID_symbol); + loc = find_loop_by_guard(SSA,to_symbol_expr(impl.op0())); + + } + else assert(false); + + + Forall_goto_program_instructions(it, function.body) + if(it==loc) + { + + instrument_instruction(inv, function.body, it); + break; + } +} + + + +void instrument_gotot::instrument_function( + const irep_idt &function_name, + goto_functionst::goto_functiont &function) +{ + #ifdef DEBUG + std::cout << "instrumenting function " << function_name << std::endl; + #endif + + if(!summary_db.exists(function_name)) + return; + + const summaryt summary=summary_db.get(function_name); + + if(!ssa_db.exists(function_name)) + return; + + const local_SSAt &SSA=ssa_db.get(function_name); + + if(summary.fw_invariant.is_nil()) return; + if(summary.fw_invariant.is_true()) return; + + //expected format /\_i g_i => inv_i + if(summary.fw_invariant.id()==ID_implies) + { + instrument_body(SSA, summary.fw_invariant, function); + } + else if(summary.fw_invariant.id()==ID_and) + { + for(unsigned i=0; i::function_mapt + function_mapt; + + function_mapt + &function_map=goto_functions.function_map; + + for(function_mapt::iterator + fit=function_map.begin(); + fit!=function_map.end(); + ++fit) + { + instrument_function(fit->first, fit->second); + } + + goto_model.goto_functions.update(); +} diff --git a/src/summarizer/instrument_goto.h b/src/summarizer/instrument_goto.h new file mode 100644 index 000000000..dbc3a1b41 --- /dev/null +++ b/src/summarizer/instrument_goto.h @@ -0,0 +1,55 @@ +/*******************************************************************\ + +Module: Instrument Goto Program with Inferred Information + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_INSTRUMENT_GOTO_H +#define CPROVER_INSTRUMENT_GOTO_H + +#include +#include + +#include "../ssa/local_ssa.h" +#include "../ssa/ssa_unwinder.h" +#include "ssa_db.h" +#include "summary_db.h" + +class instrument_gotot:public messaget +{ +public: + inline instrument_gotot(optionst &_options, + ssa_dbt &_ssa_db, + summary_dbt &_summary_db): + options(_options), + ssa_db(_ssa_db),summary_db(_summary_db) + { + } + + void operator()(goto_modelt &goto_model); + + protected: + optionst &options; + + ssa_dbt &ssa_db; + summary_dbt &summary_db; + + void instrument_function( + const irep_idt &function_name, + goto_functionst::goto_functiont &function); + + void instrument_body( + const local_SSAt &SSA, + const exprt &expr, + goto_functionst::goto_functiont &function); + + void instrument_instruction( + const exprt &expr, + goto_programt &dest, + goto_programt::targett &target); + +}; + +#endif diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp new file mode 100644 index 000000000..a6b125135 --- /dev/null +++ b/src/summarizer/preprocessing_util.cpp @@ -0,0 +1,265 @@ +#include +#include +#include +#include + +#include "../ssa/const_propagator.h" + +#include "summarizer_parse_options.h" + +/*******************************************************************\ + +Function: summarizer_parse_optionst::inline_main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::inline_main(goto_modelt &goto_model) +{ + goto_programt &main = goto_model.goto_functions.function_map[ID__start].body; + goto_programt::targett target = main.instructions.begin(); + while(target!=main.instructions.end()) + { + if(target->is_function_call()) + { + const code_function_callt &code_function_call= + to_code_function_call(target->code); + irep_idt fname = code_function_call.function().get(ID_identifier); + + debug() << "Inlining " << fname << eom; + + goto_programt tmp; + tmp.copy_from(goto_model.goto_functions.function_map[fname].body); + (--tmp.instructions.end())->make_skip(); + goto_model.goto_functions.function_map.erase(fname); + + goto_programt::targett next_target(target); + target->make_skip(); + next_target++; + main.instructions.splice(next_target, tmp.instructions); + target=next_target; + } + else target++; + } + + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::propagate_constants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::propagate_constants(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + const_propagator_ait(f_it->second,ns); + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::nondet_locals + + Inputs: + + Outputs: + + Purpose: explicitly assign a nondet_symbol to local variables + this is required by the unwinder, which would be unable + to recognise in which scope variables have been declared + +\*******************************************************************/ + +void summarizer_parse_optionst::nondet_locals(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_decl()) + { + const code_declt& decl = to_code_decl(i_it->code); + side_effect_expr_nondett nondet(decl.symbol().type()); + goto_programt::targett t = f_it->second.body.insert_after(i_it); + t->make_assignment(); + code_assignt c(decl.symbol(),nondet); + t->code.swap(c); + t->source_location = i_it->source_location; + } + } + } + goto_model.goto_functions.update(); +} + +/*******************************************************************\ + +Function: goto_unwind + + Inputs: + + Outputs: + + Purpose: unwind all loops + +\*******************************************************************/ + +void summarizer_parse_optionst::goto_unwind(goto_modelt &goto_model, unsigned k) +{ + + typedef std::vector > loopst; + + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; + + loopst loops; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_backwards_goto()) + { + goto_programt::targett loop_head = i_it->get_target(); + goto_programt::targett loop_exit = i_it; + bool has_goto_into_loop = false; + + goto_programt::targett it = loop_head; + if(it!=loop_exit) it++; + for(; it!=loop_exit; it++) + { + for( std::set::iterator + s_it = it->incoming_edges.begin(); + s_it!=it->incoming_edges.end(); ++s_it) + { + if((*s_it)->is_goto() && + (*s_it)->location_number < loop_head->location_number) + { + has_goto_into_loop = true; + break; + } + } + if(has_goto_into_loop) break; + } + if(has_goto_into_loop) + { + status() << "Unwinding jump into loop" << eom; + loops.push_back(loopst::value_type(++loop_exit,loop_head)); + } + } + } + + for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) + { + std::vector iteration_points; + unwind(body,l_it->second,l_it->first,k,iteration_points); + assert(iteration_points.size()==2); + goto_programt::targett t=body.insert_before(l_it->first); + t->make_goto(); + t->targets.push_back(iteration_points.front()); + } + } + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: remove_multiple_dereferences + + Inputs: + + Outputs: + + Purpose: temporary fix to circumvent ssa_dereference problem + +\*******************************************************************/ + +void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen) +{ + if(expr.id()==ID_member) + { + member_exprt &member_expr = to_member_expr(expr); + if(member_expr.compound().id()==ID_dereference) + { + dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); + remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); + if(deref_seen) + { + symbolt new_symbol; + new_symbol.type=member_expr.type(); + new_symbol.name="$deref"+i2string(var_counter++); + new_symbol.base_name=new_symbol.name; + new_symbol.pretty_name=new_symbol.name; + goto_model.symbol_table.add(new_symbol); + goto_programt::targett t_new = body.insert_before(t); + t_new->make_assignment(); + t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); + expr = new_symbol.symbol_expr(); + for(std::set::iterator t_it = + t->incoming_edges.begin(); + t_it != t->incoming_edges.end(); ++t_it) + { + (*t_it)->targets.clear(); + (*t_it)->targets.push_back(t_new); + } + body.compute_location_numbers(); + body.compute_target_numbers(); + body.compute_incoming_edges(); + } + } + else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); + } + else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); +} + +void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) +{ + unsigned var_counter = 0; + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_goto()) + { + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, false); + } + else if(i_it->is_assign()) + { + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); + } + } + } +} diff --git a/src/summarizer/summarizer.cpp b/src/summarizer/summarizer.cpp new file mode 100644 index 000000000..04c170e0f --- /dev/null +++ b/src/summarizer/summarizer.cpp @@ -0,0 +1,125 @@ +/*******************************************************************\ + +Module: Summarizer + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + +#include "function_signature.h" +#include "summary_db.h" +#include "summarizer.h" + +/*******************************************************************\ + +Function: summarizert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizert::operator()(const goto_modelt &goto_model) +{ + // analyze all the functions + forall_goto_functions(f_it, goto_model.goto_functions) + summarize(goto_model, f_it); +} + +/*******************************************************************\ + +Function: summarizert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizert::operator()( + const goto_modelt &goto_model, + const irep_idt &id) +{ + // analyze the given function only + + goto_functionst::function_mapt::const_iterator f_it= + goto_model.goto_functions.function_map.find(id); + + if(f_it==goto_model.goto_functions.function_map.end()) + throw "function not found"; + + summarize(goto_model, f_it); +} + +/*******************************************************************\ + +Function: summarizert::summarize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizert::summarize( + const goto_modelt &goto_model, + const goto_functionst::function_mapt::const_iterator f_it) +{ + status() << "** Analyzing " << f_it->first << messaget::eom; + + summary_dbt summary_db; + + summary_db.set_message_handler(get_message_handler()); + summary_db.read(id2string(f_it->first)); + + const namespacet ns(goto_model.symbol_table); + + // build SSA + progress() << "Building SSA" << messaget::eom; + local_SSAt SSA(f_it->second, ns); + + // simplify, if requested + if(simplify) + { + progress() << "Simplifying" << messaget::eom; + ::simplify(SSA, ns); + } + + + // fixed-point for loops + #if 0 + progress() << "Fixed-point" << messaget::eom; + ssa_fixed_point(SSA); + #endif + + update_function_signature(SSA, summary_db.summary); + + summary_db.write(); +} + +/*******************************************************************\ + +Function: summarizert::report_statistics() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizert::report_statistics() +{ +} + diff --git a/src/summarizer/summarizer.h b/src/summarizer/summarizer.h new file mode 100644 index 000000000..f4cc72785 --- /dev/null +++ b/src/summarizer/summarizer.h @@ -0,0 +1,45 @@ +/*******************************************************************\ + +Module: Summarizer + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_H +#define CPROVER_SUMMARIZER_H + +#include +#include + +#include + +#include "../ssa/local_ssa.h" + +class summarizert:public messaget +{ +public: + inline summarizert(): + simplify(false), + fixed_point(false) + { + } + + bool simplify, fixed_point; + + void operator()(const goto_modelt &); + void operator()(const goto_modelt &, const irep_idt &); + + // statistics + absolute_timet start_time; + time_periodt sat_time; + +protected: + void report_statistics(); + + void summarize( + const goto_modelt &, + const goto_functionst::function_mapt::const_iterator); +}; + +#endif diff --git a/src/summarizer/summarizer_bw_cex.cpp b/src/summarizer/summarizer_bw_cex.cpp new file mode 100644 index 000000000..424e11248 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex.cpp @@ -0,0 +1,100 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize() +{ + assert(false); //unused +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const function_namet &function_name) +{ + exprt postcondition = true_exprt(); //initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec(function_name,postcondition,true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis..." << eom; + error_assertion = _error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_baset::check() +{ + assert(false); +} diff --git a/src/summarizer/summarizer_bw_cex.h b/src/summarizer/summarizer_bw_cex.h new file mode 100644 index 000000000..5cead1add --- /dev/null +++ b/src/summarizer/summarizer_bw_cex.h @@ -0,0 +1,51 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis Base + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_BW_CEX_H +#define CPROVER_SUMMARIZER_BW_CEX_H + +#include +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include "summarizer_bw.h" + +class summarizer_bw_cex_baset : public summarizer_bwt +{ + public: + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + function_namet entry_function; + function_namet error_function; + exprt error_assertion; + + explicit summarizer_bw_cex_baset(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner), + entry_function(_entry_function), + error_function(_error_function) + {} + +}; + + +#endif diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp new file mode 100644 index 000000000..fb5ae3183 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -0,0 +1,511 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_ai.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../domains/disjunctive_analyzer.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const function_namet &function_name) +{ + exprt postcondition = true_exprt(); //initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec(function_name,summaryt::entry_call_site, + postcondition,true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (abstract)..." << eom; + error_assertion = _error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_ait::check() +{ + //TODO: store information about why have UNKNOWN + // we have to distinguish the case when we cannot decide about spuriousness + + if(!summary_db.exists(entry_function)) + return property_checkert::UNKNOWN; + + const summaryt &summary = summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + return property_checkert::UNKNOWN; + + return property_checkert::FAIL; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA = ssa_db.get(function_name); + + summaryt summary; + if(summary_db.exists(function_name)) + summary = summary_db.get(function_name); + else + { + summary.params = SSA.params; + summary.globals_in = SSA.globals_in; + summary.globals_out = SSA.globals_out; + } + + // insert assertion + exprt end_guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition = implies_exprt(end_guard,_postcondition); + if(function_name == error_function) + { + postcondition = and_exprt(postcondition,not_exprt(error_assertion)); + } + + summary.bw_postcondition = _postcondition; + +#if 0 + debug() << "Postcondition: " << + from_expr(SSA.ns, "", postcondition) << eom; +#endif + + if(_postcondition.is_false()){ + + summary.error_summaries[call_site] = false_exprt(); + + } + else{ + + // recursively compute summaries for function calls + inline_summaries(function_name,SSA,summary, + postcondition,context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary(function_name,call_site,SSA,summary,summary,postcondition,context_sensitive); + + if(function_name == error_function) + summary.has_assertion = true; + + } + + summary_db.set(function_name,summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out,SSA.ns); + debug() << out.str() << eom; + } + +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + incremental_solvert &solver = ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + //TODO: maybe allow setting this separately on the command line + optionst _options = options; + _options.set_option("intervals",true); + _options.set_option("binsearch-solver",true); + + //TODO: use a template generator without invariants + template_generator_summaryt template_generator( + _options,ssa_db,ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(),SSA,false); + + exprt::operandst c; + //add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false, + assert_postcond,noassert_postcond,c); //backward summaries + + assert_postcond.push_back(postcondition); //context + + + // assumptions must hold + for(local_SSAt::nodest::const_iterator + n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); + ++n_it) + for(local_SSAt::nodet::assumptionst::const_iterator + a_it = n_it->assumptions.begin(); + a_it != n_it->assumptions.end(); + ++a_it) + c.push_back(*a_it); + + + + +#if 0 + std::cout << from_expr(SSA.ns, "", cc) << std::endl; +#endif + + //TODO: pushing loophead_selects into the solver + + summary.error_summaries[call_site]; + if(!template_generator.empty()) + { + c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt + c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis + + //std::cout << "unsimplified constraints: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; + exprt cc = simplify_expr(conjunction(c), SSA.ns); + //exprt cc = conjunction(c); + //std::cout << "simplified constraints passed: " << from_expr(SSA.ns, "", cc) << "\n\n\n"; + + /* + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver,SSA,cc,template_generator); + analyzer.get_result(summary.error_summaries[call_site], + template_generator.inout_vars()); + */ + + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + disjunctive_analyzer(solver,SSA,cc,template_generator, + cc,summary.error_summaries[call_site], + template_generator.inout_vars()); + +#if 0 + std::cout << "SUM: " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; +#endif + + + summary.error_summaries[call_site] = + simplify_expr(summary.error_summaries[call_site], SSA.ns); + + +#if 0 + std::cout << "SUM (post simplification): " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; +#endif + + //statistics + /* + solver_instances += analyzer.get_number_of_solver_instances(); + solver_calls += analyzer.get_number_of_solver_calls(); + */ + solver_instances += disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls += disjunctive_analyzer.get_number_of_solver_calls(); + + } + else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly + { + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + + //std::cout << "unsimplified constraints: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; + exprt cc = simplify_expr(conjunction(c), SSA.ns); + //exprt cc = conjunction(c); + //std::cout << "simplified constraints passed: " << from_expr(SSA.ns, "", cc) << "\n\n\n"; + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << cc; + exprt result = false_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) result = true_exprt(); + solver.pop_context(); + summary.error_summaries[call_site] = result; + +#if 0 + std::cout << "SUM: " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; +#endif + } + + summary.error_summaries[call_site] = + simplify_expr((summary.error_summaries[call_site]), SSA.ns); //not_exprt + + summary.has_assertion = assertion_flag; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::inline_summaries(const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); + n_it != SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it = + n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); //no function pointers + + exprt postcondition_call = true_exprt(); + postcondition_call = compute_calling_context2( + function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); + + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname,summaryt::call_sitet(n_it->location), + postcondition_call,context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_ait::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + incremental_solvert &solver = ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + //analyze + /* + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + */ + + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + + //TODO: maybe allow setting this separately on the command line + optionst _options = options; + _options.set_option("intervals",true); + _options.set_option("binsearch-solver",true); + + //TODO: use a template generator without invariants + template_generator_callingcontextt template_generator( + _options,ssa_db,ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(),SSA,n_it,f_it,false); + + // collect globals at call site + std::map + cs_globals_out; + SSA.get_globals(n_it->location,cs_globals_out[f_it],false); + + exprt::operandst c; + + //add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries(SSA,summaryt::call_sitet(n_it->location),false, + assert_postcond,noassert_postcond,c); //backward summaries + assert_postcond.push_back(postcondition); //context + + + //TODO: pushing loophead_selects into the solver + + // set preconditions + local_SSAt &fSSA = ssa_db.get(fname); + + exprt postcondition_call; + + if(!template_generator.empty()){ + + c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt + c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis + + /* + analyzer(solver,SSA,conjunction(c),template_generator); + analyzer.get_result(postcondition_call, + template_generator.callingcontext_vars()); + */ + + disjunctive_analyzer(solver,SSA,conjunction(c),template_generator, + conjunction(c), postcondition_call, + template_generator.callingcontext_vars()); + + ssa_inliner.rename_to_callee(f_it, fSSA.params, + cs_globals_out[f_it],fSSA.globals_out, + postcondition_call); + + } + else{ // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << conjunction(c); + + //std::cout << "passed to solver, else branch, calling context: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n"; + + postcondition_call = false_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) postcondition_call = true_exprt(); + solver.pop_context(); + } + + debug() << "Backward calling context for " << + from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + //statistics + /* + solver_instances += analyzer.get_number_of_solver_instances(); + solver_calls += analyzer.get_number_of_solver_calls(); + */ + solver_instances += disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls += disjunctive_analyzer.get_number_of_solver_calls(); + + return postcondition_call; +} + + diff --git a/src/summarizer/summarizer_bw_cex_ai.h b/src/summarizer/summarizer_bw_cex_ai.h new file mode 100644 index 000000000..a41b9c122 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_ai.h @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Summarizer for Backwards Error Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_BW_CEX_AI_H +#define CPROVER_SUMMARIZER_BW_CEX_AI_H + +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_ait : public summarizer_bw_cex_baset +{ + public: + explicit summarizer_bw_cex_ait(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _entry_function,_error_function) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + + virtual void compute_summary_rec(const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries(const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary(const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2(const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); + +}; + + +#endif diff --git a/src/summarizer/summarizer_bw_cex_all.cpp b/src/summarizer/summarizer_bw_cex_all.cpp new file mode 100644 index 000000000..cefdd8757 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_all.cpp @@ -0,0 +1,75 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_all.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_allt::summarize() + + Inputs: + + Outputs: + + Purpose: calls multiple strategies + +\*******************************************************************/ + +void summarizer_bw_cex_allt::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (all)..." << eom; + error_assertion = _error_assertion; + + summarizer_bw_cex_concrete.summarize(error_assertion); + result = summarizer_bw_cex_concrete.check(); + + if(result == property_checkert::UNKNOWN) + { + summarizer_bw_cex_ai.summarize(error_assertion); + result = summarizer_bw_cex_ai.check(); + + if(result == property_checkert::UNKNOWN) + { + summarizer_bw_cex_complete.summarize(error_assertion); + result = summarizer_bw_cex_complete.check(); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_allt::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_allt::check() +{ + return result; +} diff --git a/src/summarizer/summarizer_bw_cex_all.h b/src/summarizer/summarizer_bw_cex_all.h new file mode 100644 index 000000000..b04dae0fb --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_all.h @@ -0,0 +1,73 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis All + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_BW_CEX_ALL_H +#define CPROVER_SUMMARIZER_BW_CEX_ALL_H + +#include +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include "summarizer_bw_cex.h" +#include "summarizer_bw_cex_concrete.h" +#include "summarizer_bw_cex_ai.h" +#include "summarizer_bw_cex_complete.h" + +class summarizer_bw_cex_allt : public summarizer_bw_cex_baset +{ + public: + explicit summarizer_bw_cex_allt(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _entry_function,_error_function), + summarizer_bw_cex_concrete(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _entry_function,_error_function), + summarizer_bw_cex_ai(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _entry_function,_error_function), + summarizer_bw_cex_complete(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _solver,_entry_function,_error_function), + result(property_checkert::UNKNOWN) + {} + + virtual void summarize(const exprt &_error_assertion); + + virtual void set_message_handler(message_handlert &handler) + { + summarizer_bw_cex_concrete.set_message_handler(handler); + summarizer_bw_cex_ai.set_message_handler(handler); + summarizer_bw_cex_complete.set_message_handler(handler); + messaget::set_message_handler(handler); + } + + + virtual property_checkert::resultt check(); + + protected: + summarizer_bw_cex_concretet summarizer_bw_cex_concrete; + summarizer_bw_cex_ait summarizer_bw_cex_ai; + summarizer_bw_cex_completet summarizer_bw_cex_complete; + property_checkert::resultt result; + +}; + + +#endif diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp new file mode 100644 index 000000000..cad7b61de --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -0,0 +1,525 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" +#include "../ssa/ssa_dependency_graph.h" + +#include "summarizer_bw_cex_complete.h" + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize +( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec(entry_function,dependency_set,-1); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (complete)..." << eom; + error_assertion = _error_assertion; + /* + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; + */ + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_completet::inline_summaries +( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) +{ + local_SSAt &SSA = ssa_db.get(function_name); + //solver << SSA.get_enabling_exprs(); + + exprt::operandst loophead_selects; + loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + exprt c = conjunction(loophead_selects); + + //std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" + // << "\t original info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; + + ssa_inliner.rename(c, counter); + +#if 0 + std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" + << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + solver << c; + + ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); + + struct worknodet{ + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index = 0; + start_node.dependency_set = dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()){ + + /* + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it != worklist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it != work_waitlist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + */ + + worknodet &worknode = worklist.front(); + + //std::cout << "working node: " << function_name << ": " << worknode.node_index << "\n"; + ///////////////////////////////////////////////////////////////////////////////////// + //std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + /* + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n\n\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + + // return if the top most node is reached + if(worknode.node_index == ssa_depgraph.top_node_index) + return worknode.dependency_set; + + // modify worknode_dependency_set if the node is an assertion + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true){ + + //std::cout << "\t\t an assertion node\n"; + for(find_symbols_sett::const_iterator d_it = ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); + d_it != ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + + + } + + // if this is a function call + if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == true){ + //std::cout << "fcall: working node: " << function_name << ": " << worknode.node_index << "\n"; + irep_idt fname = + to_symbol_expr((to_function_application_expr(ssa_depgraph.depnodes_map[worknode.node_index].node_info)).function()).get_identifier(); + + find_symbols_sett renamed_dependencies; + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // detach the '@' symbol if there + irep_idt renamed_id = ssa_inliner.rename + (id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set = renamed_dependencies; + + if(!worknode.dependency_set.empty()){ + find_symbols_sett guard_dependencies; + find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + } + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + worknode.dependency_set = + compute_summary_rec(fname,worknode.dependency_set, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter); + + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // attach the '@' symbol if not already there + irep_idt renamed_id = ssa_inliner.rename + (id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, true); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set = renamed_dependencies; + + if(!worknode.dependency_set.empty()){ + find_symbols_sett guard_dependencies; + find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + } + + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()){ + exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) + worknode_info = not_exprt(worknode_info); + + if(worknode.node_index != 0){ + if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)){ + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || + (worknode_info == error_assertion)){ + /* + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + */ + ssa_inliner.rename(worknode_info, counter); +#if 0 + std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; +#endif + solver << worknode_info; + } + } + else{ + exprt guard_binding = ssa_depgraph.depnodes_map[worknode.node_index].guard; + /* + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; + */ + ssa_inliner.rename(guard_binding, counter); +#if 0 + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; +#endif + solver << guard_binding; + } + } + } + + // if not a function call and the dependency set is non-empty + if((ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == false) && + (!worknode.dependency_set.empty())){ + + exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) + worknode_info = not_exprt(worknode_info); + + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || + (worknode_info == error_assertion)){ + worknode.dependency_set = + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols; + } + } + + for(ssa_dependency_grapht::annotated_predecessorst::const_iterator + p_it = ssa_depgraph.depnodes_map[worknode.node_index].predecessors.begin(); + p_it != ssa_depgraph.depnodes_map[worknode.node_index].predecessors.end(); + p_it++){ + + ssa_dependency_grapht::annotated_predecessort pred = *p_it; + int pred_node_index = pred.predecessor_node_index; + find_symbols_sett pred_annotation = pred.annotation; + + bool dependencies_merged = false; + for(worklistt::iterator w_it = work_waitlist.begin(); w_it != work_waitlist.end(); w_it++){ + if(w_it->node_index == pred_node_index){ + + dependencies_merged = true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ + if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged == false){ + worknodet new_worknode; + new_worknode.node_index = pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#if 0 + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it != worklist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it != work_waitlist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#if 0 + std::cout << function_name << ": covered : "; + for(int l = 0; l < covered_nodes.size(); l++){ + std::cout << covered_nodes[l] << " "; + } + std::cout << "\n"; +#endif + + worklistt iterate_work_waitlist = work_waitlist; + work_waitlist.clear(); + + for(worklistt::const_iterator w_it = iterate_work_waitlist.begin(); w_it != iterate_work_waitlist.end(); w_it++){ + worknodet waitlisted_worknode = *w_it; + + bool uncovered_successor = false; + + std::vector &waitlisted_worknode_successors = + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i = 0; i < waitlisted_worknode_successors.size(); i++){ + bool check_covered = false; + for(unsigned j = 0; j < covered_nodes.size(); j++){ + if(waitlisted_worknode_successors[i] == covered_nodes[j]){ + check_covered = true; + break; + } + } + if(!check_covered){ +#if 0 + std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " + << waitlisted_worknode_successors[i] << "\n"; +#endif + uncovered_successor = true; + break; + } + } + + if(!uncovered_successor){ + worklist.push_back(waitlisted_worknode); + } + else{ + work_waitlist.push_back(waitlisted_worknode); + } + + } + } + + /* the following code is to stop a warning; this function must + return from the first if-condition inside the while loop */ + std::cout << "check graph of the function: " << function_name << "\n"; + assert(false); + return dependency_set; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_completet::compute_summary_rec + ( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) +{ + // recursively compute summaries for function calls + return inline_summaries(function_name,dependency_set,counter); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_completet::check() +{ + solver_calls++; // for statistics + if(solver() == decision_proceduret::D_SATISFIABLE){ + //std::cout << "Solver <-- renamed info ~ SAT\n"; + return property_checkert::FAIL; + } + //std::cout << "Solver <-- renamed info ~ UNSAT\n"; + return property_checkert::UNKNOWN; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::debug_print() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::debug_print +( + const function_namet &function_name, + find_symbols_sett &dependency_set) +{ + std::cout << "DebugInfo: function -> " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it = dependency_set.begin(); + d_it != dependency_set.end(); d_it++){ + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h new file mode 100644 index 000000000..243120480 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -0,0 +1,64 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_BW_CEX_COMPLETE_H +#define CPROVER_SUMMARIZER_BW_CEX_COMPLETE_H + +#include +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_completet : public summarizer_bw_cex_baset +{ + public: + explicit summarizer_bw_cex_completet(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner, + _entry_function,_error_function), + solver(_solver) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + incremental_solvert &solver; + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); + +}; + + +#endif diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp new file mode 100644 index 000000000..f06da6367 --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -0,0 +1,614 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +//#define OPT_11 // simplify before pushing to solver +#define OPT_12 // collect, conjunct, simplify and then push to the solver + +//#define OPT_2 // a fresh solver each time + +//TODO: a bug in the fresh solver case; does not compute +//calling contexts (see struct tests in regression) + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_concrete.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const function_namet &function_name) +{ + exprt postcondition = true_exprt(); //initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec(function_name,summaryt::entry_call_site, + postcondition,true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (concrete)..." << eom; + error_assertion = _error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_concretet::check() +{ + //TODO: store information about why have UNKNOWN + // we have to distinguish the case when we cannot decide about spuriousness + + if(!summary_db.exists(entry_function)) + return property_checkert::UNKNOWN; + + const summaryt &summary = summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + return property_checkert::UNKNOWN; + + return property_checkert::FAIL; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA = ssa_db.get(function_name); + + summaryt summary; + if(summary_db.exists(function_name)) + summary = summary_db.get(function_name); + else + { + summary.params = SSA.params; + summary.globals_in = SSA.globals_in; + summary.globals_out = SSA.globals_out; + } + + // insert assertion + exprt end_guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition = implies_exprt(end_guard,_postcondition); + if(function_name == error_function) + { + postcondition = and_exprt(postcondition,not_exprt(error_assertion)); + } + + summary.bw_postcondition = _postcondition; + +#if 0 + debug() << "Postcondition: " << + from_expr(SSA.ns, "", postcondition) << eom; +#endif + + // recursively compute summaries for function calls + inline_summaries(function_name,SSA,summary, + postcondition,context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary(function_name,call_site,SSA,summary,summary,postcondition,context_sensitive); + + if(function_name == error_function) + summary.has_assertion = true; + + summary_db.set(function_name,summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out,SSA.ns); + debug() << out.str() << eom; + } + +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert* fresh_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + //incremental_solvert &solver = ssa_db.get_solver(function_name); + + incremental_solvert &solver = (*fresh_solver); +#else + incremental_solvert &solver = ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + //ENHANCE: we could reuse the solver state, but it's difficult + // (the function maybe called several times) + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + //add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false,assert_postcond,noassert_postcond,c); //backward summaries + assert_postcond.push_back(postcondition); //context + + //std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; + //std::cout << "Noassert Summary: " << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#if 0 + debug() << "Backward summaries: " << + from_expr(SSA.ns, "", conjunction(c)) << eom; +#endif + +#ifdef OPT_12 + store << SSA; +#else + solver << SSA; +#endif + + solver.new_context(); + + // assumptions must hold + for(local_SSAt::nodest::const_iterator + n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); + ++n_it) + for(local_SSAt::nodet::assumptionst::const_iterator + a_it = n_it->assumptions.begin(); + a_it != n_it->assumptions.end(); + ++a_it){ + + /* +#ifdef OPT_11 + solver << simplify_expr(*a_it, SSA.ns); +#elif OPT_12 + store.push_back(*a_it); +#else + solver << *a_it; +#endif + */ + +#ifdef OPT_11 + solver << simplify_expr(*a_it, SSA.ns); +#else +#ifdef OPT_12 + store.push_back(*a_it); +#else + solver << *a_it; +#endif +#endif + + } + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif + + /* +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#elif OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif + */ + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif + + exprt::operandst loophead_selects; + loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + + /* +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#elif OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif + */ + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif + +#ifdef OPT_12 + std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) << "\n\n\n"; + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + //statistics + solver_calls++; + + //solve + if(solver() != decision_proceduret::D_SATISFIABLE) + { + summary.error_summaries[call_site] = true_exprt(); //TODO: this is likely to be incomplete + summary.has_assertion = assertion_flag; + solver.pop_context(); + + /**/ + // if the summary is true, print the postcondition and the list of loops in this function + // this postcondition is modified, possibly twice, from what is returned by compute_calling_context2 + // pc = end_guard => original_pc, and + // pc = pc && not(assertion), if this is error function + std::cout << "==>>\n"; + std::cout << "==>> Summary: true\n"; + std::cout << "==>> Postcondition: " << from_expr(SSA.ns, "", postcondition) << "\n"; + std::cout << "==>> Function: " << function_name << "\n"; + std::cout << "==>> Loophead selects: " << from_expr(SSA.ns, "", conjunction(loophead_selects)) << "\n"; + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++){ + if(n_it->loophead!=SSA.nodes.end()){ + std::cout << "==>> Loop found: " << n_it->loophead->location->location_number << "\n"; + } + } + std::cout << "==>>\n"; + /**/ + return; + } + + //build error summary and add to summary + exprt::operandst var_values; + + for(local_SSAt::var_listt::const_iterator it = SSA.params.begin(); + it != SSA.params.end(); it++){ + exprt summ_value = solver.get(*it); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(*it, summ_value)); + } + + for(local_SSAt::var_sett::const_iterator it = SSA.globals_in.begin(); + it != SSA.globals_in.end(); it++){ + exprt summ_value = solver.get(*it); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(*it, summ_value)); + } + + for(local_SSAt::var_sett::const_iterator it = SSA.globals_out.begin(); + it != SSA.globals_out.end(); it++){ + exprt summ_value = solver.get(*it); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(*it, summ_value)); + } + + summary.error_summaries[call_site] = not_exprt(conjunction(var_values)); + summary.has_assertion = assertion_flag; + + solver.pop_context(); + +#ifdef OPT_2 + delete fresh_solver; +#endif + +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); + n_it != SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it = + n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); //no function pointers + + exprt postcondition_call = true_exprt(); + postcondition_call = compute_calling_context2( + function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); + + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname,summaryt::call_sitet(n_it->location), + postcondition_call,context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_concretet::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert* fresh_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + //incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver = (*fresh_solver); +#else + incremental_solvert &solver = ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + // collect globals at call site + std::map + cs_globals_out; + SSA.get_globals(n_it->location,cs_globals_out[f_it],false); + + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries(SSA,summaryt::call_sitet(n_it->location),false,assert_postcond,noassert_postcond,c); //backward summaries + assert_postcond.push_back(postcondition); //context + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#ifdef OPT_12 + store << SSA; +#else + solver << SSA; +#endif + + solver.new_context(); + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif + + /* +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#elif OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif + */ + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif + + exprt::operandst loophead_selects; + loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + + /* +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#elif OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif + */ + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif + +#ifdef OPT_12 + std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + + // build postcondition + exprt postcondition_call; + + if(solver() != decision_proceduret::D_SATISFIABLE) + { + postcondition_call = true_exprt(); //TODO: this is likely to be incomplete + solver.pop_context(); + return postcondition_call; + } + + bool result = solver()==decision_proceduret::D_SATISFIABLE; + assert(result); + + exprt::operandst postcond_values; + for(local_SSAt::var_sett::const_iterator it = cs_globals_out[f_it].begin(); + it != cs_globals_out[f_it].end(); it++) + { + exprt postc_value = solver.get(*it); + postcond_values.push_back(equal_exprt(*it, postc_value)); + } + postcondition_call = conjunction(postcond_values); + + solver.pop_context(); + + // get callee SSA and rename + local_SSAt &fSSA = ssa_db.get(fname); + ssa_inliner.rename_to_callee(f_it, fSSA.params, + cs_globals_out[f_it],fSSA.globals_out, + postcondition_call); + + debug() << "Backward calling context for " << + from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + //statistics + solver_calls++; + +#ifdef OPT_2 + delete fresh_solver; +#endif + + return not_exprt(postcondition_call); +} + + diff --git a/src/summarizer/summarizer_bw_cex_concrete.h b/src/summarizer/summarizer_bw_cex_concrete.h new file mode 100644 index 000000000..40b7177dd --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_concrete.h @@ -0,0 +1,77 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_BW_CEX_CONCRETE_H +#define CPROVER_SUMMARIZER_BW_CEX_CONCRETE_H + +#include +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_concretet : public summarizer_bw_cex_baset +{ + public: + explicit summarizer_bw_cex_concretet(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, + _ssa_unwinder,_ssa_inliner, + _entry_function,_error_function) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + virtual void compute_summary_rec(const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries(const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2(const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); + +}; + + +#endif diff --git a/src/summarizer/summarizer_bw_cex_wp.cpp b/src/summarizer/summarizer_bw_cex_wp.cpp new file mode 100644 index 000000000..37c629e0a --- /dev/null +++ b/src/summarizer/summarizer_bw_cex_wp.cpp @@ -0,0 +1,645 @@ +/*******************************************************************\ + +Module: Slicing-based WP Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" +#include "../ssa/ssa_dependency_graph.h" + +#include "summarizer_bw_cex_wp.h" + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize +( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec(entry_function,dependency_set,-1, + summaryt::entry_call_site); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (WP)..." << eom; + error_assertion = _error_assertion; + /* + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; + */ + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_wpt::inline_summaries +( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary) +{ + exprt::operandst slice; + + local_SSAt &SSA = ssa_db.get(function_name); + //solver << SSA.get_enabling_exprs(); + + exprt::operandst loophead_selects; + loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + exprt c = conjunction(loophead_selects); + + //std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" + // << "\t original info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; + + slice.push_back(c); + ssa_inliner.rename(c, counter); + +#if 0 + std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" + << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + solver << c; + + ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); + + struct worknodet{ + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index = 0; + start_node.dependency_set = dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()){ + + /* + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it != worklist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it != work_waitlist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + */ + + worknodet &worknode = worklist.front(); + + //std::cout << "working node: " << function_name << ": " << worknode.node_index << "\n"; + ///////////////////////////////////////////////////////////////////////////////////// + //std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + /* + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n\n\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + + // return if the top most node is reached + if(worknode.node_index == ssa_depgraph.top_node_index) + { + find_symbols_sett vars = worknode.dependency_set; + vars.insert(dependency_set.begin(), dependency_set.end()); + error_summary = simplify_summary(SSA.ns, conjunction(slice), vars); + return worknode.dependency_set; + } + + // modify worknode_dependency_set if the node is an assertion + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true){ + + //std::cout << "\t\t an assertion node\n"; + for(find_symbols_sett::const_iterator d_it = ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); + d_it != ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + + + } + + // if this is a function call + if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == true){ + //std::cout << "fcall: working node: " << function_name << ": " << worknode.node_index << "\n"; + irep_idt fname = + to_symbol_expr((to_function_application_expr(ssa_depgraph.depnodes_map[worknode.node_index].node_info)).function()).get_identifier(); + + find_symbols_sett renamed_dependencies; + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // detach the '@' symbol if there + irep_idt renamed_id = ssa_inliner.rename + (id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set = renamed_dependencies; + + if(!worknode.dependency_set.empty()){ + find_symbols_sett guard_dependencies; + find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + } + + ///////////////////////////////////////////////////////////////////////////////////// + /* + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; + */ + ///////////////////////////////////////////////////////////////////////////////////// + + worknode.dependency_set = + compute_summary_rec(fname,worknode.dependency_set, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter,summaryt::call_sitet(ssa_depgraph.depnodes_map[worknode.node_index].location)); + slice.push_back(ssa_depgraph.depnodes_map[worknode.node_index].node_info); + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // attach the '@' symbol if not already there + irep_idt renamed_id = ssa_inliner.rename + (id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, true); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set = renamed_dependencies; + + if(!worknode.dependency_set.empty()){ + find_symbols_sett guard_dependencies; + find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); + } + } + + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()){ + exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) + worknode_info = not_exprt(worknode_info); + + if(worknode.node_index != 0){ + if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)){ + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || + (worknode_info == error_assertion)){ + /* + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + */ + slice.push_back(worknode_info); + ssa_inliner.rename(worknode_info, counter); +#if 0 + std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; +#endif + solver << worknode_info; + } + } + else{ + exprt guard_binding = ssa_depgraph.depnodes_map[worknode.node_index].guard; + /* + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; + */ + ssa_inliner.rename(guard_binding, counter); +#if 0 + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; +#endif + solver << guard_binding; + slice.push_back(guard_binding); + } + } + } + + // if not a function call and the dependency set is non-empty + if((ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == false) && + (!worknode.dependency_set.empty())){ + + exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) + worknode_info = not_exprt(worknode_info); + + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || + (worknode_info == error_assertion)){ + worknode.dependency_set = + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols; + } + } + + for(ssa_dependency_grapht::annotated_predecessorst::const_iterator + p_it = ssa_depgraph.depnodes_map[worknode.node_index].predecessors.begin(); + p_it != ssa_depgraph.depnodes_map[worknode.node_index].predecessors.end(); + p_it++){ + + ssa_dependency_grapht::annotated_predecessort pred = *p_it; + int pred_node_index = pred.predecessor_node_index; + find_symbols_sett pred_annotation = pred.annotation; + + bool dependencies_merged = false; + for(worklistt::iterator w_it = work_waitlist.begin(); w_it != work_waitlist.end(); w_it++){ + if(w_it->node_index == pred_node_index){ + + dependencies_merged = true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ + if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged == false){ + worknodet new_worknode; + new_worknode.node_index = pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#if 0 + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it != worklist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it != work_waitlist.end(); w_it++){ + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#if 0 + std::cout << function_name << ": covered : "; + for(int l = 0; l < covered_nodes.size(); l++){ + std::cout << covered_nodes[l] << " "; + } + std::cout << "\n"; +#endif + + worklistt iterate_work_waitlist = work_waitlist; + work_waitlist.clear(); + + for(worklistt::const_iterator w_it = iterate_work_waitlist.begin(); w_it != iterate_work_waitlist.end(); w_it++){ + worknodet waitlisted_worknode = *w_it; + + bool uncovered_successor = false; + + std::vector &waitlisted_worknode_successors = + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i = 0; i < waitlisted_worknode_successors.size(); i++){ + bool check_covered = false; + for(unsigned j = 0; j < covered_nodes.size(); j++){ + if(waitlisted_worknode_successors[i] == covered_nodes[j]){ + check_covered = true; + break; + } + } + if(!check_covered){ +#if 0 + std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " + << waitlisted_worknode_successors[i] << "\n"; +#endif + uncovered_successor = true; + break; + } + } + + if(!uncovered_successor){ + worklist.push_back(waitlisted_worknode); + } + else{ + work_waitlist.push_back(waitlisted_worknode); + } + + } + } + + /* the following code is to stop a warning; this function must + return from the first if-condition inside the while loop */ + std::cout << "check graph of the function: " << function_name << "\n"; + assert(false); + return dependency_set; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_wpt::compute_summary_rec + ( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + const summaryt::call_sitet &call_site) +{ + local_SSAt &SSA = ssa_db.get(function_name); + summaryt summary; + if(summary_db.exists(function_name)) + summary = summary_db.get(function_name); + else + { + summary.params = SSA.params; + summary.globals_in = SSA.globals_in; + summary.globals_out = SSA.globals_out; + } + // recursively compute summaries for function calls + find_symbols_sett new_dependency_set = + inline_summaries(function_name,dependency_set,counter, + summary.error_summaries[call_site]); + + summary_db.set(function_name,summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out,SSA.ns); + debug() << out.str() << eom; + } + + return new_dependency_set; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_wpt::check() +{ + solver_calls++; // for statistics + if(solver() == decision_proceduret::D_SATISFIABLE){ + //std::cout << "Solver <-- renamed info ~ SAT\n"; + return property_checkert::FAIL; + } + //std::cout << "Solver <-- renamed info ~ UNSAT\n"; + return property_checkert::UNKNOWN; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::debug_print() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::debug_print +( + const function_namet &function_name, + find_symbols_sett &dependency_set) +{ + std::cout << "DebugInfo: function -> " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it = dependency_set.begin(); + d_it != dependency_set.end(); d_it++){ + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::simplify_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ +void summarizer_bw_cex_wpt::simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr) +{ + if(expr.id()==ID_equal) + { + replace_map[expr.op0()] = expr.op1(); + return; + } + forall_operands(it, expr) + simplify_summary_build_map(replace_map, *it); +} +bool summarizer_bw_cex_wpt::simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr) +{ + if(expr.id()==ID_function_application) + { + bool result = true; + exprt::operandst &args = to_function_application_expr(expr).arguments(); + for(size_t i=0;i +#include +#include +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_wpt : public summarizer_bw_cex_baset +{ + public: + explicit summarizer_bw_cex_wpt(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner, + _entry_function,_error_function), + solver(_solver) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + incremental_solvert &solver; + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + const summaryt::call_sitet &call_site); + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); + + exprt simplify_summary(const namespacet &ns, + exprt summary, + const find_symbols_sett &vars); + void simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr); + bool simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr); + void simplify_summary_cleanup( + const find_symbols_sett &vars, exprt &expr); + +}; + + +#endif diff --git a/src/summarizer/summarizer_fw_contexts.cpp b/src/summarizer/summarizer_fw_contexts.cpp new file mode 100644 index 000000000..0a78d403e --- /dev/null +++ b/src/summarizer/summarizer_fw_contexts.cpp @@ -0,0 +1,174 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis with Calling Context output + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "summarizer_fw_contexts.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + +/*******************************************************************\ + +Function: summarizer_fw_contextst::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::summarize() +{ + exprt precondition = true_exprt(); //initial calling context + for(functionst::const_iterator it = ssa_db.functions().begin(); + it!=ssa_db.functions().end(); it++) + { + if(excluded_functions.find(it->first)!=excluded_functions.end()) + continue; + status() << "\nSummarizing function " << it->first << eom; + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) + compute_summary_rec(it->first,precondition,false); + else status() << "Summary for function " << it->first << + " exists already" << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_fw_contextst::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, const exprt &precondition, + bool context_sensitive) +{ + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it = + n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); //no function pointers + if(!check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) + { + continue; + } + + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + if(excluded_functions.find(fname)!=excluded_functions.end()) + { + exprt precondition_call = compute_calling_context( + function_name,SSA,n_it,f_it,precondition,true); + + // output calling context + switch(ui) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml_cc("calling-context"); + xml_cc.set_attribute("function",id2string(fname)); + xml_cc.set_attribute("goto_location", + i2string(n_it->location->location_number)); + + //location + const source_locationt &source_location = + n_it->location->source_location; + xmlt xml_location; + if(source_location.is_not_nil() && source_location.get_file()!="") + xml_location=xml(source_location); + if(xml_location.name!="") + xml_cc.new_element().swap(xml_location); + + //argument ranges + xmlt xml_args("argument-ranges"); + assert(precondition_call.operands().size()%2==0); + for(unsigned i=0;i +#include +#include + +#include "summarizer_fw.h" + +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/local_ssa.h" +#include "ssa_db.h" + +#include + +class summarizer_fw_contextst : public summarizer_fwt +{ + public: + explicit summarizer_fw_contextst(optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner) : + summarizer_fwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner), + ui(ui_message_handlert::PLAIN) + { + if(_options.get_bool_option("xml-ui")) + ui = ui_message_handlert::XML_UI; + + optionst::value_listt _excluded_functions = + _options.get_list_option("do-not-analyze-functions"); + excluded_functions.insert(_excluded_functions.begin(), + _excluded_functions.end()); + + } + + virtual void summarize(); + + protected: + language_uit::uit ui; // use gui format + std::set excluded_functions; + + virtual void inline_summaries(const function_namet &function_name, + local_SSAt &SSA, + const exprt &precondition, + bool context_sensitive); +}; + + +#endif diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp new file mode 100644 index 000000000..7dba7d8e1 --- /dev/null +++ b/src/summarizer/summarizer_parse_options.cpp @@ -0,0 +1,1380 @@ +/*******************************************************************\ + +Module: Summarizer Command Line Options Processing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "array_abstraction.h" + +#include + +#include + +#include +#include "version.h" + +#include "../ssa/malloc_ssa.h" + +#include "summarizer_parse_options.h" +#include "summary_db.h" +#include "summary_checker_ai.h" +#include "summary_checker_bmc.h" +#include "summary_checker_kind.h" +#include "../ssa/split_loopheads.h" +#include "show.h" + +#define UNWIND_GOTO_INTO_LOOP 1 +#define REMOVE_MULTIPLE_DEREFERENCES 1 + +/*******************************************************************\ + +Function: summarizer_parse_optionst::summarizer_parse_optionst + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +summarizer_parse_optionst::summarizer_parse_optionst(int argc, const char **argv): + parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), + language_uit("Summarizer " CBMC_VERSION, cmdline) +{ +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::eval_verbosity + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::eval_verbosity() +{ + // this is our default verbosity + int v=messaget::M_STATISTICS; + + if(cmdline.isset("verbosity")) + { + v=unsafe_string2int(cmdline.get_value("verbosity")); + if(v<0) + v=0; + else if(v>10) + v=10; + } + + ui_message_handler.set_verbosity(v); +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::get_command_line_options + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::get_command_line_options(optionst &options) +{ + if(config.set(cmdline)) + { + usage_error(); + exit(1); + } + + if(cmdline.isset("xml-ui")) + options.set_option("xml-ui", true); + + if(cmdline.isset("debug-level")) + options.set_option("debug-level", cmdline.get_value("debug-level")); + + if(cmdline.isset("unwindset")) + options.set_option("unwindset", cmdline.get_value("unwindset")); + + if(cmdline.isset("unwind")) + options.set_option("unwind", cmdline.get_value("unwind")); + + if(cmdline.isset("inline-partial")) + options.set_option("inline-partial", cmdline.get_value("inline-partial")); + + if(cmdline.isset("inline")) + options.set_option("inline", true); + + if(cmdline.isset("spurious-check")) + options.set_option("spurious-check", cmdline.get_value("spurious-check")); + else + options.set_option("spurious-check", "all"); + + if(cmdline.isset("slice") && cmdline.isset("inline")) + options.set_option("slice", true); + else + options.set_option("slice", false); + + // check array bounds + if(cmdline.isset("bounds-check")) + options.set_option("bounds-check", true); + else + options.set_option("bounds-check", false); + + // check division by zero + if(cmdline.isset("div-by-zero-check")) + options.set_option("div-by-zero-check", true); + else + options.set_option("div-by-zero-check", false); + + // check overflow/underflow + if(cmdline.isset("signed-overflow-check")) + options.set_option("signed-overflow-check", true); + else + options.set_option("signed-overflow-check", false); + + // check overflow/underflow + if(cmdline.isset("unsigned-overflow-check")) + options.set_option("unsigned-overflow-check", true); + else + options.set_option("unsigned-overflow-check", false); + + // check overflow on floats + if(cmdline.isset("float-overflow-check")) + options.set_option("float-overflow-check", true); + else + options.set_option("float-overflow-check", false); + + // check for NaN (not a number) + if(cmdline.isset("nan-check")) + options.set_option("nan-check", true); + else + options.set_option("nan-check", false); + + // check pointers + if(cmdline.isset("pointer-check")) + options.set_option("pointer-check", true); + else + options.set_option("pointer-check", false); + + // check for memory leaks + if(cmdline.isset("memory-leak-check")) + options.set_option("memory-leak-check", true); + else + options.set_option("memory-leak-check", false); + + // check assertions + if(cmdline.isset("no-assertions")) + options.set_option("assertions", false); + else + options.set_option("assertions", true); + + // SSA equality propagation + if(cmdline.isset("ssa-propagation")) + options.set_option("ssa-propagation", true); + else + options.set_option("ssa-propagation", false); + + // use assumptions + if(cmdline.isset("no-assumptions")) + options.set_option("assumptions", false); + else + options.set_option("assumptions", true); + + // use arithmetic refinements + if(cmdline.isset("refine")) + options.set_option("refine", true); + else + options.set_option("refine", false); + + if(cmdline.isset("unit-check")) + options.set_option("unit-check", true); + else + options.set_option("unit-check", false); + + // compute standard invariants (include value at loop entry) + if(cmdline.isset("std-invariants")) + options.set_option("std-invariants", true); + else + options.set_option("std-invariants", false); + + // magic error label + if(cmdline.isset("error-label")) + options.set_option("error-label", cmdline.get_value("error-label")); + + if(cmdline.isset("havoc")) + options.set_option("havoc", true); + else if(cmdline.isset("equalities")) + { + options.set_option("equalities", true); + options.set_option("std-invariants", true); + } + else + { + if(cmdline.isset("zones")) + options.set_option("zones", true); + else if(cmdline.isset("qzones")) + options.set_option("qzones", true); + else if(cmdline.isset("octagons")) + options.set_option("octagons", true); + else //if(cmdline.isset("intervals")) //default + options.set_option("intervals", true); + + if(cmdline.isset("enum-solver")) + options.set_option("enum-solver", true); + else //if(cmdline.isset("binsearch-solver")) //default + options.set_option("binsearch-solver", true); + } + + // use incremental assertion checks + if(cmdline.isset("non-incremental")) + options.set_option("incremental", false); + else + options.set_option("incremental", true); + + // compute preconditions + if(cmdline.isset("preconditions")) + options.set_option("preconditions", true); + + // compute necessary/sufficient preconditions + if(cmdline.isset("sufficient")) + options.set_option("sufficient", true); + + // do k-induction refinement + if(cmdline.isset("k-induction")) + { + options.set_option("std-invariants", true); + options.set_option("k-induction", true); +// options.set_option("inline", true); + if(!cmdline.isset("unwind")) + options.set_option("unwind",UINT_MAX); + } + + // do incremental bmc + if(cmdline.isset("incremental-bmc")) + { + options.set_option("incremental-bmc", true); +// options.set_option("inline", true); + options.set_option("havoc", true); + if(!cmdline.isset("unwind")) + options.set_option("unwind",UINT_MAX); + } + + // all properties (default) + if(cmdline.isset("no-all-properties")) + options.set_option("all-properties", false); + else + options.set_option("all-properties", true); + + // competition mode + if(cmdline.isset("competition-mode")) + { + options.set_option("competition-mode", true); + options.set_option("all-properties", false); + } + + // instrumentation / output + if(cmdline.isset("instrument-output")) + options.set_option("instrument-output", + cmdline.get_value("instrument-output")); + + +#ifdef SHOW_CALLING_CONTEXTS + if(cmdline.isset("show-calling-contexts")) + { + if(!options.get_bool_option("intervals")) + throw "--show-calling-contexts only possible with --intervals"; + options.set_option("show-calling-contexts", true); + options.set_option("do-not-analyze-functions", + cmdline.get_value("show-calling-contexts")); + } +#endif +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::doit + + Inputs: + + Outputs: + + Purpose: invoke main modules + +\*******************************************************************/ + +int summarizer_parse_optionst::doit() +{ + if(cmdline.isset("version")) + { + std::cout << SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << std::endl; + return 0; + } + + // + // command line options + // + + optionst options; + get_command_line_options(options); + + eval_verbosity(); + + // + // Print a banner + // + status() << "SUMMARIZER version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; + + register_language(new_ansi_c_language); + register_language(new_cpp_language); + + goto_modelt goto_model; + + if(get_goto_program(options, goto_model)) + return 6; + + if(cmdline.isset("show-stats")) + { + show_stats(goto_model, std::cout); + return 7; + } + + // options for various debug outputs + + if(cmdline.isset("show-ssa")) + { + bool simplify=!cmdline.isset("no-simplify"); + irep_idt function=cmdline.get_value("function"); + show_ssa(goto_model, function, simplify, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-fixed-points")) + { + bool simplify=!cmdline.isset("no-simplify"); + irep_idt function=cmdline.get_value("function"); + show_fixed_points(goto_model, function, simplify, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-defs")) + { + irep_idt function=cmdline.get_value("function"); + show_defs(goto_model, function, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-assignments")) + { + irep_idt function=cmdline.get_value("function"); + show_assignments(goto_model, function, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-guards")) + { + irep_idt function=cmdline.get_value("function"); + show_guards(goto_model, function, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-value-sets")) + { + irep_idt function=cmdline.get_value("function"); + show_value_sets(goto_model, function, std::cout, ui_message_handler); + return 7; + } + + if(cmdline.isset("show-invariants")) + { + options.set_option("show-invariants", true); + } + + if(cmdline.isset("context-sensitive")) + { + options.set_option("context-sensitive", true); + status() << "Context-sensitive analysis from " << + goto_model.goto_functions.entry_point() << eom; + } + + if(cmdline.isset("arrays")) + { + options.set_option("arrays", true); + status() << "Do not ignore array contents" << eom; + } + + //TODO: check option inconsistencies, ignored options etc + if(options.get_bool_option("havoc")) + status() << "Havocking loops and function calls" << eom; + else if(options.get_bool_option("equalities")) + status() << "Using (dis)equalities domain" << eom; + else + { + if(options.get_bool_option("intervals")) + status() << "Using intervals domain"; + else if(options.get_bool_option("zones")) + status() << "Using zones domain"; + else if(options.get_bool_option("octagons")) + status() << "Using octagons domain"; + else assert(false); + if(options.get_bool_option("enum-solver")) + status() << " with enumeration solver"; + else if(options.get_bool_option("binsearch-solver")) + status() << " with binary search solver"; + else assert(false); + status() << eom; + } + + try + { + summary_checker_baset *summary_checker = NULL; + if(!options.get_bool_option("k-induction") && + !options.get_bool_option("incremental-bmc")) + summary_checker = new summary_checker_ait(options); + if(options.get_bool_option("k-induction") && + !options.get_bool_option("incremental-bmc")) + summary_checker = new summary_checker_kindt(options); + if(!options.get_bool_option("k-induction") && + options.get_bool_option("incremental-bmc")) + summary_checker = new summary_checker_bmct(options); + + summary_checker->set_message_handler(get_message_handler()); + summary_checker->simplify=!cmdline.isset("no-simplify"); + summary_checker->fixed_point=!cmdline.isset("no-fixed-point"); + + int retval; + if(cmdline.isset("show-vcc")) + { + std::cout << "VERIFICATION CONDITIONS:\n\n"; + summary_checker->show_vcc=true; + (*summary_checker)(goto_model); + retval = 0; + goto clean_up; + } + + // do actual analysis + switch((*summary_checker)(goto_model)) + { + case property_checkert::PASS: + report_properties(goto_model, summary_checker->property_map); + report_success(); + retval = 0; + break; + + case property_checkert::FAIL: + report_properties(goto_model, summary_checker->property_map); + report_failure(); + retval = 10; + break; + + case property_checkert::UNKNOWN: + retval = 5; + if(options.get_bool_option("preconditions")) + goto clean_up; + report_properties(goto_model, summary_checker->property_map); + report_unknown(); + break; + + default: + assert(false); + } + + if(cmdline.isset("instrument-output")) + { + summary_checker->instrument_and_output(goto_model); + } + + clean_up: + delete summary_checker; + return retval; + } + + catch(const std::string error_msg) + { + error() << error_msg << messaget::eom; + return 8; + } + + catch(const char *error_msg) + { + error() << error_msg << messaget::eom; + return 8; + } + + #if 0 + // let's log some more statistics + debug() << "Memory consumption:" << messaget::endl; + memory_info(debug()); + debug() << eom; + #endif +} + + + +void summarizer_parse_optionst::type_stats_rec( + const typet &type, + expr_statst &stats, + const namespacet &ns) +{ + + if(type.id()==ID_symbol) + type_stats_rec(ns.follow(type), stats, ns); + + if(type.id()==ID_pointer || type.id()==ID_array) + { + stats.has_array=true; + + const typet &subtype=ns.follow(type.subtype()); + + if(subtype.id()==ID_signedbv || + subtype.id()==ID_unsignedbv) + { + stats.has_string=(to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); + } + } + + if(type.has_subtypes()) + { + forall_subtypes(it, type) + { + type_stats_rec(*it, stats, ns); + } + } +} + + +/*******************************************************************\ + +Function: summarizer_parse_optionst::expr_stats_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + + +void summarizer_parse_optionst::expr_stats_rec( + const exprt &expr, + expr_statst &stats) +{ + + if(expr.id()==ID_side_effect) + { + const side_effect_exprt &side_effect_expr=to_side_effect_expr(expr); + const irep_idt &statement=side_effect_expr.get_statement(); + + if(statement==ID_malloc) + { + stats.has_malloc=true; + } + else if(statement==ID_nondet) + { + // done in statet:instantiate_rec + } + } + + if(expr.id()==ID_symbol ) + { + + } + + if(expr.has_operands()) + { + forall_operands(it, expr) + { + expr_stats_rec(*it, stats); + } + } +} + + +/*******************************************************************\ + +Function: summarizer_parse_optionst::show_stats + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, + std::ostream &out) +{ + + const namespacet ns(goto_model.symbol_table); + + expr_statst stats; + + unsigned nr_instructions=0; + unsigned nr_functions=0; + unsigned nr_loops=0; + + // analyze all the functions + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(!f_it->second.body_available()) continue; + + ++nr_functions; + + const goto_programt &goto_program=f_it->second.body; + +#if 0 + statistics() << "function size of " << f_it->first << ": " + << goto_program.instructions.size() << eom; +#endif + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + nr_instructions++; + const goto_programt::instructiont &instruction=*i_it; + + if(i_it->is_backwards_goto()) nr_loops++; + + switch(instruction.type) + { + case ASSIGN: + { + const code_assignt &assign=to_code_assign(instruction.code); + expr_stats_rec(assign.lhs(), stats); + expr_stats_rec(assign.rhs(), stats); + } + break; + case ASSUME: + expr_stats_rec(instruction.guard, stats); + break; + case ASSERT: + expr_stats_rec(instruction.guard, stats); + break; + case GOTO: + expr_stats_rec(instruction.guard, stats); + break; + + case DECL: + // someone declaring an array + type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); + + break; + + default: + // skip + break; + } // switch + } // forall instructions + } // forall functions + + out << " =============== STATS =============== " << std::endl; + out << " nr of instructions: " << nr_instructions << std::endl; + out << " nr of functions: " << nr_functions << std::endl; + out << " nr of loops: " << nr_loops << std::endl; + out << " malloc: " << (stats.has_malloc ? "YES" : "NO") << std::endl; + out << " arrays: " << (stats.has_array ? "YES" : "NO") << std::endl; + out << " strings: " << (stats.has_string ? "YES" : "NO") << std::endl; +} + + +/*******************************************************************\ + +Function: summarizer_parse_optionst::set_properties + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool summarizer_parse_optionst::set_properties(goto_modelt &goto_model) +{ + try + { + if(cmdline.isset("property")) + ::set_properties(goto_model, cmdline.get_values("property")); + } + + catch(const char *e) + { + error() << e << eom; + return true; + } + + catch(const std::string e) + { + error() << e << eom; + return true; + } + + catch(int) + { + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::get_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool summarizer_parse_optionst::get_goto_program( + const optionst &options, + goto_modelt &goto_model) +{ + if(cmdline.args.size()==0) + { + error() << "Please provide a program to verify" << eom; + return true; + } + + try + { + if(cmdline.args.size()==1 && + is_goto_binary(cmdline.args[0])) + { + status() << "Reading GOTO program from file" << eom; + + if(read_goto_binary(cmdline.args[0], + goto_model, get_message_handler())) + return true; + + config.set_from_symbol_table(goto_model.symbol_table); + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + irep_idt entry_point=goto_model.goto_functions.entry_point(); + + if(goto_model.symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) + { + error() << "The goto binary has no entry point; please complete linking" << eom; + return true; + } + } + else if(cmdline.isset("show-parse-tree")) + { + if(cmdline.args.size()!=1) + { + error() << "Please give one source file only" << eom; + return true; + } + + std::string filename=cmdline.args[0]; + + #ifdef _MSC_VER + std::ifstream infile(widen(filename).c_str()); + #else + std::ifstream infile(filename.c_str()); + #endif + + if(!infile) + { + error() << "failed to open input file `" << filename << "'" << eom; + return true; + } + + languaget *language=get_language_from_filename(filename); + + if(language==NULL) + { + error() << "failed to figure out type of file `" << filename << "'" << eom; + return true; + } + + language->set_message_handler(get_message_handler()); + + status("Parsing", filename); + + if(language->parse(infile, filename)) + { + error() << "PARSING ERROR" << eom; + return true; + } + + language->show_parse(std::cout); + return true; + } + else + { + + if(parse()) return true; + if(typecheck()) return true; + if(final()) return true; + + // we no longer need any parse trees or language files + clear_parse(); + + if(cmdline.isset("show-symbol-table")) + { + show_symbol_table(); + return true; + } + + irep_idt entry_point=goto_model.goto_functions.entry_point(); + + if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) + { + error() << "No entry point; please provide a main function" << eom; + return true; + } + + status() << "Generating GOTO Program" << eom; + + goto_convert(symbol_table, goto_model, ui_message_handler); + } + + // finally add the library + status() << "Adding CPROVER library" << eom; + link_to_library(goto_model, ui_message_handler); + + if(process_goto_program(options, goto_model)) + return true; + } + + catch(const char *e) + { + error() << e << eom; + return true; + } + + catch(const std::string e) + { + error() << e << eom; + return true; + } + + catch(int) + { + return true; + } + + catch(std::bad_alloc) + { + error() << "Out of memory" << eom; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::process_goto_program + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool summarizer_parse_optionst::process_goto_program( + const optionst &options, + goto_modelt &goto_model) +{ + try + { + // do partial inlining + if(options.get_bool_option("inline-partial")) + { + unsigned limit = options.get_unsigned_int_option("inline-partial"); + status() << "Performing partial inlining (" << limit << ")" << eom; + goto_partial_inline(goto_model, ui_message_handler, limit/2); + //TODO: where is limit multiplied by 2??? + + //remove inlined functions + Forall_goto_functions(f_it, goto_model.goto_functions) + if(f_it->first!=ID__start && + f_it->second.body.instructions.size()<=2*(limit/2)) + { + f_it->second.body.clear(); + } + } + + // add generic checks + status() << "Generic Property Instrumentation" << eom; + goto_check(options, goto_model); + + status() << "Function Pointer Removal" << eom; + remove_function_pointers( + goto_model, cmdline.isset("pointer-check")); + + // remove returns (must be done after function pointer removal) + remove_returns(goto_model); + + split_loopheads(goto_model); + + // recalculate numbers, etc. + goto_model.goto_functions.update(); + + // add loop ids + goto_model.goto_functions.compute_loop_numbers(); + + // if we aim to cover, replace + // all assertions by false to prevent simplification + if(cmdline.isset("cover-assertions")) + make_assertions_false(goto_model); + + // show it? + if(cmdline.isset("show-loops")) + { + show_loop_ids(get_ui(), goto_model); + return true; + } + +#if UNWIND_GOTO_INTO_LOOP + goto_unwind(goto_model,2); +#endif + + // now do full inlining, if requested + if(options.get_bool_option("inline")) + { + status() << "Performing full inlining" << eom; + goto_inline(goto_model, ui_message_handler); + } + + //inline __CPROVER_initialize and main + if(cmdline.isset("inline-main")) + { + inline_main(goto_model); + } + + if(!cmdline.isset("no-propagation")) + { + status() << "Constant Propagation" << eom; + propagate_constants(goto_model); + } + + //explicitly initialize all local variables + nondet_locals(goto_model); + +#if 1 + //TODO: find a better place for that + replace_malloc(goto_model,""); +#endif + +#if REMOVE_MULTIPLE_DEREFERENCES + remove_multiple_dereferences(goto_model); +#endif + + // do array abstraction + if(cmdline.isset("array-abstraction")) + { + status() << "Performing array abstraction" << eom; + array_abstraction(goto_model.symbol_table, ui_message_handler, + goto_model.goto_functions); + } + + label_properties(goto_model); + + if(cmdline.isset("show-properties")) + { + show_properties(goto_model, get_ui()); + return true; + } + + if(set_properties(goto_model)) + return true; + + // show it? + if(cmdline.isset("show-goto-functions")) + { + const namespacet ns(goto_model.symbol_table); + goto_model.goto_functions.output(ns, std::cout); + return true; + } + } + + catch(const char *e) + { + error() << e << eom; + return true; + } + + catch(const std::string e) + { + error() << e << eom; + return true; + } + + catch(int) + { + return true; + } + + catch(std::bad_alloc) + { + error() << "Out of memory" << eom; + return true; + } + + return false; +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::report_properties + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::report_properties( + const goto_modelt &goto_model, + const property_checkert::property_mapt &property_map) +{ + for(property_checkert::property_mapt::const_iterator + it=property_map.begin(); + it!=property_map.end(); + it++) + { + if(it->first=="") //TODO: some properties do not show up in initialize_property_map + continue; + + if(get_ui()==ui_message_handlert::XML_UI) + { + xmlt xml_result("result"); + xml_result.set_attribute("property", id2string(it->first)); + xml_result.set_attribute("status", property_checkert::as_string(it->second.result)); + std::cout << xml_result << "\n"; + } + else + { + status() << "[" << it->first << "] " + << it->second.location->source_location.get_comment() + << ": " + << property_checkert::as_string(it->second.result) + << eom; + } + + if(cmdline.isset("show-trace") && + it->second.result==property_checkert::FAIL) + show_counterexample(goto_model, it->second.error_trace); + } + + if(!cmdline.isset("property")) + { + status() << eom; + + unsigned failed=0; + unsigned unknown=0; + + for(property_checkert::property_mapt::const_iterator + it=property_map.begin(); + it!=property_map.end(); + it++) + { + if(it->second.result==property_checkert::UNKNOWN) + unknown++; + if(it->second.result==property_checkert::FAIL) + failed++; + } + + status() << "** " << unknown + << " of " << property_map.size() << " unknown" + << eom; + status() << "** " << failed + << " of " << property_map.size() << " failed" + << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::report_success + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::report_success() +{ + result() << "VERIFICATION SUCCESSFUL" << eom; + + switch(get_ui()) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml("cprover-status"); + xml.data="SUCCESS"; + std::cout << xml; + std::cout << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::show_counterexample + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::show_counterexample( + const goto_modelt &goto_model, + const goto_tracet &error_trace) +{ + const namespacet ns(goto_model.symbol_table); + + switch(get_ui()) + { + case ui_message_handlert::PLAIN: + std::cout << std::endl << "Counterexample:" << std::endl; + show_goto_trace(std::cout, ns, error_trace); + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml; + convert(ns, error_trace, xml); + std::cout << xml << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::report_failure + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::report_failure() +{ + result() << "VERIFICATION FAILED" << eom; + + switch(get_ui()) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml("cprover-status"); + xml.data="FAILURE"; + std::cout << xml; + std::cout << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::report_unknown + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::report_unknown() +{ + result() << "VERIFICATION INCONCLUSIVE" << eom; + + switch(get_ui()) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml("cprover-status"); + xml.data="UNKNOWN"; + std::cout << xml; + std::cout << std::endl; + } + break; + + default: + assert(false); + } +} + +/*******************************************************************\ + +Function: summarizer_parse_optionst::help + + Inputs: + + Outputs: + + Purpose: display command line help + +\*******************************************************************/ + +void summarizer_parse_optionst::help() +{ + std::cout << + "\n" + "* * Summarizer " SUMMARIZER_VERSION " - Copyright (C) 2014 * *\n" + "* * (based on CBMC " CBMC_VERSION " "; + + std::cout << "(" << (sizeof(void *)*8) << "-bit version))"; + + std::cout << " * *\n"; + + std::cout << + "* * Daniel Kroening * *\n" + "* * University of Oxford * *\n" + "* * kroening@kroening.com * *\n" + "\n" + "Usage: Purpose:\n" + "\n" + " summarizer [-?] [-h] [--help] show help\n" + " summarizer file.c ... source file names\n" + "\n" + "Frontend options:\n" + " -I path set include path (C/C++)\n" + " -D macro define preprocessor macro (C/C++)\n" + " --preprocess stop after preprocessing\n" + " --16, --32, --64 set width of int\n" + " --LP64, --ILP64, --LLP64,\n" + " --ILP32, --LP32 set width of int, long and pointers\n" + " --little-endian allow little-endian word-byte conversions\n" + " --big-endian allow big-endian word-byte conversions\n" + " --unsigned-char make \"char\" unsigned by default\n" + " --show-stats show statistics about program\n" + " --show-parse-tree show parse tree\n" + " --show-symbol-table show symbol table\n" + " --show-goto-functions show goto program\n" + " --arch set architecture (default: " + << configt::this_architecture() << ")\n" + " --os set operating system (default: " + << configt::this_operating_system() << ")\n" + #ifdef _WIN32 + " --gcc use GCC as preprocessor\n" + #endif + " --no-arch don't set up an architecture\n" + " --no-library disable built-in abstract C library\n" + " --round-to-nearest IEEE floating point rounding mode (default)\n" + " --round-to-plus-inf IEEE floating point rounding mode\n" + " --round-to-minus-inf IEEE floating point rounding mode\n" + " --round-to-zero IEEE floating point rounding mode\n" + "\n" + "Program instrumentation options:\n" + " --bounds-check enable array bounds checks\n" + " --pointer-check enable pointer checks\n" + " --array-abstraction use array and string abstraction for bounds checks\n" + " --div-by-zero-check enable division by zero checks\n" + " --memory-leak-check enable memory leak checks\n" + " --signed-overflow-check enable arithmetic over- and underflow checks\n" + " --unsigned-overflow-check enable arithmetic over- and underflow checks\n" + " --nan-check check floating-point for NaN\n" + " --error-label label check that label is unreachable\n" + " --show-properties show the properties\n" + " --no-assertions ignore user assertions\n" + " --no-assumptions ignore user assumptions\n" + " --inline inline all functions into main\n" + " --inline-partial nr inline functions smaller than the given nr of instructions\n" + " --instrument-output f output inferred information in goto-binary f\n" + "\n" + "Backend options:\n" + " --k-induction use k-induction\n" + " --unit-check check each function (similar to a unit test)\n" + " --incremental-bmc use incremental-bmc\n" + " --preconditions compute preconditions\n" + " --sufficient sufficient preconditions (default: necessary)\n" + " --context-sensitive context-sensitive analysis from entry point\n" + " --havoc havoc loops and function calls\n" + " --intervals use interval domain\n" + " --equalities use equalities and disequalities domain\n" + " --zones use zone domain\n" + " --octagons use octagon domain\n" + " --enum-solver use solver based on model enumeration\n" + " --binsearch-solver use solver based on binary search\n" + " --arrays do not ignore array contents\n" + "\n" + "Other options:\n" + " --version show version and exit\n" + " --xml-ui use XML-formatted output\n" + "\n"; +} diff --git a/src/summarizer/summarizer_parse_options.h b/src/summarizer/summarizer_parse_options.h new file mode 100644 index 000000000..542817efa --- /dev/null +++ b/src/summarizer/summarizer_parse_options.h @@ -0,0 +1,142 @@ +/*******************************************************************\ + +Module: Command Line Parsing + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARIZER_PARSE_OPTIONS_H +#define CPROVER_SUMMARIZER_PARSE_OPTIONS_H + +#include +#include + +#include + +#include + +class goto_modelt; +class optionst; + +#include "summary_checker_base.h" + +#define SUMMARIZER_OPTIONS \ + "(xml-ui)" \ + "(function):" \ + "D:I:" \ + "(depth):(context-bound):(unwind):" \ + "(bounds-check)(pointer-check)(div-by-zero-check)(memory-leak-check)" \ + "(signed-overflow-check)(unsigned-overflow-check)" \ + "(float-overflow-check)(nan-check)" \ + "(array-abstraction)(refine)" \ + "(non-incremental)" \ + "(no-assertions)(no-assumptions)" \ + "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ + "(little-endian)(big-endian)" \ + "(error-label):(verbosity):(no-library)" \ + "(version)" \ + "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)(gcc)" \ + "(ppc-macos)(unsigned-char)" \ + "(havoc)(intervals)(zones)(octagons)(equalities)(qzones)"\ + "(enum-solver)(binsearch-solver)(arrays)"\ + "(string-abstraction)(no-arch)(arch):(floatbv)(fixedbv)" \ + "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ + "(inline)(inline-main)(inline-partial):" \ + "(context-sensitive)" \ + "(preconditions)(sufficient)" \ + "(instrument-output):" \ + "(show-locs)(show-vcc)(show-properties)(show-trace)(show-fixed-points)(show-stats)" \ + "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)(show-dep-graphs)" \ + "(show-value-sets)" \ + "(show-invariants)(std-invariants)" \ + "(show-calling-contexts):" \ + "(property):(all-properties)(k-induction)(incremental-bmc)" \ + "(no-simplify)(no-fixed-point)" \ + "(no-spurious-check)(no-all-properties)(unit-check)" \ + "(spurious-check):" \ + "(competition-mode)(slice)" \ + "(no-unwinding-assertions)(no-propagation)(ssa-propagation)" + // the last line is for CBMC-regression testing only + +class summarizer_parse_optionst: + public parse_options_baset, + public language_uit +{ +public: + virtual int doit(); + virtual void help(); + + summarizer_parse_optionst(int argc, const char **argv); + summarizer_parse_optionst( + int argc, + const char **argv, + const std::string &extra_options); + +protected: + void get_command_line_options(optionst &options); + + bool get_goto_program( + const optionst &options, + goto_modelt &goto_model); + + bool process_goto_program( + const optionst &options, + goto_modelt &goto_model); + + bool set_properties(goto_modelt &); + + void report_success(); + void report_failure(); + + void report_properties( + const goto_modelt &, + const summary_checker_baset::property_mapt &); + + void show_counterexample( + const goto_modelt &, + const class goto_tracet &); + + struct expr_statst { + bool has_malloc; + bool has_string; + bool has_array; + bool has_pointer; + + expr_statst() + : has_malloc(false) + , has_string(false) + , has_array(false) + , has_pointer(false) + {} + }; + + void type_stats_rec( + const typet &type, + expr_statst &stats, + const namespacet &ns); + + void expr_stats_rec( + const exprt &expr, + expr_statst &stats); + + void show_stats( + const goto_modelt &, + std::ostream &); + + void eval_verbosity(); + void report_unknown(); + + // diverse preprocessing + void inline_main(goto_modelt &goto_model); + void propagate_constants(goto_modelt &goto_model); + void nondet_locals(goto_modelt &goto_model); + void goto_unwind(goto_modelt &goto_model, unsigned k); + void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); + exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, + bool &valid); + void remove_multiple_dereferences(goto_modelt &goto_model); + void remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen); +}; + +#endif diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp new file mode 100644 index 000000000..06d077777 --- /dev/null +++ b/src/summarizer/summary_checker_ai.cpp @@ -0,0 +1,145 @@ +/*******************************************************************\ + +Module: Summary Checker for AI + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "summary_checker_ai.h" + +/*******************************************************************\ + +Function: summary_checker_ait::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model,ns); + + ssa_unwinder.init(false,false); + + unsigned unwind = options.get_unsigned_int_option("unwind"); + if(unwind>0) + { + status() << "Unwinding" << messaget::eom; + + ssa_unwinder.init_localunwinders(); + + ssa_unwinder.unwind_all(unwind+1); + ssa_unwinder.output(debug()); debug() <second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + + status() << "Computing dependency graph of " << f_it->first << messaget::eom; + + //ssa_db.depgraph_create(f_it->first, ns, ssa_inliner); + + if(entry_function == f_it->first) + ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, true); + else + ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal + + ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); + ssa_depgraph.output(debug()); debug() << eom; + std::cout << "output SSA for function: " << f_it->first << "\n"; + ssa_db.get(f_it->first).output_verbose(std::cout); + } + } + } + + // properties + initialize_property_map(goto_model.goto_functions); + + bool preconditions = options.get_bool_option("preconditions"); + if(!options.get_bool_option("havoc")) + { + //forward analysis + summarize(goto_model,true,false); + } + if(!options.get_bool_option("havoc") && preconditions) + { + //backward analysis + summarize(goto_model,false,false); + } + + if(preconditions) + { + report_statistics(); + report_preconditions(); + return property_checkert::UNKNOWN; + } + +#ifdef SHOW_CALLINGCONTEXTS + if(options.get_bool_option("show-calling-contexts")) + return property_checkert::UNKNOWN; +#endif + +/* + irep_idt entry_function = goto_model.goto_functions.entry_point(); + if(options.get_bool_option("unit-check")) + entry_function = ""; +*/ + + std::set seen_function_calls; + property_checkert::resultt result = check_properties(entry_function, entry_function, seen_function_calls); + report_statistics(); + return result; +} + + +/*******************************************************************\ + +Function: summary_checker_ait::report_preconditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_ait::report_preconditions() +{ + result() << eom; + result() << "** " << (options.get_bool_option("sufficient") ? + "Sufficient" : "Necessary") + << " preconditions: " << eom; + ssa_dbt::functionst &functions = ssa_db.functions(); + for(ssa_dbt::functionst::iterator it = functions.begin(); + it != functions.end(); it++) + { + exprt precondition; + bool computed = summary_db.exists(it->first); + if(computed) precondition = summary_db.get(it->first).bw_precondition; + if(precondition.is_nil()) computed = false; + result() << eom << "[" << it->first << "]: " + << (!computed ? "not computed" : + from_expr(it->second->ns, "", precondition)) << eom; + } +} diff --git a/src/summarizer/summary_checker_ai.h b/src/summarizer/summary_checker_ai.h new file mode 100644 index 000000000..edde3fe34 --- /dev/null +++ b/src/summarizer/summary_checker_ai.h @@ -0,0 +1,29 @@ +/*******************************************************************\ + +Module: Summary Checker for AI + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARY_CHECKER_AI_H +#define CPROVER_SUMMARY_CHECKER_AI_H + +#include "summary_checker_base.h" + +class summary_checker_ait:public summary_checker_baset +{ +public: + inline summary_checker_ait(optionst &_options): + summary_checker_baset(_options) + { + } + + virtual resultt operator()(const goto_modelt &); + +protected: + void report_preconditions(); + +}; + +#endif diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp new file mode 100644 index 000000000..256227f6c --- /dev/null +++ b/src/summarizer/summary_checker_base.cpp @@ -0,0 +1,684 @@ +/*******************************************************************\ + +Module: Summarizer Checker Base + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" +#include "../ssa/ssa_build_goto_trace.h" +#include "../domains/ssa_analyzer.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/ssa_const_propagator.h" +#include + +#include "show.h" +#include "instrument_goto.h" + +#include "summary_checker_base.h" +#include "summarizer_bw_cex.h" +#include "summarizer_bw_cex_concrete.h" +#include "summarizer_bw_cex_ai.h" +#include "summarizer_bw_cex_complete.h" +#include "summarizer_bw_cex_wp.h" +#include "summarizer_bw_cex_all.h" + +#include "summarizer_fw.h" +#include "summarizer_bw.h" + +#ifdef SHOW_CALLING_CONTEXTS +#include "summarizer_fw_contexts.h" +#endif + +/*******************************************************************\ + +Function: summary_checker_baset::SSA_functions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::SSA_functions(const goto_modelt &goto_model, const namespacet &ns) +{ + entry_function = goto_model.goto_functions.entry_point(); + + // compute SSA for all the functions + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(!f_it->second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + status() << "Computing SSA of " << f_it->first << messaget::eom; + + ssa_db.create(f_it->first, f_it->second, ns); + local_SSAt &SSA = ssa_db.get(f_it->first); + + // simplify, if requested + if(simplify) + { + status() << "Simplifying" << messaget::eom; + ::simplify(SSA, ns); + } + + //SSA.output_verbose(std::cout); + + //SSA.output(debug()); debug() << eom; + } + + // properties + initialize_property_map(goto_model.goto_functions); +} + +/*******************************************************************\ + +Function: summary_checker_baset::summarize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::summarize(const goto_modelt &goto_model, + bool forward, + bool termination) +{ + summarizer_baset *summarizer = NULL; + +#ifdef SHOW_CALLING_CONTEXTS + if(options.get_bool_option("show-calling-contexts")) + summarizer = new summarizer_fw_contextst( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + else +#endif + { + if(forward && !termination) + summarizer = new summarizer_fwt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + if(!forward && !termination) + summarizer = new summarizer_bwt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + } + assert(summarizer != NULL); + + summarizer->set_message_handler(get_message_handler()); + + if(!options.get_bool_option("context-sensitive")) + summarizer->summarize(); + else + summarizer->summarize(entry_function); + + //statistics + solver_instances += summarizer->get_number_of_solver_instances(); + solver_calls += summarizer->get_number_of_solver_calls(); + summaries_used += summarizer->get_number_of_summaries_used(); + + delete summarizer; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: function_name != nil + checks all functions in the call graph from the entry point + else + checks all functions + + Outputs: + + Purpose: + +\*******************************************************************/ +summary_checker_baset::resultt summary_checker_baset::check_properties() +{ + std::set seen_function_calls; + return check_properties("", "", seen_function_calls); +} + +summary_checker_baset::resultt summary_checker_baset::check_properties( + irep_idt function_name, irep_idt entry_function, std::set seen_function_calls) +{ + if(function_name!="") + { + ssa_dbt::functionst::const_iterator f_it = + ssa_db.functions().find(function_name); + local_SSAt &SSA = *f_it->second; + + // call recursively for all function calls first + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); ++n_it) + { + for(local_SSAt::nodet::function_callst::const_iterator ff_it = + n_it->function_calls.begin(); + ff_it != n_it->function_calls.end(); ff_it++) + { + assert(ff_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(ff_it->function()).get_identifier(); + //ENHANCE?: can the return value be exploited? + + if(!summary_db.exists(fname) || + summary_db.get(fname).bw_transformer.is_nil()) + { +#if 0 + debug() << "Checking call " << fname << messaget::eom; +#endif + if(seen_function_calls.find(fname) == seen_function_calls.end()){ + seen_function_calls.insert(fname); + check_properties(fname, entry_function, seen_function_calls); + } + } + } + } + + //now check function itself + status() << "Checking properties of " << f_it->first << messaget::eom; + check_properties(f_it, entry_function); + } + else // check all the functions + { + for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); + f_it != ssa_db.functions().end(); f_it++) + { + status() << "Checking properties of " << f_it->first << messaget::eom; + +#if 0 + //for debugging + show_ssa_symbols(*f_it->second,std::cerr); +#endif + + check_properties(f_it); + + if(options.get_bool_option("show-invariants")) + { + if(!summary_db.exists(f_it->first)) continue; + show_invariants(*(f_it->second),summary_db.get(f_it->first),result()); + result() << eom; + } + } + } + + summary_checker_baset::resultt result = property_checkert::PASS; + if(function_name=="" || function_name==entry_function) + { + // determine overall status + for(property_mapt::const_iterator + p_it=property_map.begin(); p_it!=property_map.end(); p_it++) + { + if(p_it->second.result==FAIL) + return property_checkert::FAIL; + if(p_it->second.result==UNKNOWN) + result = property_checkert::UNKNOWN; + } + } + + return result; +} + +/*******************************************************************\ + +Function: summary_checker_baset::check_properties + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::check_properties( + const ssa_dbt::functionst::const_iterator f_it, + irep_idt entry_function) +{ + local_SSAt &SSA = *f_it->second; + if(!SSA.goto_function.body.has_assertion()) return; + + bool all_properties = options.get_bool_option("all-properties"); + + SSA.output(debug()); debug() << eom; + + // incremental version + + // solver + incremental_solvert &solver = ssa_db.get_solver(f_it->first); + solver.set_message_handler(get_message_handler()); + +#if 1 + // TEST ssa_const_propagation + if(options.get_bool_option("ssa-propagation")) + { + ssa_const_propagatort ssa_const_propagator; + std::list c; + ssa_const_propagator(c,SSA); + solver << c; + debug() << "SSA const propagation: " << eom; + for(std::list::iterator it = c.begin(); + it!=c.end(); it++) + debug() << " " << from_expr(SSA.ns,"",*it) << eom; + } +#endif + + // give SSA to solver + solver << SSA; + SSA.mark_nodes(); + + + solver.new_context(); + + exprt enabling_expr = SSA.get_enabling_exprs(); + solver << enabling_expr; + + // invariant, calling contexts + if(summary_db.exists(f_it->first)) + { + const summaryt &summary = summary_db.get(f_it->first); + if(!summary.fw_invariant.is_nil()) + solver << summary.fw_invariant; + if(!summary.fw_precondition.is_nil()) + solver << summary.fw_precondition; + } + + + //callee summaries + solver << ssa_inliner.get_summaries(SSA); + + //freeze loop head selects + exprt::operandst loophead_selects = + summarizer_baset::get_loophead_selects(SSA,ssa_unwinder.get(f_it->first),*solver.solver); + //check whether loops have been fully unwound + exprt::operandst loop_continues = + get_loop_continues(f_it->first,SSA,*solver.solver); + bool fully_unwound = + is_fully_unwound(loop_continues,loophead_selects,solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + + //spuriousness checkers + summarizer_bw_cex_baset *summarizer_bw_cex = NULL; + incremental_solvert* cex_complete_solver = + incremental_solvert::allocate(SSA.ns, + options.get_bool_option("refine")); + if(options.get_option("spurious-check") == "concrete") + { + summarizer_bw_cex = new summarizer_bw_cex_concretet( + options,summary_db,ssa_db, + ssa_unwinder,ssa_inliner, + entry_function,f_it->first); + } + if(options.get_option("spurious-check") == "abstract") + { + summarizer_bw_cex = new summarizer_bw_cex_ait( + options,summary_db,ssa_db, + ssa_unwinder,ssa_inliner, + entry_function,f_it->first); + } + else if(options.get_option("spurious-check") == "complete") + { + summarizer_bw_cex = new summarizer_bw_cex_completet( + options,summary_db,ssa_db, + ssa_unwinder,ssa_inliner,*cex_complete_solver, + entry_function,f_it->first); + } + else if(options.get_option("spurious-check") == "wp") + { + summarizer_bw_cex = new summarizer_bw_cex_wpt( + options,summary_db,ssa_db, + ssa_unwinder,ssa_inliner,*cex_complete_solver, + entry_function,f_it->first); + } + else if(options.get_option("spurious-check") == "all") + { + summarizer_bw_cex = new summarizer_bw_cex_allt( + options,summary_db,ssa_db, + ssa_unwinder,ssa_inliner,*cex_complete_solver, + entry_function,f_it->first); + } + assert(summarizer_bw_cex != NULL); + summarizer_bw_cex->set_message_handler(get_message_handler()); + + cover_goals_extt cover_goals( + solver,loophead_selects,property_map, + f_it->first!=entry_function || !fully_unwound, + all_properties, + *summarizer_bw_cex); + +#if 0 + debug() << "(C) " << from_expr(SSA.ns,"",enabling_expr) << eom; +#endif + + const goto_programt &goto_program=SSA.goto_function.body; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) + { + if(!i_it->is_assert()) + continue; + + const source_locationt &location=i_it->source_location; + std::list assertion_nodes; + SSA.find_nodes(i_it,assertion_nodes); + + irep_idt property_id = location.get_property_id(); + + //do not recheck properties that have already been decided + if(property_map[property_id].result!=UNKNOWN) continue; + + if(property_id=="") //TODO: some properties do not show up in initialize_property_map + continue; + + unsigned property_counter = 0; + for(std::list::const_iterator + n_it=assertion_nodes.begin(); + n_it!=assertion_nodes.end(); + n_it++) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it=(*n_it)->assertions.begin(); + a_it!=(*n_it)->assertions.end(); + a_it++, property_counter++) + { + exprt property=*a_it; + + if(simplify) + property=::simplify_expr(property, SSA.ns); + +#if 0 + std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; +#endif + + property_map[property_id].location = i_it; + cover_goals.goal_map[property_id].conjuncts.push_back(property); + } + } + } + + + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++) + { + // Our goal is to falsify a property. + // The following is TRUE if the conjunction is empty. + //literalt p=!solver.convert(conjunction(it->second.conjuncts)); + //cover_goals.add(p); + cover_goals.add(not_exprt(conjunction(it->second.conjuncts))); + } + + status() << "Running " << solver.solver->decision_procedure_text() << eom; + + cover_goals(); + /* + std::cout << "Output Verbose: " << entry_function << "\n"; + (ssa_db.get(entry_function)).output_verbose(std::cout); + assert(false); + */ + //set all non-covered goals to PASS except if we do not try + // to cover all goals and we have found a FAIL + if(all_properties || cover_goals.number_covered()==0) + { + std::list::const_iterator g_it= + cover_goals.goals.begin(); + for(cover_goals_extt::goal_mapt::const_iterator + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++, g_it++) + { + if(!g_it->covered) property_map[it->first].result=PASS; + } + } + + solver.pop_context(); + + debug() << "** " << cover_goals.number_covered() + << " of " << cover_goals.size() << " failed (" + << cover_goals.iterations() << " iterations)" << eom; + + delete summarizer_bw_cex; + delete cex_complete_solver; +} + +/*******************************************************************\ + +Function: summary_checker_baset::report_statistics() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::report_statistics() +{ + for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); + f_it != ssa_db.functions().end(); f_it++) + { + incremental_solvert &solver = ssa_db.get_solver(f_it->first); + unsigned calls = solver.get_number_of_solver_calls(); + if(calls>0) solver_instances++; + solver_calls += calls; + } + statistics() << "** statistics: " << eom; + statistics() << " number of solver instances: " << solver_instances << eom; + statistics() << " number of solver calls: " << solver_calls << eom; + statistics() << " number of summaries used: " + << summaries_used << eom; + statistics() << eom; +} + +/*******************************************************************\ + +Function: summary_checker_baset::do_show_vcc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::do_show_vcc( + const local_SSAt &SSA, + const goto_programt::const_targett i_it, + const local_SSAt::nodet::assertionst::const_iterator &a_it) +{ + std::cout << i_it->source_location << "\n"; + std::cout << i_it->source_location.get_comment() << "\n"; + + std::list ssa_constraints; + ssa_constraints << SSA; + + unsigned i=1; + for(std::list::const_iterator c_it=ssa_constraints.begin(); + c_it!=ssa_constraints.end(); + c_it++, i++) + std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; + + std::cout << "|--------------------------\n"; + + std::cout << "{1} " << from_expr(SSA.ns, "", *a_it) << "\n"; + + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summary_checker_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + loops in order to check whether we can unroll further + +\*******************************************************************/ + +exprt::operandst summary_checker_baset::get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, prop_convt &solver) +{ + exprt::operandst loop_continues; + + ssa_unwinder.get(function_name).loop_continuation_conditions(loop_continues); + if(loop_continues.size()==0) + { + //TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) continue; + symbol_exprt guard = SSA.guard_symbol(n_it->location); + symbol_exprt cond = SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard,cond)); + } + } + +#if 0 + std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; +#endif + + return loop_continues; +} + +/*******************************************************************\ + +Function: summary_checker_baset::is_fully_unwound + + Inputs: + + Outputs: + + Purpose: checks whether the loops have been fully unwound + +\*******************************************************************/ + +bool summary_checker_baset::is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver) +{ + solver.new_context(); + solver << and_exprt(conjunction(loophead_selects), + disjunction(loop_continues)); + + solver_calls++; //statistics + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + return false; + break; + + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + solver << conjunction(loophead_selects); + return true; + break; + + case decision_proceduret::D_ERROR: + default: + throw "error from decision procedure"; + } +} + +/*******************************************************************\ + +Function: summary_checker_baset::is_spurious + + Inputs: + + Outputs: + + Purpose: checks whether a countermodel is spurious + +\*******************************************************************/ + +bool summary_checker_baset::is_spurious(const exprt::operandst &loophead_selects, + incremental_solvert &solver) +{ + //check loop head choices in model + bool invariants_involved = false; + for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); + l_it != loophead_selects.end(); l_it++) + { + if(solver.get(l_it->op0()).is_true()) + { + invariants_involved = true; + break; + } + } + if(!invariants_involved) return false; + + // force avoiding paths going through invariants + solver << conjunction(loophead_selects); + + solver_calls++; //statistics + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + return false; + break; + + case decision_proceduret::D_UNSATISFIABLE: + return true; + break; + + case decision_proceduret::D_ERROR: + default: + throw "error from decision procedure"; + } +} + +/*******************************************************************\ + +Function: summary_checker_baset::instrument_and_output + + Inputs: + + Outputs: + + Purpose: instruments the code with the inferred information + and outputs it to a goto-binary + +\*******************************************************************/ + +void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) +{ + instrument_gotot instrument_goto(options,ssa_db,summary_db); + instrument_goto(goto_model); + std::string filename = options.get_option("instrument-output"); + status() << "Writing instrumented goto-binary " << filename << eom; + write_goto_binary(filename, + goto_model.symbol_table, + goto_model.goto_functions, get_message_handler()); +} diff --git a/src/summarizer/summary_checker_base.h b/src/summarizer/summary_checker_base.h new file mode 100644 index 000000000..d2419b7ec --- /dev/null +++ b/src/summarizer/summary_checker_base.h @@ -0,0 +1,102 @@ +/*******************************************************************\ + +Module: Summary Checker Base + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARY_CHECKER_BASE_H +#define CPROVER_SUMMARY_CHECKER_BASE_H + +#include + +#include +#include + +#include "cover_goals_ext.h" +#include "../ssa/local_ssa.h" +#include "../ssa/ssa_unwinder.h" +#include "../ssa/ssa_inliner.h" +#include "../domains/incremental_solver.h" +#include "ssa_db.h" +#include "summary_db.h" + +class summary_checker_baset:public property_checkert +{ +public: + inline summary_checker_baset(optionst &_options): + show_vcc(false), + simplify(false), + fixed_point(false), + options(_options), + ssa_db(_options),summary_db(), + ssa_unwinder(ssa_db), + ssa_inliner(summary_db), + solver_instances(0), + solver_calls(0), + summaries_used(0) + { + ssa_inliner.set_message_handler(get_message_handler()); + } + + bool show_vcc, simplify, fixed_point; + irep_idt function_to_check; + + virtual resultt operator()(const goto_modelt &) { assert(false); } + + void instrument_and_output(goto_modelt &goto_model); + + // statistics + absolute_timet start_time; + time_periodt sat_time; + +protected: + optionst &options; + + ssa_dbt ssa_db; + summary_dbt summary_db; + ssa_unwindert ssa_unwinder; + ssa_inlinert ssa_inliner; + + irep_idt entry_function; + + unsigned solver_instances; + unsigned solver_calls; + unsigned summaries_used; + void report_statistics(); + + void do_show_vcc( + const local_SSAt &, + const goto_programt::const_targett, + const local_SSAt::nodet::assertionst::const_iterator &); + + void SSA_functions(const goto_modelt &, const namespacet &ns); + + void summarize(const goto_modelt &, + bool forward=true, bool termination=false); + + property_checkert::resultt check_properties(); + property_checkert::resultt check_properties( + irep_idt function_name, + irep_idt entry_function, + std::set seen_function_calls); + void check_properties( + const ssa_dbt::functionst::const_iterator f_it, + irep_idt entry_function=""); + + void error_summary_using_vars(const ssa_dbt::functionst::const_iterator f_it); + + exprt::operandst get_loophead_selects( + const irep_idt &function_name, const local_SSAt &, prop_convt &); + bool is_spurious(const exprt::operandst& loophead_selects, + incremental_solvert&); + exprt::operandst get_loop_continues( + const irep_idt &function_name, const local_SSAt &, prop_convt &); + bool is_fully_unwound( + const exprt::operandst& loop_continues, + const exprt::operandst& loophead_selects, + incremental_solvert&); +}; + +#endif diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/summarizer/summary_checker_bmc.cpp new file mode 100644 index 000000000..c3649d534 --- /dev/null +++ b/src/summarizer/summary_checker_bmc.cpp @@ -0,0 +1,63 @@ +/*******************************************************************\ + +Module: Summarizer Checker for BMC + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_bmc.h" + + +/*******************************************************************\ + +Function: summary_checker_bmct::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_bmct::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + irep_idt entry_function = goto_model.goto_functions.entry_point(); + if(options.get_bool_option("unit-check")) + entry_function = ""; + + SSA_functions(goto_model,ns); + + ssa_unwinder.init(false,true); + + property_checkert::resultt result = property_checkert::UNKNOWN; + unsigned max_unwind = options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind = 0; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << messaget::eom; + summary_db.mark_recompute_all(); + ssa_unwinder.unwind_all(unwind+1); + std::set seen_function_calls; + result = check_properties(entry_function, entry_function, seen_function_calls); + if(result == property_checkert::PASS) + { + status() << "incremental BMC proof found after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + else if(result == property_checkert::FAIL) + { + status() << "incremental BMC counterexample found after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + } + report_statistics(); + return result; +} diff --git a/src/summarizer/summary_checker_bmc.h b/src/summarizer/summary_checker_bmc.h new file mode 100644 index 000000000..8345a41fe --- /dev/null +++ b/src/summarizer/summary_checker_bmc.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: Summary Checker for BMC + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARY_CHECKER_BMC_H +#define CPROVER_SUMMARY_CHECKER_BMC_H + +#include "summary_checker_base.h" + +class summary_checker_bmct:public summary_checker_baset +{ +public: + inline summary_checker_bmct(optionst &_options): + summary_checker_baset(_options) + { + } + + virtual resultt operator()(const goto_modelt &); + +}; + +#endif diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp new file mode 100644 index 000000000..2284d2eb2 --- /dev/null +++ b/src/summarizer/summary_checker_kind.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Summarizer Checker for k-induction + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_kind.h" + +#define GIVE_UP_INVARIANTS 7 + +/*******************************************************************\ + +Function: summary_checker_kindt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_kindt::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + irep_idt entry_function = goto_model.goto_functions.entry_point(); + if(options.get_bool_option("unit-check")) + entry_function = ""; + + SSA_functions(goto_model,ns); + + ssa_unwinder.init(true,false); + + property_checkert::resultt result = property_checkert::UNKNOWN; + unsigned max_unwind = options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind = 0; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << eom; + summary_db.mark_recompute_all(); //TODO: recompute only functions with loops + ssa_unwinder.unwind_all(unwind+1); + + std::set seen_function_calls; + result = check_properties(entry_function, entry_function, seen_function_calls); + if(result == property_checkert::UNKNOWN && + !options.get_bool_option("havoc") && + (unwind seen_function_calls; + result = check_properties(entry_function, entry_function, seen_function_calls); + } + + if(result == property_checkert::PASS) + { + status() << "k-induction proof found after " + << unwind << " unwinding(s)" << eom; + break; + } + else if(result == property_checkert::FAIL) + { + status() << "k-induction counterexample found after " + << unwind << " unwinding(s)" << eom; + break; + } + } + report_statistics(); + return result; +} + diff --git a/src/summarizer/summary_checker_kind.h b/src/summarizer/summary_checker_kind.h new file mode 100644 index 000000000..087ba6fae --- /dev/null +++ b/src/summarizer/summary_checker_kind.h @@ -0,0 +1,26 @@ +/*******************************************************************\ + +Module: Summary Checker for k-induction + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SUMMARY_CHECKER_KIND_H +#define CPROVER_SUMMARY_CHECKER_KIND_H + +#include "summary_checker_base.h" + +class summary_checker_kindt:public summary_checker_baset +{ +public: + inline summary_checker_kindt(optionst &_options): + summary_checker_baset(_options) + { + } + + virtual resultt operator()(const goto_modelt &); + +}; + +#endif From 1a0174b734cb22d4ca301c7f2db297bcd6ccc81b Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 23 Apr 2016 16:32:09 +0100 Subject: [PATCH 03/90] all commits of old branch --- src/storefront/Makefile | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/storefront/Makefile diff --git a/src/storefront/Makefile b/src/storefront/Makefile new file mode 100644 index 000000000..55c7aa7ce --- /dev/null +++ b/src/storefront/Makefile @@ -0,0 +1,27 @@ +include ../config.inc + +SRC = storefront_main.cpp storefront_parse_options.cpp data.cpp \ + property_view.cpp file_view.cpp trace_view.cpp + +OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ + $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ + ../html/html_escape$(OBJEXT) \ + ../html/syntax_highlighting$(OBJEXT) \ + ../html/logo$(OBJEXT) + +include $(CBMC)/src/config.inc +include $(CBMC)/src/common + +INCLUDES= -I $(CBMC)/src + +LIBS = + +CLEANFILES = storefront$(EXEEXT) + +all: storefront$(EXEEXT) + +############################################################################### + +storefront$(EXEEXT): $(OBJ) + $(LINKBIN) + From 772f34fe3c48f6fb87af19122717b4d5d98e4fbb Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 23 Apr 2016 17:55:53 +0100 Subject: [PATCH 04/90] fixed compilation errors after merge --- src/domains/lexlinrank_solver_enumeration.cpp | 12 +- src/domains/lexlinrank_solver_enumeration.h | 4 +- src/domains/ranking_solver_enumeration.cpp | 10 +- src/domains/ranking_solver_enumeration.h | 4 +- src/domains/ssa_analyzer.cpp | 9 +- src/ssa/local_ssa.cpp | 24 +-- src/ssa/local_ssa.h | 4 - src/ssa/ssa_unwinder.cpp | 146 ------------------ src/summarizer/cover_goals_ext.h | 11 +- src/summarizer/summary.cpp | 44 ++++++ src/summarizer/summary.h | 10 +- src/summarizer/summary_checker_ai.cpp | 36 ++++- src/summarizer/summary_checker_base.cpp | 10 +- 13 files changed, 116 insertions(+), 208 deletions(-) diff --git a/src/domains/lexlinrank_solver_enumeration.cpp b/src/domains/lexlinrank_solver_enumeration.cpp index db08cd153..a4c8d5b8e 100644 --- a/src/domains/lexlinrank_solver_enumeration.cpp +++ b/src/domains/lexlinrank_solver_enumeration.cpp @@ -7,12 +7,12 @@ //#define DEBUG_OUTER_FORMULA //#define DEBUG_INNER_FORMULA -bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) +lexlinrank_solver_enumerationt::progresst lexlinrank_solver_enumerationt::iterate(invariantt &_rank) { lexlinrank_domaint::templ_valuet &rank = static_cast(_rank); - bool improved = false; + progresst progress = CONVERGED; static std::vector number_elements_per_row; number_elements_per_row.resize(rank.size()); @@ -144,7 +144,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) } } - improved = true; + progress = CHANGED; // update the current template lexlinrank_domain.set_row_value(row, new_row_values, rank); @@ -173,7 +173,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) if(lexlinrank_domain.refine()) { debug() << "refining..." << eom; - improved = true; //refinement possible + progress = CHANGED; //refinement possible if(!refinement_constraint.is_true()) inner_solver->pop_context(); } @@ -200,7 +200,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) lexlinrank_domain.add_element(row, rank); number_inner_iterations = 0; debug() << "Inner solver: the number of inner iterations for row " << row << " was reset to " << number_inner_iterations << eom; - improved = true; + progress = CHANGED; } } } @@ -225,5 +225,5 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/lexlinrank_solver_enumeration.h b/src/domains/lexlinrank_solver_enumeration.h index 6bd7c10a9..270dd1890 100644 --- a/src/domains/lexlinrank_solver_enumeration.h +++ b/src/domains/lexlinrank_solver_enumeration.h @@ -18,7 +18,7 @@ class lexlinrank_solver_enumerationt : public strategy_solver_baset unsigned _max_elements, // lexicographic components unsigned _max_inner_iterations ) : - strategy_solver_baset(_solver, _ns), + strategy_solver_baset(_solver, literalt(), _ns), lexlinrank_domain(_lexlinrank_domain), max_elements(_max_elements), max_inner_iterations(_max_inner_iterations), @@ -28,7 +28,7 @@ class lexlinrank_solver_enumerationt : public strategy_solver_baset solver_instances++; } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: lexlinrank_domaint &lexlinrank_domain; diff --git a/src/domains/ranking_solver_enumeration.cpp b/src/domains/ranking_solver_enumeration.cpp index 9b7dced96..45a4e1109 100644 --- a/src/domains/ranking_solver_enumeration.cpp +++ b/src/domains/ranking_solver_enumeration.cpp @@ -4,12 +4,12 @@ #include "util.h" #include -bool ranking_solver_enumerationt::iterate(invariantt &_rank) +ranking_solver_enumerationt::progresst ranking_solver_enumerationt::iterate(invariantt &_rank) { linrank_domaint::templ_valuet &rank = static_cast(_rank); - bool improved = false; + progresst progress = CONVERGED; //context for "outer" solver solver.new_context(); @@ -108,7 +108,7 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) // update the current template linrank_domain.set_row_value(row, new_row_values, rank); - improved = true; + progress = CHANGED; } else { @@ -117,7 +117,7 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) if(linrank_domain.refine()) { debug() << "refining..." << eom; - improved = true; //refinement possible + progress = CHANGED; //refinement possible } else { @@ -140,5 +140,5 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/ranking_solver_enumeration.h b/src/domains/ranking_solver_enumeration.h index d52ab0bb1..01de5c570 100644 --- a/src/domains/ranking_solver_enumeration.h +++ b/src/domains/ranking_solver_enumeration.h @@ -17,7 +17,7 @@ class ranking_solver_enumerationt : public strategy_solver_baset const namespacet &_ns, unsigned _max_inner_iterations ) : - strategy_solver_baset(_solver, _ns), + strategy_solver_baset(_solver, literalt(), _ns), linrank_domain(_linrank_domain), max_inner_iterations(_max_inner_iterations), inner_solver(_ns), @@ -26,7 +26,7 @@ class ranking_solver_enumerationt : public strategy_solver_baset solver_instances++; } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: linrank_domaint &linrank_domain; diff --git a/src/domains/ssa_analyzer.cpp b/src/domains/ssa_analyzer.cpp index 4b16a14d2..841e46dbf 100644 --- a/src/domains/ssa_analyzer.cpp +++ b/src/domains/ssa_analyzer.cpp @@ -34,9 +34,9 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#define BINSEARCH_SOLVER strategy_solver_binsearcht(*static_cast(domain), solver, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch2t(*static_cast(domain), solver, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch3t(*static_cast(domain), solver, SSA, SSA.ns) +#define BINSEARCH_SOLVER strategy_solver_binsearcht(*static_cast(domain), solver, assertions_check, SSA.ns) +//#define BINSEARCH_SOLVER strategy_solver_binsearch2t(*static_cast(domain), solver, assertions_check, SSA.ns) +//#define BINSEARCH_SOLVER strategy_solver_binsearch3t(*static_cast(domain), solver, assertions_check, SSA, SSA.ns) #ifdef DEBUG #include @@ -127,7 +127,8 @@ bool ssa_analyzert::operator()(incremental_solvert &solver, { result = new predabs_domaint::templ_valuet(); strategy_solver = new strategy_solver_predabst( - *static_cast(domain), solver, SSA.ns); + *static_cast(domain), solver, + assertions_check, SSA.ns); } else if(template_generator.options.get_bool_option("binsearch-solver")) { diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index a1430fc87..99c01410f 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -280,28 +280,6 @@ void local_SSAt::find_nodes(locationt loc, std::list &_n /*******************************************************************\ -Function: local_SSAt::find_location_by_number - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -local_SSAt::locationt local_SSAt::find_location_by_number(unsigned location_number) const -{ - local_SSAt::nodest::const_iterator n_it =nodes.begin(); - for(; n_it != nodes.end(); n_it++) - { - if(n_it->location->location_number == location_number) break; - } - return n_it->location; -} - -/*******************************************************************\ - Function: local_SSAt::edge_guard Inputs: @@ -570,7 +548,7 @@ void local_SSAt::build_function_call(locationt loc) if(argtype.id()==ID_struct) { exprt lhs = read_rhs(arg, loc); - for(int j=0; jequalities.push_back(equal_exprt(lhs.operands()[j], it->operands()[j])); diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index c28491b7b..093bfec3d 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -86,10 +86,6 @@ class local_SSAt typedef std::vector function_callst; function_callst function_calls; -(??) exprt enabling_expr; //for incremental unwinding -(??) -(??) bool marked; //for incremental unwinding - //custom invariant templates typedef std::vector templatest; templatest templates; diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 7edceb340..fe7f13cf5 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -808,152 +808,6 @@ void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, #endif } - -unsigned ssa_local_unwindert::rename_required(const exprt& e, - const unsigned prev_unwinding) const -{ - if(e.id()==ID_symbol) - { - const symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,false); - bool rename_required=true; - for(std::list::iterator it=iterations.begin(); - it!=iterations.end();it++) - { - if(*it!=(prev_unwinding-1)) rename_required=false; - } - //if(iterations.back()==(prev_unwinding-1)) return iterations.size(); - if(rename_required) return iterations.size(); - - - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::const_iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth) return depth; - } - } - } - - return 0; - -} - - -void ssa_local_unwindert::rename_invariant(exprt& e,const irep_idt& suffix) const -{ - if(e.id()==ID_symbol) - { - symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,true); - - - sym.set_identifier(id2string(basename)+id2string(suffix)); - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - rename_invariant(*e_it,suffix); - } - } - } - -} -/***************************************************************************** - * - * Function : ssa_local_unwindert::rename_invariant - * - * Input : inv_in - list of input invariants that is to be renamed for reuse - * - * Output : inv_out - list of renamed invariants - * - * Purpose : For the purpose of reuse of invariant, rename all - * - * - *****************************************************************************/ -void ssa_local_unwindert::rename_invariant(const exprt::operandst& inv_in, - std::vector& inv_out,const unsigned prev_unwinding) const -{ - - if(prev_unwinding==0 || prev_unwinding==std::numeric_limits::max()) - { - return; - } - for(std::vector::const_iterator e_it=inv_in.begin(); - e_it!=inv_in.end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth==0) continue; - - std::vector iter_vector(depth-1,current_unwinding-1); - - do - { - - irep_idt suffix; - - for(std::vector::const_iterator vit=iter_vector.begin(); - vit!=iter_vector.end();vit++) - { - - suffix = id2string(suffix)+"%"+i2string(*vit); - } - suffix = id2string(suffix)+"%"+i2string(current_unwinding-1); - inv_out.push_back(*e_it); - exprt& e = inv_out.back(); - rename_invariant(e,suffix); - } while(odometer_increment(iter_vector,current_unwinding)); - } -} - -exprt ssa_local_unwindert::rename_invariant(const exprt& inv_in) const -{ - if(inv_in.is_true()) return inv_in; - - exprt::operandst inv_in_operands; - if(inv_in.id()!=ID_and) inv_in_operands.push_back(inv_in); - else inv_in_operands = inv_in.operands(); - - std::vector new_inv; - rename_invariant(inv_in_operands,new_inv,prev_unwinding); - - return conjunction(new_inv); -} - - -bool ssa_local_unwindert::odometer_increment(std::vector& odometer, - unsigned base) const -{ - if(odometer.empty()) return false; - unsigned i=odometer.size()-1; - while(true) - { - if(odometer[i] < base-1) {odometer[i]++; return true;} - odometer[i]=0; - if(i==0) return false; //overflow - i--; - - } -return false; -} - /*****************************************************************************\ * * Function : ssa_unwindert::unwind diff --git a/src/summarizer/cover_goals_ext.h b/src/summarizer/cover_goals_ext.h index aa519cc66..737d4550d 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/summarizer/cover_goals_ext.h @@ -54,15 +54,16 @@ class cover_goals_extt:public messaget const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, bool _spurious_check, bool _all_properties, - bool _build_error_trace): - summarizer_bw_cex_baset &_summarizer_bw_cex): + bool _build_error_trace, + summarizer_bw_cex_baset &_summarizer_bw_cex): + SSA(_SSA), solver(_solver), property_map(_property_map), spurious_check(_spurious_check), all_properties(_all_properties), - loophead_selects(_loophead_selects), build_error_trace(_build_error_trace), - summarizer_bw_cex(_summarizer_bw_cex) + summarizer_bw_cex(_summarizer_bw_cex), + loophead_selects(_loophead_selects) {} virtual ~cover_goals_extt(); @@ -126,8 +127,8 @@ class cover_goals_extt:public messaget incremental_solvert &solver; property_checkert::property_mapt &property_map; bool spurious_check, all_properties, build_error_trace; - exprt::operandst loophead_selects; summarizer_bw_cex_baset &summarizer_bw_cex; + exprt::operandst loophead_selects; // this method is called for each satisfying assignment virtual void assignment(); diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp index 6904fa878..16e89810d 100644 --- a/src/summarizer/summary.cpp +++ b/src/summarizer/summary.cpp @@ -63,6 +63,16 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const out << "backward invariant: " << (bw_invariant.is_nil() ? "not computed" : from_expr(ns,"",bw_invariant)) << std::endl; + out << "termination argument: "; + if(termination_argument.is_nil()) out << "not computed"; + else +#if PRETTY_PRINT + pretty_print_termination_argument(out,ns,termination_argument); +#else + out << from_expr(ns,"",termination_argument) << std::endl; +#endif + out << std::endl; + out << "terminates: " << threeval2string(terminates) << std::endl; for(error_summariest::const_iterator it = error_summaries.begin(); it != error_summaries.end(); it++) @@ -127,5 +137,39 @@ void summaryt::join(const summaryt &new_summary) combine_or(bw_postcondition,new_summary.bw_postcondition); combine_and(bw_transformer,new_summary.bw_transformer); combine_and(bw_invariant,new_summary.bw_invariant); + combine_and(termination_argument,new_summary.termination_argument); + switch(new_summary.terminates) + { + case YES: + break; + case NO: terminates=NO; + break; + case UNKNOWN: + if(terminates!=NO) terminates=UNKNOWN; + break; + default: assert(false); + } } +/*******************************************************************\ + +Function: threeval2string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string threeval2string(threevalt v) +{ + switch(v) + { + case YES: return "yes"; + case NO: return "no"; + case UNKNOWN: return "unknown"; + } + assert(false); +} diff --git a/src/summarizer/summary.h b/src/summarizer/summary.h index 4b0f6989c..2eb417660 100644 --- a/src/summarizer/summary.h +++ b/src/summarizer/summary.h @@ -15,6 +15,8 @@ Author: Daniel Kroening, kroening@kroening.com #include +#include "../ssa/local_ssa.h" + typedef enum{YES, NO, UNKNOWN} threevalt; class summaryt @@ -50,6 +52,9 @@ class summaryt predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) + predicatet termination_argument; + threevalt terminates; + bool mark_recompute; //to force recomputation of the summary // (used for invariant reuse in k-induction) @@ -78,9 +83,6 @@ class summaryt error_summariest error_summaries; //-------------- - bool mark_recompute; //to force recomputation of the summary - // (used for invariant reuse in k-induction) - void output(std::ostream &out, const namespacet &ns) const; void join(const summaryt &new_summary); @@ -92,6 +94,6 @@ class summaryt }; - +std::string threeval2string(threevalt v); #endif diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index 594771f64..8168ef9a6 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -39,7 +39,6 @@ property_checkert::resultt summary_checker_ait::operator()( ssa_unwinder.init_localunwinders(); ssa_unwinder.unwind_all(unwind); - ssa_unwinder.output(debug()); debug() <second->ns, "", precondition)) << eom; } } + +/*******************************************************************\ + +Function: summary_checker_ait::report_termination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::report_termination() +{ + result() << eom; + result() << "** Termination: " << eom; + bool all_terminate = true; + bool one_nonterminate = false; + ssa_dbt::functionst &functions = ssa_db.functions(); + for(ssa_dbt::functionst::iterator it = functions.begin(); + it != functions.end(); it++) + { + threevalt terminates = YES; + bool computed = summary_db.exists(it->first); + if(computed) terminates = summary_db.get(it->first).terminates; + all_terminate = all_terminate && (terminates==YES); + one_nonterminate = one_nonterminate || (terminates==NO); + result() << "[" << it->first << "]: " + << (!computed ? "not computed" : threeval2string(terminates)) << eom; + } + if(all_terminate) return property_checkert::PASS; + if(one_nonterminate) return property_checkert::FAIL; + return property_checkert::UNKNOWN; +} diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 8df0d3198..2efb02f6b 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -258,10 +258,11 @@ void summary_checker_baset::check_properties( const ssa_dbt::functionst::const_iterator f_it, irep_idt entry_function) { - local_SSAt &SSA = *f_it->second; + unwindable_local_SSAt &SSA = *f_it->second; if(!SSA.goto_function.body.has_assertion()) return; bool all_properties = options.get_bool_option("all-properties"); + bool build_error_trace = options.get_bool_option("show-trace"); SSA.output(debug()); debug() << eom; @@ -366,9 +367,9 @@ void summary_checker_baset::check_properties( summarizer_bw_cex->set_message_handler(get_message_handler()); cover_goals_extt cover_goals( - solver,loophead_selects,property_map, + SSA,solver,loophead_selects,property_map, f_it->first!=entry_function || !fully_unwound, - all_properties, + all_properties,build_error_trace, *summarizer_bw_cex); #if 0 @@ -403,9 +404,6 @@ void summary_checker_baset::check_properties( if(property_id=="") //TODO: some properties do not show up in initialize_property_map continue; - std::list assertion_nodes; - SSA.find_nodes(i_it,assertion_nodes); - unsigned property_counter = 0; for(std::list::const_iterator n_it=assertion_nodes.begin(); From c6f5f77bd7145ce0e6f189ea47698b023cb2a617 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Sun, 24 Apr 2016 22:19:14 +0530 Subject: [PATCH 05/90] fixed ssa_unwinder bug --- src/ssa/ssa_unwinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index fe7f13cf5..2e178e678 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -732,7 +732,7 @@ void ssa_local_unwindert::loop_continuation_conditions( { SSA.increment_unwindings(1); loop_cont.push_back(get_continuation_condition(loop)); //%0 - for(unsigned i=0; i<=loop.current_unwinding; ++i) + for(long i=0; i<=loop.current_unwinding; ++i) { //recurse into child loops for(std::vector::const_iterator l_it = loop.loop_nodes.begin(); From b5e4fd37ef20caeef63e47a8d14af551cf2b1ab6 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 25 Apr 2016 21:07:53 +0100 Subject: [PATCH 06/90] use cex analyser concrete with --inline --- src/summarizer/cover_goals_ext.cpp | 98 +++++++++++++------------ src/summarizer/summary_checker_base.cpp | 5 +- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/summarizer/cover_goals_ext.cpp b/src/summarizer/cover_goals_ext.cpp index 2c6147afc..edf21d4c3 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/summarizer/cover_goals_ext.cpp @@ -43,7 +43,7 @@ Function: cover_goals_extt::mark void cover_goals_extt::mark() { for(std::list::iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && @@ -71,7 +71,7 @@ void cover_goals_extt::constraint() exprt::operandst disjuncts; for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && !g_it->condition.is_false()) @@ -96,7 +96,7 @@ Function: cover_goals_extt::freeze_goal_variables void cover_goals_extt::freeze_goal_variables() { for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->condition.is_constant()) @@ -175,57 +175,59 @@ void cover_goals_extt::assignment() std::list::const_iterator g_it=goals.begin(); for(goal_mapt::const_iterator it=goal_map.begin(); it!=goal_map.end(); it++, g_it++) + { + if(property_map[it->first].result==property_checkert::UNKNOWN && + solver.l_get(g_it->condition).is_true()) { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.l_get(g_it->condition).is_true()) + if(spurious_check) + { + assert((g_it->cond_expression).id() == ID_not); + exprt conjunct_expr = (g_it->cond_expression).op0(); + + if(conjunct_expr.id() != ID_and) + { + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); + } + else { - if(spurious_check) + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it = + conjunct_expr.operands().begin(); + c_it != conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal = solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_false()) + failed_exprs.push_back(*c_it); + } + solver.pop_context(); //otherwise this would interfere with necessary preconditions + for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); + if(property_map[it->first].result == + property_checkert::FAIL) { - assert((g_it->cond_expression).id() == ID_not); - exprt conjunct_expr = (g_it->cond_expression).op0(); - - if(conjunct_expr.id() != ID_and) - { - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(g_it->cond_expression); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); - } - else - { - exprt::operandst failed_exprs; - for(exprt::operandst::const_iterator c_it = - conjunct_expr.operands().begin(); - c_it != conjunct_expr.operands().end(); c_it++) - { - literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_false()) - failed_exprs.push_back(*c_it); - } - solver.pop_context(); //otherwise this would interfere with necessary preconditions - for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); - if(property_map[it->first].result == - property_checkert::FAIL) - { - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); - if(!all_properties) - break; - } - } - } - solver.new_context(); + if(build_error_trace) + { + ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); + build_goto_trace(property_map[it->first].error_trace); } + break; } - else - property_map[it->first].result = property_checkert::FAIL; + } + solver.new_context(); } + } + else + property_map[it->first].result = property_checkert::FAIL; + } + if(!all_properties && + property_map[it->first].result == property_checkert::FAIL) + break; } _iterations++; //statistics diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 2efb02f6b..68c80b147 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -328,14 +328,15 @@ void summary_checker_baset::check_properties( incremental_solvert* cex_complete_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); - if(options.get_option("spurious-check") == "concrete") + if(options.get_bool_option("inline") || + options.get_option("spurious-check") == "concrete") { summarizer_bw_cex = new summarizer_bw_cex_concretet( options,summary_db,ssa_db, ssa_unwinder,ssa_inliner, entry_function,f_it->first); } - if(options.get_option("spurious-check") == "abstract") + else if(options.get_option("spurious-check") == "abstract") { summarizer_bw_cex = new summarizer_bw_cex_ait( options,summary_db,ssa_db, From 2fa037e7337281a3ba1b2766ac77b5ae18471943 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 25 Apr 2016 22:16:22 +0100 Subject: [PATCH 07/90] made sure that the executable is named 2ls --- regression/modular/Makefile | 6 +++--- src/summarizer/Makefile | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/regression/modular/Makefile b/regression/modular/Makefile index 19c23b682..2a3b5cb15 100644 --- a/regression/modular/Makefile +++ b/regression/modular/Makefile @@ -1,12 +1,12 @@ default: tests.log -FLAGS = --verbosity 10 --spurious-check concrete +FLAGS = --verbosity 10 --spurious-check complete test: - @../test.pl -c "../../../src/summarizer/summarizer $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/summarizer $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 91ae04e4a..41654d5ad 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -105,9 +105,9 @@ LIBS = # $(CUDD)/obj/libobj.a -CLEANFILES = summarizer$(EXEEXT) $(DELTA_OBJ) +CLEANFILES = 2ls$(EXEEXT) $(DELTA_OBJ) -all: summarizer$(EXEEXT) +all: 2ls$(EXEEXT) ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) @@ -121,6 +121,6 @@ endif ############################################################################### -summarizer$(EXEEXT): $(OBJ) +2ls$(EXEEXT): $(OBJ) $(LINKBIN) From ab95c3a45f0625ab58da746c579bc2f0f2851835 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 26 Apr 2016 14:22:02 +0530 Subject: [PATCH 08/90] fixed ssa_unwinder bug (#1) --- src/ssa/ssa_unwinder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index fe7f13cf5..2e178e678 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -732,7 +732,7 @@ void ssa_local_unwindert::loop_continuation_conditions( { SSA.increment_unwindings(1); loop_cont.push_back(get_continuation_condition(loop)); //%0 - for(unsigned i=0; i<=loop.current_unwinding; ++i) + for(long i=0; i<=loop.current_unwinding; ++i) { //recurse into child loops for(std::vector::const_iterator l_it = loop.loop_nodes.begin(); From fe1093615c4f43a304aed5e083f08ce3ecd6a5a0 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 26 Apr 2016 14:53:51 +0530 Subject: [PATCH 09/90] fixed warning from comparing unsigned with long --- src/ssa/ssa_unwinder.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 2e178e678..40f7f0bae 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -275,7 +275,7 @@ void ssa_local_unwindert::build_exit_conditions() void ssa_local_unwindert::unwind(unsigned k) { - if(SSA.current_unwinding >= k) + if(SSA.current_unwinding >= (long)k) return; current_enabling_expr = @@ -322,7 +322,7 @@ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) << std::endl; #endif SSA.increment_unwindings(1); - for(unsigned i = 0; i<=k; ++i) + for(long i = 0; i<=(long)k; ++i) { //add new unwindings of this loop if(i>loop.current_unwinding || is_new_parent) @@ -353,7 +353,7 @@ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) add_assertions(loop,is_last); add_hoisted_assertions(loop,is_last); } - if(i==k) + if(i==(long)k) { add_loop_head(loop); //update loop head From a56186ff209e68dec09a37041ea7a470f907e4d9 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 26 Apr 2016 18:25:09 +0530 Subject: [PATCH 10/90] bug fixes for abstract case --- src/domains/disjunctive_analyzer.cpp | 10 +++++++--- src/summarizer/summarizer_bw_cex_ai.cpp | 25 +++++++++++++++---------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/domains/disjunctive_analyzer.cpp b/src/domains/disjunctive_analyzer.cpp index b82e3cfa0..9d619c364 100644 --- a/src/domains/disjunctive_analyzer.cpp +++ b/src/domains/disjunctive_analyzer.cpp @@ -178,12 +178,12 @@ bool disjunctive_analyzert::operator()( //std::cout << "unsimplified disjunctive cond: " << from_expr(SSA.ns, "", disjunctive_conditions) << "\n\n"; exprt simple_disjunctive_conditions = simplify_expr(disjunctive_conditions, SSA.ns); //disjunctive_conditions; - //std::cout << "simplified disjunctive cond: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n"; + //std::cout << " simplified disjunctive cond: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n"; //converting simple_disjunctive_conditions into DNF //std::cout << "before conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; this->convert_to_dnf(simple_disjunctive_conditions); - //std::cout << "after conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; + //std::cout << " after conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; if(simple_disjunctive_conditions.id() == ID_or){ @@ -192,11 +192,12 @@ bool disjunctive_analyzert::operator()( exprt::operandst disjuncts=simple_disjunctive_conditions.operands(); for(exprt::operandst::iterator d_it=disjuncts.begin();d_it!=disjuncts.end();d_it++){ if((*d_it).id() == ID_not){ + //std::cout << "processing (not) disjunct: " << from_expr(SSA.ns, "", *d_it) << "\n"; + exprt::operandst ops = (*d_it).operands(); for(exprt::operandst::iterator o_it=ops.begin();o_it!=ops.end();o_it++){ if((*o_it).id() == ID_equal){ exprt::operandst ops_equality = (*o_it).operands(); - equal_exprt equal_expr_in_not = to_equal_expr(*o_it); bool constant_comparison = false; @@ -212,6 +213,9 @@ bool disjunctive_analyzert::operator()( processed_disjuncts.push_back(*d_it); } } + else{ + processed_disjuncts.push_back(*d_it); + } } } else{ diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp index fb5ae3183..f39b8d3e7 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -265,10 +265,10 @@ void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis - //std::cout << "unsimplified constraints: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; + //std::cout << "unsimplified constraints (if): " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; exprt cc = simplify_expr(conjunction(c), SSA.ns); //exprt cc = conjunction(c); - //std::cout << "simplified constraints passed: " << from_expr(SSA.ns, "", cc) << "\n\n\n"; + //std::cout << "simplified constraints passed (if): " << from_expr(SSA.ns, "", cc) << "\n\n\n"; /* ssa_analyzert analyzer; @@ -277,13 +277,14 @@ void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, analyzer.get_result(summary.error_summaries[call_site], template_generator.inout_vars()); */ - + /**/ disjunctive_analyzert disjunctive_analyzer; disjunctive_analyzer.set_message_handler(get_message_handler()); disjunctive_analyzer(solver,SSA,cc,template_generator, cc,summary.error_summaries[call_site], template_generator.inout_vars()); - + /**/ + #if 0 std::cout << "SUM: " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; #endif @@ -308,20 +309,24 @@ void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, } else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly { - c.push_back(not_exprt(conjunction(assert_postcond))); - c.push_back(not_exprt(disjunction(noassert_postcond))); + c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt + c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis + //c.push_back(not_exprt(conjunction(assert_postcond))); + //c.push_back(not_exprt(disjunction(noassert_postcond))); - //std::cout << "unsimplified constraints: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; + //std::cout << "unsimplified constraints (else): " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; exprt cc = simplify_expr(conjunction(c), SSA.ns); //exprt cc = conjunction(c); - //std::cout << "simplified constraints passed: " << from_expr(SSA.ns, "", cc) << "\n\n\n"; + //std::cout << "simplified constraints passed (else): " << from_expr(SSA.ns, "", cc) << "\n\n\n"; + + //std::cout << "enabling expressions (else): " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) << "\n\n\n"; solver << SSA; solver.new_context(); solver << SSA.get_enabling_exprs(); solver << cc; - exprt result = false_exprt(); - if(solver()!=decision_proceduret::D_SATISFIABLE) result = true_exprt(); + exprt result = true_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) result = false_exprt(); solver.pop_context(); summary.error_summaries[call_site] = result; From d93675928aaada2af39895fe1471feffa152ca90 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 26 Apr 2016 21:31:45 +0530 Subject: [PATCH 11/90] loop-specific unwinding code, in progress --- src/ssa/ssa_unwinder.cpp | 59 +++++++++++++++++++++++++++ src/ssa/ssa_unwinder.h | 4 +- src/summarizer/summary_checker_ai.cpp | 45 ++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 40f7f0bae..e91b18bee 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -260,6 +260,45 @@ void ssa_local_unwindert::build_exit_conditions() } } +/***************************************************************************** + * + * Function : ssa_local_unwindert::unwind_loop_at_location() + * + * Input : + * + * Output : + * + * Purpose : unwind the loop at the given location, up to k starting from + * previous unwindings + * + * + *****************************************************************************/ + +void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) +{ + if(SSA.current_unwinding >= (long)k) + return; + + current_enabling_expr = + symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), + bool_typet()); + SSA.enabling_exprs.push_back(current_enabling_expr); + SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away + //recursively unwind everything + SSA.current_unwindings.clear(); + + loopt &loop = loops[loc]; + + if(loop.is_root){ + unwind(loop,k,false); //recursive + assert(SSA.current_unwindings.empty()); + } + + loop.current_unwinding=k; + + return; +} + /***************************************************************************** * * Function : ssa_local_unwindert::unwind @@ -808,6 +847,26 @@ void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, #endif } +/*****************************************************************************\ + * + * Function : ssa_unwindert::unwind_loop_alone() + * + * Input : + * + * Output : + * + * Purpose : + * + *****************************************************************************/ + +void ssa_unwindert::unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned int k) +{ + assert(is_initialized); + unwinder_mapt::iterator it = unwinder_map.find(fname); + assert(it != unwinder_map.end()); + it->second.unwind_loop_at_location(loc, k); +} + /*****************************************************************************\ * * Function : ssa_unwindert::unwind diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index bf1fc5a38..ab4bdf7ce 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -26,6 +26,7 @@ class ssa_local_unwindert void init(); + void unwind_loop_at_location(unsigned loc, unsigned k); void unwind(unsigned k); //TODO: not yet sure how to do that @@ -119,7 +120,8 @@ class ssa_unwindert : public messaget void init(bool is_kinduction, bool is_bmc); void init_localunwinders(); - + + void unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned k); void unwind(const irep_idt fname, unsigned k); void unwind_all(unsigned k); diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index 8168ef9a6..e08cf4b1b 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -41,6 +41,51 @@ property_checkert::resultt summary_checker_ait::operator()( ssa_unwinder.unwind_all(unwind); } + /*********************************************************************************/ + /**************** code to test the loop-specific unwind function *****************/ + /**/ + if(unwind > 0){ + forall_goto_functions(f_it, goto_model.goto_functions){ + if(!f_it->second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + local_SSAt &SSA = ssa_db.get(f_it->first); + std::cout << "==>> Output SSA for function: " << f_it->first << "\n"; + SSA.output(std::cout); + } + } + else{ + ssa_unwinder.init_localunwinders(); // possibly required; + + // iterate over the SSA to unwind loops + forall_goto_functions(f_it, goto_model.goto_functions){ + + if(!f_it->second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + + local_SSAt &SSA = ssa_db.get(f_it->first); + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++){ + if(n_it->loophead!=SSA.nodes.end()){ + std::cout << "==>> unwinding loop with location number: " + << n_it->loophead->location->location_number << "\n"; + ssa_unwinder.unwind_loop_alone(f_it->first, + n_it->loophead->location->location_number, 1); + } + } + } + + forall_goto_functions(f_it, goto_model.goto_functions){ + if(!f_it->second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + local_SSAt &SSA = ssa_db.get(f_it->first); + std::cout << "==>> Output SSA for function: " << f_it->first << "\n"; + SSA.output(std::cout); + } + + } + /**/ + /*********************************************************************************/ + irep_idt entry_function = goto_model.goto_functions.entry_point(); if(options.get_bool_option("unit-check")) entry_function = ""; From d8176613cd0f2b5f11f9bb3bed4c6969ed179f93 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Wed, 27 Apr 2016 01:17:31 +0530 Subject: [PATCH 12/90] minor spelling correction --- src/ssa/ssa_unwinder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index ab4bdf7ce..ebcb1ec50 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -33,7 +33,7 @@ class ssa_local_unwindert /* void unwind(locationt loop_head_loc, unsigned k) { unwind(loops[loop_head_loc],k); } */ - //TOOD: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related + //TODO: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related void loop_continuation_conditions(exprt::operandst& loop_cont) const; //TODO: these two should be possible with unwindable_local_ssa facilities From 92037fc2169601b51bd0d4005d44631712d1bd34 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Wed, 27 Apr 2016 21:06:40 +0530 Subject: [PATCH 13/90] commented new code to check loop-specific unwinding --- src/summarizer/summary_checker_ai.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index e08cf4b1b..ba692d4ae 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -43,7 +43,7 @@ property_checkert::resultt summary_checker_ait::operator()( /*********************************************************************************/ /**************** code to test the loop-specific unwind function *****************/ - /**/ + /* if(unwind > 0){ forall_goto_functions(f_it, goto_model.goto_functions){ if(!f_it->second.body_available()) continue; @@ -83,7 +83,7 @@ property_checkert::resultt summary_checker_ait::operator()( } } - /**/ + */ /*********************************************************************************/ irep_idt entry_function = goto_model.goto_functions.entry_point(); From 8f77de9f68b36b413889b6fc7bb92e7bcd8292fb Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Wed, 27 Apr 2016 22:28:30 +0530 Subject: [PATCH 14/90] commented debug code --- src/summarizer/summarizer_bw_cex_concrete.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index f06da6367..5e06a3103 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -342,11 +342,12 @@ void summarizer_bw_cex_concretet::do_summary( summary.has_assertion = assertion_flag; solver.pop_context(); - /**/ + /* // if the summary is true, print the postcondition and the list of loops in this function // this postcondition is modified, possibly twice, from what is returned by compute_calling_context2 // pc = end_guard => original_pc, and // pc = pc && not(assertion), if this is error function + std::cout << "==>>\n"; std::cout << "==>> Summary: true\n"; std::cout << "==>> Postcondition: " << from_expr(SSA.ns, "", postcondition) << "\n"; @@ -359,7 +360,7 @@ void summarizer_bw_cex_concretet::do_summary( } } std::cout << "==>>\n"; - /**/ + */ return; } From eea322b7588243cafdc0a639bfb670b84a8e6dd0 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Thu, 28 Apr 2016 01:12:21 +0530 Subject: [PATCH 15/90] corrected the id generator for #arg --- src/ssa/local_ssa.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index 99c01410f..60bfd80cb 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -542,8 +542,9 @@ void local_SSAt::build_function_call(locationt loc) for(exprt::operandst::iterator it = f.arguments().begin(); it != f.arguments().end(); it++, i++) { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i),it->type()); + //symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ + // "#arg"+i2string(i),it->type()); + symbol_exprt arg(id2string(fname)+"#arg"+i2string(i)+"#"+i2string(loc->location_number),it->type()); const typet &argtype = ns.follow(it->type()); if(argtype.id()==ID_struct) { From de80c292e7386ab505034c4fbc6817d60da997d5 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Thu, 28 Apr 2016 14:49:25 +0100 Subject: [PATCH 16/90] put unwinding suffix into ID_suffix attribute --- src/ssa/unwindable_local_ssa.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 19a01b4a4..7b81e2dbd 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -233,6 +233,12 @@ Function: unwindable_local_SSAt::rename void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { + if (expr.id() == ID_function_application) + { + std::string unwind_suffix = odometer_to_string(current_unwindings, + current_unwindings.size()); + expr.set(ID_suffix,unwind_suffix); + } if(expr.id()==ID_symbol) { symbol_exprt &s = to_symbol_expr(expr); @@ -245,6 +251,7 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) std::string unwind_suffix = odometer_to_string(current_unwindings, def_level); s.set_identifier(id2string(id)+unwind_suffix); + s.set(ID_suffix,unwind_suffix); #if 0 std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << def_level << std::endl; @@ -259,6 +266,7 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { std::string unwind_suffix = odometer_to_string(current_unwindings, current_unwindings.size()); + expr.set(ID_suffix,unwind_suffix); expr.set(ID_identifier, id2string(expr.get(ID_identifier))+unwind_suffix+suffix); } From 85a5f65669a4d56e4bfc2687d3b78b0df00ff3e1 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 11 May 2016 23:14:16 +0100 Subject: [PATCH 17/90] collect nondet vars in local_SSA --- src/ssa/local_ssa.cpp | 58 ++++++++++++++++++++++++++++++++++++++++--- src/ssa/local_ssa.h | 6 ++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index 99c01410f..bafaf0eca 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -125,8 +125,58 @@ void local_SSAt::get_entry_exit_vars() goto_programt::const_targett last = goto_function.body.instructions.end(); last--; get_globals(last,globals_out,true,true,last->function); + + //get nondeterministic variables + get_nondet_vars(); +} + +/*******************************************************************\ + +Function: local_SSAt::get_nondet_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::get_nondet_vars(const exprt &expr) +{ + if(expr.id()==ID_nondet) + nondets.insert(expr); + else + forall_operands(it, expr) + get_nondet_vars(*it); +} + +void local_SSAt::get_nondet_vars() +{ + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + { + for(nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + get_nondet_vars(*e_it); + + for(nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + get_nondet_vars(*c_it); + + for(nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + get_nondet_vars(*a_it); + } } + /*******************************************************************\ Function: local_SSAt::get_globals @@ -1546,10 +1596,10 @@ void local_SSAt::nodet::output( out << "(E) " << from_expr(ns, "", *e_it) << "\n"; for(constraintst::const_iterator - e_it=constraints.begin(); - e_it!=constraints.end(); - e_it++) - out << "(C) " << from_expr(ns, "", *e_it) << "\n"; + c_it=constraints.begin(); + c_it!=constraints.end(); + c_it++) + out << "(C) " << from_expr(ns, "", *c_it) << "\n"; for(assertionst::const_iterator a_it=assertions.begin(); diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 093bfec3d..d7830fe6e 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -128,7 +128,8 @@ class local_SSAt typedef std::list var_listt; typedef std::set var_sett; var_listt params; - var_sett globals_in, globals_out; + var_sett globals_in, globals_out; + std::set nondets; bool has_function_calls() const; @@ -214,6 +215,9 @@ class local_SSAt void collect_custom_templates(); replace_mapt template_newvars; exprt template_last_newvar; + + void get_nondet_vars(const exprt &expr); + void get_nondet_vars(); }; std::vector & operator << From bf27eda4d0b19fd9bb76745e50b94069e36d38e5 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 11 May 2016 23:25:21 +0100 Subject: [PATCH 18/90] add nondets as OUT vars to summary template for backwards analysis --- src/domains/template_generator_base.cpp | 11 +++++++++++ src/domains/template_generator_base.h | 5 +++++ src/domains/template_generator_summary.cpp | 7 +++++++ 3 files changed, 23 insertions(+) diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index f2b4746ba..e6525ac25 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -322,6 +322,17 @@ void template_generator_baset::add_var(const domaint::vart &var, } } +void template_generator_baset::add_vars(const std::set &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) +{ + for(std::set::const_iterator it = vars_to_add.begin(); + it != vars_to_add.end(); it++) + add_var(*it,pre_guard,post_guard,kind,var_specs); +} + void template_generator_baset::add_vars(const local_SSAt::var_listt &vars_to_add, const domaint::guardt &pre_guard, const domaint::guardt &post_guard, diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index dc66379a5..2e423fe91 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -74,6 +74,11 @@ class template_generator_baset : public messaget domaint::guardt post_guard, const domaint::kindt &kind, domaint::var_specst &var_specs); + void add_vars(const std::set &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); void add_vars(const var_listt &vars_to_add, const domaint::guardt &pre_guard, const domaint::guardt &post_guard, diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index e3c1162a0..3fe2b1a41 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -89,6 +89,13 @@ void template_generator_summaryt::collect_variables_inout(const local_SSAt &SSA, add_vars(SSA.globals_out,last_guard,last_guard, forward ? domaint::OUT : domaint::IN, var_specs); + + // add nondets for backwards analysis + if(!forward) + { + add_vars(SSA.nondets,first_guard,first_guard, + domaint::OUT, var_specs); + } } /*******************************************************************\ From 138c575237ef78644269a90f05c1183c6cd22f45 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 11 May 2016 23:44:31 +0100 Subject: [PATCH 19/90] fixed various places where nondet_symbols in templates were not expected --- src/domains/util.cpp | 6 +++--- src/ssa/local_ssa.cpp | 2 +- src/summarizer/summarizer_bw_cex_ai.cpp | 2 ++ src/summarizer/summarizer_bw_cex_concrete.cpp | 10 ++++++++++ src/summarizer/summary.cpp | 5 +++++ src/summarizer/summary.h | 3 ++- 6 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 0c7ed7607..80d45d499 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -30,7 +30,7 @@ void extend_expr_types(exprt &expr) return; } if(expr.id()==ID_constant) return; - if(expr.id()==ID_symbol) return; + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) return; if(expr.id()==ID_index) return; if(expr.id()==ID_unary_minus) { @@ -200,7 +200,7 @@ mp_integer simplify_const_int(const exprt &expr) assert(d!=0); return simplify_const_int(expr.op0())/d; } - if(expr.id()==ID_symbol) + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { #if 0 std::cerr << "substituting default value for " << expr << std::endl; @@ -279,7 +279,7 @@ ieee_floatt simplify_const_float(const exprt &expr) v1 /= v2; return v1; } - if(expr.id()==ID_symbol) //default value if not substituted in expr + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) //default value if not substituted in expr { ieee_floatt v; v.make_zero(); diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index bafaf0eca..d3f404809 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -144,7 +144,7 @@ Function: local_SSAt::get_nondet_vars void local_SSAt::get_nondet_vars(const exprt &expr) { - if(expr.id()==ID_nondet) + if(expr.id()==ID_nondet_symbol) nondets.insert(expr); else forall_operands(it, expr) diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp index f39b8d3e7..785456a1c 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -137,6 +137,7 @@ void summarizer_bw_cex_ait::compute_summary_rec( summary.params = SSA.params; summary.globals_in = SSA.globals_in; summary.globals_out = SSA.globals_out; + summary.nondets = SSA.nondets; } // insert assertion @@ -238,6 +239,7 @@ void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, assert_postcond.push_back(postcondition); //context + //TODO: add nondet variables from callees to summary.nondets // assumptions must hold for(local_SSAt::nodest::const_iterator diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index f06da6367..5cbdc3c92 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -130,6 +130,7 @@ void summarizer_bw_cex_concretet::compute_summary_rec( summary.params = SSA.params; summary.globals_in = SSA.globals_in; summary.globals_out = SSA.globals_out; + summary.nondets = SSA.nondets; } // insert assertion @@ -227,6 +228,8 @@ void summarizer_bw_cex_concretet::do_summary( assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false,assert_postcond,noassert_postcond,c); //backward summaries assert_postcond.push_back(postcondition); //context + //TODO: add nondet variables from callees to summary.nondets + //std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; //std::cout << "Noassert Summary: " << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; @@ -387,6 +390,13 @@ void summarizer_bw_cex_concretet::do_summary( var_values.push_back(equal_exprt(*it, summ_value)); } + for(std::set::const_iterator it = summary.nondets.begin(); + it != summary.nondets.end(); it++){ + exprt summ_value = solver.get(*it); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(*it, summ_value)); + } + summary.error_summaries[call_site] = not_exprt(conjunction(var_values)); summary.has_assertion = assertion_flag; diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp index 16e89810d..c4300a613 100644 --- a/src/summarizer/summary.cpp +++ b/src/summarizer/summary.cpp @@ -42,6 +42,11 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const it != globals_out.end(); it++) out << from_expr(ns,"",*it) << " "; out << std::endl; + out << "nondets: "; + for(summaryt::expr_sett::const_iterator it = nondets.begin(); + it != nondets.end(); it++) + out << from_expr(ns,"",*it) << " "; + out << std::endl; out << "forward precondition: " << (fw_precondition.is_nil() ? "not computed" : from_expr(ns,"",fw_precondition)) << std::endl; diff --git a/src/summarizer/summary.h b/src/summarizer/summary.h index 2eb417660..3aba80933 100644 --- a/src/summarizer/summary.h +++ b/src/summarizer/summary.h @@ -26,6 +26,7 @@ class summaryt typedef std::list var_listt; typedef std::set var_sett; + typedef std::set expr_sett; summaryt() : fw_precondition(nil_exprt()), @@ -41,7 +42,7 @@ class summaryt var_listt params; var_sett globals_in, globals_out; - + expr_sett nondets; predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently From 348cdc702ab1e78818d2c9b855cc3b88780dd22e Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Fri, 13 May 2016 13:19:56 +0530 Subject: [PATCH 20/90] added a comment for new fields in loopt --- src/ssa/ssa_unwinder.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index ebcb1ec50..5f01058d2 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -66,6 +66,9 @@ class ssa_local_unwindert bool is_dowhile; bool is_root; long current_unwinding; + + // to have an enabling_expr and current_unwindings (odometert) + typedef std::map exit_mapt; exit_mapt exit_map; std::map pre_post_map; From 895e97e1ac76a854fc444017e79a1c17e76dbe53 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Fri, 13 May 2016 09:18:18 +0100 Subject: [PATCH 21/90] fixes to replace_globals when inlining --- src/ssa/local_ssa.cpp | 14 ++++++++------ src/ssa/ssa_inliner.cpp | 38 ++++++++++---------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index d3f404809..e979cd48d 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -202,22 +202,24 @@ void local_SSAt::get_globals(locationt loc, std::set &globals, std::cout << "global: " << from_expr(ns, "", read_lhs(it->get_expr(),loc)) << std::endl; #endif - if(!with_returns && - id2string(it->get_identifier()).find( - "#return_value") != std::string::npos) + bool is_return = id2string(it->get_identifier()).find( + "#return_value") != std::string::npos; + if(!with_returns && is_return) continue; //filter out return values of other functions if(with_returns && returns_for_function!="" && - id2string(it->get_identifier()).find( - "#return_value") != std::string::npos && + is_return && id2string(it->get_identifier()).find( id2string(returns_for_function)+"#return_value")==std::string::npos) continue; if(rhs_value) { - const exprt &expr = read_rhs(it->get_expr(),loc); + //workaround for the problem that + // rhs() for a return value is always the "input" return value + const exprt &expr = is_return ? + read_lhs(it->get_expr(),--loc) : read_rhs(it->get_expr(),loc); globals.insert(to_symbol_expr(expr)); } else diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 3f03f818a..edfc2e6c7 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -124,16 +124,8 @@ void ssa_inlinert::get_summary( //getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc = n_it->location; - if(forward) - { - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - } - else - { - SSA.get_globals(loc,cs_globals_out); - SSA.get_globals(loc,cs_globals_in,false); - } + SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_out,false); #if 0 std::cout << "cs_globals_in: "; @@ -153,14 +145,7 @@ void ssa_inlinert::get_summary( get_replace_params(SSA,summary.params,n_it,*f_it,bindings,counter); //equalities for globals_in - if(forward){ - //get_replace_globals_in(summary.globals_in,cs_globals_in,bindings,counter); - get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); - } - else{ - //get_replace_globals_in(summary.globals_out,cs_globals_out,bindings,counter); - get_replace_globals_in(summary.globals_out,*f_it,cs_globals_out,bindings,counter); - } + get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); //constraints for transformer @@ -193,14 +178,7 @@ void ssa_inlinert::get_summary( transformer)); //equalities for globals out (including unmodified globals) - if(forward){ - //get_replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,bindings,counter); get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); - } - else{ - //get_replace_globals_out(summary.globals_in,cs_globals_out,cs_globals_in,bindings,counter); - get_replace_globals_out(summary.globals_in,*f_it,cs_globals_out,cs_globals_in,bindings,counter); - } } /*******************************************************************\ @@ -710,7 +688,7 @@ void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_o int counter) { std::string suffix = id2string(funapp_expr.get(ID_suffix)); - + //equalities for globals_out for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) @@ -722,7 +700,8 @@ void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_o symbol_exprt rhs; if(find_corresponding_symbol(*it,globals_out,rhs)) rename(rhs,counter); - else{ + else + { bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); assert(found); rhs.set_identifier(id2string(rhs.get_identifier())+suffix); @@ -1096,10 +1075,13 @@ bool ssa_inlinert::find_corresponding_symbol(const symbol_exprt &s, it != globals.end(); it++) { #if 0 - std::cout << s_orig_id << " =?= " << get_original_identifier(*it) << std::endl; + std::cout << s.get_identifier() << " =?= " << it->get_identifier() << std::endl; #endif if(s_orig_id == get_original_identifier(*it)) { +#if 0 + std::cout << s.get_identifier() << " == " << it->get_identifier() << std::endl; +#endif s_found = *it; return true; } From 6482398e703fff50029eee843029e998debdc20c Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Fri, 13 May 2016 09:58:05 +0100 Subject: [PATCH 22/90] nondet locals only once --- regression/modular/cex7/main.c | 2 +- src/summarizer/summarizer_parse_options.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/regression/modular/cex7/main.c b/regression/modular/cex7/main.c index 309c06dd9..cfa5ab827 100644 --- a/regression/modular/cex7/main.c +++ b/regression/modular/cex7/main.c @@ -7,7 +7,7 @@ int main(int argc, char** argv) while(y<30){ y++; - assert(y<5); + assert(y<3); } return 0; diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index 8a4253e26..ebe580703 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -1091,9 +1091,6 @@ bool summarizer_parse_optionst::process_goto_program( status() << "Constant Propagation" << eom; propagate_constants(goto_model); } - - //explicitly initialize all local variables - nondet_locals(goto_model); // if we aim to cover, replace // all assertions by false to prevent simplification From ec54dce70d2540ab4f7428a3569fe8d1566a2313 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Fri, 13 May 2016 20:49:43 +0100 Subject: [PATCH 23/90] fixed filtering of non-violating assertion instances before running spurious checker; various code clean-ups --- regression/modular/cex7/main.c | 2 +- src/ssa/ssa_inliner.cpp | 19 +-- src/ssa/ssa_unwinder.cpp | 2 +- src/summarizer/cover_goals_ext.cpp | 32 +++-- src/summarizer/summarizer_bw_cex_concrete.cpp | 120 ++++++++---------- 5 files changed, 76 insertions(+), 99 deletions(-) diff --git a/regression/modular/cex7/main.c b/regression/modular/cex7/main.c index cfa5ab827..8ab3e91f9 100644 --- a/regression/modular/cex7/main.c +++ b/regression/modular/cex7/main.c @@ -7,7 +7,7 @@ int main(int argc, char** argv) while(y<30){ y++; - assert(y<3); + assert(y<4); } return 0; diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index edfc2e6c7..857bc11b5 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -2,7 +2,7 @@ Module: SSA Inliner -Author: Peter Schrammel +Author: Peter Schrammel, Madhukar Kumar \*******************************************************************/ @@ -81,19 +81,12 @@ void ssa_inlinert::get_bindings( #endif //equalities for arguments - //bindings_in.push_back(get_replace_params(SSA.params,*f_it)); get_replace_params(SSA,fSSA.params,n_it,*f_it,bindings_in,counter); //equalities for globals_in - //bindings_in.push_back(get_replace_globals_in(SSA.globals_in,cs_globals_in)); - - //get_replace_globals_in(fSSA.globals_in,cs_globals_in,bindings_in,counter); get_replace_globals_in(fSSA.globals_in,*f_it,cs_globals_in,bindings_in,counter); //equalities for globals out (including unmodified globals) - //bindings_out.push_back(get_replace_globals_out(SSA.globals_out,cs_globals_in,cs_globals_out)); - - //get_replace_globals_out(fSSA.globals_out,cs_globals_in,cs_globals_out,bindings_out,counter); get_replace_globals_out(fSSA.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings_out,counter); } @@ -337,11 +330,6 @@ Function: ssa_inlinert::replace \*******************************************************************/ -/* -void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - bool recursive, bool rename) -*/ void ssa_inlinert::replace(local_SSAt &SSA, const ssa_dbt &ssa_db, int counter, @@ -591,8 +579,6 @@ void ssa_inlinert::get_replace_params(const local_SSAt &SSA, exprt::operandst &c, int counter) { - //std::string suffix = id2string(funapp_expr.get(ID_suffix)); - //equalities for arguments local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); @@ -638,9 +624,6 @@ void ssa_inlinert::get_replace_params(const local_SSAt &SSA, exprt lhs = *p_it; //copy rename(lhs,counter); c.push_back(equal_exprt(lhs,*it)); - //symbol_exprt sexpr = to_symbol_expr(*it); - //sexpr.set_identifier(id2string(sexpr.get_identifier())+suffix); - //c.push_back(equal_exprt(lhs,sexpr)); } } } diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 40f7f0bae..e8f071b4a 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -444,7 +444,7 @@ void ssa_local_unwindert::add_loop_body(loopt &loop) * * Output : * - * Purpose : adds the new loop head + * Purpose : adds the assertions * *****************************************************************************/ diff --git a/src/summarizer/cover_goals_ext.cpp b/src/summarizer/cover_goals_ext.cpp index edf21d4c3..3976e90b2 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/summarizer/cover_goals_ext.cpp @@ -193,31 +193,35 @@ void cover_goals_extt::assignment() } else { + //filter out assertion instances that are not violated exprt::operandst failed_exprs; for(exprt::operandst::const_iterator c_it = conjunct_expr.operands().begin(); - c_it != conjunct_expr.operands().end(); c_it++) + c_it != conjunct_expr.operands().end(); c_it++) { literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_false()) + if(solver.l_get(conjunct_literal).is_true()) + { +#ifdef DEBUG + std::cout << "failed_expr: " + << from_expr(SSA.ns, "", *c_it) << std::endl; +#endif failed_exprs.push_back(*c_it); + } } solver.pop_context(); //otherwise this would interfere with necessary preconditions - for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); - if(property_map[it->first].result == + summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); + property_map[it->first].result = summarizer_bw_cex.check(); + if(property_map[it->first].result == property_checkert::FAIL) + { + if(build_error_trace) { - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); - } - break; + ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); + build_goto_trace(property_map[it->first].error_trace); } + solver.new_context(); + break; } solver.new_context(); } diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 5cbdc3c92..373a1ec4a 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -138,7 +138,7 @@ void summarizer_bw_cex_concretet::compute_summary_rec( exprt postcondition = implies_exprt(end_guard,_postcondition); if(function_name == error_function) { - postcondition = and_exprt(postcondition,not_exprt(error_assertion)); + postcondition = not_exprt(error_assertion); //and_exprt(postcondition,not_exprt(error_assertion)); } summary.bw_postcondition = _postcondition; @@ -197,10 +197,11 @@ void summarizer_bw_cex_concretet::do_summary( // solver #ifdef OPT_2 - incremental_solvert* fresh_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); - //incremental_solvert &solver = ssa_db.get_solver(function_name); - + incremental_solvert* fresh_solver = + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); incremental_solvert &solver = (*fresh_solver); + SSA.unmark_nodes(); + exprt::operandst store; #else incremental_solvert &solver = ssa_db.get_solver(function_name); #endif @@ -230,24 +231,32 @@ void summarizer_bw_cex_concretet::do_summary( //TODO: add nondet variables from callees to summary.nondets - //std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; - //std::cout << "Noassert Summary: " << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; +#ifdef DEBUG + std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; + std::cout << "Noassert Summary: " << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; +#endif c.push_back(not_exprt(conjunction(assert_postcond))); c.push_back(not_exprt(disjunction(noassert_postcond))); -#if 0 +#ifdef DEBUG debug() << "Backward summaries: " << - from_expr(SSA.ns, "", conjunction(c)) << eom; + from_expr(SSA.ns, "", simplify_expr(conjunction(c),SSA.ns)) << eom; #endif #ifdef OPT_12 store << SSA; +#else +#ifdef OPT_2 + store << SSA; #else solver << SSA; #endif - +#endif + +#ifndef OPT_2 solver.new_context(); +#endif // assumptions must hold for(local_SSAt::nodest::const_iterator @@ -257,81 +266,76 @@ void summarizer_bw_cex_concretet::do_summary( for(local_SSAt::nodet::assumptionst::const_iterator a_it = n_it->assumptions.begin(); a_it != n_it->assumptions.end(); - ++a_it){ - - /* -#ifdef OPT_11 - solver << simplify_expr(*a_it, SSA.ns); -#elif OPT_12 - store.push_back(*a_it); -#else - solver << *a_it; -#endif - */ + ++a_it) + { #ifdef OPT_11 solver << simplify_expr(*a_it, SSA.ns); #else #ifdef OPT_12 store.push_back(*a_it); +#else +#ifdef OPT_2 + store.push_back(*a_it); #else solver << *a_it; #endif +#endif #endif } #ifdef OPT_12 store.push_back(SSA.get_enabling_exprs()); +#else +#ifdef OPT_2 + store.push_back(SSA.get_enabling_exprs()); #else solver << SSA.get_enabling_exprs(); #endif - - /* -#ifdef OPT_11 - solver << simplify_expr(conjunction(c), SSA.ns); -#elif OPT_12 - store.push_back(conjunction(c)); -#else - solver << conjunction(c); #endif - */ - + #ifdef OPT_11 solver << simplify_expr(conjunction(c), SSA.ns); #else #ifdef OPT_12 store.push_back(conjunction(c)); +#else +#ifdef OPT_2 + store.push_back(conjunction(c)); #else solver << conjunction(c); #endif +#endif #endif exprt::operandst loophead_selects; loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); - - /* -#ifdef OPT_11 - solver << simplify_expr(conjunction(loophead_selects), SSA.ns); -#elif OPT_12 - store.push_back(conjunction(loophead_selects)); -#else - solver << conjunction(loophead_selects); -#endif - */ #ifdef OPT_11 solver << simplify_expr(conjunction(loophead_selects), SSA.ns); #else #ifdef OPT_12 store.push_back(conjunction(loophead_selects)); +#else +#ifdef OPT_2 + store.push_back(conjunction(loophead_selects)); #else solver << conjunction(loophead_selects); #endif #endif +#endif #ifdef OPT_12 +#ifdef DEBUG std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif +#ifdef OPT_2 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) << "\n\n\n"; +#endif solver << simplify_expr(conjunction(store), SSA.ns); #endif @@ -339,17 +343,19 @@ void summarizer_bw_cex_concretet::do_summary( solver_calls++; //solve - if(solver() != decision_proceduret::D_SATISFIABLE) + if(solver() == decision_proceduret::D_UNSATISFIABLE) { summary.error_summaries[call_site] = true_exprt(); //TODO: this is likely to be incomplete summary.has_assertion = assertion_flag; +#ifndef OPT_2 solver.pop_context(); +#endif - /**/ // if the summary is true, print the postcondition and the list of loops in this function // this postcondition is modified, possibly twice, from what is returned by compute_calling_context2 // pc = end_guard => original_pc, and // pc = pc && not(assertion), if this is error function +#ifdef DEBUG std::cout << "==>>\n"; std::cout << "==>> Summary: true\n"; std::cout << "==>> Postcondition: " << from_expr(SSA.ns, "", postcondition) << "\n"; @@ -362,7 +368,7 @@ void summarizer_bw_cex_concretet::do_summary( } } std::cout << "==>>\n"; - /**/ +#endif return; } @@ -400,7 +406,9 @@ void summarizer_bw_cex_concretet::do_summary( summary.error_summaries[call_site] = not_exprt(conjunction(var_values)); summary.has_assertion = assertion_flag; +#ifndef OPT_2 solver.pop_context(); +#endif #ifdef OPT_2 delete fresh_solver; @@ -480,8 +488,8 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( // solver #ifdef OPT_2 - incremental_solvert* fresh_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); - //incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert* fresh_solver = + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); incremental_solvert &solver = (*fresh_solver); #else incremental_solvert &solver = ssa_db.get_solver(function_name); @@ -528,16 +536,6 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( solver << SSA.get_enabling_exprs(); #endif - /* -#ifdef OPT_11 - solver << simplify_expr(conjunction(c), SSA.ns); -#elif OPT_12 - store.push_back(conjunction(c)); -#else - solver << conjunction(c); -#endif - */ - #ifdef OPT_11 solver << simplify_expr(conjunction(c), SSA.ns); #else @@ -551,16 +549,6 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( exprt::operandst loophead_selects; loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); - /* -#ifdef OPT_11 - solver << simplify_expr(conjunction(loophead_selects), SSA.ns); -#elif OPT_12 - store.push_back(conjunction(loophead_selects)); -#else - solver << conjunction(loophead_selects); -#endif - */ - #ifdef OPT_11 solver << simplify_expr(conjunction(loophead_selects), SSA.ns); #else @@ -572,7 +560,9 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( #endif #ifdef OPT_12 +#ifdef DEBUG std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; +#endif solver << simplify_expr(conjunction(store), SSA.ns); #endif From e7870fc783fe42769780d8ab01e89d2547b74b28 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Mon, 16 May 2016 13:23:56 +0530 Subject: [PATCH 24/90] modified loop-specific unwinder to iterate over all loops --- src/ssa/ssa_unwinder.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index e91b18bee..03e219be7 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -286,15 +286,25 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away //recursively unwind everything SSA.current_unwindings.clear(); - - loopt &loop = loops[loc]; - - if(loop.is_root){ - unwind(loop,k,false); //recursive + + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it){ + if(!it->second.is_root) + continue; + + if(it->first == loc) + unwind(it->second,k,false); //recursive + else + unwind(it->second,it->second.current_unwinding,false); //recursive + assert(SSA.current_unwindings.empty()); } - - loop.current_unwinding=k; + + //update current unwinding + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + { + if(it->first == loc) + it->second.current_unwinding=k; + } return; } From 3b4ea1d47156399c4d27c0e0fe99c3bc064185df Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Fri, 20 May 2016 12:18:21 +0530 Subject: [PATCH 25/90] selective unwinding code --- src/ssa/local_ssa.cpp | 4 ++ src/ssa/local_ssa.h | 2 + src/ssa/ssa_unwinder.cpp | 56 ++++++++++++++++++++++----- src/ssa/ssa_unwinder.h | 4 +- src/summarizer/summary_checker_ai.cpp | 4 +- 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index a1d05beaa..f8fee84d1 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -1890,6 +1890,7 @@ Function: local_SSAt::get_enabling_expr exprt local_SSAt::get_enabling_exprs() const { + /* exprt::operandst result; result.reserve(enabling_exprs.size()); for(std::list::const_iterator it = enabling_exprs.begin(); @@ -1900,4 +1901,7 @@ exprt local_SSAt::get_enabling_exprs() const else result.push_back(*it); } return conjunction(result); + */ + + return combined_enabling_expr; } diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index d7830fe6e..398a60995 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -122,6 +122,8 @@ class local_SSAt // for incremental unwinding std::list enabling_exprs; + + exprt combined_enabling_expr; // combined enabling expr for loop-specific unwindings exprt get_enabling_exprs() const; // function entry and exit variables diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 03e219be7..978304c5c 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -279,10 +279,33 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) if(SSA.current_unwinding >= (long)k) return; - current_enabling_expr = - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), + loopt &loop = loops[loc]; + loop.loop_enabling_expr_current = + symbol_exprt("unwind::"+id2string(fname)+"loc::"+i2string(loc)+"::enable"+i2string(k), bool_typet()); - SSA.enabling_exprs.push_back(current_enabling_expr); + + loop.loop_enabling_exprs.push_back(loop.loop_enabling_expr_current); + exprt::operandst ssa_current_enabling_expr; + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it){ + exprt::operandst result; + for(exprt::operandst::iterator e_it = ((it->second).loop_enabling_exprs).begin(); + e_it != ((it->second).loop_enabling_exprs).end(); e_it++){ + exprt::operandst::iterator lh = e_it; lh++; + if(lh != ((it->second).loop_enabling_exprs).end()) result.push_back(not_exprt(*e_it)); + else result.push_back(*e_it); + } + ssa_current_enabling_expr.push_back(conjunction(result)); + } + + SSA.combined_enabling_expr = conjunction(ssa_current_enabling_expr); + + + //current_enabling_expr = + // symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), + // bool_typet()); + //SSA.enabling_exprs.push_back(current_enabling_expr); + + SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away //recursively unwind everything SSA.current_unwindings.clear(); @@ -292,9 +315,9 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) continue; if(it->first == loc) - unwind(it->second,k,false); //recursive + unwind(it->second,k,false,false); //recursive else - unwind(it->second,it->second.current_unwinding,false); //recursive + unwind(it->second,it->second.current_unwinding,false,true,k,loc); //recursive assert(SSA.current_unwindings.empty()); } @@ -338,7 +361,7 @@ void ssa_local_unwindert::unwind(unsigned k) { if(!it->second.is_root) continue; - unwind(it->second,k,false); //recursive + unwind(it->second,k,false,false); //recursive assert(SSA.current_unwindings.empty()); } //update current unwinding @@ -362,7 +385,8 @@ void ssa_local_unwindert::unwind(unsigned k) * *****************************************************************************/ -void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) +void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, + bool propagate, unsigned prop_unwind, unsigned prop_loc) { odometert context = SSA.current_unwindings; #ifdef DEBUG @@ -423,8 +447,22 @@ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) #ifdef DEBUG std::cout << i << ">" << loop.current_unwinding << std::endl; #endif - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent); + if(propagate == true){ + // if this child loop is the desired loop then unwind k and do not propagate + // else unwind loop.current_unwinding and propagate + if(*l_it == prop_loc){ + unwind(loops[*l_it],k,i>loop.current_unwinding || + is_new_parent,false); + } + else{ + unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || + is_new_parent,true,prop_unwind,prop_loc); + } + } + else{ + unwind(loops[*l_it],loops[*l_it].current_unwinding, + i>loop.current_unwinding || is_new_parent,false); + } } SSA.increment_unwindings(0); } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 5f01058d2..1cc65008f 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -68,6 +68,8 @@ class ssa_local_unwindert long current_unwinding; // to have an enabling_expr and current_unwindings (odometert) + exprt::operandst loop_enabling_exprs; + exprt loop_enabling_expr_current; typedef std::map exit_mapt; exit_mapt exit_map; @@ -93,7 +95,7 @@ class ssa_local_unwindert void build_pre_post_map(); void build_exit_conditions(); - void unwind(loopt &loop, unsigned k, bool is_new_parent); + void unwind(loopt &loop, unsigned k, bool is_new_parent, bool propagate = false, unsigned prop_unwind = 0, unsigned prop_loc = 0); exprt get_continuation_condition(const loopt& loop) const; void loop_continuation_conditions(const loopt& loop, diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index ba692d4ae..e08cf4b1b 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -43,7 +43,7 @@ property_checkert::resultt summary_checker_ait::operator()( /*********************************************************************************/ /**************** code to test the loop-specific unwind function *****************/ - /* + /**/ if(unwind > 0){ forall_goto_functions(f_it, goto_model.goto_functions){ if(!f_it->second.body_available()) continue; @@ -83,7 +83,7 @@ property_checkert::resultt summary_checker_ait::operator()( } } - */ + /**/ /*********************************************************************************/ irep_idt entry_function = goto_model.goto_functions.entry_point(); From 75c55eb95f04293eea494a75fad49cedc814a5eb Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Fri, 20 May 2016 14:54:21 +0100 Subject: [PATCH 26/90] output unsat core for spurious cex, uses incremental solver debug facilities at the moment --- regression/modular/Makefile | 2 +- src/domains/incremental_solver.h | 4 ++-- src/summarizer/summarizer_bw_cex_complete.cpp | 15 +++++++++++++++ src/summarizer/summarizer_bw_cex_concrete.cpp | 8 ++++---- src/summarizer/summary_checker_base.cpp | 3 +++ 5 files changed, 25 insertions(+), 7 deletions(-) diff --git a/regression/modular/Makefile b/regression/modular/Makefile index 2a3b5cb15..c26b769b2 100644 --- a/regression/modular/Makefile +++ b/regression/modular/Makefile @@ -1,6 +1,6 @@ default: tests.log -FLAGS = --verbosity 10 --spurious-check complete +FLAGS = --verbosity 10 --spurious-check concrete test: @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 8ce4ab945..fe1716ae3 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -24,8 +24,8 @@ Author: Peter Schrammel //#define NON_INCREMENTAL // (experimental) //#define DISPLAY_FORMULA -//#define DEBUG_FORMULA -//#define DEBUG_OUTPUT +#define DEBUG_FORMULA +#define DEBUG_OUTPUT class incremental_solvert : public messaget { diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index cad7b61de..a8d2db0dd 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -26,6 +26,8 @@ Author: Peter Schrammel #include "summarizer_bw_cex_complete.h" +#define SHOW_UNSAT_CORE + /*******************************************************************\ Function: summarizer_bw_cex_completet::summarize() @@ -494,6 +496,19 @@ property_checkert::resultt summarizer_bw_cex_completet::check() //std::cout << "Solver <-- renamed info ~ SAT\n"; return property_checkert::FAIL; } +#ifdef SHOW_UNSAT_CORE + else + { + for(unsigned i=0; iis_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; + } + } +#endif + //std::cout << "Solver <-- renamed info ~ UNSAT\n"; return property_checkert::UNKNOWN; } diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 373a1ec4a..87c27ee72 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -138,7 +138,7 @@ void summarizer_bw_cex_concretet::compute_summary_rec( exprt postcondition = implies_exprt(end_guard,_postcondition); if(function_name == error_function) { - postcondition = not_exprt(error_assertion); //and_exprt(postcondition,not_exprt(error_assertion)); + postcondition = and_exprt(postcondition,not_exprt(error_assertion)); } summary.bw_postcondition = _postcondition; @@ -327,8 +327,8 @@ void summarizer_bw_cex_concretet::do_summary( #endif #ifdef OPT_12 -#ifdef DEBUG - std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) << "\n\n\n"; +#if 1 + std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; #endif solver << simplify_expr(conjunction(store), SSA.ns); #endif @@ -560,7 +560,7 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( #endif #ifdef OPT_12 -#ifdef DEBUG +#if 1 std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; #endif solver << simplify_expr(conjunction(store), SSA.ns); diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 68c80b147..a05437bb0 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -328,6 +328,9 @@ void summary_checker_baset::check_properties( incremental_solvert* cex_complete_solver = incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); +#if 1 + cex_complete_solver->set_message_handler(get_message_handler()); +#endif if(options.get_bool_option("inline") || options.get_option("spurious-check") == "concrete") { From b2f574fc212c35994806e2bbe910d02226e95d4a Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 09:40:01 +0100 Subject: [PATCH 27/90] Srivas' examples --- regression/modular/Makefile | 2 +- regression/modular/kind1/main.c | 22 ++++++++++++++++++++ regression/modular/kind1/test.desc | 6 ++++++ regression/modular/kind2/main.c | 28 +++++++++++++++++++++++++ regression/modular/kind2/test.desc | 6 ++++++ regression/modular/kind3/main.c | 26 +++++++++++++++++++++++ regression/modular/kind3/test.desc | 6 ++++++ regression/modular/kind4/main.c | 33 ++++++++++++++++++++++++++++++ regression/modular/kind4/test.desc | 6 ++++++ 9 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 regression/modular/kind1/main.c create mode 100644 regression/modular/kind1/test.desc create mode 100644 regression/modular/kind2/main.c create mode 100644 regression/modular/kind2/test.desc create mode 100644 regression/modular/kind3/main.c create mode 100644 regression/modular/kind3/test.desc create mode 100644 regression/modular/kind4/main.c create mode 100644 regression/modular/kind4/test.desc diff --git a/regression/modular/Makefile b/regression/modular/Makefile index c26b769b2..2a3b5cb15 100644 --- a/regression/modular/Makefile +++ b/regression/modular/Makefile @@ -1,6 +1,6 @@ default: tests.log -FLAGS = --verbosity 10 --spurious-check concrete +FLAGS = --verbosity 10 --spurious-check complete test: @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" diff --git a/regression/modular/kind1/main.c b/regression/modular/kind1/main.c new file mode 100644 index 000000000..679c7c8be --- /dev/null +++ b/regression/modular/kind1/main.c @@ -0,0 +1,22 @@ +#include + +//Simple K-Induction safe example for K=4 +//No procedure calls; loop inv required: a != b != c + +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) + { + assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + i++; + } + + return 0; +} + diff --git a/regression/modular/kind1/test.desc b/regression/modular/kind1/test.desc new file mode 100644 index 000000000..630457914 --- /dev/null +++ b/regression/modular/kind1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind2/main.c b/regression/modular/kind2/main.c new file mode 100644 index 000000000..0925661c3 --- /dev/null +++ b/regression/modular/kind2/main.c @@ -0,0 +1,28 @@ +#include + + +int foo(int a, int b, int c) +{ + return a+1; +} + +// This main illustrates K-induction with proc call +// Safety can be shown for K = 4 after using summary TRUE for foo +// Only unwinding loop is required +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { + assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + if (b == c) a = foo(a,b,c); + i++; + } + + return 0; +} + diff --git a/regression/modular/kind2/test.desc b/regression/modular/kind2/test.desc new file mode 100644 index 000000000..630457914 --- /dev/null +++ b/regression/modular/kind2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind3/main.c b/regression/modular/kind3/main.c new file mode 100644 index 000000000..813307dda --- /dev/null +++ b/regression/modular/kind3/main.c @@ -0,0 +1,26 @@ +#include + + +int bar(int a, int b, int c) +{ + return c; +} + +//This main illustrates need for refinement of bar +//based on spurious CEX +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { + assert(a != b); + sc = c; c = b; b = a; a = bar(a,b,sc); + //a, b, c = bar(a,b,c), a, b; parallel assignment; + i++; + } + + return 0; +} + diff --git a/regression/modular/kind3/test.desc b/regression/modular/kind3/test.desc new file mode 100644 index 000000000..630457914 --- /dev/null +++ b/regression/modular/kind3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind4/main.c b/regression/modular/kind4/main.c new file mode 100644 index 000000000..f8dca89ae --- /dev/null +++ b/regression/modular/kind4/main.c @@ -0,0 +1,33 @@ +#include + +int foo(int a, int b, int c) +{ + return a+1; +} + +int bar(int a, int b, int c) +{ + return c; +} + +// This main illustrates K-induction with proc call +// Safety can be shown for K = 4 after using summary TRUE for foo +// Only unwinding loop is required +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,c,sc, i = 0; + __CPROVER_assume(a != b && b != c && c != a); + + while (i < limit) { + assert(a != b); + sc = c; c = b; b = a; a = sc; + //a, b, c = c, a, b; parallel assignment; + if (b == c) a = foo(a,b,c); + else c = bar(a,b,c); + i++; + } + + return 0; +} + diff --git a/regression/modular/kind4/test.desc b/regression/modular/kind4/test.desc new file mode 100644 index 000000000..630457914 --- /dev/null +++ b/regression/modular/kind4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ From d8577d36c82cacc5f858df2c436eff03c5c6d5cd Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 10:38:44 +0100 Subject: [PATCH 28/90] moved assertion into bar() --- regression/modular/kind4/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/regression/modular/kind4/main.c b/regression/modular/kind4/main.c index f8dca89ae..5b4106e35 100644 --- a/regression/modular/kind4/main.c +++ b/regression/modular/kind4/main.c @@ -1,5 +1,6 @@ #include + int foo(int a, int b, int c) { return a+1; @@ -7,9 +8,11 @@ int foo(int a, int b, int c) int bar(int a, int b, int c) { + assert(a != b); return c; } + // This main illustrates K-induction with proc call // Safety can be shown for K = 4 after using summary TRUE for foo // Only unwinding loop is required @@ -20,11 +23,12 @@ int main(int argc, char** argv) __CPROVER_assume(a != b && b != c && c != a); while (i < limit) { - assert(a != b); +// assert(a != b); sc = c; c = b; b = a; a = sc; //a, b, c = c, a, b; parallel assignment; if (b == c) a = foo(a,b,c); - else c = bar(a,b,c); + //else //TODO: investigate why k-ind doesn't terminate if the call to bar is here + c = bar(a,b,c); i++; } From c9e59c82b98c9fcc5e33fa604430c98abbedceb3 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 16:19:47 +0100 Subject: [PATCH 29/90] recompute dependency graphs after unwinding --- src/ssa/local_ssa.cpp | 4 +-- src/summarizer/summary_checker_ai.cpp | 27 ++++------------- src/summarizer/summary_checker_base.cpp | 39 +++++++++++++++++++++++++ src/summarizer/summary_checker_base.h | 1 + src/summarizer/summary_checker_bmc.cpp | 11 +++++++ src/summarizer/summary_checker_kind.cpp | 10 +++++++ 6 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index e979cd48d..27f846796 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -594,8 +594,8 @@ void local_SSAt::build_function_call(locationt loc) for(exprt::operandst::iterator it = f.arguments().begin(); it != f.arguments().end(); it++, i++) { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i),it->type()); + symbol_exprt arg(id2string(fname)+ + "#arg"+i2string(i)+"#"+i2string(loc->location_number),it->type()); const typet &argtype = ns.follow(it->type()); if(argtype.id()==ID_struct) { diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index 8168ef9a6..c332fb117 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -45,29 +45,12 @@ property_checkert::resultt summary_checker_ait::operator()( if(options.get_bool_option("unit-check")) entry_function = ""; - if(!(options.get_bool_option("inline"))){ + if(!(options.get_bool_option("inline"))) + { if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")){ - // compute dependency graph for all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - - status() << "Computing dependency graph of " << f_it->first << messaget::eom; - - //ssa_db.depgraph_create(f_it->first, ns, ssa_inliner); - - if(entry_function == f_it->first) - ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, true); - else - ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal - - ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); - ssa_depgraph.output(debug()); debug() << eom; - std::cout << "output SSA for function: " << f_it->first << "\n"; - ssa_db.get(f_it->first).output_verbose(std::cout); - } + (options.get_option("spurious-check") != "abstract")) + { + SSA_dependency_graphs(goto_model, ns); } } diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index a05437bb0..5c0b69123 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -47,6 +47,45 @@ Author: Peter Schrammel #include "summarizer_fw_contexts.h" #endif + +/*******************************************************************\ + +Function: summary_checker_baset::SSA_dependency_graphs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_baset::SSA_dependency_graphs( + const goto_modelt &goto_model, + const namespacet &ns) +{ + // compute dependency graph for all the functions + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(!f_it->second.body_available()) continue; + if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + + status() << "Computing dependency graph of " << f_it->first << messaget::eom; + + //ssa_db.depgraph_create(f_it->first, ns, ssa_inliner); + + if(entry_function == f_it->first) + ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, true); + else + ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal + + ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); + ssa_depgraph.output(debug()); debug() << eom; + std::cout << "output SSA for function: " << f_it->first << "\n"; + ssa_db.get(f_it->first).output_verbose(std::cout); + } +} + /*******************************************************************\ Function: summary_checker_baset::SSA_functions diff --git a/src/summarizer/summary_checker_base.h b/src/summarizer/summary_checker_base.h index d2419b7ec..1c907891a 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/summarizer/summary_checker_base.h @@ -72,6 +72,7 @@ class summary_checker_baset:public property_checkert const local_SSAt::nodet::assertionst::const_iterator &); void SSA_functions(const goto_modelt &, const namespacet &ns); + void SSA_dependency_graphs(const goto_modelt &, const namespacet &ns); void summarize(const goto_modelt &, bool forward=true, bool termination=false); diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/summarizer/summary_checker_bmc.cpp index c15331294..0744d60d3 100644 --- a/src/summarizer/summary_checker_bmc.cpp +++ b/src/summarizer/summary_checker_bmc.cpp @@ -43,6 +43,17 @@ property_checkert::resultt summary_checker_bmct::operator()( status() << "Unwinding (k=" << unwind << ")" << messaget::eom; summary_db.mark_recompute_all(); ssa_unwinder.unwind_all(unwind); + + //dependency graphs + if(!(options.get_bool_option("inline"))) + { + if((options.get_option("spurious-check") != "concrete") && + (options.get_option("spurious-check") != "abstract")) + { + SSA_dependency_graphs(goto_model, ns); + } + } + std::set seen_function_calls; result = check_properties(entry_function, entry_function, seen_function_calls); if(result == property_checkert::PASS) diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index d5f2826bf..a3793d35c 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -44,6 +44,16 @@ property_checkert::resultt summary_checker_kindt::operator()( status() << "Unwinding (k=" << unwind << ")" << eom; summary_db.mark_recompute_all(); //TODO: recompute only functions with loops ssa_unwinder.unwind_all(unwind); + + //dependency graphs + if(!(options.get_bool_option("inline"))) + { + if((options.get_option("spurious-check") != "concrete") && + (options.get_option("spurious-check") != "abstract")) + { + SSA_dependency_graphs(goto_model, ns); + } + } std::set seen_function_calls; result = check_properties(entry_function, entry_function, seen_function_calls); From fd451e1efdb0deb690e631a18217ccc74c4c2572 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 16:49:45 +0100 Subject: [PATCH 30/90] fixed renaming of nondets on inlining --- src/ssa/ssa_inliner.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 857bc11b5..d500f7114 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -809,10 +809,9 @@ Function: ssa_inlinert::rename() void ssa_inlinert::rename(exprt &expr, int counter) { - if(expr.id()==ID_symbol) + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - symbol_exprt &sexpr = to_symbol_expr(expr); - std::string id_str = id2string(sexpr.get_identifier()); + std::string id_str = id2string(expr.get(ID_identifier)); irep_idt id; if(id_str.find('@') != std::string::npos) @@ -820,7 +819,7 @@ void ssa_inlinert::rename(exprt &expr, int counter) else id = id_str+"@"+i2string(counter); - sexpr.set_identifier(id); + expr.set(ID_identifier,id); } for(exprt::operandst::iterator it = expr.operands().begin(); it != expr.operands().end(); it++) From 6a99d32512a5783fd332fccb68c9645cc911a2e3 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 16:55:27 +0100 Subject: [PATCH 31/90] propagate nondets in error summaries up to entry point --- src/summarizer/summarizer_bw_cex_ai.cpp | 8 +++++++- src/summarizer/summarizer_bw_cex_concrete.cpp | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp index 785456a1c..2d7ec6dec 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -239,7 +239,13 @@ void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, assert_postcond.push_back(postcondition); //context - //TODO: add nondet variables from callees to summary.nondets + //add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond),summary_vars); + for(std::set::const_iterator it = summary_vars.begin(); + it != summary_vars.end(); ++it) + if(it->id()==ID_nondet_symbol) + summary.nondets.insert(*it); // assumptions must hold for(local_SSAt::nodest::const_iterator diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 87c27ee72..19ecbd3b7 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -229,7 +229,13 @@ void summarizer_bw_cex_concretet::do_summary( assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false,assert_postcond,noassert_postcond,c); //backward summaries assert_postcond.push_back(postcondition); //context - //TODO: add nondet variables from callees to summary.nondets + //add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond),summary_vars); + for(std::set::const_iterator it = summary_vars.begin(); + it != summary_vars.end(); ++it) + if(it->id()==ID_nondet_symbol) + summary.nondets.insert(*it); #ifdef DEBUG std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; From 4ebc6618785b4669ed4c14a8f2555e60cbf4175f Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 23 May 2016 17:35:27 +0100 Subject: [PATCH 32/90] fixed use of ssa_expr in trace output --- regression/modular/kind5/main.c | 18 ++++++++++++++++++ regression/modular/kind5/test.desc | 6 ++++++ src/ssa/ssa_build_goto_trace.cpp | 8 ++++---- 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 regression/modular/kind5/main.c create mode 100644 regression/modular/kind5/test.desc diff --git a/regression/modular/kind5/main.c b/regression/modular/kind5/main.c new file mode 100644 index 000000000..6f6d29b27 --- /dev/null +++ b/regression/modular/kind5/main.c @@ -0,0 +1,18 @@ +#include + +int main(int argc, char** argv) +{ + unsigned int limit; + int a,b,sc, i = 0; + __CPROVER_assume(a != b); + + while (i < limit) + { + assert(a != b); + sc = b; b = a; a = sc; + i++; + } + + return 0; +} + diff --git a/regression/modular/kind5/test.desc b/regression/modular/kind5/test.desc new file mode 100644 index 000000000..630457914 --- /dev/null +++ b/regression/modular/kind5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/src/ssa/ssa_build_goto_trace.cpp b/src/ssa/ssa_build_goto_trace.cpp index c24d24188..a257e3134 100644 --- a/src/ssa/ssa_build_goto_trace.cpp +++ b/src/ssa/ssa_build_goto_trace.cpp @@ -164,15 +164,15 @@ bool ssa_build_goto_tracet::record_step( step.full_lhs_value=rhs_simplified; if(lhs_simplified.id()==ID_symbol) { - step.lhs_object = to_ssa_expr(lhs_simplified); - step.lhs_object_value=rhs_simplified; //filter out internal stuff - if(id2string(step.lhs_object.get_identifier()).find("#") + if(id2string(to_symbol_expr(lhs_simplified).get_identifier()).find("#") != std::string::npos) break; //filter out undetermined values - if(step.lhs_object_value.id()!=ID_constant) + if(rhs_simplified.id()!=ID_constant) break; + step.lhs_object = ssa_exprt(lhs_simplified); + step.lhs_object_value=rhs_simplified; } if(step.lhs_object.is_nil()) break; From 877fc7719305d69d6d21e6622e9df04ab48d2e0b Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 24 May 2016 19:20:34 +0530 Subject: [PATCH 33/90] minor bug fixes --- src/ssa/local_ssa.cpp | 15 ++++++++++++-- src/ssa/local_ssa.h | 2 +- src/summarizer/summarizer_bw_cex_concrete.cpp | 20 ------------------- src/summarizer/summary_checker_base.cpp | 1 - 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index da9c2f446..cb003f106 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -19,6 +19,9 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include + + #include #include @@ -1904,6 +1907,14 @@ exprt local_SSAt::get_enabling_exprs() const } return conjunction(result); */ - - return combined_enabling_expr; + + + if(combined_enabling_expr.is_not_nil()){ + std::cout << "combined enabling expr:" << from_expr(ns, "", combined_enabling_expr) << "\n"; + return combined_enabling_expr; + } + else{ + std::cout << "combined enabling expr is nil; returning true\n"; + return true_exprt(); + } } diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 398a60995..8965dad7f 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -123,7 +123,7 @@ class local_SSAt // for incremental unwinding std::list enabling_exprs; - exprt combined_enabling_expr; // combined enabling expr for loop-specific unwindings + exprt combined_enabling_expr = true_exprt(); // combined enabling expr for loop-specific unwindings exprt get_enabling_exprs() const; // function entry and exit variables diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index aad7db3de..268690039 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -351,26 +351,6 @@ void summarizer_bw_cex_concretet::do_summary( solver.pop_context(); #endif -#ifdef DEBUG - /* - // if the summary is true, print the postcondition and the list of loops in this function - // this postcondition is modified, possibly twice, from what is returned by compute_calling_context2 - // pc = end_guard => original_pc, and - // pc = pc && not(assertion), if this is error function - std::cout << "==>>\n"; - std::cout << "==>> Summary: true\n"; - std::cout << "==>> Postcondition: " << from_expr(SSA.ns, "", postcondition) << "\n"; - std::cout << "==>> Function: " << function_name << "\n"; - std::cout << "==>> Loophead selects: " << from_expr(SSA.ns, "", conjunction(loophead_selects)) << "\n"; - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++){ - if(n_it->loophead!=SSA.nodes.end()){ - std::cout << "==>> Loop found: " << n_it->loophead->location->location_number << "\n"; - } - } - std::cout << "==>>\n"; - */ -#endif return; } diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index a05437bb0..b0fe87ca9 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -307,7 +307,6 @@ void summary_checker_baset::check_properties( solver << summary.fw_precondition; } - //callee summaries solver << ssa_inliner.get_summaries(SSA); From 684519a90cc7ac4de68f14f8ee9f59a17cd4f857 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 25 May 2016 09:35:33 +0100 Subject: [PATCH 34/90] unsat core output (without using incremental solver debug mode) --- src/domains/incremental_solver.h | 4 +- src/summarizer/summarizer_bw_cex_complete.cpp | 51 ++++++++++++++++--- src/summarizer/summarizer_bw_cex_complete.h | 4 ++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index fe1716ae3..8ce4ab945 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -24,8 +24,8 @@ Author: Peter Schrammel //#define NON_INCREMENTAL // (experimental) //#define DISPLAY_FORMULA -#define DEBUG_FORMULA -#define DEBUG_OUTPUT +//#define DEBUG_FORMULA +//#define DEBUG_OUTPUT class incremental_solvert : public messaget { diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index a8d2db0dd..4bcc56376 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -2,7 +2,7 @@ Module: Simple Complete Counterexample-based Backward Analysis -Author: Peter Schrammel +Author: Madhukar Kumar, Peter Schrammel \*******************************************************************/ @@ -95,7 +95,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries int counter) { local_SSAt &SSA = ssa_db.get(function_name); - //solver << SSA.get_enabling_exprs(); +#ifdef SHOW_UNSAT_CORE + add_to_formula(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif exprt::operandst loophead_selects; loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); @@ -111,7 +115,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; #endif +#ifdef SHOW_UNSAT_CORE + add_to_formula(c); +#else solver << c; +#endif ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); @@ -302,7 +310,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries << worknode.node_index << "\t renamed info ~ " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; #endif +#ifdef SHOW_UNSAT_CORE + add_to_formula(worknode_info); +#else solver << worknode_info; +#endif } } else{ @@ -318,7 +330,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries << worknode.node_index << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; #endif - solver << guard_binding; +#ifdef SHOW_UNSAT_CORE + add_to_formula(guard_binding); +#else + solver << guard_binding; +#endif } } } @@ -492,6 +508,9 @@ Function: summarizer_bw_cex_completet::check() property_checkert::resultt summarizer_bw_cex_completet::check() { solver_calls++; // for statistics +#ifdef SHOW_UNSAT_CORE + solver.solver->set_assumptions(formula); +#endif if(solver() == decision_proceduret::D_SATISFIABLE){ //std::cout << "Solver <-- renamed info ~ SAT\n"; return property_checkert::FAIL; @@ -499,12 +518,11 @@ property_checkert::resultt summarizer_bw_cex_completet::check() #ifdef SHOW_UNSAT_CORE else { - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; - else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + const local_SSAt &SSA = ssa_db.get(entry_function); + if(solver.solver->is_in_conflict(formula[i])) + debug() << "is_in_conflict: " << from_expr(SSA.ns, "", formula_expr[i]) << eom; } } #endif @@ -538,3 +556,20 @@ void summarizer_bw_cex_completet::debug_print } std::cout << "\n"; } + +void summarizer_bw_cex_completet::add_to_formula(const exprt &expr) +{ + literalt l = solver.solver->convert(expr); + if(l.is_false()) + { + literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", + bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); + } + else if(!l.is_true()) + { + formula.push_back(l); + formula_expr.push_back(expr); + } +} diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h index 243120480..a5cb85f87 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -44,6 +44,8 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset protected: incremental_solvert &solver; + bvt formula; //for UNSAT core + exprt::operandst formula_expr; //for debugging virtual find_symbols_sett inline_summaries( const function_namet &function_name, @@ -58,6 +60,8 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset const function_namet &function_name, find_symbols_sett &dependency_set); + void add_to_formula(const exprt &expr); + }; From c71f093fc10f71d7c6611c5e59e311f698bba523 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 25 May 2016 12:34:08 +0100 Subject: [PATCH 35/90] get reason for spuriousness --- src/summarizer/summarizer_bw_cex.h | 10 +++++++++- src/summarizer/summary_checker_base.cpp | 2 ++ src/summarizer/summary_checker_base.h | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/summarizer/summarizer_bw_cex.h b/src/summarizer/summarizer_bw_cex.h index 5cead1add..83d2c9e44 100644 --- a/src/summarizer/summarizer_bw_cex.h +++ b/src/summarizer/summarizer_bw_cex.h @@ -22,16 +22,24 @@ Author: Kumar Madhukar, Peter Schrammel class summarizer_bw_cex_baset : public summarizer_bwt { public: + struct reasont + { + std::set function_names; + std::set loop_ids; + }; + virtual void summarize(); virtual void summarize(const function_namet &entry_function); virtual void summarize(const exprt &_error_assertion); virtual property_checkert::resultt check(); - + virtual void get_reason(reasont &_reason) { _reason = reason; } + protected: function_namet entry_function; function_namet error_function; exprt error_assertion; + reasont reason; explicit summarizer_bw_cex_baset(optionst &_options, summary_dbt &_summary_db, diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 5c0b69123..621febbb9 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -510,6 +510,8 @@ void summary_checker_baset::check_properties( solver.pop_context(); + summarizer_bw_cex->get_reason(reason); + debug() << "** " << cover_goals.number_covered() << " of " << cover_goals.size() << " failed (" << cover_goals.iterations() << " iterations)" << eom; diff --git a/src/summarizer/summary_checker_base.h b/src/summarizer/summary_checker_base.h index 1c907891a..5cf648707 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/summarizer/summary_checker_base.h @@ -21,6 +21,7 @@ Author: Peter Schrammel #include "../domains/incremental_solver.h" #include "ssa_db.h" #include "summary_db.h" +#include "summarizer_bw_cex.h" class summary_checker_baset:public property_checkert { @@ -61,6 +62,8 @@ class summary_checker_baset:public property_checkert irep_idt entry_function; + summarizer_bw_cex_baset::reasont reason; + unsigned solver_instances; unsigned solver_calls; unsigned summaries_used; From 43714a82f2b47b1a24ea1f1800a3d31fa6935517 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Thu, 26 May 2016 10:01:45 +0100 Subject: [PATCH 36/90] to start with, add all functions and loop ids to reason --- src/summarizer/summarizer_bw_cex.h | 11 +++++++++-- src/summarizer/summarizer_bw_cex_complete.cpp | 19 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex.h b/src/summarizer/summarizer_bw_cex.h index 83d2c9e44..e11a6db05 100644 --- a/src/summarizer/summarizer_bw_cex.h +++ b/src/summarizer/summarizer_bw_cex.h @@ -25,7 +25,14 @@ class summarizer_bw_cex_baset : public summarizer_bwt struct reasont { std::set function_names; - std::set loop_ids; + std::set loop_ids; //TODO: location_number should be sufficient + + void merge(const reasont &other) + { + function_names.insert(other.function_names.begin(), + other.function_names.end()); + loop_ids.insert(other.loop_ids.begin(), other.loop_ids.end()); + } }; virtual void summarize(); @@ -33,7 +40,7 @@ class summarizer_bw_cex_baset : public summarizer_bwt virtual void summarize(const exprt &_error_assertion); virtual property_checkert::resultt check(); - virtual void get_reason(reasont &_reason) { _reason = reason; } + virtual void get_reason(reasont &_reason) { _reason.merge(reason); } protected: function_namet entry_function; diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 4bcc56376..d89cf02be 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -95,12 +95,25 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries int counter) { local_SSAt &SSA = ssa_db.get(function_name); + + //add enabling expressions + exprt enable_exprs = SSA.get_enabling_exprs(); + ssa_inliner.rename(enable_exprs, counter); + #ifdef SHOW_UNSAT_CORE - add_to_formula(SSA.get_enabling_exprs()); + add_to_formula(enable_exprs); #else - solver << SSA.get_enabling_exprs(); + solver << enable_exprs; #endif - + + //TODO: let's just put everything into the reason + reason.function_names.insert(function_name); + for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); ++n_it) + if (n_it->loophead != SSA.nodes.end()) + reason.loop_ids.insert(n_it->loophead->location); + + //add loop selects exprt::operandst loophead_selects; loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); exprt c = conjunction(loophead_selects); From db80f3c30e84d14c738b2a4806ad02aaec18caf4 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Thu, 26 May 2016 10:40:47 +0100 Subject: [PATCH 37/90] reason grouped by functions; identify callee by call site location --- src/summarizer/summarizer_bw_cex.h | 23 ++++++++++++++----- src/summarizer/summarizer_bw_cex_complete.cpp | 9 +++++--- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex.h b/src/summarizer/summarizer_bw_cex.h index e11a6db05..b76a214fb 100644 --- a/src/summarizer/summarizer_bw_cex.h +++ b/src/summarizer/summarizer_bw_cex.h @@ -22,16 +22,27 @@ Author: Kumar Madhukar, Peter Schrammel class summarizer_bw_cex_baset : public summarizer_bwt { public: - struct reasont + struct reason_infot { - std::set function_names; - std::set loop_ids; //TODO: location_number should be sufficient + typedef local_SSAt::locationt function_infot; //call_site; restriction: we assume that there is a single function call in an SSA node + typedef local_SSAt::locationt loop_infot; + std::set functions; + std::set loops; + }; + class reasont : public std::map + { + public: void merge(const reasont &other) { - function_names.insert(other.function_names.begin(), - other.function_names.end()); - loop_ids.insert(other.loop_ids.begin(), other.loop_ids.end()); + for(reasont::const_iterator it = other.begin(); + it != other.end(); ++it) + { + reason_infot &r = (*this)[it->first]; + r.functions.insert(it->second.functions.begin(), + it->second.functions.end()); + r.loops.insert(it->second.loops.begin(), it->second.loops.end()); + } } }; diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index d89cf02be..78585bbd8 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -106,12 +106,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries solver << enable_exprs; #endif - //TODO: let's just put everything into the reason - reason.function_names.insert(function_name); + //TODO: let's just put all loops into the reason for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) if (n_it->loophead != SSA.nodes.end()) - reason.loop_ids.insert(n_it->loophead->location); + reason[function_name].loops.insert(n_it->loophead->location); //add loop selects exprt::operandst loophead_selects; @@ -271,6 +270,10 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries */ ///////////////////////////////////////////////////////////////////////////////////// + //TODO: just put all function calls into reason + reason[function_name].functions.insert(ssa_depgraph.depnodes_map[worknode.node_index].location); + + //recurse worknode.dependency_set = compute_summary_rec(fname,worknode.dependency_set, ssa_depgraph.depnodes_map[worknode.node_index].rename_counter); From 25a27f926ccb947287e065f6dcb9d49d58452c44 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Fri, 27 May 2016 13:35:36 +0530 Subject: [PATCH 38/90] selective unwinding and inlining code - in progress --- src/summarizer/summary_checker_kind.cpp | 60 ++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index a3793d35c..2dd262d05 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -43,7 +43,65 @@ property_checkert::resultt summary_checker_kindt::operator()( { status() << "Unwinding (k=" << unwind << ")" << eom; summary_db.mark_recompute_all(); //TODO: recompute only functions with loops - ssa_unwinder.unwind_all(unwind); + //ssa_unwinder.unwind_all(unwind); + + // unwind loops "selectively" (those that seem to be the "reason") + for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it){ + for(std::set::const_iterator l_it = it->second.loops.begin(); + l_it != it->second.loops.end(); l_it++){ + ssa_unwinder.unwind_loop_alone(it->first, (*l_it)->location_number, unwind); + } + } + + // inline functions "selectively" (those that seem to be the "reason") + for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it){ + for(std::set::const_iterator f_it = it->second.functions.begin(); + f_it != it->second.functions.end(); f_it++){ + local_SSAt &SSA = ssa_db.get(it->first); + + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++){ + + local_SSAt::nodet &node=*n_it; + if(node.location == *(f_it)){ + + for(local_SSAt::nodet::function_callst::const_iterator fc_it=node.function_calls.begin(); + fc_it!=node.function_calls.end(); fc_it++){ + + irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); + if(ssa_db.exists(fname)) + { + const local_SSAt &fSSA = ssa_db.get(fname); + + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; + int counter = ssa_inliner.get_rename_counter(); + + ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); + equal_exprt e = to_equal_expr(guard_binding); + node.equalities.push_back(e); + + + ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); + // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) + + // copy fSSA's each node's items (equalities, assertions, etc.) + // into node's corresponding item (after renaming) + + + + } + + + } + } + } + + + + + } + } //dependency graphs if(!(options.get_bool_option("inline"))) From 62d56ae3d1e89e879eb89c23137f1142e188ad05 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Fri, 27 May 2016 19:06:20 +0530 Subject: [PATCH 39/90] code for unwinding and inlining --- src/summarizer/summary_checker_kind.cpp | 119 +++++++++++++++++++++--- 1 file changed, 105 insertions(+), 14 deletions(-) diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 2dd262d05..60b1055aa 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -59,46 +59,137 @@ property_checkert::resultt summary_checker_kindt::operator()( f_it != it->second.functions.end(); f_it++){ local_SSAt &SSA = ssa_db.get(it->first); - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + std::list inline_nodes; + std::vector first_node_equalities; + int counter = ssa_inliner.get_rename_counter(); + + for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++){ - + local_SSAt::nodet &node=*n_it; + if(node.location == *(f_it)){ + bool clear_function_call = false; + for(local_SSAt::nodet::function_callst::const_iterator fc_it=node.function_calls.begin(); fc_it!=node.function_calls.end(); fc_it++){ irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); if(ssa_db.exists(fname)) { - const local_SSAt &fSSA = ssa_db.get(fname); + clear_function_call = true; + + local_SSAt &fSSA = ssa_db.get(fname); exprt guard_binding; exprt::operandst bindings_in, bindings_out; - int counter = ssa_inliner.get_rename_counter(); - + + // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); equal_exprt e = to_equal_expr(guard_binding); node.equalities.push_back(e); - ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); - // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) - // copy fSSA's each node's items (equalities, assertions, etc.) - // into node's corresponding item (after renaming) + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + + for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet fnode=*fn_it; + inline_nodes.push_back(fnode); + } + + if(fname == entry_function){ + // first_node_equalities should contain all the equalities from the first node of fSSA + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + first_node_equalities.push_back(*e_it); + } + break; + } + } + else{ + // except those (the one) that start with "ssa::guard" and have true in the rhs + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + // unless lhs starts with "ssa::guard" and rhs is true + + equal_exprt e = to_equal_expr(*e_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true())){ + // ignore the equality in this case + } + else{ + first_node_equalities.push_back(*e_it); + } + + } + break; + } + } - - } - - } + + if(clear_function_call == true) + node.function_calls.clear(); + } } - + bool replace_first_node_equalities = true; + if(inline_nodes.size() > 0){ + for(std::list::iterator in_it = inline_nodes.begin(); + in_it != inline_nodes.end(); in_it++){ + local_SSAt::nodet &inline_node = *in_it; + + if(replace_first_node_equalities == true){ + inline_node.equalities.clear(); + for(std::vector::iterator e_it=first_node_equalities.begin(); + e_it!=first_node_equalities.end(); e_it++){ + inline_node.equalities.push_back(*e_it); + } + replace_first_node_equalities = false; + } + + for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); + e_it!=inline_node.equalities.end(); e_it++){ + ssa_inliner.rename(*e_it, counter); + } + + for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); + c_it!=inline_node.constraints.end(); c_it++){ + ssa_inliner.rename(*c_it, counter); + } + + for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); + a_it!=inline_node.assertions.end(); a_it++){ + ssa_inliner.rename(*a_it, counter); + } + + // push inline_node into SSA + SSA.nodes.push_back(inline_node); + + } + } } } From 1828bb9c60f4ebf743f6c4baf1d0ecbb9ec80796 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Sat, 28 May 2016 09:27:03 +0530 Subject: [PATCH 40/90] minor fix in inlining code --- src/summarizer/summary_checker_kind.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 60b1055aa..0ec4c560f 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -126,6 +126,9 @@ property_checkert::resultt summary_checker_kindt::operator()( for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); fn_it != fSSA.nodes.end(); fn_it++){ local_SSAt::nodet &fnode=*fn_it; + + bool ignore_equality = true; + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); e_it!=fnode.equalities.end(); e_it++){ // unless lhs starts with "ssa::guard" and rhs is true @@ -133,8 +136,8 @@ property_checkert::resultt summary_checker_kindt::operator()( equal_exprt e = to_equal_expr(*e_it); exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true())){ - // ignore the equality in this case + if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ + ignore_equality = false; } else{ first_node_equalities.push_back(*e_it); From 98bf6535efda4093584e2b736a4ad5c6b147e1a0 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 28 May 2016 14:06:34 +0100 Subject: [PATCH 41/90] more k-induction tests --- regression/kiki/unwind21/test.desc | 2 +- regression/modular/induction1/main.c | 12 +++++ regression/modular/induction1/test.desc | 6 +++ regression/modular/induction2/main.c | 12 +++++ regression/modular/induction2/test.desc | 6 +++ regression/modular/induction3/main.c | 12 +++++ regression/modular/induction3/test.desc | 6 +++ regression/modular/induction4/main.c | 16 +++++++ regression/modular/induction4/test.desc | 6 +++ regression/modular/induction5/main.c | 16 +++++++ regression/modular/induction5/test.desc | 6 +++ regression/modular/induction7/main.c | 23 +++++++++ regression/modular/induction7/test.desc | 6 +++ regression/modular/induction8/main.c | 19 ++++++++ regression/modular/induction8/test.desc | 6 +++ regression/modular/loop25/main.c | 12 +++++ regression/modular/loop25/test.desc | 6 +++ regression/modular/loop27/main.c | 13 +++++ regression/modular/loop27/test.desc | 6 +++ regression/modular/loop28/main.c | 8 ++++ regression/modular/loop28/test.desc | 6 +++ regression/modular/s3_clnt_1/main.c | 64 +++++++++++++++++++++++++ regression/modular/s3_clnt_1/test.desc | 6 +++ regression/modular/scope1/main.c | 11 +++++ regression/modular/scope1/test.desc | 6 +++ 25 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 regression/modular/induction1/main.c create mode 100644 regression/modular/induction1/test.desc create mode 100644 regression/modular/induction2/main.c create mode 100644 regression/modular/induction2/test.desc create mode 100644 regression/modular/induction3/main.c create mode 100644 regression/modular/induction3/test.desc create mode 100644 regression/modular/induction4/main.c create mode 100644 regression/modular/induction4/test.desc create mode 100644 regression/modular/induction5/main.c create mode 100644 regression/modular/induction5/test.desc create mode 100644 regression/modular/induction7/main.c create mode 100644 regression/modular/induction7/test.desc create mode 100644 regression/modular/induction8/main.c create mode 100644 regression/modular/induction8/test.desc create mode 100644 regression/modular/loop25/main.c create mode 100644 regression/modular/loop25/test.desc create mode 100644 regression/modular/loop27/main.c create mode 100644 regression/modular/loop27/test.desc create mode 100644 regression/modular/loop28/main.c create mode 100644 regression/modular/loop28/test.desc create mode 100644 regression/modular/s3_clnt_1/main.c create mode 100644 regression/modular/s3_clnt_1/test.desc create mode 100644 regression/modular/scope1/main.c create mode 100644 regression/modular/scope1/test.desc diff --git a/regression/kiki/unwind21/test.desc b/regression/kiki/unwind21/test.desc index f4c56630b..f2e264817 100644 --- a/regression/kiki/unwind21/test.desc +++ b/regression/kiki/unwind21/test.desc @@ -1,4 +1,4 @@ -KNOWNBUG +CORE main.c --k-induction ^EXIT=10$ diff --git a/regression/modular/induction1/main.c b/regression/modular/induction1/main.c new file mode 100644 index 000000000..23a7e0054 --- /dev/null +++ b/regression/modular/induction1/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1; + + while(1) + { + if(x==2) x=-x; + if(x>0) x++; + if(x==0) assert(0); + if(-10<=x && x<0) x--; + } +} diff --git a/regression/modular/induction1/test.desc b/regression/modular/induction1/test.desc new file mode 100644 index 000000000..34e465e33 --- /dev/null +++ b/regression/modular/induction1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction2/main.c b/regression/modular/induction2/main.c new file mode 100644 index 000000000..76f88d6eb --- /dev/null +++ b/regression/modular/induction2/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + while(1) + { + z = y; + y = x; + x = -x; + assert(x==z); + } +} diff --git a/regression/modular/induction2/test.desc b/regression/modular/induction2/test.desc new file mode 100644 index 000000000..3bf2a93f1 --- /dev/null +++ b/regression/modular/induction2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction3/main.c b/regression/modular/induction3/main.c new file mode 100644 index 000000000..38ef4b24f --- /dev/null +++ b/regression/modular/induction3/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 0, y = 0, z = 0; + + while(1) + { + z = -y; + y = -x; + x++; + assert(x<=z+2); + } +} diff --git a/regression/modular/induction3/test.desc b/regression/modular/induction3/test.desc new file mode 100644 index 000000000..3bf2a93f1 --- /dev/null +++ b/regression/modular/induction3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction4/main.c b/regression/modular/induction4/main.c new file mode 100644 index 000000000..c55adf781 --- /dev/null +++ b/regression/modular/induction4/main.c @@ -0,0 +1,16 @@ +#define a 2 + +extern int nondet_int(); + +int main() { + int i=0, n=3; + + int sn0 = nondet_int(); + int sn = sn0; + + while(i=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/modular/induction8/test.desc b/regression/modular/induction8/test.desc new file mode 100644 index 000000000..34e465e33 --- /dev/null +++ b/regression/modular/induction8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop25/main.c b/regression/modular/loop25/main.c new file mode 100644 index 000000000..8febd85d7 --- /dev/null +++ b/regression/modular/loop25/main.c @@ -0,0 +1,12 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + while(x==z) + { + z = y; + y = x; + x = -x; + } + assert(0); +} diff --git a/regression/modular/loop25/test.desc b/regression/modular/loop25/test.desc new file mode 100644 index 000000000..6755e58a3 --- /dev/null +++ b/regression/modular/loop25/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop27/main.c b/regression/modular/loop27/main.c new file mode 100644 index 000000000..ca9acda4c --- /dev/null +++ b/regression/modular/loop27/main.c @@ -0,0 +1,13 @@ +void main() +{ + int x = 1, y = -1, z = 1; + + do + { + z = y; + y = x; + x = -x; + } + while(x==z); + assert(0); +} diff --git a/regression/modular/loop27/test.desc b/regression/modular/loop27/test.desc new file mode 100644 index 000000000..34e465e33 --- /dev/null +++ b/regression/modular/loop27/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop28/main.c b/regression/modular/loop28/main.c new file mode 100644 index 000000000..7ae8d5301 --- /dev/null +++ b/regression/modular/loop28/main.c @@ -0,0 +1,8 @@ +void main() { + int b = 3; + unsigned int j=0; + while (j<1 && b!=3) { + j++; + } + assert(j<1); +} diff --git a/regression/modular/loop28/test.desc b/regression/modular/loop28/test.desc new file mode 100644 index 000000000..34e465e33 --- /dev/null +++ b/regression/modular/loop28/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/s3_clnt_1/main.c b/regression/modular/s3_clnt_1/main.c new file mode 100644 index 000000000..f958efad6 --- /dev/null +++ b/regression/modular/s3_clnt_1/main.c @@ -0,0 +1,64 @@ +#include + +extern int __VERIFIER_nondet_int(); + +int ssl3_connect(void) +{ + int s__state ; + int blastFlag ; + + s__state = 12292; + blastFlag = 0; + + while (1) { + if (s__state == 12292) { + goto switch_1_12292; + } else { + if (s__state == 4368) { + goto switch_1_4368; + } else { + if (s__state == 4384) { + goto switch_1_4384; + } else { + if (s__state == 4400) { + goto switch_1_4400; + } else { + return 0; + if (0) { + switch_1_12292: /* CIL Label */ + s__state = 4368; + continue; + switch_1_4368: /* CIL Label */ ; + blastFlag++; + s__state = 4384; + continue; + switch_1_4384: /* CIL Label */ ; + blastFlag++; + s__state = 4400; + continue; + switch_1_4400: /* CIL Label */ ; + if (blastFlag == 2) { + break; + } + continue; + } + } + } + } + } + } + assert(0); + return -1; +} +int main(void) +{ + ssl3_connect(); + return 0; +} + +/* +We get hoisted assertion: +(C) $guard#ls50%2 && ($cond#22%2 || $cond#36%2 || $cond#49%2) ==> ($guard#51 ==> FALSE) + +But $cond#36%2 is a return and no break condition! +*/ diff --git a/regression/modular/s3_clnt_1/test.desc b/regression/modular/s3_clnt_1/test.desc new file mode 100644 index 000000000..4d84485b2 --- /dev/null +++ b/regression/modular/s3_clnt_1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction +^EXIT=10$ +^SIGNAL=0$ +^.*FAILURE$ diff --git a/regression/modular/scope1/main.c b/regression/modular/scope1/main.c new file mode 100644 index 000000000..ec678aac1 --- /dev/null +++ b/regression/modular/scope1/main.c @@ -0,0 +1,11 @@ +void main() +{ + int y = 5; + int i; + for(i=0; i<10; i+=y) + { + int y = 20; + } + assert(y==5); + assert(i==10); +} diff --git a/regression/modular/scope1/test.desc b/regression/modular/scope1/test.desc new file mode 100644 index 000000000..34e465e33 --- /dev/null +++ b/regression/modular/scope1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ From 10559ea5ad4e3a564e31a9f952fd48629c450b5d Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 28 May 2016 16:03:27 +0100 Subject: [PATCH 42/90] spurious check refactoring started --- src/summarizer/cover_goals_ext.cpp | 85 +++++++--------- src/summarizer/cover_goals_ext.h | 26 +++-- src/summarizer/summarizer_base.cpp | 89 ++++++++++++++++- src/summarizer/summarizer_base.h | 26 +++-- src/summarizer/summarizer_bw_cex_complete.cpp | 10 +- src/summarizer/summarizer_bw_cex_concrete.cpp | 40 ++++++-- src/summarizer/summary_checker_base.cpp | 98 +------------------ 7 files changed, 195 insertions(+), 179 deletions(-) diff --git a/src/summarizer/cover_goals_ext.cpp b/src/summarizer/cover_goals_ext.cpp index 3976e90b2..0549456e2 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/summarizer/cover_goals_ext.cpp @@ -43,7 +43,7 @@ Function: cover_goals_extt::mark void cover_goals_extt::mark() { for(std::list::iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && @@ -71,7 +71,7 @@ void cover_goals_extt::constraint() exprt::operandst disjuncts; for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->covered && !g_it->condition.is_false()) @@ -96,7 +96,7 @@ Function: cover_goals_extt::freeze_goal_variables void cover_goals_extt::freeze_goal_variables() { for(std::list::const_iterator - g_it=goals.begin(); + g_it=goals.begin(); g_it!=goals.end(); g_it++) if(!g_it->condition.is_constant()) @@ -179,55 +179,41 @@ void cover_goals_extt::assignment() if(property_map[it->first].result==property_checkert::UNKNOWN && solver.l_get(g_it->condition).is_true()) { - if(spurious_check) + assert((g_it->cond_expression).id() == ID_not); + exprt conjunct_expr = (g_it->cond_expression).op0(); + + if(conjunct_expr.id() != ID_and) { - assert((g_it->cond_expression).id() == ID_not); - exprt conjunct_expr = (g_it->cond_expression).op0(); - - if(conjunct_expr.id() != ID_and) - { - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(g_it->cond_expression); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); - } - else - { - //filter out assertion instances that are not violated - exprt::operandst failed_exprs; - for(exprt::operandst::const_iterator c_it = - conjunct_expr.operands().begin(); - c_it != conjunct_expr.operands().end(); c_it++) - { - literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_true()) - { -#ifdef DEBUG - std::cout << "failed_expr: " - << from_expr(SSA.ns, "", *c_it) << std::endl; -#endif - failed_exprs.push_back(*c_it); - } - } - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); - property_map[it->first].result = summarizer_bw_cex.check(); - if(property_map[it->first].result == - property_checkert::FAIL) - { - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); - } - solver.new_context(); - break; - } - solver.new_context(); - } + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); } else - property_map[it->first].result = property_checkert::FAIL; + { + //filter out assertion instances that are not violated + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it = + conjunct_expr.operands().begin(); + c_it != conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal = solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_true()) + failed_exprs.push_back(*c_it); + } + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); + } + } + if(property_map[it->first].result == property_checkert::FAIL) + { + if(build_error_trace) + { + ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); + build_goto_trace(property_map[it->first].error_trace); + } } if(!all_properties && property_map[it->first].result == property_checkert::FAIL) @@ -236,3 +222,4 @@ void cover_goals_extt::assignment() _iterations++; //statistics } + diff --git a/src/summarizer/cover_goals_ext.h b/src/summarizer/cover_goals_ext.h index 737d4550d..f2150f090 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/summarizer/cover_goals_ext.h @@ -53,18 +53,17 @@ class cover_goals_extt:public messaget incremental_solvert &_solver, const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, - bool _spurious_check, bool _all_properties, - bool _build_error_trace, - summarizer_bw_cex_baset &_summarizer_bw_cex): - SSA(_SSA), - solver(_solver), - property_map(_property_map), - spurious_check(_spurious_check), - all_properties(_all_properties), - build_error_trace(_build_error_trace), - summarizer_bw_cex(_summarizer_bw_cex), - loophead_selects(_loophead_selects) - {} + bool _all_properties, + bool _build_error_trace, + summarizer_bw_cex_baset &_summarizer_bw_cex) + : + SSA(_SSA), + solver(_solver), + property_map(_property_map), + all_properties(_all_properties), + build_error_trace(_build_error_trace), + summarizer_bw_cex(_summarizer_bw_cex) + {} virtual ~cover_goals_extt(); @@ -126,9 +125,8 @@ class cover_goals_extt:public messaget unsigned _number_covered, _iterations; incremental_solvert &solver; property_checkert::property_mapt &property_map; - bool spurious_check, all_properties, build_error_trace; + bool all_properties, build_error_trace; summarizer_bw_cex_baset &summarizer_bw_cex; - exprt::operandst loophead_selects; // this method is called for each satisfying assignment virtual void assignment(); diff --git a/src/summarizer/summarizer_base.cpp b/src/summarizer/summarizer_base.cpp index 480df89dc..4f877c6c8 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/summarizer/summarizer_base.cpp @@ -415,12 +415,13 @@ Function: summarizer_baset::get_loophead_selects \*******************************************************************/ -exprt::operandst summarizer_baset::get_loophead_selects( +void summarizer_baset::get_loophead_selects( const local_SSAt &SSA, const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver) + prop_convt &solver, + exprt::operandst &loophead_selects) { - exprt::operandst loophead_selects; + //TODO: this should be provided by unwindable_local_SSA for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { @@ -440,6 +441,86 @@ exprt::operandst summarizer_baset::get_loophead_selects( << from_expr(SSA.ns,"",conjunction(loophead_selects)) << std::endl; #endif +} + +/*******************************************************************\ + +Function: summarizer_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + loops in order to check whether we can unroll further + +\*******************************************************************/ + +void summarizer_baset::get_loop_continues( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver, + exprt::operandst &loop_continues) +{ + //TODO: this should be provided by unwindable_local_SSA + + ssa_local_unwinder.loop_continuation_conditions(loop_continues); + if(loop_continues.size()==0) + { + //TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) continue; + symbol_exprt guard = SSA.guard_symbol(n_it->location); + symbol_exprt cond = SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard,cond)); + } + } + +#if 0 + std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_baset::is_fully_unwound + + Inputs: + + Outputs: - return loophead_selects; + Purpose: checks whether the loops have been fully unwound + +\*******************************************************************/ + +bool summarizer_baset::is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver) +{ + solver.new_context(); + solver << and_exprt(conjunction(loophead_selects), + disjunction(loop_continues)); + + solver_calls++; //statistics + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + return false; + break; + + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + solver << conjunction(loophead_selects); + return true; + break; + + case decision_proceduret::D_ERROR: + default: + throw "error from decision procedure"; + } } diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h index be8d80ae3..252c90da0 100644 --- a/src/summarizer/summarizer_base.h +++ b/src/summarizer/summarizer_base.h @@ -47,15 +47,27 @@ class summarizer_baset : public messaget virtual void summarize(); virtual void summarize(const function_namet &entry_function); - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } - unsigned get_number_of_summaries_used() { return summaries_used; } + static void get_loop_continues( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver, + exprt::operandst &loop_continues); - static exprt::operandst get_loophead_selects( - const local_SSAt &SSA, + static void get_loophead_selects( + const local_SSAt &SSA, const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver); - + prop_convt &solver, + exprt::operandst &loophead_selects); + + static bool is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver); + + inline unsigned get_number_of_solver_instances() { return solver_instances; } + inline unsigned get_number_of_solver_calls() { return solver_calls; } + inline unsigned get_number_of_summaries_used() { return summaries_used; } + protected: optionst &options; summary_dbt &summary_db; diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 78585bbd8..9e8c70440 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -543,7 +543,15 @@ property_checkert::resultt summarizer_bw_cex_completet::check() } #endif - //std::cout << "Solver <-- renamed info ~ UNSAT\n"; + //check whether loops have been fully unwound + bool fully_unwound = + is_fully_unwound(loop_continues,loophead_selects,solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + return property_heckert::PASS; + return property_checkert::UNKNOWN; } diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 19ecbd3b7..fc226a9a8 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -87,19 +87,39 @@ Function: summarizer_bw_cex_concretet::check() property_checkert::resultt summarizer_bw_cex_concretet::check() { - //TODO: store information about why have UNKNOWN - // we have to distinguish the case when we cannot decide about spuriousness - + property_checkert::resultt result = property_checkert::FAIL; if(!summary_db.exists(entry_function)) - return property_checkert::UNKNOWN; + { + result = property_checkert::UNKNOWN; + } + else + { + const summaryt &summary = summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + result = property_checkert::UNKNOWN; + } - const summaryt &summary = summary_db.get(entry_function); - if(summary.error_summaries.empty() || - summary.error_summaries.begin()->second.is_nil() || - summary.error_summaries.begin()->second.is_true()) - return property_checkert::UNKNOWN; + //we are only complete if we are in the entry function + if(result == property_checkert::UNKNOWN && + entry_function == error_function) + { + incremental_solvert &solver = ssa_db.get_solver(entry_function); + //these have not been collected yet + get_loop_continues(entry_function, + ssa_db.get(entry_function), solver, loop_continues); + //check whether loops have been fully unwound + bool fully_unwound = + is_fully_unwound(loop_continues,loophead_selects,solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result = property_heckert::PASS; + } - return property_checkert::FAIL; + return result; } /*******************************************************************\ diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 621febbb9..2d3381bee 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -352,15 +352,8 @@ void summary_checker_baset::check_properties( //freeze loop head selects exprt::operandst loophead_selects = - summarizer_baset::get_loophead_selects(SSA,ssa_unwinder.get(f_it->first),*solver.solver); - //check whether loops have been fully unwound - exprt::operandst loop_continues = - get_loop_continues(f_it->first,SSA,*solver.solver); - bool fully_unwound = - is_fully_unwound(loop_continues,loophead_selects,solver); - status() << "Loops " << (fully_unwound ? "" : "not ") - << "fully unwound" << eom; - + summarizer_baset::get_loophead_selects(SSA, + ssa_unwinder.get(f_it->first),*solver.solver); //spuriousness checkers summarizer_bw_cex_baset *summarizer_bw_cex = NULL; @@ -410,9 +403,8 @@ void summary_checker_baset::check_properties( summarizer_bw_cex->set_message_handler(get_message_handler()); cover_goals_extt cover_goals( - SSA,solver,loophead_selects,property_map, - f_it->first!=entry_function || !fully_unwound, - all_properties,build_error_trace, + SSA, solver,loophead_selects, property_map, + all_properties, build_error_trace, *summarizer_bw_cex); #if 0 @@ -586,88 +578,6 @@ void summary_checker_baset::do_show_vcc( std::cout << "\n"; } -/*******************************************************************\ - -Function: summary_checker_baset::get_loop_continues - - Inputs: - - Outputs: - - Purpose: returns the loop continuation guards at the end of the - loops in order to check whether we can unroll further - -\*******************************************************************/ - -exprt::operandst summary_checker_baset::get_loop_continues( - const irep_idt &function_name, - const local_SSAt &SSA, prop_convt &solver) -{ - //TODO: this should be provided by unwindable_local_SSA - exprt::operandst loop_continues; - - ssa_unwinder.get(function_name).loop_continuation_conditions(loop_continues); - if(loop_continues.size()==0) - { - //TODO: this should actually be done transparently by the unwinder - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - symbol_exprt guard = SSA.guard_symbol(n_it->location); - symbol_exprt cond = SSA.cond_symbol(n_it->location); - loop_continues.push_back(and_exprt(guard,cond)); - } - } - -#if 0 - std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; -#endif - - return loop_continues; -} - -/*******************************************************************\ - -Function: summary_checker_baset::is_fully_unwound - - Inputs: - - Outputs: - - Purpose: checks whether the loops have been fully unwound - -\*******************************************************************/ - -bool summary_checker_baset::is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver) -{ - solver.new_context(); - solver << and_exprt(conjunction(loophead_selects), - disjunction(loop_continues)); - - solver_calls++; //statistics - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - solver.pop_context(); - return false; - break; - - case decision_proceduret::D_UNSATISFIABLE: - solver.pop_context(); - solver << conjunction(loophead_selects); - return true; - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } -} /*******************************************************************\ From fc0720a778268360b0ad710c061513c92efcb77b Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 28 May 2016 18:29:51 +0100 Subject: [PATCH 43/90] refactoring done, except loop continuations --- src/ssa/ssa_dependency_graph.cpp | 26 +- src/ssa/ssa_dependency_graph.h | 3 +- src/summarizer/cover_goals_ext.h | 1 - src/summarizer/summarizer_base.h | 10 +- src/summarizer/summarizer_bw_cex_ai.cpp | 46 +- src/summarizer/summarizer_bw_cex_complete.cpp | 428 +++++++++--------- src/summarizer/summarizer_bw_cex_complete.h | 2 + src/summarizer/summarizer_bw_cex_concrete.cpp | 18 +- src/summarizer/summarizer_bw_cex_wp.cpp | 2 +- src/summarizer/summary_checker_base.cpp | 8 +- 10 files changed, 306 insertions(+), 238 deletions(-) diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp index 26bf05fba..fc7875824 100644 --- a/src/ssa/ssa_dependency_graph.cpp +++ b/src/ssa/ssa_dependency_graph.cpp @@ -49,7 +49,8 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet sink_node; sink_node.is_assertion = false; sink_node.is_function_call = false; - + sink_node.is_loop = false; + // globals_out is the used_symbol of the sink_node for(local_SSAt::var_sett::const_iterator g_it = SSA.globals_out.begin(); g_it != SSA.globals_out.end(); g_it++){ @@ -88,9 +89,26 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet temp_node; temp_node.is_assertion = false; temp_node.is_function_call = false; + temp_node.is_loop = false; temp_node.node_info = *e_it; temp_node.location = n_it->location; + //loop-head select + //TODO: this is an ugly hack (this can be changed as soon as unwindable_local_SSA provides smooth renaming with odometers) + //if(n_it->loophead!=SSA.nodes.end()) + if(e_it->op1().id()==ID_if && + e_it->op1().op0().id()==ID_symbol) + { + std::string var_string = id2string(e_it->op1().op0().get(ID_identifier)); + if(((var_string.substr(0,14)) == "ssa::$guard#ls")) + { + temp_node.is_loop = true; +/* symbol_exprt lsguard = SSA.name(SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(lsguard,*n_it,true);*/ + temp_node.guard = not_exprt(e_it->op1().op0()); + } + } //temp_node.trivial_guard = true; equal_exprt e = to_equal_expr(*e_it); @@ -130,6 +148,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet temp_node; temp_node.is_assertion = false; temp_node.is_function_call = false; + temp_node.is_loop = false; temp_node.node_info = *c_it; temp_node.location = n_it->location; find_symbols(*c_it,temp_node.used_symbols); @@ -147,6 +166,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet temp_node; temp_node.is_assertion = true; temp_node.is_function_call = false; + temp_node.is_loop = false; temp_node.node_info = *a_it; temp_node.location = n_it->location; find_symbols(*a_it,temp_node.used_symbols); @@ -221,6 +241,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet temp_node; temp_node.is_assertion = false; temp_node.is_function_call = false; + temp_node.is_loop = false; temp_node.node_info = *b_it; temp_node.location = n_it->location; @@ -241,6 +262,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet temp_node; temp_node.is_assertion = false; temp_node.is_function_call = false; + temp_node.is_loop = false; temp_node.node_info = *b_it; temp_node.location = n_it->location; @@ -259,6 +281,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli temp_node.guard = guard_binding; temp_node.is_assertion = false; temp_node.is_function_call = true; + temp_node.is_loop = false; temp_node.node_info = *f_it; temp_node.rename_counter = counter; temp_node.location = n_it->location; @@ -299,6 +322,7 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli depnodet source_node; source_node.is_assertion = false; source_node.is_function_call = false; + source_node.is_loop = false; // params and globals_in are the modified_symbols at source_node diff --git a/src/ssa/ssa_dependency_graph.h b/src/ssa/ssa_dependency_graph.h index 7d360f218..1acb50978 100644 --- a/src/ssa/ssa_dependency_graph.h +++ b/src/ssa/ssa_dependency_graph.h @@ -29,9 +29,10 @@ class ssa_dependency_grapht{ struct depnodet{ exprt node_info; - exprt guard; + exprt guard; //guard binding or loop-head select bool is_assertion; bool is_function_call; + bool is_loop; //bool trivial_guard; int rename_counter; find_symbols_sett used_symbols; diff --git a/src/summarizer/cover_goals_ext.h b/src/summarizer/cover_goals_ext.h index f2150f090..a2673bebb 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/summarizer/cover_goals_ext.h @@ -51,7 +51,6 @@ class cover_goals_extt:public messaget public: explicit inline cover_goals_extt(unwindable_local_SSAt &_SSA, incremental_solvert &_solver, - const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, bool _all_properties, bool _build_error_trace, diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h index 252c90da0..210dd6e65 100644 --- a/src/summarizer/summarizer_base.h +++ b/src/summarizer/summarizer_base.h @@ -59,11 +59,6 @@ class summarizer_baset : public messaget prop_convt &solver, exprt::operandst &loophead_selects); - static bool is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver); - inline unsigned get_number_of_solver_instances() { return solver_instances; } inline unsigned get_number_of_solver_calls() { return solver_calls; } inline unsigned get_number_of_summaries_used() { return summaries_used; } @@ -110,6 +105,11 @@ class summarizer_baset : public messaget local_SSAt &SSA, const exprt &cond); + bool is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver); + //statistics unsigned solver_instances; unsigned solver_calls; diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp index 2d7ec6dec..27734fb20 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -82,19 +82,45 @@ Function: summarizer_bw_cex_ait::check() property_checkert::resultt summarizer_bw_cex_ait::check() { - //TODO: store information about why have UNKNOWN - // we have to distinguish the case when we cannot decide about spuriousness - + property_checkert::resultt result = property_checkert::FAIL; if(!summary_db.exists(entry_function)) - return property_checkert::UNKNOWN; + { + result = property_checkert::UNKNOWN; + } + else + { + const summaryt &summary = summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + result = property_checkert::UNKNOWN; + } - const summaryt &summary = summary_db.get(entry_function); - if(summary.error_summaries.empty() || - summary.error_summaries.begin()->second.is_nil() || - summary.error_summaries.begin()->second.is_true()) - return property_checkert::UNKNOWN; + //we are only complete if we are in the entry function + if(result == property_checkert::UNKNOWN && + entry_function == error_function) + { + incremental_solvert &solver = ssa_db.get_solver(entry_function); + const local_SSAt &ssa = ssa_db.get(entry_function); + const ssa_local_unwindert &ssa_local_unwinder = + ssa_unwinder.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects(ssa, ssa_local_unwinder, + *solver.solver, loophead_selects); + get_loop_continues(ssa, ssa_local_unwinder, + *solver.solver, loop_continues); + //check whether loops have been fully unwound + bool fully_unwound = + is_fully_unwound(loop_continues,loophead_selects,solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result = property_checkert::PASS; + } - return property_checkert::FAIL; + return result; } /*******************************************************************\ diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 9e8c70440..422d22fc9 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -42,7 +42,7 @@ Function: summarizer_bw_cex_completet::summarize() void summarizer_bw_cex_completet::summarize ( - const function_namet &entry_function) + const function_namet &entry_function) { // no dependencies to begin with find_symbols_sett dependency_set; @@ -68,9 +68,9 @@ void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) status() << "\nBackward error analysis (complete)..." << eom; error_assertion = _error_assertion; /* - std::cout << "error assertion: " - << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) - << "\n"; + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; */ summarize(entry_function); } @@ -90,9 +90,9 @@ Function: summarizer_bw_cex_completet::inline_summaries() find_symbols_sett summarizer_bw_cex_completet::inline_summaries ( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter) + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) { local_SSAt &SSA = ssa_db.get(function_name); @@ -112,26 +112,6 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries if (n_it->loophead != SSA.nodes.end()) reason[function_name].loops.insert(n_it->loophead->location); - //add loop selects - exprt::operandst loophead_selects; - loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); - exprt c = conjunction(loophead_selects); - - //std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" - // << "\t original info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; - - ssa_inliner.rename(c, counter); - -#if 0 - std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" - << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; -#endif - -#ifdef SHOW_UNSAT_CORE - add_to_formula(c); -#else - solver << c; -#endif ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); @@ -153,33 +133,35 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries while(!worklist.empty()){ /* - std::cout << "worklist: "; - for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it != worklist.end(); w_it++){ std::cout << w_it->node_index << " "; - } - std::cout << "\n"; + } + std::cout << "\n"; - std::cout << "\t waitlist: "; - for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it != work_waitlist.end(); w_it++){ std::cout << w_it->node_index << " "; - } - std::cout << "\n"; + } + std::cout << "\n"; */ worknodet &worknode = worklist.front(); + const ssa_dependency_grapht::depnodet &depnode = + ssa_depgraph.depnodes_map[worknode.node_index]; //std::cout << "working node: " << function_name << ": " << worknode.node_index << "\n"; ///////////////////////////////////////////////////////////////////////////////////// //std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; /* - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ std::cout << *d_it; - } - std::cout << "\n\n\n"; + } + std::cout << "\n\n\n"; */ ///////////////////////////////////////////////////////////////////////////////////// @@ -189,24 +171,24 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries return worknode.dependency_set; // modify worknode_dependency_set if the node is an assertion - if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true){ + if(depnode.is_assertion == true){ //std::cout << "\t\t an assertion node\n"; - for(find_symbols_sett::const_iterator d_it = ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); - d_it != ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); d_it++){ - worknode.dependency_set.insert(*d_it); + for(find_symbols_sett::const_iterator d_it = depnode.used_symbols.begin(); + d_it != depnode.used_symbols.end(); d_it++){ + worknode.dependency_set.insert(*d_it); } ///////////////////////////////////////////////////////////////////////////////////// /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; */ ///////////////////////////////////////////////////////////////////////////////////// @@ -215,90 +197,90 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries } // if this is a function call - if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == true){ + if(depnode.is_function_call == true){ //std::cout << "fcall: working node: " << function_name << ": " << worknode.node_index << "\n"; irep_idt fname = - to_symbol_expr((to_function_application_expr(ssa_depgraph.depnodes_map[worknode.node_index].node_info)).function()).get_identifier(); + to_symbol_expr((to_function_application_expr(depnode.node_info)).function()).get_identifier(); find_symbols_sett renamed_dependencies; ///////////////////////////////////////////////////////////////////////////////////// /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; */ ///////////////////////////////////////////////////////////////////////////////////// for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; - // detach the '@' symbol if there - irep_idt renamed_id = ssa_inliner.rename - (id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); - renamed_dependencies.insert(renamed_id); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // detach the '@' symbol if there + irep_idt renamed_id = ssa_inliner.rename + (id, + depnode.rename_counter, false); + renamed_dependencies.insert(renamed_id); } worknode.dependency_set = renamed_dependencies; if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, - guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); } } ///////////////////////////////////////////////////////////////////////////////////// /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; + std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); + d_it != worknode.dependency_set.end(); d_it++){ + std::cout << *d_it; + } + std::cout << "\n"; */ ///////////////////////////////////////////////////////////////////////////////////// //TODO: just put all function calls into reason - reason[function_name].functions.insert(ssa_depgraph.depnodes_map[worknode.node_index].location); + reason[function_name].functions.insert(depnode.location); //recurse worknode.dependency_set = - compute_summary_rec(fname,worknode.dependency_set, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter); + compute_summary_rec(fname,worknode.dependency_set, + depnode.rename_counter); renamed_dependencies.clear(); for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; - // attach the '@' symbol if not already there - irep_idt renamed_id = ssa_inliner.rename - (id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, true); - renamed_dependencies.insert(renamed_id); + d_it != worknode.dependency_set.end(); d_it++){ + irep_idt id = *d_it; + // attach the '@' symbol if not already there + irep_idt renamed_id = ssa_inliner.rename + (id, + depnode.rename_counter, true); + renamed_dependencies.insert(renamed_id); } worknode.dependency_set = renamed_dependencies; if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); + d_it != guard_dependencies.end(); d_it++){ + worknode.dependency_set.insert(*d_it); } } @@ -306,74 +288,94 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries // if the dependency set is non-empty if(!worknode.dependency_set.empty()){ - exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; - if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) - worknode_info = not_exprt(worknode_info); + exprt worknode_info = depnode.node_info; + if(depnode.is_assertion == true) + worknode_info = not_exprt(worknode_info); if(worknode.node_index != 0){ - if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)){ - if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || - (worknode_info == error_assertion)){ - /* - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t original info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - */ - ssa_inliner.rename(worknode_info, counter); + if(!(depnode.is_function_call)){ + if((depnode.is_assertion == false) || + (worknode_info == error_assertion)){ + /* + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + */ + ssa_inliner.rename(worknode_info, counter); #if 0 - std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t renamed info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; #endif #ifdef SHOW_UNSAT_CORE - add_to_formula(worknode_info); + add_to_formula(worknode_info); #else - solver << worknode_info; + solver << worknode_info; #endif - } - } - else{ - exprt guard_binding = ssa_depgraph.depnodes_map[worknode.node_index].guard; - /* - std::cout << "Solver <-- " << function_name << ": (bind) node#:" - << worknode.node_index << "\t original info ~ " - << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; - */ - ssa_inliner.rename(guard_binding, counter); + + if(depnode.is_loop) + { + //loop head selects + exprt lsguard = depnode.guard; + ssa_inliner.rename(lsguard, counter); + loophead_selects.push_back(lsguard); + solver.solver->set_frozen(solver.convert(lsguard)); + + //loop continuations + exprt::operandst local_loop_continues; +/* ssa_local_unwinder.get_loop_continuations(depnode.location, + local_loop_continues);*/ + for(size_t i=0; inode_index == pred_node_index){ + if(w_it->node_index == pred_node_index){ - dependencies_merged = true; + dependencies_merged = true; - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ - if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ - (w_it->dependency_set).insert(*a_it); - } - } - } - break; - } + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ + if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } } if(dependencies_merged == false){ - worknodet new_worknode; - new_worknode.node_index = pred_node_index; + worknodet new_worknode; + new_worknode.node_index = pred_node_index; - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) - new_worknode.dependency_set.insert(*a_it); - } + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } - work_waitlist.push_back(new_worknode); + work_waitlist.push_back(new_worknode); } } #if 0 std::cout << function_name << ": worklist: "; for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ + w_it != worklist.end(); w_it++){ std::cout << w_it->node_index << " "; } std::cout << "\n"; @@ -424,7 +426,7 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries std::cout << "\t" << function_name << ": waitlist: "; for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ + w_it != work_waitlist.end(); w_it++){ std::cout << w_it->node_index << " "; } std::cout << "\n"; @@ -450,31 +452,31 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries bool uncovered_successor = false; std::vector &waitlisted_worknode_successors = - ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; for(unsigned i = 0; i < waitlisted_worknode_successors.size(); i++){ - bool check_covered = false; - for(unsigned j = 0; j < covered_nodes.size(); j++){ - if(waitlisted_worknode_successors[i] == covered_nodes[j]){ - check_covered = true; - break; - } - } - if(!check_covered){ + bool check_covered = false; + for(unsigned j = 0; j < covered_nodes.size(); j++){ + if(waitlisted_worknode_successors[i] == covered_nodes[j]){ + check_covered = true; + break; + } + } + if(!check_covered){ #if 0 - std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " - << waitlisted_worknode_successors[i] << "\n"; + std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " + << waitlisted_worknode_successors[i] << "\n"; #endif - uncovered_successor = true; - break; - } + uncovered_successor = true; + break; + } } if(!uncovered_successor){ - worklist.push_back(waitlisted_worknode); + worklist.push_back(waitlisted_worknode); } else{ - work_waitlist.push_back(waitlisted_worknode); + work_waitlist.push_back(waitlisted_worknode); } } @@ -500,10 +502,10 @@ Function: summarizer_bw_cex_completet::compute_summary_rec() \*******************************************************************/ find_symbols_sett summarizer_bw_cex_completet::compute_summary_rec - ( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter) +( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) { // recursively compute summaries for function calls return inline_summaries(function_name,dependency_set,counter); @@ -523,12 +525,20 @@ Function: summarizer_bw_cex_completet::check() property_checkert::resultt summarizer_bw_cex_completet::check() { - solver_calls++; // for statistics +//add loophead selects +#ifdef SHOW_UNSAT_CORE + add_to_formula(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif + #ifdef SHOW_UNSAT_CORE solver.solver->set_assumptions(formula); #endif - if(solver() == decision_proceduret::D_SATISFIABLE){ - //std::cout << "Solver <-- renamed info ~ SAT\n"; + + solver_calls++; // for statistics + if(solver() == decision_proceduret::D_SATISFIABLE) + { return property_checkert::FAIL; } #ifdef SHOW_UNSAT_CORE @@ -536,10 +546,10 @@ property_checkert::resultt summarizer_bw_cex_completet::check() { for(unsigned i=0; iis_in_conflict(formula[i])) - debug() << "is_in_conflict: " << from_expr(SSA.ns, "", formula_expr[i]) << eom; - } + debug() << "is_in_conflict: " << from_expr(ns, "", formula_expr[i]) << eom; + } } #endif @@ -547,10 +557,10 @@ property_checkert::resultt summarizer_bw_cex_completet::check() bool fully_unwound = is_fully_unwound(loop_continues,loophead_selects,solver); status() << "Loops " << (fully_unwound ? "" : "not ") - << "fully unwound" << eom; + << "fully unwound" << eom; if(fully_unwound) - return property_heckert::PASS; + return property_checkert::PASS; return property_checkert::UNKNOWN; } @@ -568,16 +578,16 @@ Function: summarizer_bw_cex_completet::debug_print() \*******************************************************************/ void summarizer_bw_cex_completet::debug_print -( - const function_namet &function_name, - find_symbols_sett &dependency_set) + ( + const function_namet &function_name, + find_symbols_sett &dependency_set) { std::cout << "DebugInfo: function -> " << function_name - << " ; dependency_set -> "; + << " ; dependency_set -> "; for(find_symbols_sett::iterator d_it = dependency_set.begin(); - d_it != dependency_set.end(); d_it++){ - std::cout << *d_it << ", "; - } + d_it != dependency_set.end(); d_it++){ + std::cout << *d_it << ", "; +} std::cout << "\n"; } @@ -586,14 +596,14 @@ void summarizer_bw_cex_completet::add_to_formula(const exprt &expr) literalt l = solver.solver->convert(expr); if(l.is_false()) { - literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); - } + literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", + bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); +} else if(!l.is_true()) { - formula.push_back(l); - formula_expr.push_back(expr); - } + formula.push_back(l); + formula_expr.push_back(expr); +} } diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h index a5cb85f87..37f80d672 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -46,6 +46,8 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset incremental_solvert &solver; bvt formula; //for UNSAT core exprt::operandst formula_expr; //for debugging + exprt::operandst loophead_selects; + exprt::operandst loop_continues; virtual find_symbols_sett inline_summaries( const function_namet &function_name, diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index fc226a9a8..4afe6131a 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -106,9 +106,15 @@ property_checkert::resultt summarizer_bw_cex_concretet::check() entry_function == error_function) { incremental_solvert &solver = ssa_db.get_solver(entry_function); - //these have not been collected yet - get_loop_continues(entry_function, - ssa_db.get(entry_function), solver, loop_continues); + const local_SSAt &ssa = ssa_db.get(entry_function); + const ssa_local_unwindert &ssa_local_unwinder = + ssa_unwinder.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects(ssa, ssa_local_unwinder, + *solver.solver, loophead_selects); + get_loop_continues(ssa, ssa_local_unwinder, + *solver.solver, loop_continues); //check whether loops have been fully unwound bool fully_unwound = is_fully_unwound(loop_continues,loophead_selects,solver); @@ -116,7 +122,7 @@ property_checkert::resultt summarizer_bw_cex_concretet::check() << "fully unwound" << eom; if(fully_unwound) - result = property_heckert::PASS; + result = property_checkert::PASS; } return result; @@ -336,7 +342,7 @@ void summarizer_bw_cex_concretet::do_summary( #endif exprt::operandst loophead_selects; - loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); #ifdef OPT_11 solver << simplify_expr(conjunction(loophead_selects), SSA.ns); @@ -573,7 +579,7 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( #endif exprt::operandst loophead_selects; - loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); #ifdef OPT_11 solver << simplify_expr(conjunction(loophead_selects), SSA.ns); diff --git a/src/summarizer/summarizer_bw_cex_wp.cpp b/src/summarizer/summarizer_bw_cex_wp.cpp index 37c629e0a..97d0010cd 100644 --- a/src/summarizer/summarizer_bw_cex_wp.cpp +++ b/src/summarizer/summarizer_bw_cex_wp.cpp @@ -100,7 +100,7 @@ find_symbols_sett summarizer_bw_cex_wpt::inline_summaries //solver << SSA.get_enabling_exprs(); exprt::operandst loophead_selects; - loophead_selects = this->get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver); + get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); exprt c = conjunction(loophead_selects); //std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 2d3381bee..829cd4f44 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -351,9 +351,9 @@ void summary_checker_baset::check_properties( solver << ssa_inliner.get_summaries(SSA); //freeze loop head selects - exprt::operandst loophead_selects = - summarizer_baset::get_loophead_selects(SSA, - ssa_unwinder.get(f_it->first),*solver.solver); + exprt::operandst loophead_selects; + summarizer_baset::get_loophead_selects(SSA, + ssa_unwinder.get(f_it->first),*solver.solver, loophead_selects); //spuriousness checkers summarizer_bw_cex_baset *summarizer_bw_cex = NULL; @@ -403,7 +403,7 @@ void summary_checker_baset::check_properties( summarizer_bw_cex->set_message_handler(get_message_handler()); cover_goals_extt cover_goals( - SSA, solver,loophead_selects, property_map, + SSA, solver, property_map, all_properties, build_error_trace, *summarizer_bw_cex); From cccda17720047ec879f9a5f749cee25f3735172c Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 28 May 2016 21:17:09 +0100 Subject: [PATCH 44/90] loop-specific continuation conditions --- src/ssa/ssa_unwinder.cpp | 77 ++++++++++++++++--- src/ssa/ssa_unwinder.h | 10 ++- src/summarizer/summarizer_base.cpp | 34 +++++++- src/summarizer/summarizer_base.h | 7 +- src/summarizer/summarizer_bw_cex_ai.cpp | 5 +- src/summarizer/summarizer_bw_cex_complete.cpp | 7 +- src/summarizer/summarizer_bw_cex_concrete.cpp | 5 +- 7 files changed, 118 insertions(+), 27 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index db186db05..ab261bf5a 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -759,29 +759,81 @@ void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) /*****************************************************************************\ * - * Function : ssa_local_unwindert::loop_continuation_conditions + * Function : ssa_local_unwindert::compute_loop_continuation_conditions * * Input : * * Output : * - * Purpose : return loop continuation conditions for all loops in this function + * Purpose : compute loop continuation conditions for all loops in this function * *****************************************************************************/ -void ssa_local_unwindert::loop_continuation_conditions( - exprt::operandst& loop_cont) const +void ssa_local_unwindert::compute_loop_continuation_conditions() { + //clear old ones + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + { + it->second.current_continuation_conditions.clear(); + } + //compute new SSA.current_unwindings.clear(); - for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) { if(!it->second.is_root) continue; - loop_continuation_conditions(it->second,loop_cont); //recursive + compute_loop_continuation_conditions(it->second); //recursive assert(SSA.current_unwindings.empty()); } } +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::loop_continuation_conditions + * + * Input : + * + * Output : + * + * Purpose : return loop continuation conditions for all loops in this function + * + *****************************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + exprt::operandst &loop_cont) const +{ + for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) + { + loop_cont.insert(loop_cont.end(), + it->second.current_continuation_conditions.begin(), + it->second.current_continuation_conditions.end()); + } +} + +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::loop_continuation_conditions + * + * Input : + * + * Output : + * + * Purpose : return loop continuation conditions for all instances + * of the given loop in this function + * + *****************************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + const locationt& loop_id, + exprt::operandst &loop_cont) const +{ + const loopt &loop = loops.at(loop_id->location_number); + loop_cont.insert(loop_cont.end(), + loop.current_continuation_conditions.begin(), + loop.current_continuation_conditions.end()); +} + + /***************************************************************************** * * Function : ssa_local_unwindert::get_continuation_condition @@ -804,7 +856,7 @@ exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const /*****************************************************************************\ * - * Function : ssa_local_unwindert::loop_continuation_conditions + * Function : ssa_local_unwindert::compute_loop_continuation_conditions * * Input : * @@ -814,18 +866,19 @@ exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const * *****************************************************************************/ -void ssa_local_unwindert::loop_continuation_conditions( - const loopt& loop, exprt::operandst& loop_cont) const +void ssa_local_unwindert::compute_loop_continuation_conditions( + loopt& loop) { SSA.increment_unwindings(1); - loop_cont.push_back(get_continuation_condition(loop)); //%0 + loop.current_continuation_conditions.push_back( + get_continuation_condition(loop)); //%0 for(long i=0; i<=loop.current_unwinding; ++i) { //recurse into child loops - for(std::vector::const_iterator l_it = loop.loop_nodes.begin(); + for(std::vector::iterator l_it = loop.loop_nodes.begin(); l_it != loop.loop_nodes.end(); ++l_it) { - loop_continuation_conditions(loops.at(*l_it),loop_cont); + compute_loop_continuation_conditions(loops.at(*l_it)); } SSA.increment_unwindings(0); } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 1cc65008f..0a9eea496 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -34,7 +34,10 @@ class ssa_local_unwindert { unwind(loops[loop_head_loc],k); } */ //TODO: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related - void loop_continuation_conditions(exprt::operandst& loop_cont) const; + void compute_loop_continuation_conditions(); + void loop_continuation_conditions(exprt::operandst &loop_cont) const; + void loop_continuation_conditions(const locationt& loop_id, + exprt::operandst &loop_cont) const; //TODO: these two should be possible with unwindable_local_ssa facilities //exprt rename_invariant(const exprt& inv_in) const; @@ -71,6 +74,8 @@ class ssa_local_unwindert exprt::operandst loop_enabling_exprs; exprt loop_enabling_expr_current; + exprt::operandst current_continuation_conditions; + typedef std::map exit_mapt; exit_mapt exit_map; std::map pre_post_map; @@ -98,8 +103,7 @@ class ssa_local_unwindert void unwind(loopt &loop, unsigned k, bool is_new_parent, bool propagate = false, unsigned prop_unwind = 0, unsigned prop_loc = 0); exprt get_continuation_condition(const loopt& loop) const; - void loop_continuation_conditions(const loopt& loop, - exprt::operandst &loop_cont) const; + void compute_loop_continuation_conditions(loopt& loop); void add_loop_body(loopt &loop); void add_assertions(loopt &loop, bool is_last); diff --git a/src/summarizer/summarizer_base.cpp b/src/summarizer/summarizer_base.cpp index 4f877c6c8..24500018f 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/summarizer/summarizer_base.cpp @@ -458,12 +458,12 @@ Function: summarizer_baset::get_loop_continues void summarizer_baset::get_loop_continues( const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, + ssa_local_unwindert &ssa_local_unwinder, exprt::operandst &loop_continues) { //TODO: this should be provided by unwindable_local_SSA + ssa_local_unwinder.compute_loop_continuation_conditions(); ssa_local_unwinder.loop_continuation_conditions(loop_continues); if(loop_continues.size()==0) { @@ -483,6 +483,36 @@ void summarizer_baset::get_loop_continues( #endif } +void summarizer_baset::get_loop_continues( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues) +{ + //TODO: this should be provided by unwindable_local_SSA + + ssa_local_unwinder.loop_continuation_conditions(loop_id, loop_continues); + if(loop_continues.size()==0) + { + //TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) continue; + if(n_it->loophead->location!=loop_id) continue; + symbol_exprt guard = SSA.guard_symbol(n_it->location); + symbol_exprt cond = SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard,cond)); + break; + } + } + +#if 0 + std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; +#endif +} + + /*******************************************************************\ Function: summarizer_baset::is_fully_unwound diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h index 210dd6e65..8dd3f8c18 100644 --- a/src/summarizer/summarizer_base.h +++ b/src/summarizer/summarizer_base.h @@ -47,10 +47,15 @@ class summarizer_baset : public messaget virtual void summarize(); virtual void summarize(const function_namet &entry_function); + static void get_loop_continues( + const local_SSAt &SSA, + ssa_local_unwindert &ssa_local_unwinder, + exprt::operandst &loop_continues); + static void get_loop_continues( const local_SSAt &SSA, const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, + const local_SSAt::locationt &loop_id, exprt::operandst &loop_continues); static void get_loophead_selects( diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/summarizer/summarizer_bw_cex_ai.cpp index 27734fb20..6d676e175 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/summarizer/summarizer_bw_cex_ai.cpp @@ -102,14 +102,13 @@ property_checkert::resultt summarizer_bw_cex_ait::check() { incremental_solvert &solver = ssa_db.get_solver(entry_function); const local_SSAt &ssa = ssa_db.get(entry_function); - const ssa_local_unwindert &ssa_local_unwinder = + ssa_local_unwindert &ssa_local_unwinder = ssa_unwinder.get(entry_function); exprt::operandst loophead_selects; exprt::operandst loop_continues; get_loophead_selects(ssa, ssa_local_unwinder, *solver.solver, loophead_selects); - get_loop_continues(ssa, ssa_local_unwinder, - *solver.solver, loop_continues); + get_loop_continues(ssa, ssa_local_unwinder, loop_continues); //check whether loops have been fully unwound bool fully_unwound = is_fully_unwound(loop_continues,loophead_selects,solver); diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 422d22fc9..6a6ca30b6 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -95,7 +95,8 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries int counter) { local_SSAt &SSA = ssa_db.get(function_name); - + ssa_local_unwindert &ssa_local_unwinder = ssa_unwinder.get(function_name); + ssa_local_unwinder.compute_loop_continuation_conditions(); //add enabling expressions exprt enable_exprs = SSA.get_enabling_exprs(); ssa_inliner.rename(enable_exprs, counter); @@ -324,8 +325,8 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries //loop continuations exprt::operandst local_loop_continues; -/* ssa_local_unwinder.get_loop_continuations(depnode.location, - local_loop_continues);*/ + get_loop_continues(SSA, ssa_local_unwinder, depnode.location, + local_loop_continues); for(size_t i=0; i Date: Sat, 28 May 2016 21:58:04 +0100 Subject: [PATCH 45/90] update enable_expr after 0th unwinding --- regression/kiki/Makefile | 2 +- src/ssa/ssa_unwinder.cpp | 58 +++-- src/ssa/ssa_unwinder.h | 1 + src/summarizer/summary_checker_kind.cpp | 270 +++++++++++++----------- 4 files changed, 186 insertions(+), 145 deletions(-) diff --git a/regression/kiki/Makefile b/regression/kiki/Makefile index d2ff74910..1b86b08f2 100644 --- a/regression/kiki/Makefile +++ b/regression/kiki/Makefile @@ -1,6 +1,6 @@ default: tests.log -FLAGS = --verbosity 10 +FLAGS = --verbosity 10 --inline test: @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index ab261bf5a..0afbc58a0 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -31,6 +31,7 @@ void ssa_local_unwindert::init() build_pre_post_map(); build_exit_conditions(); unwind(0); + compute_enable_expr(); #ifdef DEBUG SSA.output_verbose(std::cout); #endif @@ -285,20 +286,6 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) bool_typet()); loop.loop_enabling_exprs.push_back(loop.loop_enabling_expr_current); - exprt::operandst ssa_current_enabling_expr; - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it){ - exprt::operandst result; - for(exprt::operandst::iterator e_it = ((it->second).loop_enabling_exprs).begin(); - e_it != ((it->second).loop_enabling_exprs).end(); e_it++){ - exprt::operandst::iterator lh = e_it; lh++; - if(lh != ((it->second).loop_enabling_exprs).end()) result.push_back(not_exprt(*e_it)); - else result.push_back(*e_it); - } - ssa_current_enabling_expr.push_back(conjunction(result)); - } - - SSA.combined_enabling_expr = conjunction(ssa_current_enabling_expr); - //current_enabling_expr = // symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), @@ -328,7 +315,8 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) if(it->first == loc) it->second.current_unwinding=k; } - + + compute_enable_expr(); return; } @@ -353,7 +341,10 @@ void ssa_local_unwindert::unwind(unsigned k) current_enabling_expr = symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), bool_typet()); - SSA.enabling_exprs.push_back(current_enabling_expr); + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + { + it->second.loop_enabling_exprs.push_back(current_enabling_expr); + } SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away //recursively unwind everything SSA.current_unwindings.clear(); @@ -369,6 +360,8 @@ void ssa_local_unwindert::unwind(unsigned k) { it->second.current_unwinding=k; } + + compute_enable_expr(); } /***************************************************************************** @@ -757,6 +750,39 @@ void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) } } +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::compute_enable_expr + * + * Input : + * + * Output : + * + * Purpose : updates the enable_expr + * + *****************************************************************************/ + +void ssa_local_unwindert::compute_enable_expr() +{ + exprt::operandst ssa_current_enabling_expr; + for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) + { + for(exprt::operandst::const_iterator + e_it = ((it->second).loop_enabling_exprs).begin(); + e_it != ((it->second).loop_enabling_exprs).end(); e_it++) + { + exprt::operandst::const_iterator lh = e_it; lh++; + if(lh != ((it->second).loop_enabling_exprs).end()) + ssa_current_enabling_expr.push_back(not_exprt(*e_it)); + else + ssa_current_enabling_expr.push_back(*e_it); + } + } + + SSA.combined_enabling_expr = conjunction(ssa_current_enabling_expr); +} + + /*****************************************************************************\ * * Function : ssa_local_unwindert::compute_loop_continuation_conditions diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 0a9eea496..14e59b3f5 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -35,6 +35,7 @@ class ssa_local_unwindert //TODO: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related void compute_loop_continuation_conditions(); + void compute_enable_expr(); void loop_continuation_conditions(exprt::operandst &loop_cont) const; void loop_continuation_conditions(const locationt& loop_id, exprt::operandst &loop_cont) const; diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 0ec4c560f..e10a46511 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -9,6 +9,7 @@ Author: Peter Schrammel #include "summary_checker_kind.h" #define GIVE_UP_INVARIANTS 7 +//#define UNWIND_ALL /*******************************************************************\ @@ -28,7 +29,7 @@ property_checkert::resultt summary_checker_kindt::operator()( const namespacet ns(goto_model.symbol_table); irep_idt entry_function = goto_model.goto_functions.entry_point(); if(options.get_bool_option("unit-check")) - entry_function = ""; + entry_function = ""; SSA_functions(goto_model,ns); @@ -43,156 +44,168 @@ property_checkert::resultt summary_checker_kindt::operator()( { status() << "Unwinding (k=" << unwind << ")" << eom; summary_db.mark_recompute_all(); //TODO: recompute only functions with loops - //ssa_unwinder.unwind_all(unwind); - + +#ifdef UNWIND_ALL + ssa_unwinder.unwind_all(unwind); +#else // unwind loops "selectively" (those that seem to be the "reason") - for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it){ + for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + { for(std::set::const_iterator l_it = it->second.loops.begin(); - l_it != it->second.loops.end(); l_it++){ - ssa_unwinder.unwind_loop_alone(it->first, (*l_it)->location_number, unwind); + l_it != it->second.loops.end(); l_it++) + { + ssa_unwinder.unwind_loop_alone(it->first, (*l_it)->location_number, unwind); } } // inline functions "selectively" (those that seem to be the "reason") - for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it){ + for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + { for(std::set::const_iterator f_it = it->second.functions.begin(); - f_it != it->second.functions.end(); f_it++){ - local_SSAt &SSA = ssa_db.get(it->first); + f_it != it->second.functions.end(); f_it++) + { + local_SSAt &SSA = ssa_db.get(it->first); - std::list inline_nodes; - std::vector first_node_equalities; - int counter = ssa_inliner.get_rename_counter(); + std::list inline_nodes; + std::vector first_node_equalities; + int counter = ssa_inliner.get_rename_counter(); - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++){ + for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { - local_SSAt::nodet &node=*n_it; + local_SSAt::nodet &node=*n_it; - if(node.location == *(f_it)){ + if(node.location == *(f_it)) + { - bool clear_function_call = false; + bool clear_function_call = false; - for(local_SSAt::nodet::function_callst::const_iterator fc_it=node.function_calls.begin(); - fc_it!=node.function_calls.end(); fc_it++){ + for(local_SSAt::nodet::function_callst::const_iterator fc_it=node.function_calls.begin(); + fc_it!=node.function_calls.end(); fc_it++) + { - irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); - if(ssa_db.exists(fname)) - { - clear_function_call = true; + irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); + if(ssa_db.exists(fname)) + { + clear_function_call = true; - local_SSAt &fSSA = ssa_db.get(fname); + local_SSAt &fSSA = ssa_db.get(fname); - exprt guard_binding; - exprt::operandst bindings_in, bindings_out; + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; - // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) - ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); - equal_exprt e = to_equal_expr(guard_binding); - node.equalities.push_back(e); + // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) + ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); + equal_exprt e = to_equal_expr(guard_binding); + node.equalities.push_back(e); - ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); - - for(exprt::operandst::const_iterator b_it=bindings_in.begin(); - b_it!=bindings_in.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - for(exprt::operandst::const_iterator b_it=bindings_out.begin(); - b_it!=bindings_out.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet fnode=*fn_it; - inline_nodes.push_back(fnode); - } - - if(fname == entry_function){ - // first_node_equalities should contain all the equalities from the first node of fSSA - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - first_node_equalities.push_back(*e_it); - } - break; - } - } - else{ - // except those (the one) that start with "ssa::guard" and have true in the rhs - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - - bool ignore_equality = true; + ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); + + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + + for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet fnode=*fn_it; + inline_nodes.push_back(fnode); + } + + if(fname == entry_function){ + // first_node_equalities should contain all the equalities from the first node of fSSA + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + first_node_equalities.push_back(*e_it); + } + break; + } + } + else{ + // except those (the one) that start with "ssa::guard" and have true in the rhs + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + + bool ignore_equality = true; - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - // unless lhs starts with "ssa::guard" and rhs is true - - equal_exprt e = to_equal_expr(*e_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ - ignore_equality = false; - } - else{ - first_node_equalities.push_back(*e_it); - } + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + // unless lhs starts with "ssa::guard" and rhs is true + + equal_exprt e = to_equal_expr(*e_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ + ignore_equality = false; + } + else{ + first_node_equalities.push_back(*e_it); + } - } - break; - } - } + } + break; + } + } - } - } + } + } - if(clear_function_call == true) - node.function_calls.clear(); + if(clear_function_call == true) + node.function_calls.clear(); - } - } + } + } - bool replace_first_node_equalities = true; + bool replace_first_node_equalities = true; - if(inline_nodes.size() > 0){ - for(std::list::iterator in_it = inline_nodes.begin(); - in_it != inline_nodes.end(); in_it++){ - local_SSAt::nodet &inline_node = *in_it; - - if(replace_first_node_equalities == true){ - inline_node.equalities.clear(); - for(std::vector::iterator e_it=first_node_equalities.begin(); - e_it!=first_node_equalities.end(); e_it++){ - inline_node.equalities.push_back(*e_it); - } - replace_first_node_equalities = false; - } + if(inline_nodes.size() > 0) + { + for(std::list::iterator in_it = inline_nodes.begin(); + in_it != inline_nodes.end(); in_it++) + { + local_SSAt::nodet &inline_node = *in_it; + + if(replace_first_node_equalities == true) + { + inline_node.equalities.clear(); + for(std::vector::iterator e_it=first_node_equalities.begin(); + e_it!=first_node_equalities.end(); e_it++){ + inline_node.equalities.push_back(*e_it); + } + replace_first_node_equalities = false; + } - for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); - e_it!=inline_node.equalities.end(); e_it++){ - ssa_inliner.rename(*e_it, counter); - } + for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); + e_it!=inline_node.equalities.end(); e_it++){ + ssa_inliner.rename(*e_it, counter); + } - for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); - c_it!=inline_node.constraints.end(); c_it++){ - ssa_inliner.rename(*c_it, counter); - } - - for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); - a_it!=inline_node.assertions.end(); a_it++){ - ssa_inliner.rename(*a_it, counter); - } - - // push inline_node into SSA - SSA.nodes.push_back(inline_node); + for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); + c_it!=inline_node.constraints.end(); c_it++){ + ssa_inliner.rename(*c_it, counter); + } + + for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); + a_it!=inline_node.assertions.end(); a_it++){ + ssa_inliner.rename(*a_it, counter); + } + + // push inline_node into SSA + SSA.nodes.push_back(inline_node); - } - } + } + } } } @@ -201,12 +214,13 @@ property_checkert::resultt summary_checker_kindt::operator()( if(!(options.get_bool_option("inline"))) { if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")) + (options.get_option("spurious-check") != "abstract")) { - SSA_dependency_graphs(goto_model, ns); + SSA_dependency_graphs(goto_model, ns); } } - + +#endif std::set seen_function_calls; result = check_properties(entry_function, entry_function, seen_function_calls); if(result == property_checkert::UNKNOWN && @@ -222,13 +236,13 @@ property_checkert::resultt summary_checker_kindt::operator()( if(result == property_checkert::PASS) { status() << "k-induction proof found after " - << unwind << " unwinding(s)" << eom; + << unwind << " unwinding(s)" << eom; break; } else if(result == property_checkert::FAIL) { status() << "k-induction counterexample found after " - << unwind << " unwinding(s)" << eom; + << unwind << " unwinding(s)" << eom; break; } } From c72193ecabab5c0b7657924870136cbe329a6156 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 28 May 2016 22:52:45 +0100 Subject: [PATCH 46/90] concrete only complete with inline --- src/summarizer/summarizer_bw_cex_concrete.cpp | 4 ++-- src/summarizer/summary_checker_base.cpp | 4 +++- src/summarizer/summary_checker_kind.cpp | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 978b54207..80ef87fcd 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -101,9 +101,9 @@ property_checkert::resultt summarizer_bw_cex_concretet::check() result = property_checkert::UNKNOWN; } - //we are only complete if we are in the entry function + //we are only complete if everything was inlined if(result == property_checkert::UNKNOWN && - entry_function == error_function) + options.get_bool_option("inline")) { incremental_solvert &solver = ssa_db.get_solver(entry_function); const local_SSAt &ssa = ssa_db.get(entry_function); diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index c1d090f10..8b2da9239 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -78,11 +78,13 @@ void summary_checker_baset::SSA_dependency_graphs( ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, true); else ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal - + +#if 0 ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); ssa_depgraph.output(debug()); debug() << eom; std::cout << "output SSA for function: " << f_it->first << "\n"; ssa_db.get(f_it->first).output_verbose(std::cout); +#endif } } diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index e10a46511..99c72b653 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -9,7 +9,7 @@ Author: Peter Schrammel #include "summary_checker_kind.h" #define GIVE_UP_INVARIANTS 7 -//#define UNWIND_ALL +#define UNWIND_ALL /*******************************************************************\ From 82a1d1baa781aa29d4d5ac980f4d291e3361ae86 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 10:33:48 +0100 Subject: [PATCH 47/90] take into account enabling_expr in depgraph construction --- src/ssa/local_ssa.cpp | 29 +++++++---------------------- src/ssa/local_ssa.h | 4 +--- src/ssa/ssa_dependency_graph.cpp | 24 ++++++++++++++++++++++-- src/ssa/ssa_unwinder.cpp | 8 +++----- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index cb003f106..b7bebed0c 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -1895,26 +1895,11 @@ Function: local_SSAt::get_enabling_expr exprt local_SSAt::get_enabling_exprs() const { - /* - exprt::operandst result; - result.reserve(enabling_exprs.size()); - for(std::list::const_iterator it = enabling_exprs.begin(); - it != enabling_exprs.end(); ++it) - { - std::list::const_iterator lh = it; lh++; - if(lh != enabling_exprs.end()) result.push_back(not_exprt(*it)); - else result.push_back(*it); - } - return conjunction(result); - */ - - - if(combined_enabling_expr.is_not_nil()){ - std::cout << "combined enabling expr:" << from_expr(ns, "", combined_enabling_expr) << "\n"; - return combined_enabling_expr; - } - else{ - std::cout << "combined enabling expr is nil; returning true\n"; - return true_exprt(); - } + exprt result = conjunction(enabling_exprs); + +#if 1 + std::cout << "current enabling expr:" << from_expr(ns, "", result) << "\n"; +#endif + + return result; } diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 8965dad7f..64130a90d 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -121,9 +121,7 @@ class local_SSAt } // for incremental unwinding - std::list enabling_exprs; - - exprt combined_enabling_expr = true_exprt(); // combined enabling expr for loop-specific unwindings + std::vector enabling_exprs; exprt get_enabling_exprs() const; // function entry and exit variables diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp index fc7875824..3a34a14e1 100644 --- a/src/ssa/ssa_dependency_graph.cpp +++ b/src/ssa/ssa_dependency_graph.cpp @@ -75,10 +75,30 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli bool ignore_equality_done = false; for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++){ - + n_it != SSA.nodes.end(); n_it++) + { const local_SSAt::nodet &node=*n_it; + // check whether this node is enabled + if(!node.enabling_expr.is_true()) + { + bool enabled=true; + const irep_idt &enable=to_symbol_expr(node.enabling_expr).get_identifier(); + for(size_t i=0; isecond).loop_enabling_exprs).end()) - ssa_current_enabling_expr.push_back(not_exprt(*e_it)); + SSA.enabling_exprs.push_back(not_exprt(*e_it)); else - ssa_current_enabling_expr.push_back(*e_it); + SSA.enabling_exprs.push_back(*e_it); } } - - SSA.combined_enabling_expr = conjunction(ssa_current_enabling_expr); } From 1fdcb57f0c41c55b97e15deda8843fd563dbb8d2 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 11:30:20 +0100 Subject: [PATCH 48/90] fixed all no-cex regressions for complete --- regression/modular/{nocex7 => kind6}/main.c | 0 .../modular/{nocex7 => kind6}/test.desc | 2 +- regression/modular/nocex6/test.desc | 4 +- src/ssa/local_ssa.cpp | 270 +++++++++--------- src/ssa/ssa_unwinder.cpp | 3 + src/summarizer/summary_checker_ai.cpp | 2 + 6 files changed, 148 insertions(+), 133 deletions(-) rename regression/modular/{nocex7 => kind6}/main.c (100%) rename regression/modular/{nocex7 => kind6}/test.desc (72%) diff --git a/regression/modular/nocex7/main.c b/regression/modular/kind6/main.c similarity index 100% rename from regression/modular/nocex7/main.c rename to regression/modular/kind6/main.c diff --git a/regression/modular/nocex7/test.desc b/regression/modular/kind6/test.desc similarity index 72% rename from regression/modular/nocex7/test.desc rename to regression/modular/kind6/test.desc index dc053e6ab..630457914 100644 --- a/regression/modular/nocex7/test.desc +++ b/regression/modular/kind6/test.desc @@ -1,6 +1,6 @@ CORE main.c ---havoc +--havoc --k-induction ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex6/test.desc b/regression/modular/nocex6/test.desc index 895f3abe9..1f240717a 100644 --- a/regression/modular/nocex6/test.desc +++ b/regression/modular/nocex6/test.desc @@ -1,6 +1,6 @@ CORE main.c --havoc --unwind 2 -^EXIT=10$ +^EXIT=5$ ^SIGNAL=0$ -^VERIFICATION FAILED$ +^VERIFICATION INCONCLUSIVE$ diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index b7bebed0c..f0a5a5fca 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -99,7 +99,7 @@ void local_SSAt::get_entry_exit_vars() const code_typet::parameterst ¶meter_types = goto_function.type.parameters(); for(code_typet::parameterst::const_iterator - it=parameter_types.begin(); it!=parameter_types.end(); it++) + it=parameter_types.begin(); it!=parameter_types.end(); it++) { const code_typet::parametert ¶meter=*it; const irep_idt &identifier=parameter.get_identifier(); @@ -160,21 +160,21 @@ void local_SSAt::get_nondet_vars() n_it!=nodes.end(); n_it++) { for(nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); - e_it!=n_it->equalities.end(); - e_it++) + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) get_nondet_vars(*e_it); for(nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); - c_it!=n_it->constraints.end(); - c_it++) + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) get_nondet_vars(*c_it); for(nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) get_nondet_vars(*a_it); } } @@ -193,41 +193,51 @@ Function: local_SSAt::get_globals \*******************************************************************/ void local_SSAt::get_globals(locationt loc, std::set &globals, - bool rhs_value, bool with_returns, - const irep_idt &returns_for_function) const + bool rhs_value, bool with_returns, + const irep_idt &returns_for_function) const { { const std::set &ssa_globals = assignments.ssa_objects.globals; for(std::set::const_iterator it = ssa_globals.begin(); - it != ssa_globals.end(); it++) + it != ssa_globals.end(); it++) { #if 0 std::cout << "global: " << from_expr(ns, "", read_lhs(it->get_expr(),loc)) << std::endl; #endif bool is_return = id2string(it->get_identifier()).find( - "#return_value") != std::string::npos; + "#return_value") != std::string::npos; if(!with_returns && is_return) - continue; + continue; //filter out return values of other functions if(with_returns && returns_for_function!="" && is_return && id2string(it->get_identifier()).find( id2string(returns_for_function)+"#return_value")==std::string::npos) - continue; + continue; if(rhs_value) { //workaround for the problem that // rhs() for a return value is always the "input" return value - const exprt &expr = is_return ? - read_lhs(it->get_expr(),--loc) : read_rhs(it->get_expr(),loc); - globals.insert(to_symbol_expr(expr)); + if(is_return) + { + if(loc != goto_function.body.instructions.begin()) + { + const exprt &expr = read_lhs(it->get_expr(),--loc); + globals.insert(to_symbol_expr(expr)); + } + } + else + { + const exprt &expr = read_rhs(it->get_expr(),loc); + globals.insert(to_symbol_expr(expr)); + } } else { - const exprt &expr = read_lhs(it->get_expr(),loc); + const exprt &expr = read_lhs(it->get_expr(),loc); globals.insert(to_symbol_expr(expr)); } } @@ -256,13 +266,13 @@ void local_SSAt::collect_custom_templates() { //search for templates in the loop for(local_SSAt::nodest::iterator nn_it=n_it->loophead; - nn_it!=n_it; nn_it++) + nn_it!=n_it; nn_it++) { - if(nn_it->templates.empty()) continue; - n_it->loophead->templates.insert(n_it->loophead->templates.end(), - nn_it->templates.begin(), - nn_it->templates.end()); - nn_it->templates.clear(); + if(nn_it->templates.empty()) continue; + n_it->loophead->templates.insert(n_it->loophead->templates.end(), + nn_it->templates.begin(), + nn_it->templates.end()); + nn_it->templates.clear(); } } } @@ -381,7 +391,7 @@ void local_SSAt::build_phi_nodes(locationt loc) nodet &node= *(--nodes.end()); for(objectst::const_iterator - o_it=ssa_objects.objects.begin(); + o_it=ssa_objects.objects.begin(); o_it!=ssa_objects.objects.end(); o_it++) { // phi-node for this object here? @@ -390,9 +400,9 @@ void local_SSAt::build_phi_nodes(locationt loc) if(p_it==phi_nodes.end()) continue; // none - #ifdef DEBUG - std::cout << "PHI " << o_it->get_identifier() << "\n"; - #endif +#ifdef DEBUG + std::cout << "PHI " << o_it->get_identifier() << "\n"; +#endif //ignore custom template variables if(id2string(o_it->get_identifier()). @@ -410,44 +420,44 @@ void local_SSAt::build_phi_nodes(locationt loc) for(ssa_domaint::loc_def_mapt::const_iterator incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + incoming_it!=incoming.end(); + incoming_it++) if(incoming_it->second.is_input() || - incoming_it->first < loc->location_number) - { - // it's a forward edge - exprt incoming_value=name(*o_it, incoming_it->second); - //TODO: investigate: here is some nondeterminism - // whether g2 (=g1&c1 (maybe)) or (g1&c1) is used, - // not sure whether this has consequences - // (further than the SSA looking different each time you generate it) - exprt incoming_guard=edge_guard(get_location(incoming_it->first),loc); - - if(rhs.is_nil()) // first - rhs=incoming_value; - else - rhs=if_exprt(incoming_guard, incoming_value, rhs); - } + incoming_it->first < loc->location_number) + { + // it's a forward edge + exprt incoming_value=name(*o_it, incoming_it->second); + //TODO: investigate: here is some nondeterminism + // whether g2 (=g1&c1 (maybe)) or (g1&c1) is used, + // not sure whether this has consequences + // (further than the SSA looking different each time you generate it) + exprt incoming_guard=edge_guard(get_location(incoming_it->first),loc); + + if(rhs.is_nil()) // first + rhs=incoming_value; + else + rhs=if_exprt(incoming_guard, incoming_value, rhs); + } // now do backwards for(ssa_domaint::loc_def_mapt::const_iterator incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + incoming_it!=incoming.end(); + incoming_it++) if(!incoming_it->second.is_input() && - incoming_it->first >= loc->location_number) - { - // it's a backwards edge - const locationt &iloc = get_location(incoming_it->first); - exprt incoming_value=name(*o_it, LOOP_BACK, iloc); - exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); - - if(rhs.is_nil()) // first - rhs=incoming_value; - else - rhs=if_exprt(incoming_select, incoming_value, rhs); - } + incoming_it->first >= loc->location_number) + { + // it's a backwards edge + const locationt &iloc = get_location(incoming_it->first); + exprt incoming_value=name(*o_it, LOOP_BACK, iloc); + exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); + + if(rhs.is_nil()) // first + rhs=incoming_value; + else + rhs=if_exprt(incoming_select, incoming_value, rhs); + } symbol_exprt lhs=name(*o_it, PHI, loc); @@ -496,7 +506,7 @@ void local_SSAt::build_transfer(locationt loc) // template declarations if(code_assign.lhs().id()==ID_symbol && id2string(code_assign.lhs().get(ID_identifier)). - find("return_value_" TEMPLATE_NEWVAR) != std::string::npos) + find("return_value_" TEMPLATE_NEWVAR) != std::string::npos) { //propagate equalities through replace map exprt lhs = code_assign.lhs(); @@ -555,7 +565,7 @@ void local_SSAt::build_function_call(locationt loc) //template declarations if(code_function_call.function().id()==ID_symbol && has_prefix(TEMPLATE_DECL, - id2string(code_function_call.function().get(ID_identifier)))) + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); n_it->templates.push_back(code_function_call.arguments()[0]); @@ -565,8 +575,8 @@ void local_SSAt::build_function_call(locationt loc) #if 0 std::cout << "found template declaration: " - << from_expr(ns,"",code_function_call.arguments()[0]) - << std::endl; + << from_expr(ns,"",code_function_call.arguments()[0]) + << std::endl; #endif template_newvars.clear(); return; @@ -581,7 +591,7 @@ void local_SSAt::build_function_call(locationt loc) //access to "new" value in template declarations if(code_function_call.function().id()==ID_symbol && has_prefix(TEMPLATE_NEWVAR, - id2string(code_function_call.function().get(ID_identifier)))) + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); template_last_newvar = f; @@ -592,27 +602,27 @@ void local_SSAt::build_function_call(locationt loc) f = to_function_application_expr(read_rhs(f, loc)); assert(f.function().id()==ID_symbol); //no function pointers irep_idt fname = to_symbol_expr(f.function()).get_identifier(); - //add equalities for arguments + //add equalities for arguments unsigned i=0; for(exprt::operandst::iterator it = f.arguments().begin(); - it != f.arguments().end(); it++, i++) + it != f.arguments().end(); it++, i++) { //symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - // "#arg"+i2string(i),it->type()); + // "#arg"+i2string(i),it->type()); symbol_exprt arg(id2string(fname)+"#arg"+i2string(i)+"#"+i2string(loc->location_number),it->type()); const typet &argtype = ns.follow(it->type()); if(argtype.id()==ID_struct) { - exprt lhs = read_rhs(arg, loc); - for(size_t j=0; jequalities.push_back(equal_exprt(lhs.operands()[j], - it->operands()[j])); - } + n_it->equalities.push_back(equal_exprt(lhs.operands()[j], + it->operands()[j])); + } } else { - n_it->equalities.push_back(equal_exprt(arg,*it)); + n_it->equalities.push_back(equal_exprt(arg,*it)); } *it = arg; } @@ -673,7 +683,7 @@ void local_SSAt::build_guard(locationt loc) sources.push_back(true_exprt()); for(guard_mapt::incomingt::const_iterator - i_it=entry.incoming.begin(); + i_it=entry.incoming.begin(); i_it!=entry.incoming.end(); i_it++) { @@ -784,12 +794,12 @@ Function: local_SSAt::assertions_to_constraints void local_SSAt::assertions_to_constraints() { for(nodest::iterator - n_it=nodes.begin(); + n_it=nodes.begin(); n_it!=nodes.end(); n_it++) { n_it->constraints.insert(n_it->constraints.end(), - n_it->assertions.begin(),n_it->assertions.end()); + n_it->assertions.begin(),n_it->assertions.end()); } } @@ -830,12 +840,12 @@ void local_SSAt::assertions_after_loop() const exprt::operandst &assertions = assertion_map[loopheads.back()->location]; n_it->assertions_after_loop.insert(n_it->assertions_after_loop.end(), - assertions.begin(),assertions.end()); + assertions.begin(),assertions.end()); assertion_map[loopheads.back()->location].clear(); - //do not consider assertions after another loop + //do not consider assertions after another loop } else // we should have reached the beginning - assert(n_it==nodes.begin()); + assert(n_it==nodes.begin()); } if(n_it->loophead!=nodes.end()) { @@ -1003,7 +1013,7 @@ exprt local_SSAt::read_node_in( const ssa_domaint::loc_def_mapt &incoming=p_it->second; for(ssa_domaint::loc_def_mapt::const_iterator - incoming_it=incoming.begin(); + incoming_it=incoming.begin(); incoming_it!=incoming.end(); incoming_it++) { @@ -1075,21 +1085,21 @@ exprt local_SSAt::read_rhs(const exprt &expr, locationt loc) const unsigned counter=0; replace_side_effects_rec(tmp1, loc, counter); - #ifdef DEBUG +#ifdef DEBUG std::cout << "read_rhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; - #endif +#endif exprt tmp2=dereference(tmp1, loc); - #ifdef DEBUG +#ifdef DEBUG std::cout << "read_rhs tmp2: " << from_expr(ns, "", tmp2) << '\n'; - #endif +#endif exprt result=read_rhs_rec(tmp2, loc); - #ifdef DEBUG +#ifdef DEBUG std::cout << "read_rhs result: " << from_expr(ns, "", result) << '\n'; - #endif +#endif return result; } @@ -1207,7 +1217,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const result.operands().resize(components.size()); for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { @@ -1265,10 +1275,10 @@ void local_SSAt::replace_side_effects_rec( { assert(false); /* counter++; - std::string tmp_suffix= + std::string tmp_suffix= i2string(loc->location_number)+ "."+i2string(counter)+suffix; - expr=malloc_ssa(side_effect_expr, tmp_suffix, ns);*/ + expr=malloc_ssa(side_effect_expr, tmp_suffix, ns);*/ } else { @@ -1300,12 +1310,12 @@ symbol_exprt local_SSAt::name( unsigned cnt=loc->location_number; irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi": - kind==LOOP_BACK?"lb": - kind==LOOP_SELECT?"ls": - "")+ - i2string(cnt)+ - (kind==LOOP_SELECT?std::string(""):suffix); + (kind==PHI?"phi": + kind==LOOP_BACK?"lb": + kind==LOOP_SELECT?"ls": + "")+ + i2string(cnt)+ + (kind==LOOP_SELECT?std::string(""):suffix); #ifdef DEBUG std::cout << "name " << kind << ": " << new_id << '\n'; @@ -1381,7 +1391,7 @@ Function: local_SSAt::nondet_symbol \*******************************************************************/ exprt local_SSAt::nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const + locationt loc, unsigned counter) const { exprt s(ID_nondet_symbol, type); const irep_idt identifier= @@ -1422,7 +1432,7 @@ void local_SSAt::assign_rec( const struct_typet::componentst &components=struct_type.components(); for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { @@ -1532,7 +1542,7 @@ void local_SSAt::output(std::ostream &out) const { for(nodest::const_iterator n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + n_it != nodes.end(); n_it++) { if(n_it->empty()) continue; n_it->output(out, ns); @@ -1556,7 +1566,7 @@ void local_SSAt::output_verbose(std::ostream &out) const { for(nodest::const_iterator n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + n_it != nodes.end(); n_it++) { if(n_it->empty()) continue; out << "*** " << n_it->location->location_number @@ -1564,10 +1574,10 @@ void local_SSAt::output_verbose(std::ostream &out) const n_it->output(out, ns); if(n_it->loophead!=nodes.end()) out << "loop back to location " - << n_it->loophead->location->location_number << "\n"; + << n_it->loophead->location->location_number << "\n"; if(!n_it->enabling_expr.is_true()) out << "enabled if " - << from_expr(ns, "", n_it->enabling_expr) << "\n"; + << from_expr(ns, "", n_it->enabling_expr) << "\n"; out << "\n"; } out << "(enable) " << from_expr(ns, "", get_enabling_exprs()) << "\n\n"; @@ -1596,25 +1606,25 @@ void local_SSAt::nodet::output( out << "(not marked)" << "\n"; #endif for(equalitiest::const_iterator - e_it=equalities.begin(); + e_it=equalities.begin(); e_it!=equalities.end(); e_it++) out << "(E) " << from_expr(ns, "", *e_it) << "\n"; for(constraintst::const_iterator - c_it=constraints.begin(); + c_it=constraints.begin(); c_it!=constraints.end(); c_it++) out << "(C) " << from_expr(ns, "", *c_it) << "\n"; for(assertionst::const_iterator - a_it=assertions.begin(); + a_it=assertions.begin(); a_it!=assertions.end(); a_it++) out << "(A) " << from_expr(ns, "", *a_it) << "\n"; for(function_callst::const_iterator - f_it=function_calls.begin(); + f_it=function_calls.begin(); f_it!=function_calls.end(); f_it++) out << "(F) " << from_expr(ns, "", *f_it) << "\n"; @@ -1622,7 +1632,7 @@ void local_SSAt::nodet::output( #if 0 if(!assertions_after_loop.empty()) out << "(assertions-after-loop) " - << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; + << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; #endif } @@ -1694,27 +1704,27 @@ std::vector & operator << ( ssa_slicer(dest,src); #else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + n_it != src.nodes.end(); n_it++) { if(n_it->marked) continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); else dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); else dest.push_back(*c_it); } @@ -1745,27 +1755,27 @@ std::list & operator << ( ssa_slicer(dest,src); #else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + n_it != src.nodes.end(); n_it++) { if(n_it->marked) continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); else dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); else dest.push_back(*c_it); } @@ -1795,31 +1805,31 @@ decision_proceduret & operator << ( std::list tmp; tmp << src; for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) + it != tmp.end(); it++) dest << *it; #else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + n_it != src.nodes.end(); n_it++) { if(n_it->marked) continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + dest << implies_exprt(n_it->enabling_expr,*e_it); else dest << *e_it; } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + dest << implies_exprt(n_it->enabling_expr,*c_it); else dest << *c_it; } @@ -1848,31 +1858,31 @@ incremental_solvert & operator << ( std::list tmp; tmp << src; for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) + it != tmp.end(); it++) dest << *it; #else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + n_it != src.nodes.end(); n_it++) { if(n_it->marked) continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + dest << implies_exprt(n_it->enabling_expr,*e_it); else dest << *e_it; } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + dest << implies_exprt(n_it->enabling_expr,*c_it); else dest << *c_it; } diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index bc6c868c8..af144242a 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -851,6 +851,9 @@ void ssa_local_unwindert::loop_continuation_conditions( const locationt& loop_id, exprt::operandst &loop_cont) const { + if(loops.empty()) //ignore silently, TBD + return; + const loopt &loop = loops.at(loop_id->location_number); loop_cont.insert(loop_cont.end(), loop.current_continuation_conditions.begin(), diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index b87a0255b..7fe506e7f 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -41,6 +41,7 @@ property_checkert::resultt summary_checker_ait::operator()( ssa_unwinder.unwind_all(unwind); } +#if 0 /*********************************************************************************/ /**************** code to test the loop-specific unwind function *****************/ /**/ @@ -85,6 +86,7 @@ property_checkert::resultt summary_checker_ait::operator()( } /**/ /*********************************************************************************/ +#endif irep_idt entry_function = goto_model.goto_functions.entry_point(); if(options.get_bool_option("unit-check")) From 96e954955290d8eb80eab79907c86e417537bbd9 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 13:30:06 +0100 Subject: [PATCH 49/90] factor out SSA refinements into separate classes --- src/ssa/Makefile | 3 +- src/ssa/ssa_refiner.h | 21 +++ src/ssa/ssa_refiner_monolithic.cpp | 30 ++++ src/ssa/ssa_refiner_monolithic.h | 44 ++++++ src/ssa/ssa_refiner_selective.cpp | 195 +++++++++++++++++++++++ src/ssa/ssa_refiner_selective.h | 74 +++++++++ src/ssa/ssa_unwinder.cpp | 27 ++++ src/ssa/ssa_unwinder.h | 2 + src/summarizer/Makefile | 2 + src/summarizer/summarizer_bw_cex.h | 25 +-- src/summarizer/summary_checker_bmc.cpp | 27 +++- src/summarizer/summary_checker_kind.cpp | 200 +++--------------------- 12 files changed, 446 insertions(+), 204 deletions(-) create mode 100644 src/ssa/ssa_refiner.h create mode 100644 src/ssa/ssa_refiner_monolithic.cpp create mode 100644 src/ssa/ssa_refiner_monolithic.h create mode 100644 src/ssa/ssa_refiner_selective.cpp create mode 100644 src/ssa/ssa_refiner_selective.h diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 8e526b430..52023ab4b 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -7,7 +7,8 @@ SRC = local_ssa.cpp \ ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ unwindable_local_ssa.cpp split_loopheads.cpp const_propagator.cpp ssa_const_propagator.cpp \ - replace_symbol_ext.cpp ssa_dependency_graph.cpp + replace_symbol_ext.cpp ssa_dependency_graph.cpp \ + ssa_refiner_monolithic.cpp ssa_refiner_selective.cpp include $(CBMC)/src/config.inc include $(CBMC)/src/common diff --git a/src/ssa/ssa_refiner.h b/src/ssa/ssa_refiner.h new file mode 100644 index 000000000..0c933e0da --- /dev/null +++ b/src/ssa/ssa_refiner.h @@ -0,0 +1,21 @@ +/*******************************************************************\ + +Module: SSA Refiner Base Class + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SSA_REFINER_H +#define CPROVER_SSA_REFINER_H + +#include + +class ssa_refinert : public messaget +{ + public: + virtual bool operator()() { assert(false); } + virtual unsigned get_unwind() { assert(false); } +}; + +#endif diff --git a/src/ssa/ssa_refiner_monolithic.cpp b/src/ssa/ssa_refiner_monolithic.cpp new file mode 100644 index 000000000..a7191481c --- /dev/null +++ b/src/ssa/ssa_refiner_monolithic.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: SSA Refiner for Monolithic Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "ssa_refiner_monolithic.h" + +/*******************************************************************\ + +Function: ssa_refiner_monolithict::operator() + + Inputs: + + Outputs: + + Purpose: refine all + +\*******************************************************************/ + +bool ssa_refiner_monolithict::operator()() +{ + status() << "Unwinding (k=" << unwind << ")" << eom; + summary_db.mark_recompute_all(); //TODO: recompute only functions with loops + ssa_unwinder.unwind_all(unwind); + + return unwind++::const_iterator l_it = + it->second.loops.begin(); + l_it != it->second.loops.end(); l_it++) + { + unsigned new_unwind = ssa_unwinder.unwind_loop_once_more(it->first, + (*l_it)->location_number); + unwind = std::max(unwind, new_unwind); + } + } + +#if 0 + // inline functions "selectively" (those that seem to be the "reason") + for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + { + for(std::set::const_iterator f_it = + it->second.functions.begin(); + f_it != it->second.functions.end(); f_it++) + { + local_SSAt &SSA = ssa_db.get(it->first); + + std::list inline_nodes; + std::vector first_node_equalities; + int counter = ssa_inliner.get_rename_counter(); + + for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + + local_SSAt::nodet &node=*n_it; + + if(node.location == *(f_it)) + { + + bool clear_function_call = false; + + for(local_SSAt::nodet::function_callst::const_iterator fc_it = + node.function_calls.begin(); + fc_it!=node.function_calls.end(); fc_it++) + { + + irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); + if(ssa_db.exists(fname)) + { + clear_function_call = true; + + local_SSAt &fSSA = ssa_db.get(fname); + + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; + + // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) + ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); + equal_exprt e = to_equal_expr(guard_binding); + node.equalities.push_back(e); + + ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); + + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++){ + equal_exprt e = to_equal_expr(*b_it); + node.equalities.push_back(e); + } + + for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet fnode=*fn_it; + inline_nodes.push_back(fnode); + } + + if(fname == entry_function){ + // first_node_equalities should contain all the equalities from the first node of fSSA + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + first_node_equalities.push_back(*e_it); + } + break; + } + } + else{ + // except those (the one) that start with "ssa::guard" and have true in the rhs + for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++){ + local_SSAt::nodet &fnode=*fn_it; + + bool ignore_equality = true; + + for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); + e_it!=fnode.equalities.end(); e_it++){ + // unless lhs starts with "ssa::guard" and rhs is true + + equal_exprt e = to_equal_expr(*e_it); + exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); + std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ + ignore_equality = false; + } + else{ + first_node_equalities.push_back(*e_it); + } + + } + break; + } + } + + } + } + + if(clear_function_call == true) + node.function_calls.clear(); + + } + } + + bool replace_first_node_equalities = true; + + if(inline_nodes.size() > 0) + { + for(std::list::iterator in_it = inline_nodes.begin(); + in_it != inline_nodes.end(); in_it++) + { + local_SSAt::nodet &inline_node = *in_it; + + if(replace_first_node_equalities == true) + { + inline_node.equalities.clear(); + for(std::vector::iterator e_it=first_node_equalities.begin(); + e_it!=first_node_equalities.end(); e_it++){ + inline_node.equalities.push_back(*e_it); + } + replace_first_node_equalities = false; + } + + for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); + e_it!=inline_node.equalities.end(); e_it++){ + ssa_inliner.rename(*e_it, counter); + } + + for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); + c_it!=inline_node.constraints.end(); c_it++){ + ssa_inliner.rename(*c_it, counter); + } + + for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); + a_it!=inline_node.assertions.end(); a_it++){ + ssa_inliner.rename(*a_it, counter); + } + + // push inline_node into SSA + SSA.nodes.push_back(inline_node); + + } + } + + } + } +#endif + + return unwind functions; + std::set loops; + }; + + class reasont : public std::map + { + public: + void merge(const reasont &other) + { + for(reasont::const_iterator it = other.begin(); + it != other.end(); ++it) + { + reason_infot &r = (*this)[it->first]; + r.functions.insert(it->second.functions.begin(), + it->second.functions.end()); + r.loops.insert(it->second.loops.begin(), it->second.loops.end()); + } + } + }; + + + explicit ssa_refiner_selectivet( + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + unsigned _max_unwind, + ssa_inlinert &_ssa_inliner, + reasont &_reason + ) : + ssa_db(_ssa_db), + ssa_unwinder(_ssa_unwinder), + max_unwind(_max_unwind), + ssa_inliner(_ssa_inliner), + reason(_reason) + {} + + virtual bool operator()(); + virtual unsigned get_unwind() { return unwind; } + + protected: + ssa_dbt &ssa_db; + ssa_unwindert &ssa_unwinder; + const unsigned max_unwind; + ssa_inlinert &ssa_inliner; + unsigned unwind; + reasont &reason; +}; + +#endif diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index af144242a..aa2fb4aa1 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -275,6 +275,13 @@ void ssa_local_unwindert::build_exit_conditions() * *****************************************************************************/ +unsigned ssa_local_unwindert::unwind_loop_at_location(unsigned loc) +{ + unsigned k = loops.at(loc).current_unwinding+1; + unwind_loop_at_location(loc,k); + return k; +} + void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) { if(SSA.current_unwinding >= (long)k) @@ -995,6 +1002,26 @@ void ssa_unwindert::unwind_loop_alone(const irep_idt fname, unsigned loc, unsign it->second.unwind_loop_at_location(loc, k); } +/*****************************************************************************\ + * + * Function : ssa_unwindert::unwind_loop_once_more() + * + * Input : + * + * Output : + * + * Purpose : + * + *****************************************************************************/ + +unsigned ssa_unwindert::unwind_loop_once_more(const irep_idt fname, unsigned loc) +{ + assert(is_initialized); + unwinder_mapt::iterator it = unwinder_map.find(fname); + assert(it != unwinder_map.end()); + return it->second.unwind_loop_at_location(loc); +} + /*****************************************************************************\ * * Function : ssa_unwindert::unwind diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 14e59b3f5..b8896e910 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -27,6 +27,7 @@ class ssa_local_unwindert void init(); void unwind_loop_at_location(unsigned loc, unsigned k); + unsigned unwind_loop_at_location(unsigned loc); void unwind(unsigned k); //TODO: not yet sure how to do that @@ -132,6 +133,7 @@ class ssa_unwindert : public messaget void init_localunwinders(); void unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned k); + unsigned unwind_loop_once_more(const irep_idt fname, unsigned loc); void unwind(const irep_idt fname, unsigned k); void unwind_all(unsigned k); diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 41654d5ad..878ad3397 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -52,6 +52,8 @@ DELTA_OBJ+=\ ../ssa/replace_symbol_ext$(OBJEXT) \ ../ssa/ssa_const_propagator$(OBJEXT) \ ../ssa/ssa_dependency_graph$(OBJEXT) \ + ../ssa/ssa_refiner_monolithic$(OBJEXT) \ + ../ssa/ssa_refiner_selective$(OBJEXT) \ ../functions/summary$(OBJEXT) \ ../functions/get_function$(OBJEXT) \ ../functions/path_util$(OBJEXT) \ diff --git a/src/summarizer/summarizer_bw_cex.h b/src/summarizer/summarizer_bw_cex.h index b76a214fb..8da077347 100644 --- a/src/summarizer/summarizer_bw_cex.h +++ b/src/summarizer/summarizer_bw_cex.h @@ -14,6 +14,7 @@ Author: Kumar Madhukar, Peter Schrammel #include #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" +#include "../ssa/ssa_refiner_selective.h" #include "../ssa/local_ssa.h" #include "ssa_db.h" @@ -22,29 +23,7 @@ Author: Kumar Madhukar, Peter Schrammel class summarizer_bw_cex_baset : public summarizer_bwt { public: - struct reason_infot - { - typedef local_SSAt::locationt function_infot; //call_site; restriction: we assume that there is a single function call in an SSA node - typedef local_SSAt::locationt loop_infot; - std::set functions; - std::set loops; - }; - - class reasont : public std::map - { - public: - void merge(const reasont &other) - { - for(reasont::const_iterator it = other.begin(); - it != other.end(); ++it) - { - reason_infot &r = (*this)[it->first]; - r.functions.insert(it->second.functions.begin(), - it->second.functions.end()); - r.loops.insert(it->second.loops.begin(), it->second.loops.end()); - } - } - }; + typedef ssa_refiner_selectivet::reasont reasont; virtual void summarize(); virtual void summarize(const function_namet &entry_function); diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/summarizer/summary_checker_bmc.cpp index 0744d60d3..dac6f2571 100644 --- a/src/summarizer/summary_checker_bmc.cpp +++ b/src/summarizer/summary_checker_bmc.cpp @@ -8,6 +8,10 @@ Author: Peter Schrammel #include "summary_checker_bmc.h" +#include "../ssa/ssa_refiner.h" +#include "../ssa/ssa_refiner_monolithic.h" +#include "../ssa/ssa_refiner_selective.h" + /*******************************************************************\ @@ -38,24 +42,35 @@ property_checkert::resultt summary_checker_bmct::operator()( status() << "Max-unwind is " << max_unwind << eom; ssa_unwinder.init_localunwinders(); - for(unsigned unwind = 0; unwind<=max_unwind; unwind++) + ssa_refinert *ssa_refiner; + if((options.get_bool_option("inline"))) + ssa_refiner = new ssa_refiner_monolithict(summary_db, ssa_unwinder, + max_unwind); + else + ssa_refiner = new ssa_refiner_selectivet(ssa_db, ssa_unwinder, + max_unwind, ssa_inliner, reason); + ssa_refiner->set_message_handler(get_message_handler()); + + //while can refine + while((*ssa_refiner)()) { - status() << "Unwinding (k=" << unwind << ")" << messaget::eom; - summary_db.mark_recompute_all(); - ssa_unwinder.unwind_all(unwind); + unsigned unwind = ssa_refiner->get_unwind(); //dependency graphs if(!(options.get_bool_option("inline"))) { if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")) + (options.get_option("spurious-check") != "abstract")) { - SSA_dependency_graphs(goto_model, ns); + SSA_dependency_graphs(goto_model, ns); } } + //check std::set seen_function_calls; result = check_properties(entry_function, entry_function, seen_function_calls); + + //result if(result == property_checkert::PASS) { status() << "incremental BMC proof found after " diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 99c72b653..417e8ee22 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -1,15 +1,17 @@ /*******************************************************************\ -Module: Summarizer Checker for k-induction +Module: Summary Checker for k-induction Author: Peter Schrammel \*******************************************************************/ #include "summary_checker_kind.h" +#include "../ssa/ssa_refiner.h" +#include "../ssa/ssa_refiner_monolithic.h" +#include "../ssa/ssa_refiner_selective.h" #define GIVE_UP_INVARIANTS 7 -#define UNWIND_ALL /*******************************************************************\ @@ -40,175 +42,19 @@ property_checkert::resultt summary_checker_kindt::operator()( status() << "Max-unwind is " << max_unwind << eom; ssa_unwinder.init_localunwinders(); - for(unsigned unwind = 0; unwind<=max_unwind; unwind++) + ssa_refinert *ssa_refiner; + if((options.get_bool_option("inline"))) + ssa_refiner = new ssa_refiner_monolithict(summary_db, ssa_unwinder, + max_unwind); + else + ssa_refiner = new ssa_refiner_selectivet(ssa_db, ssa_unwinder, + max_unwind, ssa_inliner, reason); + ssa_refiner->set_message_handler(get_message_handler()); + + //while can refine + while((*ssa_refiner)()) { - status() << "Unwinding (k=" << unwind << ")" << eom; - summary_db.mark_recompute_all(); //TODO: recompute only functions with loops - -#ifdef UNWIND_ALL - ssa_unwinder.unwind_all(unwind); -#else - // unwind loops "selectively" (those that seem to be the "reason") - for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) - { - for(std::set::const_iterator l_it = it->second.loops.begin(); - l_it != it->second.loops.end(); l_it++) - { - ssa_unwinder.unwind_loop_alone(it->first, (*l_it)->location_number, unwind); - } - } - - // inline functions "selectively" (those that seem to be the "reason") - for(summarizer_bw_cex_baset::reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) - { - for(std::set::const_iterator f_it = it->second.functions.begin(); - f_it != it->second.functions.end(); f_it++) - { - local_SSAt &SSA = ssa_db.get(it->first); - - std::list inline_nodes; - std::vector first_node_equalities; - int counter = ssa_inliner.get_rename_counter(); - - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - - local_SSAt::nodet &node=*n_it; - - if(node.location == *(f_it)) - { - - bool clear_function_call = false; - - for(local_SSAt::nodet::function_callst::const_iterator fc_it=node.function_calls.begin(); - fc_it!=node.function_calls.end(); fc_it++) - { - - irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); - if(ssa_db.exists(fname)) - { - clear_function_call = true; - - local_SSAt &fSSA = ssa_db.get(fname); - - exprt guard_binding; - exprt::operandst bindings_in, bindings_out; - - // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) - ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); - equal_exprt e = to_equal_expr(guard_binding); - node.equalities.push_back(e); - - ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); - - for(exprt::operandst::const_iterator b_it=bindings_in.begin(); - b_it!=bindings_in.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - for(exprt::operandst::const_iterator b_it=bindings_out.begin(); - b_it!=bindings_out.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet fnode=*fn_it; - inline_nodes.push_back(fnode); - } - - if(fname == entry_function){ - // first_node_equalities should contain all the equalities from the first node of fSSA - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - first_node_equalities.push_back(*e_it); - } - break; - } - } - else{ - // except those (the one) that start with "ssa::guard" and have true in the rhs - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - - bool ignore_equality = true; - - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - // unless lhs starts with "ssa::guard" and rhs is true - - equal_exprt e = to_equal_expr(*e_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ - ignore_equality = false; - } - else{ - first_node_equalities.push_back(*e_it); - } - - } - break; - } - } - - } - } - - if(clear_function_call == true) - node.function_calls.clear(); - - } - } - - bool replace_first_node_equalities = true; - - if(inline_nodes.size() > 0) - { - for(std::list::iterator in_it = inline_nodes.begin(); - in_it != inline_nodes.end(); in_it++) - { - local_SSAt::nodet &inline_node = *in_it; - - if(replace_first_node_equalities == true) - { - inline_node.equalities.clear(); - for(std::vector::iterator e_it=first_node_equalities.begin(); - e_it!=first_node_equalities.end(); e_it++){ - inline_node.equalities.push_back(*e_it); - } - replace_first_node_equalities = false; - } - - for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); - e_it!=inline_node.equalities.end(); e_it++){ - ssa_inliner.rename(*e_it, counter); - } - - for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); - c_it!=inline_node.constraints.end(); c_it++){ - ssa_inliner.rename(*c_it, counter); - } - - for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); - a_it!=inline_node.assertions.end(); a_it++){ - ssa_inliner.rename(*a_it, counter); - } - - // push inline_node into SSA - SSA.nodes.push_back(inline_node); - - } - } - - } - } + unsigned unwind = ssa_refiner->get_unwind(); //dependency graphs if(!(options.get_bool_option("inline"))) @@ -219,10 +65,13 @@ property_checkert::resultt summary_checker_kindt::operator()( SSA_dependency_graphs(goto_model, ns); } } - -#endif + + //check std::set seen_function_calls; - result = check_properties(entry_function, entry_function, seen_function_calls); + result = check_properties(entry_function, entry_function, + seen_function_calls); + + //do static analysis and check again if(result == property_checkert::UNKNOWN && !options.get_bool_option("havoc") && (unwind seen_function_calls; - result = check_properties(entry_function, entry_function, seen_function_calls); + result = check_properties(entry_function, entry_function, + seen_function_calls); } + //result if(result == property_checkert::PASS) { status() << "k-induction proof found after " @@ -246,6 +97,7 @@ property_checkert::resultt summary_checker_kindt::operator()( break; } } + delete ssa_refiner; report_statistics(); return result; } From 0598f08c2b732a7f34488ebd87cc111f371e5c05 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 16:16:00 +0100 Subject: [PATCH 50/90] unwind output one-off --- src/ssa/ssa_refiner_monolithic.h | 2 +- src/ssa/ssa_refiner_selective.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ssa/ssa_refiner_monolithic.h b/src/ssa/ssa_refiner_monolithic.h index 5bdc562f0..77a86fde3 100644 --- a/src/ssa/ssa_refiner_monolithic.h +++ b/src/ssa/ssa_refiner_monolithic.h @@ -32,7 +32,7 @@ class ssa_refiner_monolithict : public ssa_refinert {} virtual bool operator()(); - virtual unsigned get_unwind() { return unwind; } + virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } protected: summary_dbt &summary_db; diff --git a/src/ssa/ssa_refiner_selective.h b/src/ssa/ssa_refiner_selective.h index 72e40af87..f97329f01 100644 --- a/src/ssa/ssa_refiner_selective.h +++ b/src/ssa/ssa_refiner_selective.h @@ -60,7 +60,7 @@ class ssa_refiner_selectivet : public ssa_refinert {} virtual bool operator()(); - virtual unsigned get_unwind() { return unwind; } + virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } protected: ssa_dbt &ssa_db; From 84eef104a2c3e455c420b4c1fb456a9fedb6ff5c Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 16:59:59 +0100 Subject: [PATCH 51/90] flag functions as inlined and debug output in ssa_refiner about what is being refined --- src/ssa/local_ssa.h | 9 ++++---- src/ssa/ssa_refiner_selective.cpp | 21 +++++++++++++++++++ src/summarizer/summarizer_bw_cex_concrete.cpp | 11 +++++++++- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 64130a90d..314d6cc36 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -60,7 +60,7 @@ class local_SSAt std::list::iterator _loophead) : enabling_expr(true_exprt()), - marked(false), + marked(false), function_calls_inlined(false), location(_location), loophead(_loophead) { @@ -85,6 +85,7 @@ class local_SSAt typedef std::vector function_callst; function_callst function_calls; + bool function_calls_inlined; //custom invariant templates typedef std::vector templatest; @@ -112,12 +113,12 @@ class local_SSAt void mark_nodes() { for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = true; + n_it!=nodes.end(); n_it++) n_it->marked = true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = false; + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) n_it->marked = false; } // for incremental unwinding diff --git a/src/ssa/ssa_refiner_selective.cpp b/src/ssa/ssa_refiner_selective.cpp index 4406cbb75..1d77dc29f 100644 --- a/src/ssa/ssa_refiner_selective.cpp +++ b/src/ssa/ssa_refiner_selective.cpp @@ -31,10 +31,31 @@ bool ssa_refiner_selectivet::operator()() { unsigned new_unwind = ssa_unwinder.unwind_loop_once_more(it->first, (*l_it)->location_number); + debug() << "Refining function " << it->first << ": unwinding loop at " + << (*l_it)->location_number << " (k=" << new_unwind << ")" << eom; unwind = std::max(unwind, new_unwind); } } + // inline functions "selectively" (those that seem to be the "reason") + for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + { + for(std::set::const_iterator f_it = + it->second.functions.begin(); + f_it != it->second.functions.end(); f_it++) + { + local_SSAt &SSA = ssa_db.get(it->first); + local_SSAt::nodest::iterator n_it = SSA.find_node(*f_it); + assert(n_it->function_calls.size()==1); + n_it->function_calls_inlined = true; + + irep_idt fname = to_symbol_expr(n_it->function_calls.begin() + ->function()).get_identifier(); + debug() << "Refining function " << it->first << ": inlining call to " + << fname << " at " << (*f_it)->location_number<< eom; + } + } + #if 0 // inline functions "selectively" (those that seem to be the "reason") for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 80ef87fcd..1d05323c8 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -146,6 +146,12 @@ void summarizer_bw_cex_concretet::compute_summary_rec( bool context_sensitive) { local_SSAt &SSA = ssa_db.get(function_name); + + //TODO: let's just put all loops into the reason + for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); ++n_it) + if (n_it->loophead != SSA.nodes.end()) + reason[function_name].loops.insert(n_it->loophead->location); summaryt summary; if(summary_db.exists(function_name)) @@ -462,7 +468,10 @@ void summarizer_bw_cex_concretet::inline_summaries( exprt postcondition_call = true_exprt(); postcondition_call = compute_calling_context2( - function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); + function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); + + //TODO: just put all function calls into reason + reason[function_name].functions.insert(n_it->location); irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); status() << "Recursively summarizing function " << fname << eom; From 9c70e4f0e267a10e69542f3f2628613f420cfc15 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 18:52:04 +0100 Subject: [PATCH 52/90] recursive gathering of summaries and inlined functions --- src/ssa/ssa_inliner.cpp | 766 ++++++++++++++------------ src/ssa/ssa_inliner.h | 47 +- src/ssa/ssa_refiner_selective.cpp | 156 ------ src/summarizer/summary_checker_base.h | 2 +- 4 files changed, 457 insertions(+), 514 deletions(-) diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index d500f7114..5c5e9bf72 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -24,11 +24,11 @@ Function: ssa_inlinert::get_guard_binding \*******************************************************************/ void ssa_inlinert::get_guard_binding( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - exprt &guard_binding, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter) { exprt callee_guard, caller_guard; callee_guard = fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); @@ -51,13 +51,13 @@ Function: ssa_inlinert::get_bindings \*******************************************************************/ void ssa_inlinert::get_bindings( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - exprt::operandst &bindings_in, - exprt::operandst &bindings_out, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter) { //getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; @@ -91,6 +91,87 @@ void ssa_inlinert::get_bindings( } +/*******************************************************************\ + +Function: ssa_inlinert::get_inlined + + Inputs: + + Outputs: + + Purpose: get inlined function call + + +\*******************************************************************/ + +bool ssa_inlinert::get_inlined( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + int counter, + bool error_summ) +{ + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + const local_SSAt &fSSA = ssa_db.get(fname); + + bool assertion_flag = get_summaries(fSSA, + summaryt::call_sitet(fSSA.goto_function.body.instructions.end()), + forward,assert_summaries,noassert_summaries,bindings,error_summ); + + //bindings + exprt guard_binding; + get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); + bindings.push_back(guard_binding); + get_bindings(SSA,fSSA,n_it,f_it,bindings,bindings,counter); + + bool first_equality = true; + for(local_SSAt::nodest::const_iterator n_it = fSSA.nodes.begin(); + n_it != fSSA.nodes.end(); n_it++) + { + const local_SSAt::nodet &fnode=*n_it; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it = + fnode.equalities.begin(); e_it!=fnode.equalities.end(); e_it++) + { + // unless lhs starts with "ssa::guard" and rhs is true + // because that one is replaced by the guard binding + const equal_exprt &e = to_equal_expr(*e_it); + const exprt &lhs = e.lhs(); const exprt &rhs = e.rhs(); + std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); + if((var_string.substr(0,11) == "ssa::$guard") && + rhs.is_true() && first_equality) + { + first_equality = false; + } + else + { + noassert_summaries.push_back(*e_it); + rename(noassert_summaries.back(), counter); + } + } + for(local_SSAt::nodet::constraintst::const_iterator c_it = + fnode.constraints.begin(); c_it!=fnode.constraints.end(); c_it++) + { + noassert_summaries.push_back(*c_it); + rename(noassert_summaries.back(), counter); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it = + fnode.assertions.begin(); a_it!=fnode.assertions.end(); a_it++) + { + assert_summaries.push_back(*a_it); + rename(assert_summaries.back(), counter); + assertion_flag = true; + } + } + + return assertion_flag; +} + + /*******************************************************************\ Function: ssa_inlinert::get_summary @@ -99,25 +180,28 @@ Function: ssa_inlinert::get_summary Outputs: - Purpose: get summary for function call + Purpose: get summary for non-inlined function calls \*******************************************************************/ -void ssa_inlinert::get_summary( +bool ssa_inlinert::get_summary( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, bool forward, - exprt::operandst &summaries, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, exprt::operandst &bindings, int counter, bool error_summ) { + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + const summaryt &summary = summary_db.get(fname); + //getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_in); SSA.get_globals(loc,cs_globals_out,false); #if 0 @@ -145,33 +229,43 @@ void ssa_inlinert::get_summary( exprt transformer; if(error_summ) - { - // update transformer using the error_summaries map - summaryt::call_sitet call_site(loc); - summaryt::error_summariest::const_iterator e_it = - summary.error_summaries.find(call_site); - if(e_it != summary.error_summaries.end() && - !e_it->second.is_nil()) - transformer = e_it->second; - else - transformer = true_exprt(); - } + { + // update transformer using the error_summaries map + summaryt::call_sitet call_site(loc); + summaryt::error_summariest::const_iterator e_it = + summary.error_summaries.find(call_site); + if(e_it != summary.error_summaries.end() && + !e_it->second.is_nil()) + transformer = e_it->second; + else + transformer = true_exprt(); + } else - { - if(forward) - transformer = summary.fw_transformer.is_nil() ? true_exprt() : - summary.fw_transformer; - else - transformer = summary.bw_transformer.is_nil() ? true_exprt() : - summary.bw_transformer; - } + { + if(forward) + transformer = summary.fw_transformer.is_nil() ? true_exprt() : + summary.fw_transformer; + else + transformer = summary.bw_transformer.is_nil() ? true_exprt() : + summary.bw_transformer; + } rename(transformer,counter); - summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), - transformer)); + if(summary.has_assertion) + { + assert_summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), + transformer)); + } + else + { + noassert_summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), + transformer)); + } //equalities for globals out (including unmodified globals) - get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); + get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); + + return summary.has_assertion; } /*******************************************************************\ @@ -194,68 +288,59 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) } void ssa_inlinert::get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings) { - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(summary_db.exists(fname)) - { - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname), - forward,summaries,bindings,counter); - } - } - } + get_summaries(SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward,summaries,summaries,bindings); } bool ssa_inlinert::get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings) + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ) { bool assertion_flag = false; for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); + n_it->function_calls.begin(); f_it != n_it->function_calls.end(); f_it++) + { + //do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site == this_call_site) + continue; + + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + //get inlined function + if(n_it->function_calls_inlined) { - //do not use summary for current call site - summaryt::call_sitet this_call_site(n_it->location); - if(current_call_site == this_call_site) - continue; - - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries - if(summary_db.exists(fname)) - { - summaryt summary = summary_db.get(fname); - if(summary.has_assertion == true){ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); - assertion_flag = true; - } - else{ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); - } - } + counter++; + bool new_assertion_flag = + get_inlined(SSA,n_it,f_it, + forward,assert_summaries,noassert_summaries, + bindings,counter,error_summ); + assertion_flag = assertion_flag || new_assertion_flag; } + //get summary + else if(summary_db.exists(fname)) + { + counter++; + bool new_assertion_flag = + get_summary(SSA,n_it,f_it, + forward,assert_summaries,noassert_summaries, + bindings,counter,error_summ); + assertion_flag = assertion_flag || new_assertion_flag; + } + } } return assertion_flag; } @@ -276,18 +361,17 @@ Function: ssa_inlinert::replace \*******************************************************************/ void ssa_inlinert::replace(local_SSAt &SSA, - bool forward, - bool preconditions_as_assertions, - int counter) + bool forward, + bool preconditions_as_assertions, + int counter) { for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); + f_it = n_it->function_calls.begin(); f_it != n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); if(summary_db.exists(fname)) @@ -296,15 +380,15 @@ void ssa_inlinert::replace(local_SSAt &SSA, status() << "Replacing function " << fname << " by summary" << eom; - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); + //getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc = n_it->location; + SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_out,false); //replace replace(SSA,n_it,f_it,cs_globals_in,cs_globals_out,summary, - forward,preconditions_as_assertions,counter); + forward,preconditions_as_assertions,counter); //remove function_call rm_function_calls.insert(f_it); @@ -331,58 +415,58 @@ Function: ssa_inlinert::replace \*******************************************************************/ void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - int counter, - bool recursive, bool rename) + const ssa_dbt &ssa_db, + int counter, + bool recursive, bool rename) { for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::iterator + f_it = n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - if(ssa_db.exists(fname)) + if(ssa_db.exists(fname)) { status() << "Inlining function " << fname << eom; local_SSAt fSSA = ssa_db.get(fname); //copy if(rename) - { - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); + { + //getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc = n_it->location; + SSA.get_globals(loc,cs_globals_in); + SSA.get_globals(loc,cs_globals_out,false); - if(recursive) - { - replace(fSSA,ssa_db,true,counter); - } - - //replace - replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); - } + if(recursive) + { + replace(fSSA,ssa_db,true,counter); + } + + //replace + replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); + } else // just add to nodes - { - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++) - { - debug() << "new node: "; fn_it->output(debug(),fSSA.ns); - debug() << eom; + { + for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); + fn_it != fSSA.nodes.end(); fn_it++) + { + debug() << "new node: "; fn_it->output(debug(),fSSA.ns); + debug() << eom; - new_nodes.push_back(*fn_it); - } - } + new_nodes.push_back(*fn_it); + } + } } - else debug() << "No body available for function " << fname << eom; - commit_node(n_it); - } - commit_nodes(SSA.nodes,n_it); + else debug() << "No body available for function " << fname << eom; + commit_node(n_it); } + commit_nodes(SSA.nodes,n_it); + } } /*******************************************************************\ @@ -398,14 +482,14 @@ Function: ssa_inlinert::replace() \*******************************************************************/ void ssa_inlinert::replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const summaryt &summary, - bool forward, - bool preconditions_as_assertions, - int counter) + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + bool forward, + bool preconditions_as_assertions, + int counter) { //equalities for arguments replace_params(summary.params,*f_it,counter); @@ -418,19 +502,19 @@ void ssa_inlinert::replace(local_SSAt &SSA, if(forward) precondition = summary.fw_precondition; else precondition = summary.bw_precondition; if(!preconditions_as_assertions) - { - rename(precondition,counter); - node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); - } + { + rename(precondition,counter); + node->constraints.push_back( + implies_exprt(SSA.guard_symbol(node->location), + precondition)); + } else - { - rename(precondition,counter); - node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); - } + { + rename(precondition,counter); + node->assertions.push_back( + implies_exprt(SSA.guard_symbol(node->location), + precondition)); + } exprt transformer; if(forward) transformer = summary.fw_transformer; else transformer = summary.bw_transformer; @@ -462,12 +546,12 @@ void ssa_inlinert::replace(local_SSAt &SSA, \*******************************************************************/ void ssa_inlinert::replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const local_SSAt &function, - int counter) + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &function, + int counter) { //equalities for arguments replace_params(function.params,*f_it,counter); @@ -478,7 +562,7 @@ void ssa_inlinert::replace(local_SSAt::nodest &nodes, //add function body for(local_SSAt::nodest::const_iterator n_it = function.nodes.begin(); n_it != function.nodes.end(); n_it++) - { + { local_SSAt::nodet n = *n_it; //copy rename(n,counter); new_nodes.push_back(n); @@ -504,40 +588,40 @@ Function: ssa_inlinert::replace_globals_in() \*******************************************************************/ void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &globals, - exprt::operandst &c, - int counter) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter) { std::string suffix = id2string(funapp_expr.get(ID_suffix)); //equalities for globals_in for(summaryt::var_sett::const_iterator it = globals_in.begin(); it != globals_in.end(); it++) + { + symbol_exprt lhs = *it; //copy + rename(lhs,counter); + symbol_exprt rhs; + if(find_corresponding_symbol(*it,globals,rhs)) { - symbol_exprt lhs = *it; //copy - rename(lhs,counter); - symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) - { - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - debug() << "binding: " << lhs.get_identifier() << " == " - << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs,rhs)); - } + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + c.push_back(equal_exprt(lhs,rhs)); + } #if 0 - else - warning() << "'" << it->get_identifier() - << "' not bound in caller" << eom; + else + warning() << "'" << it->get_identifier() + << "' not bound in caller" << eom; #endif - } + } //return conjunction(c); } void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals, - int counter) + const local_SSAt::var_sett &globals, + int counter) { //equalities for globals_in for(summaryt::var_sett::const_iterator it = globals_in.begin(); @@ -547,11 +631,11 @@ void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, rename(lhs,counter); symbol_exprt rhs; if(find_corresponding_symbol(*it,globals,rhs)) - { - debug() << "binding: " << lhs.get_identifier() << " == " - << rhs.get_identifier() << eom; - new_equs.push_back(equal_exprt(lhs,rhs)); - } + { + debug() << "binding: " << lhs.get_identifier() << " == " + << rhs.get_identifier() << eom; + new_equs.push_back(equal_exprt(lhs,rhs)); + } #if 0 else warning() << "'" << it->get_identifier() @@ -573,82 +657,82 @@ Function: ssa_inlinert::replace_params() \*******************************************************************/ void ssa_inlinert::get_replace_params(const local_SSAt &SSA, - const local_SSAt::var_listt ¶ms, - local_SSAt::nodest::const_iterator n_it, - const function_application_exprt &funapp_expr, - exprt::operandst &c, - int counter) + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter) { //equalities for arguments local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); it != funapp_expr.arguments().end(); it++, p_it++) - { + { #if 0 - std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*it) << std::endl; + std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*it) << std::endl; #endif #if 0 - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis - { - warning() << "ignoring excess function arguments" << eom; - break; - } + local_SSAt::var_listt::const_iterator next_p_it = p_it; + if(funapp_expr.arguments().size() != params.size() && + ++next_p_it==params.end()) //TODO: handle ellipsis + { + warning() << "ignoring excess function arguments" << eom; + break; + } #endif - if(SSA.ns.follow(it->type()).id()==ID_struct) - { - exprt rhs = SSA.read_rhs(*it, n_it->location); //copy + if(SSA.ns.follow(it->type()).id()==ID_struct) + { + exprt rhs = SSA.read_rhs(*it, n_it->location); //copy #if 0 - std::cout << "split param " << from_expr(SSA.ns,"",*it) - << " into " << from_expr(SSA.ns,"",rhs) << std::endl; + std::cout << "split param " << from_expr(SSA.ns,"",*it) + << " into " << from_expr(SSA.ns,"",rhs) << std::endl; #endif - forall_operands(o_it, rhs) - { - assert(p_it!=params.end()); - exprt lhs = *p_it; //copy - rename(lhs,counter); + forall_operands(o_it, rhs) + { + assert(p_it!=params.end()); + exprt lhs = *p_it; //copy + rename(lhs,counter); #if 0 - std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; + std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; #endif - c.push_back(equal_exprt(lhs,*o_it)); - ++p_it; - } - } - else - { - exprt lhs = *p_it; //copy - rename(lhs,counter); - c.push_back(equal_exprt(lhs,*it)); + c.push_back(equal_exprt(lhs,*o_it)); + ++p_it; } } + else + { + exprt lhs = *p_it; //copy + rename(lhs,counter); + c.push_back(equal_exprt(lhs,*it)); + } + } } void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr, - int counter) + const function_application_exprt &funapp_expr, + int counter) { //equalities for arguments local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); it != funapp_expr.arguments().end(); it++, p_it++) + { + local_SSAt::var_listt::const_iterator next_p_it = p_it; + if(funapp_expr.arguments().size() != params.size() && + ++next_p_it==params.end()) //TODO: handle ellipsis { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis - { - warning() << "ignoring excess function arguments" << eom; - break; - } - - exprt lhs = *p_it; //copy - rename(lhs,counter); - new_equs.push_back(equal_exprt(lhs,*it)); + warning() << "ignoring excess function arguments" << eom; + break; } + + exprt lhs = *p_it; //copy + rename(lhs,counter); + new_equs.push_back(equal_exprt(lhs,*it)); + } } /*******************************************************************\ @@ -664,52 +748,52 @@ Function: ssa_inlinert::replace_globals_out() \*******************************************************************/ void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - exprt::operandst &c, - int counter) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter) { std::string suffix = id2string(funapp_expr.get(ID_suffix)); //equalities for globals_out for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) - { - symbol_exprt lhs = *it; //copy + { + symbol_exprt lhs = *it; //copy - lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + lhs.set_identifier(id2string(lhs.get_identifier())+suffix); - symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals_out,rhs)) - rename(rhs,counter); - else - { - bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); - assert(found); - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - } - c.push_back(equal_exprt(lhs,rhs)); + symbol_exprt rhs; + if(find_corresponding_symbol(*it,globals_out,rhs)) + rename(rhs,counter); + else + { + bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); + assert(found); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); } + c.push_back(equal_exprt(lhs,rhs)); + } } void ssa_inlinert::replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - int counter) + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter) { //equalities for globals_out for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) - { - symbol_exprt rhs = *it; //copy - symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs,counter); - else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - new_equs.push_back(equal_exprt(lhs,rhs)); - } + { + symbol_exprt rhs = *it; //copy + symbol_exprt lhs; + if(find_corresponding_symbol(*it,globals_out,lhs)) + rename(lhs,counter); + else + assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); + new_equs.push_back(equal_exprt(lhs,rhs)); + } } /*******************************************************************\ @@ -725,7 +809,7 @@ Function: ssa_inlinert::havoc() \*******************************************************************/ void ssa_inlinert::havoc(local_SSAt::nodet &node, - local_SSAt::nodet::function_callst::iterator f_it) + local_SSAt::nodet::function_callst::iterator f_it) { //remove function call rm_function_calls.insert(f_it); @@ -769,19 +853,19 @@ irep_idt ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ //find first @ where afterwards there are no letters size_t pos = std::string::npos; for(size_t i=0;iarguments().size()); @@ -916,7 +1000,7 @@ void ssa_inlinert::rename_to_caller( #endif replace_map[*it] = symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + "@"+i2string(++counter),it->type()); } } @@ -951,35 +1035,35 @@ void ssa_inlinert::rename_to_callee( local_SSAt::var_listt::const_iterator next_p_it = p_it; if(f_it->arguments().size() != params.size() && ++next_p_it==params.end()) //TODO: handle ellipsis - { - warning() << "ignoring excess function arguments" << eom; - break; - } + { + warning() << "ignoring excess function arguments" << eom; + break; + } replace_map[*it] = *p_it; } /* replace_expr(replace_map,expr); - replace_map.clear(); //arguments might contain globals, - // thus, we have to replace them separately - */ + replace_map.clear(); //arguments might contain globals, + // thus, we have to replace them separately + */ for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); it != cs_globals_in.end(); it++) + { + symbol_exprt cg; + if(find_corresponding_symbol(*it,globals_in,cg)) + replace_map[*it] = cg; + else { - symbol_exprt cg; - if(find_corresponding_symbol(*it,globals_in,cg)) - replace_map[*it] = cg; - else - { #if 0 - warning() << "'" << it->get_identifier() - << "' not bound in caller" << eom; + warning() << "'" << it->get_identifier() + << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); - } + replace_map[*it] = + symbol_exprt(id2string(it->get_identifier())+ + "@"+i2string(++counter),it->type()); } + } replace_expr(replace_map,expr); } @@ -1003,7 +1087,7 @@ void ssa_inlinert::commit_node(local_SSAt::nodest::iterator node) { //remove obsolete function calls for(std::set::iterator - it = rm_function_calls.begin(); + it = rm_function_calls.begin(); it != rm_function_calls.end(); it++) { node->function_calls.erase(*it); @@ -1012,7 +1096,7 @@ void ssa_inlinert::commit_node(local_SSAt::nodest::iterator node) //insert new equalities node->equalities.insert(node->equalities.end(), - new_equs.begin(),new_equs.end()); + new_equs.begin(),new_equs.end()); new_equs.clear(); } @@ -1029,7 +1113,7 @@ Function: ssa_inlinert::commit_nodes() \*******************************************************************/ bool ssa_inlinert::commit_nodes(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator n_pos) + local_SSAt::nodest::iterator n_pos) { if(new_nodes.empty()) return true; nodes.splice(n_pos,new_nodes,new_nodes.begin(),new_nodes.end()); @@ -1049,25 +1133,25 @@ Function: ssa_inlinert::find_corresponding_symbol \*******************************************************************/ bool ssa_inlinert::find_corresponding_symbol(const symbol_exprt &s, - const local_SSAt::var_sett &globals, - symbol_exprt &s_found) + const local_SSAt::var_sett &globals, + symbol_exprt &s_found) { const irep_idt &s_orig_id = get_original_identifier(s); for(local_SSAt::var_sett::const_iterator it = globals.begin(); it != globals.end(); it++) - { + { #if 0 - std::cout << s.get_identifier() << " =?= " << it->get_identifier() << std::endl; + std::cout << s.get_identifier() << " =?= " << it->get_identifier() << std::endl; #endif - if(s_orig_id == get_original_identifier(*it)) - { + if(s_orig_id == get_original_identifier(*it)) + { #if 0 - std::cout << s.get_identifier() << " == " << it->get_identifier() << std::endl; + std::cout << s.get_identifier() << " == " << it->get_identifier() << std::endl; #endif - s_found = *it; - return true; - } + s_found = *it; + return true; } + } return false; } diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index e588c3354..2c511c62c 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -21,9 +21,11 @@ class ssa_dbt; class ssa_inlinert : public messaget { public: - explicit ssa_inlinert(summary_dbt &_summary_db) : - counter(0), - summary_db(_summary_db) + explicit ssa_inlinert(summary_dbt &_summary_db, + ssa_dbt &_ssa_db) : + counter(0), + summary_db(_summary_db), + ssa_db(_ssa_db) {} void get_guard_binding(const local_SSAt &SSA, @@ -38,25 +40,37 @@ class ssa_inlinert : public messaget exprt::operandst &bindings_in, exprt::operandst &bindings_out, int counter); - void get_summary(const local_SSAt &SSA, + bool get_summary(const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, bool forward, - exprt::operandst &summaries, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, exprt::operandst &bindings, int counter, bool error_summ = false); - void get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter - bool get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + bool get_inlined(const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + int counter, + bool error_summ); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ = false); //TODO: need to explicitly pass the correct counter exprt get_summaries(const local_SSAt &SSA); //TODO: need to explicitly pass the correct counter @@ -134,6 +148,7 @@ class ssa_inlinert : public messaget protected: unsigned counter; summary_dbt &summary_db; + ssa_dbt &ssa_db; local_SSAt::nodest new_nodes; local_SSAt::nodet::equalitiest new_equs; diff --git a/src/ssa/ssa_refiner_selective.cpp b/src/ssa/ssa_refiner_selective.cpp index 1d77dc29f..2b09a3f68 100644 --- a/src/ssa/ssa_refiner_selective.cpp +++ b/src/ssa/ssa_refiner_selective.cpp @@ -56,161 +56,5 @@ bool ssa_refiner_selectivet::operator()() } } -#if 0 - // inline functions "selectively" (those that seem to be the "reason") - for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) - { - for(std::set::const_iterator f_it = - it->second.functions.begin(); - f_it != it->second.functions.end(); f_it++) - { - local_SSAt &SSA = ssa_db.get(it->first); - - std::list inline_nodes; - std::vector first_node_equalities; - int counter = ssa_inliner.get_rename_counter(); - - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - - local_SSAt::nodet &node=*n_it; - - if(node.location == *(f_it)) - { - - bool clear_function_call = false; - - for(local_SSAt::nodet::function_callst::const_iterator fc_it = - node.function_calls.begin(); - fc_it!=node.function_calls.end(); fc_it++) - { - - irep_idt fname = to_symbol_expr(fc_it->function()).get_identifier(); - if(ssa_db.exists(fname)) - { - clear_function_call = true; - - local_SSAt &fSSA = ssa_db.get(fname); - - exprt guard_binding; - exprt::operandst bindings_in, bindings_out; - - // put guard_binding, bindings_in, bindings_out in the caller's SSA (equalities) - ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); - equal_exprt e = to_equal_expr(guard_binding); - node.equalities.push_back(e); - - ssa_inliner.get_bindings(SSA,fSSA,n_it,fc_it,bindings_in,bindings_out,counter); - - for(exprt::operandst::const_iterator b_it=bindings_in.begin(); - b_it!=bindings_in.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - for(exprt::operandst::const_iterator b_it=bindings_out.begin(); - b_it!=bindings_out.end(); b_it++){ - equal_exprt e = to_equal_expr(*b_it); - node.equalities.push_back(e); - } - - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet fnode=*fn_it; - inline_nodes.push_back(fnode); - } - - if(fname == entry_function){ - // first_node_equalities should contain all the equalities from the first node of fSSA - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - first_node_equalities.push_back(*e_it); - } - break; - } - } - else{ - // except those (the one) that start with "ssa::guard" and have true in the rhs - for(local_SSAt::nodest::iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++){ - local_SSAt::nodet &fnode=*fn_it; - - bool ignore_equality = true; - - for(local_SSAt::nodet::equalitiest::iterator e_it=fnode.equalities.begin(); - e_it!=fnode.equalities.end(); e_it++){ - // unless lhs starts with "ssa::guard" and rhs is true - - equal_exprt e = to_equal_expr(*e_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true()) && (ignore_equality == true)){ - ignore_equality = false; - } - else{ - first_node_equalities.push_back(*e_it); - } - - } - break; - } - } - - } - } - - if(clear_function_call == true) - node.function_calls.clear(); - - } - } - - bool replace_first_node_equalities = true; - - if(inline_nodes.size() > 0) - { - for(std::list::iterator in_it = inline_nodes.begin(); - in_it != inline_nodes.end(); in_it++) - { - local_SSAt::nodet &inline_node = *in_it; - - if(replace_first_node_equalities == true) - { - inline_node.equalities.clear(); - for(std::vector::iterator e_it=first_node_equalities.begin(); - e_it!=first_node_equalities.end(); e_it++){ - inline_node.equalities.push_back(*e_it); - } - replace_first_node_equalities = false; - } - - for(local_SSAt::nodet::equalitiest::iterator e_it=inline_node.equalities.begin(); - e_it!=inline_node.equalities.end(); e_it++){ - ssa_inliner.rename(*e_it, counter); - } - - for(local_SSAt::nodet::constraintst::iterator c_it=inline_node.constraints.begin(); - c_it!=inline_node.constraints.end(); c_it++){ - ssa_inliner.rename(*c_it, counter); - } - - for(local_SSAt::nodet::assertionst::iterator a_it=inline_node.assertions.begin(); - a_it!=inline_node.assertions.end(); a_it++){ - ssa_inliner.rename(*a_it, counter); - } - - // push inline_node into SSA - SSA.nodes.push_back(inline_node); - - } - } - - } - } -#endif - return unwind Date: Sun, 29 May 2016 22:14:18 +0100 Subject: [PATCH 53/90] basic unsat-core based refinement selection --- src/domains/incremental_solver.h | 2 +- src/ssa/ssa_refiner_selective.h | 3 +- src/summarizer/summarizer_bw_cex_complete.cpp | 109 ++++++++++++++---- src/summarizer/summarizer_bw_cex_complete.h | 12 ++ 4 files changed, 103 insertions(+), 23 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 8ce4ab945..b290bea77 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -23,7 +23,7 @@ Author: Peter Schrammel //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -//#define DISPLAY_FORMULA +#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT diff --git a/src/ssa/ssa_refiner_selective.h b/src/ssa/ssa_refiner_selective.h index f97329f01..9d2d8f352 100644 --- a/src/ssa/ssa_refiner_selective.h +++ b/src/ssa/ssa_refiner_selective.h @@ -56,11 +56,12 @@ class ssa_refiner_selectivet : public ssa_refinert ssa_unwinder(_ssa_unwinder), max_unwind(_max_unwind), ssa_inliner(_ssa_inliner), + unwind(0), reason(_reason) {} virtual bool operator()(); - virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } + virtual unsigned get_unwind() { return unwind; } protected: ssa_dbt &ssa_db; diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 6a6ca30b6..b8d5cecad 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -26,7 +26,7 @@ Author: Madhukar Kumar, Peter Schrammel #include "summarizer_bw_cex_complete.h" -#define SHOW_UNSAT_CORE +//#define SHOW_UNSAT_CORE /*******************************************************************\ @@ -107,12 +107,13 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries solver << enable_exprs; #endif +#if 0 //TODO: let's just put all loops into the reason for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) if (n_it->loophead != SSA.nodes.end()) reason[function_name].loops.insert(n_it->loophead->location); - +#endif ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); @@ -253,8 +254,10 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries */ ///////////////////////////////////////////////////////////////////////////////////// +#if 0 //TODO: just put all function calls into reason reason[function_name].functions.insert(depnode.location); +#endif //recurse worknode.dependency_set = @@ -321,12 +324,13 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries exprt lsguard = depnode.guard; ssa_inliner.rename(lsguard, counter); loophead_selects.push_back(lsguard); - solver.solver->set_frozen(solver.convert(lsguard)); + //solver.solver->set_frozen(solver.convert(lsguard)); + add_reason_to_check(lsguard,function_name,false,depnode.location); //loop continuations exprt::operandst local_loop_continues; get_loop_continues(SSA, ssa_local_unwinder, depnode.location, - local_loop_continues); + local_loop_continues); for(size_t i=0; iset_assumptions(formula); -#endif solver_calls++; // for statistics if(solver() == decision_proceduret::D_SATISFIABLE) @@ -545,11 +549,36 @@ property_checkert::resultt summarizer_bw_cex_completet::check() #ifdef SHOW_UNSAT_CORE else { + const namespacet &ns = ssa_db.get(entry_function).ns; + for(unsigned i=0; iis_in_conflict(formula[i])) + debug() << "is_in_conflict: " << from_expr(ns, "", formula_expr[i]) << eom; + } + } +#else + else + { + const namespacet &ns = ssa_db.get(entry_function).ns; + //get reasons for spuriousness for(unsigned i=0; iis_in_conflict(formula[i])) + { debug() << "is_in_conflict: " << from_expr(ns, "", formula_expr[i]) << eom; + const reason_to_checkt &r = reasons_to_check[i]; + if(r.is_function) + reason[r.function_name].functions.insert(r.info); + else + reason[r.function_name].loops.insert(r.info); + } + } + bvt assumptions; + solver.solver->set_assumptions(assumptions); + for(unsigned i=0; i " << function_name << " ; dependency_set -> "; for(find_symbols_sett::iterator d_it = dependency_set.begin(); - d_it != dependency_set.end(); d_it++){ - std::cout << *d_it << ", "; -} + d_it != dependency_set.end(); d_it++){ + std::cout << *d_it << ", "; + } std::cout << "\n"; } @@ -597,14 +626,52 @@ void summarizer_bw_cex_completet::add_to_formula(const exprt &expr) literalt l = solver.solver->convert(expr); if(l.is_false()) { - literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); -} + literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", + bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); + } else if(!l.is_true()) { - formula.push_back(l); - formula_expr.push_back(expr); + formula.push_back(l); + formula_expr.push_back(expr); + } } + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::add_reason_to_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt & info) +{ + literalt l = solver.solver->convert(expr); + if(l.is_false()) + { + literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", + bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); + } + else if(!l.is_true()) + { + formula.push_back(l); + formula_expr.push_back(expr); + reasons_to_check.push_back(reason_to_checkt()); + reason_to_checkt &r = reasons_to_check.back(); + r.function_name = function_name; + r.info = info; + r.is_function = is_function; + } } diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h index 37f80d672..51901fd5a 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -49,6 +49,18 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset exprt::operandst loophead_selects; exprt::operandst loop_continues; + struct reason_to_checkt { + function_namet function_name; + bool is_function; + local_SSAt::locationt info; + }; + std::vector reasons_to_check; + void add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt & info); + virtual find_symbols_sett inline_summaries( const function_namet &function_name, find_symbols_sett &dependency_set, From e2938a0fe9635c503f2603852f0f3afdcf610cf3 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 29 May 2016 22:17:57 +0100 Subject: [PATCH 54/90] todo: loophead selects and continuation expressions must be recursively collected for inlined functions by ssa_inliner --- src/summarizer/summarizer_base.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/summarizer/summarizer_base.cpp b/src/summarizer/summarizer_base.cpp index 24500018f..4edaaa34c 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/summarizer/summarizer_base.cpp @@ -421,6 +421,8 @@ void summarizer_baset::get_loophead_selects( prop_convt &solver, exprt::operandst &loophead_selects) { + //TODO: need to ask ssa_inliner regarding inlined functions + //TODO: this should be provided by unwindable_local_SSA for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) @@ -489,6 +491,8 @@ void summarizer_baset::get_loop_continues( const local_SSAt::locationt &loop_id, exprt::operandst &loop_continues) { + //TODO: need to ask ssa_inliner regarding inlined functions + //TODO: this should be provided by unwindable_local_SSA ssa_local_unwinder.loop_continuation_conditions(loop_id, loop_continues); From 3e291ed0166489290ad617eb068bc93f7d271d53 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Mon, 30 May 2016 16:23:56 +0530 Subject: [PATCH 55/90] unwind value propagation bug fixed --- src/ssa/ssa_unwinder.cpp | 42 ++++++++++++++++++++++++++-------------- src/ssa/ssa_unwinder.h | 6 ++++-- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index aa2fb4aa1..118895a17 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -344,7 +344,7 @@ void ssa_local_unwindert::unwind(unsigned k) { if(SSA.current_unwinding >= (long)k) return; - + current_enabling_expr = symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), bool_typet()); @@ -353,13 +353,15 @@ void ssa_local_unwindert::unwind(unsigned k) it->second.loop_enabling_exprs.push_back(current_enabling_expr); } SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away + //recursively unwind everything SSA.current_unwindings.clear(); for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) { if(!it->second.is_root) continue; - unwind(it->second,k,false,false); //recursive + //unwind(it->second,k,false,false); //recursive + unwind(it->second,k,false,true,k,0,true); //recursive assert(SSA.current_unwindings.empty()); } //update current unwinding @@ -386,7 +388,8 @@ void ssa_local_unwindert::unwind(unsigned k) *****************************************************************************/ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, - bool propagate, unsigned prop_unwind, unsigned prop_loc) + bool propagate, unsigned prop_unwind, + unsigned prop_loc, bool propagate_all) { odometert context = SSA.current_unwindings; #ifdef DEBUG @@ -447,26 +450,35 @@ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, #ifdef DEBUG std::cout << i << ">" << loop.current_unwinding << std::endl; #endif - if(propagate == true){ - // if this child loop is the desired loop then unwind k and do not propagate - // else unwind loop.current_unwinding and propagate - if(*l_it == prop_loc){ - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent,false); + if(propagate_all == true){ + unwind(loops[*l_it],k,i>loop.current_unwinding || + is_new_parent,false); + } + else{ + if(propagate == true){ + // if this child loop is the desired loop then unwind k and do not propagate + // else unwind loop.current_unwinding and propagate + if(*l_it == prop_loc){ + unwind(loops[*l_it],k,i>loop.current_unwinding || + is_new_parent,false); + } + else{ + unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || + is_new_parent,true,prop_unwind,prop_loc); + } } else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || - is_new_parent,true,prop_unwind,prop_loc); + unwind(loops[*l_it],loops[*l_it].current_unwinding, + i>loop.current_unwinding || is_new_parent,false); } } - else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding, - i>loop.current_unwinding || is_new_parent,false); - } } SSA.increment_unwindings(0); } SSA.increment_unwindings(-1); +#if 0 + std::cout << "calling add_exit_merge with k = " << k << "\n"; +#endif add_exit_merges(loop,k); } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index b8896e910..e2816afc1 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -102,8 +102,10 @@ class ssa_local_unwindert void build_pre_post_map(); void build_exit_conditions(); - void unwind(loopt &loop, unsigned k, bool is_new_parent, bool propagate = false, unsigned prop_unwind = 0, unsigned prop_loc = 0); - + void unwind(loopt &loop, unsigned k, bool is_new_parent, + bool propagate = false, unsigned prop_unwind = 0, + unsigned prop_loc = 0, bool propagate_all = false); + exprt get_continuation_condition(const loopt& loop) const; void compute_loop_continuation_conditions(loopt& loop); From 1b24bbdb6d6227454fa09f6fa9f5bd818d4725ab Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 12:08:21 +0100 Subject: [PATCH 56/90] fully-unwound check fixed as far as possible --- src/domains/incremental_solver.cpp | 1 + src/domains/incremental_solver.h | 19 ++++- src/ssa/ssa_unwinder.cpp | 4 +- src/summarizer/summarizer_bw_cex_complete.cpp | 70 ++++++------------- src/summarizer/summarizer_bw_cex_complete.h | 2 - 5 files changed, 40 insertions(+), 56 deletions(-) diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index 9001b2e41..9cf762b96 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -116,6 +116,7 @@ void incremental_solvert::debug_add_to_formula(const exprt &expr) debug() << "literal " << l << ": " << from_expr(ns,"",expr) << eom; #endif formula.push_back(l); + formula_expr.push_back(expr); } #endif } diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index b290bea77..36a1cf7d2 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -19,11 +19,10 @@ Author: Peter Schrammel #include "domain.h" #include "util.h" -//#define DISPLAY_FORMULA //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -#define DISPLAY_FORMULA +//#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT @@ -93,8 +92,21 @@ class incremental_solvert : public messaget solver->set_assumptions(whole_formula); #endif #endif - +#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) + decision_proceduret::resultt result = (*solver)(); + if(result==decision_proceduret::D_UNSATISFIABLE) + { + for(unsigned i=0; iis_in_conflict(formula[i])) + std::cout << "is_in_conflict: " + << from_expr(ns, "", formula_expr[i]) << std::endl; + } + } + return result; +#else return (*solver)(); +#endif } exprt get(const exprt& expr) { return solver->get(expr); } @@ -123,6 +135,7 @@ class incremental_solvert : public messaget //for debugging bvt formula; + exprt::operandst formula_expr; void debug_add_to_formula(const exprt &expr); //context assumption literals diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index aa2fb4aa1..47d2f703f 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -289,7 +289,7 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) loopt &loop = loops[loc]; loop.loop_enabling_expr_current = - symbol_exprt("unwind::"+id2string(fname)+"loc::"+i2string(loc)+"::enable"+i2string(k), + symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k), bool_typet()); loop.loop_enabling_exprs.push_back(loop.loop_enabling_expr_current); @@ -346,7 +346,7 @@ void ssa_local_unwindert::unwind(unsigned k) return; current_enabling_expr = - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), + symbol_exprt("unwind$"+id2string(fname)+"$enable"+i2string(k), bool_typet()); for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) { diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index b8d5cecad..c281779d3 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -26,7 +26,7 @@ Author: Madhukar Kumar, Peter Schrammel #include "summarizer_bw_cex_complete.h" -//#define SHOW_UNSAT_CORE +#define REFINE_ALL /*******************************************************************\ @@ -101,13 +101,9 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries exprt enable_exprs = SSA.get_enabling_exprs(); ssa_inliner.rename(enable_exprs, counter); -#ifdef SHOW_UNSAT_CORE - add_to_formula(enable_exprs); -#else solver << enable_exprs; -#endif -#if 0 +#ifdef REFINE_ALL //TODO: let's just put all loops into the reason for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) @@ -312,11 +308,11 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries << worknode.node_index << "\t renamed info ~ " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; #endif -#ifdef SHOW_UNSAT_CORE - add_to_formula(worknode_info); -#else - solver << worknode_info; -#endif + + if(depnode.is_assertion) //keep for later + error_assertion = worknode_info; + else + solver << worknode_info; if(depnode.is_loop) { @@ -354,11 +350,10 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; #endif -#ifdef SHOW_UNSAT_CORE - add_to_formula(guard_binding); +#ifdef REFINE_ALL + solver << guard_binding; #else add_reason_to_check(guard_binding,function_name,true,depnode.location); - //solver << guard_binding; #endif } } @@ -533,35 +528,27 @@ Function: summarizer_bw_cex_completet::check() property_checkert::resultt summarizer_bw_cex_completet::check() { //add loophead selects -#ifdef SHOW_UNSAT_CORE - add_to_formula(conjunction(loophead_selects)); +#ifdef REFINE_ALL + solver.new_context(); + solver << error_assertion; + solver << conjunction(loophead_selects); #else -// solver << conjunction(loophead_selects); -#endif - + formula.push_back(solver.solver->convert(error_assertion)); solver.solver->set_assumptions(formula); +#endif solver_calls++; // for statistics if(solver() == decision_proceduret::D_SATISFIABLE) { + //pop_context() not necessary return property_checkert::FAIL; } -#ifdef SHOW_UNSAT_CORE - else - { - const namespacet &ns = ssa_db.get(entry_function).ns; - for(unsigned i=0; iis_in_conflict(formula[i])) - debug() << "is_in_conflict: " << from_expr(ns, "", formula_expr[i]) << eom; - } - } -#else +#ifndef REFINE_ALL else { const namespacet &ns = ssa_db.get(entry_function).ns; //get reasons for spuriousness - for(unsigned i=0; iis_in_conflict(formula[i])) { @@ -575,12 +562,14 @@ property_checkert::resultt summarizer_bw_cex_completet::check() } bvt assumptions; solver.solver->set_assumptions(assumptions); - for(unsigned i=0; iconvert(expr); - if(l.is_false()) - { - literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); - } - else if(!l.is_true()) - { - formula.push_back(l); - formula_expr.push_back(expr); - } -} - /*******************************************************************\ Function: summarizer_bw_cex_completet::add_reason_to_check diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h index 51901fd5a..ef1827457 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -74,8 +74,6 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset const function_namet &function_name, find_symbols_sett &dependency_set); - void add_to_formula(const exprt &expr); - }; From 83264b50c658d8c7df0872632bb63c046392896c Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Mon, 30 May 2016 17:22:25 +0530 Subject: [PATCH 57/90] unwind value propagation bug fixed (#5) --- src/ssa/ssa_unwinder.cpp | 42 ++++++++++++++++++++++++++-------------- src/ssa/ssa_unwinder.h | 6 ++++-- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 47d2f703f..0216cbfdb 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -344,7 +344,7 @@ void ssa_local_unwindert::unwind(unsigned k) { if(SSA.current_unwinding >= (long)k) return; - + current_enabling_expr = symbol_exprt("unwind$"+id2string(fname)+"$enable"+i2string(k), bool_typet()); @@ -353,13 +353,15 @@ void ssa_local_unwindert::unwind(unsigned k) it->second.loop_enabling_exprs.push_back(current_enabling_expr); } SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away + //recursively unwind everything SSA.current_unwindings.clear(); for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) { if(!it->second.is_root) continue; - unwind(it->second,k,false,false); //recursive + //unwind(it->second,k,false,false); //recursive + unwind(it->second,k,false,true,k,0,true); //recursive assert(SSA.current_unwindings.empty()); } //update current unwinding @@ -386,7 +388,8 @@ void ssa_local_unwindert::unwind(unsigned k) *****************************************************************************/ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, - bool propagate, unsigned prop_unwind, unsigned prop_loc) + bool propagate, unsigned prop_unwind, + unsigned prop_loc, bool propagate_all) { odometert context = SSA.current_unwindings; #ifdef DEBUG @@ -447,26 +450,35 @@ void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, #ifdef DEBUG std::cout << i << ">" << loop.current_unwinding << std::endl; #endif - if(propagate == true){ - // if this child loop is the desired loop then unwind k and do not propagate - // else unwind loop.current_unwinding and propagate - if(*l_it == prop_loc){ - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent,false); + if(propagate_all == true){ + unwind(loops[*l_it],k,i>loop.current_unwinding || + is_new_parent,false); + } + else{ + if(propagate == true){ + // if this child loop is the desired loop then unwind k and do not propagate + // else unwind loop.current_unwinding and propagate + if(*l_it == prop_loc){ + unwind(loops[*l_it],k,i>loop.current_unwinding || + is_new_parent,false); + } + else{ + unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || + is_new_parent,true,prop_unwind,prop_loc); + } } else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || - is_new_parent,true,prop_unwind,prop_loc); + unwind(loops[*l_it],loops[*l_it].current_unwinding, + i>loop.current_unwinding || is_new_parent,false); } } - else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding, - i>loop.current_unwinding || is_new_parent,false); - } } SSA.increment_unwindings(0); } SSA.increment_unwindings(-1); +#if 0 + std::cout << "calling add_exit_merge with k = " << k << "\n"; +#endif add_exit_merges(loop,k); } diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index b8896e910..e2816afc1 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -102,8 +102,10 @@ class ssa_local_unwindert void build_pre_post_map(); void build_exit_conditions(); - void unwind(loopt &loop, unsigned k, bool is_new_parent, bool propagate = false, unsigned prop_unwind = 0, unsigned prop_loc = 0); - + void unwind(loopt &loop, unsigned k, bool is_new_parent, + bool propagate = false, unsigned prop_unwind = 0, + unsigned prop_loc = 0, bool propagate_all = false); + exprt get_continuation_condition(const loopt& loop) const; void compute_loop_continuation_conditions(loopt& loop); From 74a25b231e4e15ac5333f72e4dde00da6d6573c1 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 13:23:56 +0100 Subject: [PATCH 58/90] check whether function has assertion was too semantic, we needed to set the property map correctly --- src/summarizer/summary_checker_base.cpp | 147 +++++++++++++----------- 1 file changed, 83 insertions(+), 64 deletions(-) diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 8b2da9239..4b8862b4c 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -102,7 +102,7 @@ Function: summary_checker_baset::SSA_functions void summary_checker_baset::SSA_functions(const goto_modelt &goto_model, const namespacet &ns) { - entry_function = goto_model.goto_functions.entry_point(); + entry_function = goto_model.goto_functions.entry_point(); // compute SSA for all the functions forall_goto_functions(f_it, goto_model.goto_functions) @@ -141,8 +141,8 @@ Function: summary_checker_baset::summarize \*******************************************************************/ void summary_checker_baset::summarize(const goto_modelt &goto_model, - bool forward, - bool termination) + bool forward, + bool termination) { summarizer_baset *summarizer = NULL; @@ -153,18 +153,18 @@ void summary_checker_baset::summarize(const goto_modelt &goto_model, else #endif { - if(forward && !termination) - summarizer = new summarizer_fwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(forward && termination) - summarizer = new summarizer_fw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(!forward && !termination) - summarizer = new summarizer_bwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - if(!forward && termination) - summarizer = new summarizer_bw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + if(forward && !termination) + summarizer = new summarizer_fwt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + if(forward && termination) + summarizer = new summarizer_fw_termt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + if(!forward && !termination) + summarizer = new summarizer_bwt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + if(!forward && termination) + summarizer = new summarizer_bw_termt( + options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); } assert(summarizer != NULL); @@ -215,27 +215,27 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( // call recursively for all function calls first for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); ++n_it) + n_it != SSA.nodes.end(); ++n_it) { for(local_SSAt::nodet::function_callst::const_iterator ff_it = - n_it->function_calls.begin(); - ff_it != n_it->function_calls.end(); ff_it++) + n_it->function_calls.begin(); + ff_it != n_it->function_calls.end(); ff_it++) { - assert(ff_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(ff_it->function()).get_identifier(); + assert(ff_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(ff_it->function()).get_identifier(); //ENHANCE?: can the return value be exploited? - if(!summary_db.exists(fname) || - summary_db.get(fname).bw_transformer.is_nil()) - { + if(!summary_db.exists(fname) || + summary_db.get(fname).bw_transformer.is_nil()) + { #if 0 debug() << "Checking call " << fname << messaget::eom; #endif if(seen_function_calls.find(fname) == seen_function_calls.end()){ - seen_function_calls.insert(fname); - check_properties(fname, entry_function, seen_function_calls); - } - } + seen_function_calls.insert(fname); + check_properties(fname, entry_function, seen_function_calls); + } + } } } @@ -246,7 +246,7 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( else // check all the functions { for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) + f_it != ssa_db.functions().end(); f_it++) { status() << "Checking properties of " << f_it->first << messaget::eom; @@ -259,9 +259,9 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( if(options.get_bool_option("show-invariants")) { - if(!summary_db.exists(f_it->first)) continue; - show_invariants(*(f_it->second),summary_db.get(f_it->first),result()); - result() << eom; + if(!summary_db.exists(f_it->first)) continue; + show_invariants(*(f_it->second),summary_db.get(f_it->first),result()); + result() << eom; } } } @@ -271,12 +271,12 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( { // determine overall status for(property_mapt::const_iterator - p_it=property_map.begin(); p_it!=property_map.end(); p_it++) + p_it=property_map.begin(); p_it!=property_map.end(); p_it++) { if(p_it->second.result==FAIL) - return property_checkert::FAIL; + return property_checkert::FAIL; if(p_it->second.result==UNKNOWN) - result = property_checkert::UNKNOWN; + result = property_checkert::UNKNOWN; } } @@ -300,7 +300,27 @@ void summary_checker_baset::check_properties( irep_idt entry_function) { unwindable_local_SSAt &SSA = *f_it->second; - if(!SSA.goto_function.body.has_assertion()) return; + + //check whether function has assertions + // SSA.goto_function.body.has_assertion() has become too semantic + bool has_assertion = false; + for(goto_programt::instructionst::const_iterator + i_it=SSA.goto_function.body.instructions.begin(); + i_it!=SSA.goto_function.body.instructions.end(); + i_it++) + { + if(!i_it->is_assert()) + continue; + + irep_idt property_id = i_it->source_location.get_property_id(); + + if(i_it->guard.is_true()) + property_map[property_id].result=PASS; + else + has_assertion=true; + } + if(!has_assertion) + return; bool all_properties = options.get_bool_option("all-properties"); bool build_error_trace = options.get_bool_option("show-trace"); @@ -313,7 +333,7 @@ void summary_checker_baset::check_properties( incremental_solvert &solver = ssa_db.get_solver(f_it->first); solver.set_message_handler(get_message_handler()); -#if 1 +#if 0 // TEST ssa_const_propagation if(options.get_bool_option("ssa-propagation")) { @@ -323,7 +343,7 @@ void summary_checker_baset::check_properties( solver << c; debug() << "SSA const propagation: " << eom; for(std::list::iterator it = c.begin(); - it!=c.end(); it++) + it!=c.end(); it++) debug() << " " << from_expr(SSA.ns,"",*it) << eom; } #endif @@ -332,7 +352,6 @@ void summary_checker_baset::check_properties( solver << SSA; SSA.mark_nodes(); - solver.new_context(); exprt enabling_expr = SSA.get_enabling_exprs(); @@ -360,7 +379,7 @@ void summary_checker_baset::check_properties( summarizer_bw_cex_baset *summarizer_bw_cex = NULL; incremental_solvert* cex_complete_solver = incremental_solvert::allocate(SSA.ns, - options.get_bool_option("refine")); + options.get_bool_option("refine")); #if 1 cex_complete_solver->set_message_handler(get_message_handler()); #endif @@ -415,7 +434,7 @@ void summary_checker_baset::check_properties( const goto_programt &goto_program=SSA.goto_function.body; for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); + i_it=goto_program.instructions.begin(); i_it!=goto_program.instructions.end(); i_it++) { @@ -442,32 +461,32 @@ void summary_checker_baset::check_properties( unsigned property_counter = 0; for(std::list::const_iterator - n_it=assertion_nodes.begin(); + n_it=assertion_nodes.begin(); n_it!=assertion_nodes.end(); n_it++) { for(local_SSAt::nodet::assertionst::const_iterator - a_it=(*n_it)->assertions.begin(); - a_it!=(*n_it)->assertions.end(); - a_it++, property_counter++) + a_it=(*n_it)->assertions.begin(); + a_it!=(*n_it)->assertions.end(); + a_it++, property_counter++) { - exprt property=*a_it; + exprt property=*a_it; - if(simplify) - property=::simplify_expr(property, SSA.ns); + if(simplify) + property=::simplify_expr(property, SSA.ns); #if 0 - std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; + std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; #endif - property_map[property_id].location = i_it; - cover_goals.goal_map[property_id].conjuncts.push_back(property); + property_map[property_id].location = i_it; + cover_goals.goal_map[property_id].conjuncts.push_back(property); } } } for(cover_goals_extt::goal_mapt::const_iterator - it=cover_goals.goal_map.begin(); + it=cover_goals.goal_map.begin(); it!=cover_goals.goal_map.end(); it++) { @@ -482,9 +501,9 @@ void summary_checker_baset::check_properties( cover_goals(); /* - std::cout << "Output Verbose: " << entry_function << "\n"; - (ssa_db.get(entry_function)).output_verbose(std::cout); - assert(false); + std::cout << "Output Verbose: " << entry_function << "\n"; + (ssa_db.get(entry_function)).output_verbose(std::cout); + assert(false); */ //set all non-covered goals to PASS except if we do not try // to cover all goals and we have found a FAIL @@ -493,9 +512,9 @@ void summary_checker_baset::check_properties( std::list::const_iterator g_it= cover_goals.goals.begin(); for(cover_goals_extt::goal_mapt::const_iterator - it=cover_goals.goal_map.begin(); - it!=cover_goals.goal_map.end(); - it++, g_it++) + it=cover_goals.goal_map.begin(); + it!=cover_goals.goal_map.end(); + it++, g_it++) { if(!g_it->covered) property_map[it->first].result=PASS; } @@ -506,8 +525,8 @@ void summary_checker_baset::check_properties( summarizer_bw_cex->get_reason(reason); debug() << "** " << cover_goals.number_covered() - << " of " << cover_goals.size() << " failed (" - << cover_goals.iterations() << " iterations)" << eom; + << " of " << cover_goals.size() << " failed (" + << cover_goals.iterations() << " iterations)" << eom; delete summarizer_bw_cex; delete cex_complete_solver; @@ -528,7 +547,7 @@ Function: summary_checker_baset::report_statistics() void summary_checker_baset::report_statistics() { for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) + f_it != ssa_db.functions().end(); f_it++) { incremental_solvert &solver = ssa_db.get_solver(f_it->first); unsigned calls = solver.get_number_of_solver_calls(); @@ -593,12 +612,12 @@ Function: summary_checker_baset::is_spurious \*******************************************************************/ bool summary_checker_baset::is_spurious(const exprt::operandst &loophead_selects, - incremental_solvert &solver) + incremental_solvert &solver) { //check loop head choices in model bool invariants_involved = false; for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); - l_it != loophead_selects.end(); l_it++) + l_it != loophead_selects.end(); l_it++) { if(solver.get(l_it->op0()).is_true()) { @@ -649,6 +668,6 @@ void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) std::string filename = options.get_option("instrument-output"); status() << "Writing instrumented goto-binary " << filename << eom; write_goto_binary(filename, - goto_model.symbol_table, - goto_model.goto_functions, get_message_handler()); + goto_model.symbol_table, + goto_model.goto_functions, get_message_handler()); } From e73b4824d94e0490eba24f62700dd3283840a64e Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 13:50:53 +0100 Subject: [PATCH 59/90] loop enabling expr fixed --- src/ssa/ssa_unwinder.cpp | 8 ++------ src/ssa/ssa_unwinder.h | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 0216cbfdb..28ae56299 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -288,11 +288,7 @@ void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) return; loopt &loop = loops[loc]; - loop.loop_enabling_expr_current = - symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k), - bool_typet()); - - loop.loop_enabling_exprs.push_back(loop.loop_enabling_expr_current); + loop.loop_enabling_exprs.push_back(symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k),bool_typet())); //current_enabling_expr = // symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), @@ -603,7 +599,7 @@ void ssa_local_unwindert::add_loop_head(loopt &loop) SSA.nodes.push_back(loop.body_nodes.front()); //copy loop head local_SSAt::nodet &node=SSA.nodes.back(); node.marked = false; - node.enabling_expr = current_enabling_expr; + node.enabling_expr = loop.loop_enabling_exprs.back(); for (local_SSAt::nodet::equalitiest::iterator e_it = node.equalities.begin(); e_it != node.equalities.end(); e_it++) { diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index e2816afc1..bbecdc74f 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -74,7 +74,7 @@ class ssa_local_unwindert // to have an enabling_expr and current_unwindings (odometert) exprt::operandst loop_enabling_exprs; - exprt loop_enabling_expr_current; + //exprt loop_enabling_expr_current; //seems superfluous exprt::operandst current_continuation_conditions; From 23a589fead93f87a14e310d6f6eb0d966f3b4756 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 13:57:23 +0100 Subject: [PATCH 60/90] loop enabling expr fixed, removed superfluous variables --- src/ssa/ssa_unwinder.cpp | 7 ++----- src/ssa/ssa_unwinder.h | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 28ae56299..bc82fc1d7 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -341,12 +341,9 @@ void ssa_local_unwindert::unwind(unsigned k) if(SSA.current_unwinding >= (long)k) return; - current_enabling_expr = - symbol_exprt("unwind$"+id2string(fname)+"$enable"+i2string(k), - bool_typet()); for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) { - it->second.loop_enabling_exprs.push_back(current_enabling_expr); + it->second.loop_enabling_exprs.push_back(symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(it->first)+"$enable"+i2string(k),bool_typet())); } SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away @@ -684,7 +681,7 @@ void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, SSA.nodes.end())); //add new node local_SSAt::nodet &node=SSA.nodes.back(); - node.enabling_expr = current_enabling_expr; + node.enabling_expr = loop.loop_enabling_exprs.back(); for (loopt::exit_mapt::const_iterator x_it = loop.exit_map.begin(); x_it != loop.exit_map.end(); x_it++) diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index bbecdc74f..eb4105e07 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -52,7 +52,6 @@ class ssa_local_unwindert const irep_idt fname; unwindable_local_SSAt& SSA; bool is_kinduction,is_bmc; - symbol_exprt current_enabling_expr; //TODO must become loop-specific class loopt //loop tree { @@ -74,7 +73,6 @@ class ssa_local_unwindert // to have an enabling_expr and current_unwindings (odometert) exprt::operandst loop_enabling_exprs; - //exprt loop_enabling_expr_current; //seems superfluous exprt::operandst current_continuation_conditions; From 1b76b2a6fc20140f2044373f7cea4e7e0dd9d504 Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Mon, 30 May 2016 19:09:59 +0530 Subject: [PATCH 61/90] unwind value propagation bug fixed (#6) From fe07b2cb1adce4f8afd1c190e95449771c118af6 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 14:47:12 +0100 Subject: [PATCH 62/90] signed/unsigned comparison fixed --- src/domains/ranking_solver_enumeration.cpp | 1 + src/domains/ranking_solver_enumeration.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/domains/ranking_solver_enumeration.cpp b/src/domains/ranking_solver_enumeration.cpp index 45a4e1109..e5bebbb01 100644 --- a/src/domains/ranking_solver_enumeration.cpp +++ b/src/domains/ranking_solver_enumeration.cpp @@ -85,6 +85,7 @@ ranking_solver_enumerationt::progresst ranking_solver_enumerationt::iterate(inva debug() << "inner solve()" << eom; solver_calls++; if(inner_solver() == decision_proceduret::D_SATISFIABLE && + //TODO: not sure where number_inner_iterations is used number_inner_iterations < max_inner_iterations) { diff --git a/src/domains/ranking_solver_enumeration.h b/src/domains/ranking_solver_enumeration.h index 01de5c570..8c07e0429 100644 --- a/src/domains/ranking_solver_enumeration.h +++ b/src/domains/ranking_solver_enumeration.h @@ -34,7 +34,7 @@ class ranking_solver_enumerationt : public strategy_solver_baset // the "inner" solver const unsigned max_inner_iterations; incremental_solvert inner_solver; - int number_inner_iterations; + unsigned number_inner_iterations; }; #endif From 6548e301fb9b9664854c1c56d20e27b7e8ad6070 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 15:10:01 +0100 Subject: [PATCH 63/90] missing regressions --- regression/modular/nested11/main.c | 19 +++++++++++++++++++ regression/modular/nested11/test.desc | 6 ++++++ regression/modular/nested12/main.c | 18 ++++++++++++++++++ regression/modular/nested12/test.desc | 6 ++++++ 4 files changed, 49 insertions(+) create mode 100644 regression/modular/nested11/main.c create mode 100644 regression/modular/nested11/test.desc create mode 100644 regression/modular/nested12/main.c create mode 100644 regression/modular/nested12/test.desc diff --git a/regression/modular/nested11/main.c b/regression/modular/nested11/main.c new file mode 100644 index 000000000..790b6c4b3 --- /dev/null +++ b/regression/modular/nested11/main.c @@ -0,0 +1,19 @@ +void main() +{ + int x,y; + for(x=0;x<10;) + { + for(y=0;y Date: Mon, 30 May 2016 16:43:20 +0100 Subject: [PATCH 64/90] simplified example that exposes problem with monolithic --k-induction --havoc --- regression/kiki/induction7a/main.c | 12 ++++++++++++ regression/kiki/induction7a/test.desc | 6 ++++++ src/summarizer/summarizer_bw_cex_concrete.cpp | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 regression/kiki/induction7a/main.c create mode 100644 regression/kiki/induction7a/test.desc diff --git a/regression/kiki/induction7a/main.c b/regression/kiki/induction7a/main.c new file mode 100644 index 000000000..5a9e3cf63 --- /dev/null +++ b/regression/kiki/induction7a/main.c @@ -0,0 +1,12 @@ +void main() +{ + int s=0; + int x=0; + while(1) + { + s += 2; + if(x==1) s++; + x++; + assert(s == 2*x); + } +} diff --git a/regression/kiki/induction7a/test.desc b/regression/kiki/induction7a/test.desc new file mode 100644 index 000000000..b3256a114 --- /dev/null +++ b/regression/kiki/induction7a/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --havoc +^EXIT=10$ +^SIGNAL=0$ +^.*FAILURE$ diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 1d05323c8..79f4a791c 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -14,6 +14,8 @@ Author: Kumar Madhukar, Peter Schrammel //TODO: a bug in the fresh solver case; does not compute //calling contexts (see struct tests in regression) +#define DEBUG + #include #include From 16cdbd6aad64650851b2cdae0a9734fb4c9da2b2 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 17:52:00 +0100 Subject: [PATCH 65/90] wrong assertions feeded into cex checker; output assignments in incremental_solver debug mode --- src/domains/incremental_solver.h | 17 +++++++++++++++-- src/ssa/ssa_refiner_monolithic.cpp | 2 +- src/ssa/ssa_refiner_selective.cpp | 2 +- src/summarizer/cover_goals_ext.cpp | 12 +++++++++++- src/summarizer/summarizer_bw_cex_concrete.cpp | 9 ++++----- 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 36a1cf7d2..2f7f89aa4 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -15,6 +15,7 @@ Author: Peter Schrammel #include #include #include +#include #include "domain.h" #include "util.h" @@ -23,8 +24,8 @@ Author: Peter Schrammel //#define NON_INCREMENTAL // (experimental) //#define DISPLAY_FORMULA -//#define DEBUG_FORMULA -//#define DEBUG_OUTPUT +#define DEBUG_FORMULA +#define DEBUG_OUTPUT class incremental_solvert : public messaget { @@ -103,6 +104,18 @@ class incremental_solvert : public messaget << from_expr(ns, "", formula_expr[i]) << std::endl; } } + if(result==decision_proceduret::D_SATISFIABLE) + { + std::set vars; + for(unsigned i=0; i::const_iterator it = vars.begin(); + it != vars.end(); ++it) + { + std::cout << "assignment: " << from_expr(ns, "", *it) << " = " + << from_expr(ns, "", solver->get(*it)) << std::endl; + } + } return result; #else return (*solver)(); diff --git a/src/ssa/ssa_refiner_monolithic.cpp b/src/ssa/ssa_refiner_monolithic.cpp index a7191481c..ceb26639e 100644 --- a/src/ssa/ssa_refiner_monolithic.cpp +++ b/src/ssa/ssa_refiner_monolithic.cpp @@ -26,5 +26,5 @@ bool ssa_refiner_monolithict::operator()() summary_db.mark_recompute_all(); //TODO: recompute only functions with loops ssa_unwinder.unwind_all(unwind); - return unwind++cond_expression).id() == ID_not); exprt conjunct_expr = (g_it->cond_expression).op0(); +#if 0 + std::cout << "FAILED EXPR: " + << from_expr(SSA.ns, "", conjunct_expr) << std::endl; +#endif if(conjunct_expr.id() != ID_and) { @@ -198,8 +202,14 @@ void cover_goals_extt::assignment() c_it != conjunct_expr.operands().end(); c_it++) { literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_true()) + if(solver.l_get(conjunct_literal).is_false()) + { failed_exprs.push_back(*c_it); +#if 0 + std::cout << "failed_expr: " + << from_expr(SSA.ns, "", *c_it) << std::endl; +#endif + } } solver.pop_context(); //otherwise this would interfere with necessary preconditions summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/summarizer/summarizer_bw_cex_concrete.cpp index 79f4a791c..f03435ef0 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/summarizer/summarizer_bw_cex_concrete.cpp @@ -14,7 +14,7 @@ Author: Kumar Madhukar, Peter Schrammel //TODO: a bug in the fresh solver case; does not compute //calling contexts (see struct tests in regression) -#define DEBUG +//#define DEBUG #include @@ -177,8 +177,7 @@ void summarizer_bw_cex_concretet::compute_summary_rec( summary.bw_postcondition = _postcondition; #if 0 - debug() << "Postcondition: " << - from_expr(SSA.ns, "", postcondition) << eom; + debug() << "Postcondition: " << from_expr(SSA.ns, "", postcondition) << eom; #endif // recursively compute summaries for function calls @@ -366,7 +365,7 @@ void summarizer_bw_cex_concretet::do_summary( #endif #ifdef OPT_12 -#if 1 +#ifdef DEBUG std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; #endif solver << simplify_expr(conjunction(store), SSA.ns); @@ -584,7 +583,7 @@ exprt summarizer_bw_cex_concretet::compute_calling_context2( #endif #ifdef OPT_12 -#if 1 +#ifdef DEBUG std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; #endif solver << simplify_expr(conjunction(store), SSA.ns); From 9eb2e6bedc2cd91d97d3fbc60c4c095593b9de25 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 18:37:57 +0100 Subject: [PATCH 66/90] collecting renamed error assertions - still some of them are missed --- src/domains/incremental_solver.h | 6 ++--- src/summarizer/summarizer_bw_cex_complete.cpp | 27 ++++++++++++++----- src/summarizer/summarizer_bw_cex_complete.h | 1 + 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 2f7f89aa4..834d6d4ae 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -23,9 +23,9 @@ Author: Peter Schrammel //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -//#define DISPLAY_FORMULA -#define DEBUG_FORMULA -#define DEBUG_OUTPUT +#define DISPLAY_FORMULA +//#define DEBUG_FORMULA +//#define DEBUG_OUTPUT class incremental_solvert : public messaget { diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index c281779d3..f4ddc9cce 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -289,13 +289,23 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries // if the dependency set is non-empty if(!worknode.dependency_set.empty()){ exprt worknode_info = depnode.node_info; - if(depnode.is_assertion == true) - worknode_info = not_exprt(worknode_info); + + bool is_error_assertion = false; + assert(error_assertion.id()==ID_not); + if(error_assertion.op0().id()!=ID_and) + is_error_assertion = (worknode_info == error_assertion.op0()); + else + forall_operands(a_it, error_assertion.op0()) + if(worknode_info == *a_it) + { + is_error_assertion = true; + break; + } if(worknode.node_index != 0){ if(!(depnode.is_function_call)){ - if((depnode.is_assertion == false) || - (worknode_info == error_assertion)){ + if(!depnode.is_assertion || is_error_assertion) + { /* std::cout << "Solver <-- " << function_name << ": (node) node#:" << worknode.node_index << "\t original info ~ " @@ -310,7 +320,7 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries #endif if(depnode.is_assertion) //keep for later - error_assertion = worknode_info; + renamed_error_assertion.push_back(worknode_info); else solver << worknode_info; @@ -527,13 +537,16 @@ Function: summarizer_bw_cex_completet::check() property_checkert::resultt summarizer_bw_cex_completet::check() { + assert(!renamed_error_assertion.empty()); //otherwise the error assertion was not renamed + //add loophead selects #ifdef REFINE_ALL solver.new_context(); - solver << error_assertion; + solver << not_exprt(conjunction(renamed_error_assertion)); solver << conjunction(loophead_selects); #else - formula.push_back(solver.solver->convert(error_assertion)); + formula.push_back(solver.solver->convert( + not_exprt(conjunction(renamed_error_assertion)))); solver.solver->set_assumptions(formula); #endif diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/summarizer/summarizer_bw_cex_complete.h index ef1827457..8646c409d 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/summarizer/summarizer_bw_cex_complete.h @@ -48,6 +48,7 @@ class summarizer_bw_cex_completet : public summarizer_bw_cex_baset exprt::operandst formula_expr; //for debugging exprt::operandst loophead_selects; exprt::operandst loop_continues; + exprt::operandst renamed_error_assertion; struct reason_to_checkt { function_namet function_name; From 53bc2ff803384169b883e9d82325f67c95e98361 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 20:52:08 +0100 Subject: [PATCH 67/90] assertion renaming in cex_complete fixed --- src/domains/incremental_solver.h | 12 ++++++-- src/summarizer/cover_goals_ext.cpp | 8 +++++ src/summarizer/summarizer_bw_cex_complete.cpp | 30 ++++++++++++------- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 834d6d4ae..9cec701ef 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -23,7 +23,7 @@ Author: Peter Schrammel //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -#define DISPLAY_FORMULA +//#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT @@ -93,8 +93,10 @@ class incremental_solvert : public messaget solver->set_assumptions(whole_formula); #endif #endif -#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) +#if defined(DEBUG_FORMULA) || defined(DISPLAY_FORMULA) decision_proceduret::resultt result = (*solver)(); +#endif +#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) if(result==decision_proceduret::D_UNSATISFIABLE) { for(unsigned i=0; i vars; @@ -117,7 +121,8 @@ class incremental_solvert : public messaget } } return result; -#else +#endif +#if !defined(DEBUG_FORMULA) && !defined(DISPLAY_FORMULA) return (*solver)(); #endif } @@ -198,6 +203,7 @@ static inline incremental_solvert & operator << ( << from_expr(dest.ns,"",src) << std::endl; else std::cerr << "add_to_solver: " << from_expr(dest.ns,"",src) << std::endl; + dest.formula_expr.push_back(src); #endif #ifdef NON_INCREMENTAL diff --git a/src/summarizer/cover_goals_ext.cpp b/src/summarizer/cover_goals_ext.cpp index 874b889a1..dc154a671 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/summarizer/cover_goals_ext.cpp @@ -179,6 +179,13 @@ void cover_goals_extt::assignment() if(property_map[it->first].result==property_checkert::UNKNOWN && solver.l_get(g_it->condition).is_true()) { +#if 1 + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); +#else // THE ASSERTIONS THAT FAIL COULD BE RATHER ARBITRARY SINCE THE FORMULA + // IS NOT "ROOTED" IN AN INITIAL STATE. assert((g_it->cond_expression).id() == ID_not); exprt conjunct_expr = (g_it->cond_expression).op0(); #if 0 @@ -216,6 +223,7 @@ void cover_goals_extt::assignment() property_map[it->first].result = summarizer_bw_cex.check(); solver.new_context(); } +#endif } if(property_map[it->first].result == property_checkert::FAIL) { diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index f4ddc9cce..b90f912d4 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -287,20 +287,28 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries } // if the dependency set is non-empty - if(!worknode.dependency_set.empty()){ + if(!worknode.dependency_set.empty()) + { exprt worknode_info = depnode.node_info; bool is_error_assertion = false; - assert(error_assertion.id()==ID_not); - if(error_assertion.op0().id()!=ID_and) - is_error_assertion = (worknode_info == error_assertion.op0()); - else - forall_operands(a_it, error_assertion.op0()) - if(worknode_info == *a_it) - { - is_error_assertion = true; - break; - } + if(depnode.is_assertion) + { +#if 0 + std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) << std::endl; + std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) << std::endl; +#endif + assert(error_assertion.id()==ID_not); + if(error_assertion.op0().id()!=ID_and) + is_error_assertion = (worknode_info == error_assertion.op0()); + else + forall_operands(a_it, error_assertion.op0()) + if(worknode_info == *a_it) + { + is_error_assertion = true; + break; + } + } if(worknode.node_index != 0){ if(!(depnode.is_function_call)){ From 9ff3c6200e742d3967cc467e645f542c80c3c5a6 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 21:22:22 +0100 Subject: [PATCH 68/90] be more robust w.r.t. functions that are not in the SSA db --- src/summarizer/summary_checker_base.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 4b8862b4c..76d9be884 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -211,6 +211,7 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( { ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().find(function_name); + assert(f_it != ssa_db.functions().end()); local_SSAt &SSA = *f_it->second; // call recursively for all function calls first @@ -223,10 +224,11 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( { assert(ff_it->function().id()==ID_symbol); //no function pointers irep_idt fname = to_symbol_expr(ff_it->function()).get_identifier(); + //ENHANCE?: can the return value be exploited? - - if(!summary_db.exists(fname) || - summary_db.get(fname).bw_transformer.is_nil()) + if(ssa_db.functions().find(fname)!=ssa_db.functions().end() && + (!summary_db.exists(fname) || + summary_db.get(fname).bw_transformer.is_nil())) { #if 0 debug() << "Checking call " << fname << messaget::eom; From 331bafc71f2ef29682e2cdc5485273c5bf6fe037 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 21:46:59 +0100 Subject: [PATCH 69/90] assumptions must hold in backwards analysis --- src/summarizer/summarizer_bw_cex_complete.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index b90f912d4..a11851038 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -103,6 +103,17 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries solver << enable_exprs; + // assumptions must hold + for(local_SSAt::nodest::const_iterator + n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) + for(local_SSAt::nodet::assumptionst::const_iterator + a_it = n_it->assumptions.begin(); a_it != n_it->assumptions.end(); ++a_it) + { + exprt assumption = *a_it; + ssa_inliner.rename(assumption, counter); + solver << assumption; + } + #ifdef REFINE_ALL //TODO: let's just put all loops into the reason for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); From 1dcdecb09e2c045aacbdca1449a8838811093e89 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 22:10:48 +0100 Subject: [PATCH 70/90] clean up summary_checker_base --- src/summarizer/summarizer_bw_cex_complete.cpp | 2 +- src/summarizer/summary_checker_ai.cpp | 2 +- src/summarizer/summary_checker_base.cpp | 71 ++++--------------- src/summarizer/summary_checker_base.h | 16 +---- src/summarizer/summary_checker_bmc.cpp | 3 +- src/summarizer/summary_checker_kind.cpp | 4 +- 6 files changed, 23 insertions(+), 75 deletions(-) diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index a11851038..8c2a7ef1e 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -261,7 +261,7 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries */ ///////////////////////////////////////////////////////////////////////////////////// -#if 0 +#ifdef REFINE_ALL //TODO: just put all function calls into reason reason[function_name].functions.insert(depnode.location); #endif diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp index 7fe506e7f..5fd215cb9 100644 --- a/src/summarizer/summary_checker_ai.cpp +++ b/src/summarizer/summary_checker_ai.cpp @@ -142,7 +142,7 @@ property_checkert::resultt summary_checker_ait::operator()( */ std::set seen_function_calls; - property_checkert::resultt result = check_properties(entry_function, entry_function, seen_function_calls); + property_checkert::resultt result = check_properties(entry_function, entry_function, seen_function_calls, false); report_statistics(); return result; } diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 76d9be884..6874b32da 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -201,11 +201,14 @@ Function: summary_checker_baset::check_properties summary_checker_baset::resultt summary_checker_baset::check_properties() { std::set seen_function_calls; - return check_properties("", "", seen_function_calls); + return check_properties("", "", seen_function_calls, false); } summary_checker_baset::resultt summary_checker_baset::check_properties( - irep_idt function_name, irep_idt entry_function, std::set seen_function_calls) + irep_idt function_name, + irep_idt entry_function, + std::set seen_function_calls, + bool is_inlined) { if(function_name!="") { @@ -235,15 +238,19 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( #endif if(seen_function_calls.find(fname) == seen_function_calls.end()){ seen_function_calls.insert(fname); - check_properties(fname, entry_function, seen_function_calls); + check_properties(fname, entry_function, seen_function_calls, + n_it->function_calls_inlined); } } } } - //now check function itself - status() << "Checking properties of " << f_it->first << messaget::eom; - check_properties(f_it, entry_function); + if(!is_inlined) + { + //now check function itself + status() << "Checking properties of " << f_it->first << messaget::eom; + check_properties(f_it, entry_function); + } } else // check all the functions { @@ -372,10 +379,12 @@ void summary_checker_baset::check_properties( //callee summaries solver << ssa_inliner.get_summaries(SSA); +#if 0 //freeze loop head selects exprt::operandst loophead_selects; summarizer_baset::get_loophead_selects(SSA, ssa_unwinder.get(f_it->first),*solver.solver, loophead_selects); +#endif //spuriousness checkers summarizer_bw_cex_baset *summarizer_bw_cex = NULL; @@ -600,56 +609,6 @@ void summary_checker_baset::do_show_vcc( std::cout << "\n"; } - -/*******************************************************************\ - -Function: summary_checker_baset::is_spurious - - Inputs: - - Outputs: - - Purpose: checks whether a countermodel is spurious - -\*******************************************************************/ - -bool summary_checker_baset::is_spurious(const exprt::operandst &loophead_selects, - incremental_solvert &solver) -{ - //check loop head choices in model - bool invariants_involved = false; - for(exprt::operandst::const_iterator l_it = loophead_selects.begin(); - l_it != loophead_selects.end(); l_it++) - { - if(solver.get(l_it->op0()).is_true()) - { - invariants_involved = true; - break; - } - } - if(!invariants_involved) return false; - - // force avoiding paths going through invariants - solver << conjunction(loophead_selects); - - solver_calls++; //statistics - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - return false; - break; - - case decision_proceduret::D_UNSATISFIABLE: - return true; - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } -} - /*******************************************************************\ Function: summary_checker_baset::instrument_and_output diff --git a/src/summarizer/summary_checker_base.h b/src/summarizer/summary_checker_base.h index b1e7e3eb8..024db61d0 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/summarizer/summary_checker_base.h @@ -84,23 +84,11 @@ class summary_checker_baset:public property_checkert property_checkert::resultt check_properties( irep_idt function_name, irep_idt entry_function, - std::set seen_function_calls); + std::set seen_function_calls, + bool is_inlined); void check_properties( const ssa_dbt::functionst::const_iterator f_it, irep_idt entry_function=""); - - void error_summary_using_vars(const ssa_dbt::functionst::const_iterator f_it); - - exprt::operandst get_loophead_selects( - const irep_idt &function_name, const local_SSAt &, prop_convt &); - bool is_spurious(const exprt::operandst& loophead_selects, - incremental_solvert&); - exprt::operandst get_loop_continues( - const irep_idt &function_name, const local_SSAt &, prop_convt &); - bool is_fully_unwound( - const exprt::operandst& loop_continues, - const exprt::operandst& loophead_selects, - incremental_solvert&); }; #endif diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/summarizer/summary_checker_bmc.cpp index dac6f2571..a4edfb589 100644 --- a/src/summarizer/summary_checker_bmc.cpp +++ b/src/summarizer/summary_checker_bmc.cpp @@ -68,7 +68,8 @@ property_checkert::resultt summary_checker_bmct::operator()( //check std::set seen_function_calls; - result = check_properties(entry_function, entry_function, seen_function_calls); + result = check_properties(entry_function, entry_function, + seen_function_calls, false); //result if(result == property_checkert::PASS) diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 417e8ee22..790102fef 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -69,7 +69,7 @@ property_checkert::resultt summary_checker_kindt::operator()( //check std::set seen_function_calls; result = check_properties(entry_function, entry_function, - seen_function_calls); + seen_function_calls, false); //do static analysis and check again if(result == property_checkert::UNKNOWN && @@ -80,7 +80,7 @@ property_checkert::resultt summary_checker_kindt::operator()( summarize(goto_model); std::set seen_function_calls; result = check_properties(entry_function, entry_function, - seen_function_calls); + seen_function_calls, false); } //result From d16b72eec9b733f5d4b0b5fb1b671f595b476f16 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 22:44:41 +0100 Subject: [PATCH 71/90] check for inlined functions whether they have assertions --- src/ssa/ssa_inliner.cpp | 2 + src/summarizer/summary_checker_base.cpp | 69 ++++++++++++++++++------- src/summarizer/summary_checker_base.h | 3 ++ 3 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 5c5e9bf72..e20e36a51 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -1193,3 +1193,5 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) return id; } + + diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 6874b32da..363e96649 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -311,24 +311,7 @@ void summary_checker_baset::check_properties( unwindable_local_SSAt &SSA = *f_it->second; //check whether function has assertions - // SSA.goto_function.body.has_assertion() has become too semantic - bool has_assertion = false; - for(goto_programt::instructionst::const_iterator - i_it=SSA.goto_function.body.instructions.begin(); - i_it!=SSA.goto_function.body.instructions.end(); - i_it++) - { - if(!i_it->is_assert()) - continue; - - irep_idt property_id = i_it->source_location.get_property_id(); - - if(i_it->guard.is_true()) - property_map[property_id].result=PASS; - else - has_assertion=true; - } - if(!has_assertion) + if(!has_assertion(f_it->first)) return; bool all_properties = options.get_bool_option("all-properties"); @@ -632,3 +615,53 @@ void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) goto_model.symbol_table, goto_model.goto_functions, get_message_handler()); } + +/*******************************************************************\ + +Function: summary_checker_baset::has_assertion + + Inputs: + + Outputs: + + Purpose: searches recursively for assertions in inlined functions + +\*******************************************************************/ + +bool summary_checker_baset::has_assertion(irep_idt function_name) +{ + // SSA.goto_function.body.has_assertion() has become too semantic + bool _has_assertion = false; + const local_SSAt &SSA = ssa_db.get(function_name); + + for(local_SSAt::nodest::const_iterator + n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it = n_it->assertions.begin(); a_it != n_it->assertions.end(); ++a_it) + { + irep_idt property_id = n_it->location->source_location.get_property_id(); + + if(n_it->location->guard.is_true()) + property_map[property_id].result=PASS; + else + _has_assertion=true; + } + if(!n_it->function_calls_inlined) + continue; + + for(local_SSAt::nodet::function_callst::const_iterator + f_it = n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); ++f_it) + { + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + if(ssa_db.functions().find(fname)==ssa_db.functions().end()) + continue; + + bool new_has_assertion = has_assertion(fname); //recurse + _has_assertion = _has_assertion || new_has_assertion; + } + } + + return _has_assertion; +} diff --git a/src/summarizer/summary_checker_base.h b/src/summarizer/summary_checker_base.h index 024db61d0..bbcd9bd6b 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/summarizer/summary_checker_base.h @@ -89,6 +89,9 @@ class summary_checker_baset:public property_checkert void check_properties( const ssa_dbt::functionst::const_iterator f_it, irep_idt entry_function=""); + + bool has_assertion(irep_idt function_name); + }; #endif From 7c58c7a5f42475a4f9fae20111d5dfe8497d24f0 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Mon, 30 May 2016 23:36:29 +0100 Subject: [PATCH 72/90] recursively collect assertions from inlined functions through inliner --- src/ssa/ssa_inliner.cpp | 63 +++++++++++++++++---- src/ssa/ssa_inliner.h | 24 +++++++- src/summarizer/summary_checker_base.cpp | 75 ++++++++----------------- 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index e20e36a51..3226b947e 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -112,6 +112,7 @@ bool ssa_inlinert::get_inlined( exprt::operandst &assert_summaries, exprt::operandst &noassert_summaries, exprt::operandst &bindings, + assertion_mapt &assertion_map, int counter, bool error_summ) { @@ -120,7 +121,8 @@ bool ssa_inlinert::get_inlined( bool assertion_flag = get_summaries(fSSA, summaryt::call_sitet(fSSA.goto_function.body.instructions.end()), - forward,assert_summaries,noassert_summaries,bindings,error_summ); + forward,assert_summaries,noassert_summaries, + bindings,assertion_map,error_summ); //bindings exprt guard_binding; @@ -287,14 +289,48 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) return and_exprt(conjunction(bindings),conjunction(summaries)); } +exprt ssa_inlinert::get_summaries(const local_SSAt &SSA, + assertion_mapt &assertion_map) +{ + exprt::operandst summaries,bindings; + get_summaries(SSA,true,summaries,bindings,assertion_map); + return and_exprt(conjunction(bindings),conjunction(summaries)); +} + void ssa_inlinert::get_summaries(const local_SSAt &SSA, bool forward, exprt::operandst &summaries, exprt::operandst &bindings) { + assertion_mapt assertion_map; get_summaries(SSA, summaryt::call_sitet(SSA.goto_function.body.instructions.end()), - forward,summaries,summaries,bindings); + forward,summaries,summaries,bindings,assertion_map); +} + +void ssa_inlinert::get_summaries(const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map) +{ + get_summaries(SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward,summaries,summaries,bindings,assertion_map); +} + +bool ssa_inlinert::get_summaries(const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ) +{ + assertion_mapt assertion_map; + return get_summaries(SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward,assert_summaries,noassert_summaries,bindings,assertion_map); } bool ssa_inlinert::get_summaries(const local_SSAt &SSA, @@ -303,12 +339,20 @@ bool ssa_inlinert::get_summaries(const local_SSAt &SSA, exprt::operandst &assert_summaries, exprt::operandst &noassert_summaries, exprt::operandst &bindings, + assertion_mapt &assertion_map, bool error_summ) { bool assertion_flag = false; for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { + for(local_SSAt::nodet::assertionst::const_iterator a_it = + n_it->assertions.begin(); + a_it != n_it->assertions.end(); a_it++) + { + assertion_map[n_it->location].push_back(*a_it); + rename(assertion_map[n_it->location].back(), counter); + } for(local_SSAt::nodet::function_callst::const_iterator f_it = n_it->function_calls.begin(); f_it != n_it->function_calls.end(); f_it++) @@ -327,7 +371,7 @@ bool ssa_inlinert::get_summaries(const local_SSAt &SSA, bool new_assertion_flag = get_inlined(SSA,n_it,f_it, forward,assert_summaries,noassert_summaries, - bindings,counter,error_summ); + bindings,assertion_map,counter,error_summ); assertion_flag = assertion_flag || new_assertion_flag; } //get summary @@ -829,6 +873,8 @@ Function: ssa_inlinert::rename() irep_idt ssa_inlinert::rename(irep_idt &id, int counter) { + if(counter<0) + return id; return id2string(id)+"@"+i2string(counter); } @@ -895,13 +941,10 @@ void ssa_inlinert::rename(exprt &expr, int counter) { if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - std::string id_str = id2string(expr.get(ID_identifier)); - irep_idt id; - - if(id_str.find('@') != std::string::npos) - id = id_str; - else - id = id_str+"@"+i2string(counter); + irep_idt id = expr.get(ID_identifier); + std::string id_str = id2string(id); + if(id_str.find('@') == std::string::npos) + rename(id, counter); expr.set(ID_identifier,id); } diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index 2c511c62c..69da35492 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -23,11 +23,13 @@ class ssa_inlinert : public messaget public: explicit ssa_inlinert(summary_dbt &_summary_db, ssa_dbt &_ssa_db) : - counter(0), + counter(-1), summary_db(_summary_db), ssa_db(_ssa_db) {} + typedef std::map assertion_mapt; + void get_guard_binding(const local_SSAt &SSA, const local_SSAt &fSSA, local_SSAt::nodest::const_iterator n_it, @@ -56,8 +58,15 @@ class ssa_inlinert : public messaget exprt::operandst &assert_summaries, exprt::operandst &noassert_summaries, exprt::operandst &bindings, + assertion_mapt &assertion_map, int counter, bool error_summ); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter void get_summaries( const local_SSAt &SSA, bool forward, @@ -71,8 +80,19 @@ class ssa_inlinert : public messaget exprt::operandst &noassert_summaries, exprt::operandst &bindings, bool error_summ = false); //TODO: need to explicitly pass the correct counter + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + bool error_summ = false); //TODO: need to explicitly pass the correct counter exprt get_summaries(const local_SSAt &SSA); //TODO: need to explicitly pass the correct counter + exprt get_summaries(const local_SSAt &SSA, + assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter void replace(local_SSAt &SSA, local_SSAt::nodest::iterator node, @@ -146,7 +166,7 @@ class ssa_inlinert : public messaget void rename(exprt &expr, int counter); protected: - unsigned counter; + int counter; summary_dbt &summary_db; ssa_dbt &ssa_db; diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 363e96649..26436cf76 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -359,15 +359,9 @@ void summary_checker_baset::check_properties( solver << summary.fw_precondition; } - //callee summaries - solver << ssa_inliner.get_summaries(SSA); - -#if 0 - //freeze loop head selects - exprt::operandst loophead_selects; - summarizer_baset::get_loophead_selects(SSA, - ssa_unwinder.get(f_it->first),*solver.solver, loophead_selects); -#endif + //callee summaries and inlined functions + ssa_inlinert::assertion_mapt assertion_map; + solver << ssa_inliner.get_summaries(SSA, assertion_map); //spuriousness checkers summarizer_bw_cex_baset *summarizer_bw_cex = NULL; @@ -421,61 +415,38 @@ void summary_checker_baset::check_properties( all_properties, build_error_trace, *summarizer_bw_cex); -#if 0 - debug() << "(C) " << from_expr(SSA.ns,"",enabling_expr) << eom; -#endif - - const goto_programt &goto_program=SSA.goto_function.body; - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) + for(ssa_inlinert::assertion_mapt::const_iterator + aa_it=assertion_map.begin(); + aa_it!=assertion_map.end(); + aa_it++) { - if(!i_it->is_assert()) - continue; - - const source_locationt &location=i_it->source_location; - std::list assertion_nodes; - SSA.find_nodes(i_it,assertion_nodes); - - irep_idt property_id = location.get_property_id(); - - if(i_it->guard.is_true()) - { - property_map[property_id].result=PASS; - continue; - } + irep_idt property_id = aa_it->first->source_location.get_property_id(); //do not recheck properties that have already been decided - if(property_map[property_id].result!=UNKNOWN) continue; + if(property_map[property_id].result!=UNKNOWN) + continue; +#if 0 if(property_id=="") //TODO: some properties do not show up in initialize_property_map continue; +#endif - unsigned property_counter = 0; - for(std::list::const_iterator - n_it=assertion_nodes.begin(); - n_it!=assertion_nodes.end(); - n_it++) + for(exprt::operandst::const_iterator + a_it=aa_it->second.begin(); + a_it!=aa_it->second.end(); + a_it++) { - for(local_SSAt::nodet::assertionst::const_iterator - a_it=(*n_it)->assertions.begin(); - a_it!=(*n_it)->assertions.end(); - a_it++, property_counter++) - { - exprt property=*a_it; + exprt property=*a_it; - if(simplify) - property=::simplify_expr(property, SSA.ns); + if(simplify) + property=::simplify_expr(property, SSA.ns); -#if 0 - std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; +#if 1 + std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; #endif - property_map[property_id].location = i_it; - cover_goals.goal_map[property_id].conjuncts.push_back(property); - } + property_map[property_id].location = aa_it->first; + cover_goals.goal_map[property_id].conjuncts.push_back(property); } } From 196db09c9f529f214107b04af9cbe01d36760eac Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Tue, 31 May 2016 00:19:40 +0100 Subject: [PATCH 73/90] sync --- src/domains/incremental_solver.h | 2 +- src/summarizer/summary_checker_base.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 9cec701ef..90475f005 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -23,7 +23,7 @@ Author: Peter Schrammel //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -//#define DISPLAY_FORMULA +#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index 26436cf76..cc579143b 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -79,7 +79,7 @@ void summary_checker_baset::SSA_dependency_graphs( else ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal -#if 0 +#if 1 ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); ssa_depgraph.output(debug()); debug() << eom; std::cout << "output SSA for function: " << f_it->first << "\n"; From 65a22614698f4a06313384e39cc6517644256cde Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Tue, 31 May 2016 09:17:40 +0100 Subject: [PATCH 74/90] depgraph renaming problem fixed --- src/ssa/ssa_dependency_graph.cpp | 18 ++++---- src/ssa/ssa_inliner.cpp | 46 +++++-------------- src/ssa/ssa_inliner.h | 11 ++--- src/summarizer/summarizer_bw_cex_complete.cpp | 13 +++--- src/summarizer/summarizer_bw_cex_wp.cpp | 22 ++++----- 5 files changed, 39 insertions(+), 71 deletions(-) diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp index 3a34a14e1..651563082 100644 --- a/src/ssa/ssa_dependency_graph.cpp +++ b/src/ssa/ssa_dependency_graph.cpp @@ -311,25 +311,25 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli for(local_SSAt::var_listt::const_iterator p_it = fSSA.params.begin(); p_it != fSSA.params.end(); p_it++){ irep_idt id = (*p_it).get(ID_identifier); - irep_idt sym = ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(sym); - temp_node.used_symbols.insert(sym); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); } for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_in.begin(); g_it != fSSA.globals_in.end(); g_it++){ irep_idt id = (*g_it).get(ID_identifier); - irep_idt sym = ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(sym); - temp_node.used_symbols.insert(sym); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); } for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_out.begin(); g_it != fSSA.globals_out.end(); g_it++){ irep_idt id = (*g_it).get(ID_identifier); - irep_idt sym = ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(sym); - temp_node.modified_symbols.insert(sym); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.modified_symbols.insert(id); } depnodes_map.push_back(temp_node); diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 3226b947e..d3bf88ba4 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -871,31 +871,12 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -irep_idt ssa_inlinert::rename(irep_idt &id, int counter) -{ - if(counter<0) - return id; - return id2string(id)+"@"+i2string(counter); -} - - -/*******************************************************************\ - -Function: ssa_inlinert::rename() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -irep_idt ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ +void ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ std::string id_str = id2string(id); - if(attach == false){ + if(!attach) + { //find first @ where afterwards there are no letters size_t pos = std::string::npos; for(size_t i=0;i=0) + id = id_str+"@"+i2string(counter); } } @@ -937,21 +915,19 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(exprt &expr, int counter) +void ssa_inlinert::rename(exprt &expr, int counter, bool attach) { if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { irep_idt id = expr.get(ID_identifier); - std::string id_str = id2string(id); - if(id_str.find('@') == std::string::npos) - rename(id, counter); + rename(id, counter, attach); expr.set(ID_identifier,id); } for(exprt::operandst::iterator it = expr.operands().begin(); it != expr.operands().end(); it++) { - rename(*it,counter); + rename(*it, counter, attach); } } diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index 69da35492..f108c2565 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -153,17 +153,12 @@ class ssa_inlinert : public messaget static irep_idt get_original_identifier(const symbol_exprt &s); - irep_idt rename(irep_idt &id, int counter); + void rename(irep_idt &id, int counter, bool attach=true); - irep_idt rename(irep_idt &id, int counter, bool attach); - - int get_rename_counter(){ - counter++; - return counter; - } + int get_rename_counter() { return ++counter; } // moved from protected to public, for use in summarizer_bw_cex_complete - void rename(exprt &expr, int counter); + void rename(exprt &expr, int counter, bool attach=true); protected: int counter; diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index 8c2a7ef1e..bb03256bc 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -67,6 +67,7 @@ void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) { status() << "\nBackward error analysis (complete)..." << eom; error_assertion = _error_assertion; + ssa_inliner.rename(error_assertion,-1,false); /* std::cout << "error assertion: " << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) @@ -228,10 +229,9 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; + irep_idt renamed_id = *d_it; // detach the '@' symbol if there - irep_idt renamed_id = ssa_inliner.rename - (id, + ssa_inliner.rename(renamed_id, depnode.rename_counter, false); renamed_dependencies.insert(renamed_id); } @@ -276,10 +276,9 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; + irep_idt renamed_id = *d_it; // attach the '@' symbol if not already there - irep_idt renamed_id = ssa_inliner.rename - (id, + ssa_inliner.rename(renamed_id, depnode.rename_counter, true); renamed_dependencies.insert(renamed_id); } @@ -305,7 +304,7 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries bool is_error_assertion = false; if(depnode.is_assertion) { -#if 0 +#if 1 std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) << std::endl; std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) << std::endl; #endif diff --git a/src/summarizer/summarizer_bw_cex_wp.cpp b/src/summarizer/summarizer_bw_cex_wp.cpp index 97d0010cd..86b0b6e54 100644 --- a/src/summarizer/summarizer_bw_cex_wp.cpp +++ b/src/summarizer/summarizer_bw_cex_wp.cpp @@ -225,12 +225,11 @@ find_symbols_sett summarizer_bw_cex_wpt::inline_summaries for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; - // detach the '@' symbol if there - irep_idt renamed_id = ssa_inliner.rename - (id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); - renamed_dependencies.insert(renamed_id); + irep_idt renamed_id = *d_it; + // detach the '@' symbol if there + ssa_inliner.rename(renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); + renamed_dependencies.insert(renamed_id); } worknode.dependency_set = renamed_dependencies; @@ -267,12 +266,11 @@ find_symbols_sett summarizer_bw_cex_wpt::inline_summaries for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); d_it != worknode.dependency_set.end(); d_it++){ - irep_idt id = *d_it; - // attach the '@' symbol if not already there - irep_idt renamed_id = ssa_inliner.rename - (id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, true); - renamed_dependencies.insert(renamed_id); + irep_idt renamed_id = *d_it; + // detach the '@' symbol if there + ssa_inliner.rename(renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); + renamed_dependencies.insert(renamed_id); } worknode.dependency_set = renamed_dependencies; From 544afa519923afe345bd67ef8cd48aaf77ea1472 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Tue, 31 May 2016 10:45:19 +0100 Subject: [PATCH 75/90] inlined functions must not contain negated assertions; assertions are added separately --- src/domains/incremental_solver.cpp | 53 ++++++++++++------- src/domains/incremental_solver.h | 8 +-- src/ssa/local_ssa.cpp | 2 +- src/ssa/ssa_inliner.cpp | 2 + src/summarizer/summarizer_bw_cex_complete.cpp | 2 +- src/summarizer/summary_checker_base.cpp | 4 +- 6 files changed, 44 insertions(+), 27 deletions(-) diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index 9cf762b96..c251326c8 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -90,33 +90,48 @@ void incremental_solvert::make_context_permanent() #endif } -void incremental_solvert::debug_add_to_formula(const exprt &expr) +void incremental_solvert::debug_add_to_formula(const exprt &_expr, + exprt activation) { + if(_expr.id()!=ID_and) + { #ifdef NON_INCREMENTAL - // no debug mode for non-incremental yet + // no debug mode for non-incremental yet + assert(0); #else - literalt l = solver->convert(expr); - if(l.is_false()) - { + exprt expr; + if(activation.is_nil()) + expr = _expr; + else + expr = or_exprt(_expr, activation); + literalt l = solver->convert(expr); + if(l.is_false()) + { #ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": false = " << from_expr(ns,"",expr) <convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); + literalt dummy = solver->convert(symbol_exprt("goto_symex::\\dummy", + bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); #ifdef DEBUG_OUTPUT - debug() << "literal " << dummy << ", " << !dummy << ": " - << from_expr(ns,"",expr) << eom; + debug() << "literal " << dummy << ", " << !dummy << ": " + << from_expr(ns,"",expr) << eom; #endif - } - else if(!l.is_true()) - { + } + else if(!l.is_true()) + { #ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": " << from_expr(ns,"",expr) << eom; + debug() << "literal " << l << ": " << from_expr(ns,"",expr) << eom; #endif - formula.push_back(l); - formula_expr.push_back(expr); - } + formula.push_back(l); + formula_expr.push_back(expr); + } #endif + } + else + { + forall_operands(it, _expr) + debug_add_to_formula(*it, activation); + } } diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index 90475f005..e30a2c1da 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -23,7 +23,7 @@ Author: Peter Schrammel //#define NO_ARITH_REFINEMENT //#define NON_INCREMENTAL // (experimental) -#define DISPLAY_FORMULA +//#define DISPLAY_FORMULA //#define DEBUG_FORMULA //#define DEBUG_OUTPUT @@ -154,7 +154,7 @@ class incremental_solvert : public messaget //for debugging bvt formula; exprt::operandst formula_expr; - void debug_add_to_formula(const exprt &expr); + void debug_add_to_formula(const exprt &expr, exprt activation=nil_exprt()); //context assumption literals bvt activation_literals; @@ -217,8 +217,8 @@ static inline incremental_solvert & operator << ( *dest.solver << src; #else if(!dest.activation_literals.empty()) - dest.debug_add_to_formula( - or_exprt(src,literal_exprt(!dest.activation_literals.back()))); + dest.debug_add_to_formula(src, + literal_exprt(!dest.activation_literals.back())); else dest.debug_add_to_formula(src); #endif diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index f0a5a5fca..cb8722778 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -1907,7 +1907,7 @@ exprt local_SSAt::get_enabling_exprs() const { exprt result = conjunction(enabling_exprs); -#if 1 +#if 0 std::cout << "current enabling expr:" << from_expr(ns, "", result) << "\n"; #endif diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index d3bf88ba4..735dfec87 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -164,8 +164,10 @@ bool ssa_inlinert::get_inlined( for(local_SSAt::nodet::assertionst::const_iterator a_it = fnode.assertions.begin(); a_it!=fnode.assertions.end(); a_it++) { +#if 0 assert_summaries.push_back(*a_it); rename(assert_summaries.back(), counter); +#endif assertion_flag = true; } } diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/summarizer/summarizer_bw_cex_complete.cpp index bb03256bc..9ba0d6ecb 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/summarizer/summarizer_bw_cex_complete.cpp @@ -304,7 +304,7 @@ find_symbols_sett summarizer_bw_cex_completet::inline_summaries bool is_error_assertion = false; if(depnode.is_assertion) { -#if 1 +#if 0 std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) << std::endl; std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) << std::endl; #endif diff --git a/src/summarizer/summary_checker_base.cpp b/src/summarizer/summary_checker_base.cpp index cc579143b..c5eb6dc6d 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/summarizer/summary_checker_base.cpp @@ -79,7 +79,7 @@ void summary_checker_baset::SSA_dependency_graphs( else ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal -#if 1 +#if 0 ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); ssa_depgraph.output(debug()); debug() << eom; std::cout << "output SSA for function: " << f_it->first << "\n"; @@ -442,7 +442,7 @@ void summary_checker_baset::check_properties( property=::simplify_expr(property, SSA.ns); #if 1 - std::cout << "property: " << from_expr(SSA.ns, "", property) << std::endl; + debug() << "property: " << from_expr(SSA.ns, "", property) << eom; #endif property_map[property_id].location = aa_it->first; From 2da8de3548a28a92d91345fe4466316324e836e1 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Tue, 31 May 2016 12:43:37 +0100 Subject: [PATCH 76/90] regression tests refactored --- regression/kiki-modular/Makefile | 20 ++ .../cex-struct1/main.c | 0 regression/kiki-modular/cex-struct1/test.desc | 6 + .../cex-struct2/main.c | 0 regression/kiki-modular/cex-struct2/test.desc | 6 + .../cex-struct3/main.c | 0 regression/kiki-modular/cex-struct3/test.desc | 6 + .../{modular => kiki-modular}/cex1/main.c | 0 regression/kiki-modular/cex1/test.desc | 6 + .../{modular => kiki-modular}/cex10/main.c | 0 regression/kiki-modular/cex10/test.desc | 6 + .../{modular => kiki-modular}/cex13/main.c | 0 regression/kiki-modular/cex13/test.desc | 6 + .../{modular => kiki-modular}/cex2/main.c | 0 regression/kiki-modular/cex2/test.desc | 6 + .../{modular => kiki-modular}/cex3/main.c | 0 regression/kiki-modular/cex3/test.desc | 6 + .../{modular => kiki-modular}/cex4/main.c | 0 regression/kiki-modular/cex4/test.desc | 6 + .../{modular => kiki-modular}/cex5/main.c | 0 regression/kiki-modular/cex5/test.desc | 6 + .../{modular => kiki-modular}/cex6/main.c | 0 regression/kiki-modular/cex6/test.desc | 6 + .../{modular => kiki-modular}/cex7/main.c | 0 regression/kiki-modular/cex7/test.desc | 6 + .../{modular => kiki-modular}/cex8/main.c | 0 regression/kiki-modular/cex8/test.desc | 6 + .../{modular => kiki-modular}/cex9/main.c | 0 regression/kiki-modular/cex9/test.desc | 6 + .../induction1/main.c | 0 .../induction1}/test.desc | 2 +- .../induction2/main.c | 0 regression/kiki-modular/induction2/test.desc | 6 + .../induction3/main.c | 0 regression/kiki-modular/induction3/test.desc | 6 + .../induction4/main.c | 0 .../induction4}/test.desc | 2 +- .../induction5/main.c | 0 .../induction5/test.desc | 2 +- .../induction7/main.c | 0 .../induction7/test.desc | 2 +- .../induction8/main.c | 0 regression/kiki-modular/induction8/test.desc | 6 + regression/kiki-modular/inline-refine1/main.c | 17 ++ .../kiki-modular/inline-refine1/test.desc | 6 + .../inline-refine2}/main.c | 0 .../kiki-modular/inline-refine2/test.desc | 6 + .../inline-refine3}/main.c | 0 .../kiki-modular/inline-refine3/test.desc | 6 + .../inline-refine4}/main.c | 0 .../kiki-modular/inline-refine4/test.desc | 6 + .../inline-refine5}/main.c | 0 .../kiki-modular/inline-refine5/test.desc | 6 + .../inline-refine6}/main.c | 0 .../kiki-modular/inline-refine6/test.desc | 6 + .../inline-refine7}/main.c | 0 .../kiki-modular/inline-refine7/test.desc | 6 + .../inline-refine8}/main.c | 0 .../kiki-modular/inline-refine8/test.desc | 6 + regression/kiki-modular/inline-refine9/main.c | 11 ++ .../kiki-modular/inline-refine9/test.desc | 6 + .../{modular => kiki-modular}/kind1/main.c | 0 regression/kiki-modular/kind1/test.desc | 6 + .../{modular => kiki-modular}/kind2/main.c | 0 regression/kiki-modular/kind2/test.desc | 6 + .../{modular => kiki-modular}/kind3/main.c | 0 regression/kiki-modular/kind3/test.desc | 6 + .../{modular => kiki-modular}/kind4/main.c | 0 regression/kiki-modular/kind4/test.desc | 6 + .../{modular => kiki-modular}/kind5/main.c | 0 regression/kiki-modular/kind5/test.desc | 6 + .../{modular => kiki-modular}/kind6/main.c | 0 regression/kiki-modular/kind6/test.desc | 6 + .../cex11 => kiki-modular/kind7}/main.c | 0 regression/kiki-modular/kind7/test.desc | 6 + .../cex12 => kiki-modular/kind8}/main.c | 0 regression/kiki-modular/kind8/test.desc | 6 + .../{modular => kiki-modular}/loop25/main.c | 0 regression/kiki-modular/loop25/test.desc | 6 + .../{modular => kiki-modular}/loop27/main.c | 0 regression/kiki-modular/loop27/test.desc | 6 + .../{modular => kiki-modular}/loop28/main.c | 0 regression/kiki-modular/loop28/test.desc | 6 + .../{modular => kiki-modular}/nested11/main.c | 0 regression/kiki-modular/nested11/test.desc | 6 + .../{modular => kiki-modular}/nested12/main.c | 0 .../nested12/test.desc | 2 +- regression/kiki-modular/nocex1/main.c | 15 ++ regression/kiki-modular/nocex1/test.desc | 6 + regression/kiki-modular/nocex2/main.c | 16 ++ regression/kiki-modular/nocex2/test.desc | 6 + regression/kiki-modular/nocex3/main.c | 18 ++ regression/kiki-modular/nocex3/test.desc | 6 + regression/kiki-modular/nocex4/main.c | 20 ++ regression/kiki-modular/nocex4/test.desc | 6 + regression/kiki-modular/nocex5/main.c | 30 +++ regression/kiki-modular/nocex5/test.desc | 6 + regression/kiki-modular/nocex6/main.c | 15 ++ regression/kiki-modular/nocex6/test.desc | 6 + regression/kiki-modular/nocex8/main.c | 16 ++ regression/kiki-modular/nocex8/test.desc | 6 + regression/kiki-modular/nocex9/main.c | 11 ++ regression/kiki-modular/nocex9/test.desc | 6 + .../s3_clnt_1/main.c | 0 regression/kiki-modular/s3_clnt_1/test.desc | 6 + .../{modular => kiki-modular}/scope1/main.c | 0 regression/kiki-modular/scope1/test.desc | 6 + regression/kiki-modular/unwind-refine1/main.c | 15 ++ .../kiki-modular/unwind-refine1/test.desc | 6 + regression/modular/kind1/test.desc | 6 - regression/modular/kind2/test.desc | 6 - regression/modular/kind3/test.desc | 6 - regression/modular/kind4/test.desc | 6 - regression/modular/kind5/test.desc | 6 - regression/modular/kind6/test.desc | 6 - regression/modular/loop25/test.desc | 6 - regression/modular/loop28/test.desc | 6 - regression/modular/nested11/test.desc | 6 - regression/modular/s3_clnt_1/test.desc | 6 - regression/modular/scope1/test.desc | 6 - regression/spurious-check-abstract/Makefile | 20 ++ .../cex-struct1/main.c | 180 ++++++++++++++++++ .../cex-struct1/test.desc | 0 .../cex-struct2/main.c | 22 +++ .../cex-struct2/test.desc | 0 .../cex-struct3/main.c | 23 +++ .../cex-struct3/test.desc | 0 .../spurious-check-abstract/cex1/main.c | 15 ++ .../cex1/test.desc | 0 .../spurious-check-abstract/cex10/main.c | 21 ++ .../cex10/test.desc | 0 .../spurious-check-abstract/cex13/main.c | 16 ++ .../cex13/test.desc | 0 .../cex2/main.c} | 0 .../cex2/test.desc | 0 .../spurious-check-abstract/cex3/main.c | 16 ++ .../cex3/test.desc | 0 .../spurious-check-abstract/cex4/main.c | 11 ++ .../cex4/test.desc | 0 .../spurious-check-abstract/cex5/main.c | 15 ++ .../cex5/test.desc | 0 .../spurious-check-abstract/cex6/main.c | 20 ++ .../cex6/test.desc | 0 .../spurious-check-abstract/cex7/main.c | 15 ++ .../cex7/test.desc | 0 .../spurious-check-abstract/cex8/main.c | 20 ++ .../cex8/test.desc | 0 .../spurious-check-abstract/cex9/main.c | 24 +++ .../cex9/test.desc | 0 .../spurious-check-abstract/nocex1/main.c | 15 ++ .../nocex1/test.desc | 0 .../spurious-check-abstract/nocex10/main.c | 11 ++ .../spurious-check-abstract/nocex10/test.desc | 6 + .../spurious-check-abstract/nocex2/main.c | 16 ++ .../nocex2/test.desc | 0 .../spurious-check-abstract/nocex3/main.c | 18 ++ .../nocex3/test.desc | 0 .../spurious-check-abstract/nocex4/main.c | 20 ++ .../nocex4/test.desc | 0 .../spurious-check-abstract/nocex5/main.c | 30 +++ .../nocex5/test.desc | 0 .../spurious-check-abstract/nocex6/main.c | 15 ++ .../nocex6/test.desc | 0 .../spurious-check-abstract/nocex8/main.c | 16 ++ .../nocex8/test.desc | 0 .../spurious-check-abstract/nocex9/main.c | 11 ++ .../spurious-check-abstract/nocex9/test.desc | 6 + .../Makefile | 0 .../cex-struct1/main.c | 180 ++++++++++++++++++ .../cex-struct1/test.desc | 6 + .../cex-struct2/main.c | 22 +++ .../cex-struct2/test.desc | 6 + .../cex-struct3/main.c | 23 +++ .../cex-struct3/test.desc | 6 + .../spurious-check-complete/cex1/main.c | 15 ++ .../spurious-check-complete/cex1/test.desc | 6 + .../spurious-check-complete/cex10/main.c | 21 ++ .../spurious-check-complete/cex10/test.desc | 6 + .../spurious-check-complete/cex13/main.c | 16 ++ .../spurious-check-complete/cex13/test.desc | 6 + .../spurious-check-complete/cex2/main.c | 16 ++ .../spurious-check-complete/cex2/test.desc | 6 + .../spurious-check-complete/cex3/main.c | 16 ++ .../spurious-check-complete/cex3/test.desc | 6 + .../spurious-check-complete/cex4/main.c | 11 ++ .../spurious-check-complete/cex4/test.desc | 6 + .../spurious-check-complete/cex5/main.c | 15 ++ .../spurious-check-complete/cex5/test.desc | 6 + .../spurious-check-complete/cex6/main.c | 20 ++ .../spurious-check-complete/cex6/test.desc | 6 + .../spurious-check-complete/cex7/main.c | 15 ++ .../spurious-check-complete/cex7/test.desc | 6 + .../spurious-check-complete/cex8/main.c | 20 ++ .../spurious-check-complete/cex8/test.desc | 6 + .../spurious-check-complete/cex9/main.c | 24 +++ .../spurious-check-complete/cex9/test.desc | 6 + .../spurious-check-complete/nocex1/main.c | 15 ++ .../nocex1}/test.desc | 2 +- .../spurious-check-complete/nocex10/main.c | 11 ++ .../spurious-check-complete/nocex10/test.desc | 6 + .../spurious-check-complete/nocex2/main.c | 16 ++ .../nocex2}/test.desc | 2 +- .../spurious-check-complete/nocex3/main.c | 18 ++ .../nocex3}/test.desc | 2 +- .../spurious-check-complete/nocex4/main.c | 20 ++ .../nocex4}/test.desc | 2 +- .../spurious-check-complete/nocex5/main.c | 30 +++ .../spurious-check-complete/nocex5/test.desc | 6 + .../spurious-check-complete/nocex6/main.c | 15 ++ .../spurious-check-complete/nocex6/test.desc | 6 + .../spurious-check-complete/nocex8/main.c | 16 ++ .../spurious-check-complete/nocex8/test.desc | 6 + .../spurious-check-complete/nocex9/main.c | 11 ++ .../spurious-check-complete/nocex9/test.desc | 6 + regression/spurious-check-concrete/Makefile | 20 ++ .../cex-struct1/main.c | 180 ++++++++++++++++++ .../cex-struct1/test.desc | 6 + .../cex-struct2/main.c | 22 +++ .../cex-struct2/test.desc | 6 + .../cex-struct3/main.c | 23 +++ .../cex-struct3/test.desc | 6 + .../spurious-check-concrete/cex1/main.c | 15 ++ .../spurious-check-concrete/cex1/test.desc | 6 + .../spurious-check-concrete/cex10/main.c | 21 ++ .../spurious-check-concrete/cex10/test.desc | 6 + .../spurious-check-concrete/cex13/main.c | 16 ++ .../spurious-check-concrete/cex13/test.desc | 6 + .../spurious-check-concrete/cex2/main.c | 16 ++ .../spurious-check-concrete/cex2/test.desc | 6 + .../spurious-check-concrete/cex3/main.c | 16 ++ .../spurious-check-concrete/cex3/test.desc | 6 + .../spurious-check-concrete/cex4/main.c | 11 ++ .../spurious-check-concrete/cex4/test.desc | 6 + .../spurious-check-concrete/cex5/main.c | 15 ++ .../spurious-check-concrete/cex5/test.desc | 6 + .../spurious-check-concrete/cex6/main.c | 20 ++ .../spurious-check-concrete/cex6/test.desc | 6 + .../spurious-check-concrete/cex7/main.c | 15 ++ .../spurious-check-concrete/cex7/test.desc | 6 + .../spurious-check-concrete/cex8/main.c | 20 ++ .../spurious-check-concrete/cex8/test.desc | 6 + .../spurious-check-concrete/cex9/main.c | 24 +++ .../spurious-check-concrete/cex9/test.desc | 6 + .../spurious-check-concrete/nocex1/main.c | 15 ++ .../spurious-check-concrete/nocex1/test.desc | 6 + .../spurious-check-concrete/nocex10/main.c | 11 ++ .../spurious-check-concrete/nocex10/test.desc | 6 + .../spurious-check-concrete/nocex2/main.c | 16 ++ .../spurious-check-concrete/nocex2/test.desc | 6 + .../spurious-check-concrete/nocex3/main.c | 18 ++ .../spurious-check-concrete/nocex3/test.desc | 6 + .../spurious-check-concrete/nocex4/main.c | 20 ++ .../spurious-check-concrete/nocex4/test.desc | 6 + .../spurious-check-concrete/nocex5/main.c | 30 +++ .../spurious-check-concrete/nocex5/test.desc | 6 + .../spurious-check-concrete/nocex6/main.c | 15 ++ .../spurious-check-concrete/nocex6/test.desc | 6 + .../spurious-check-concrete/nocex8/main.c | 16 ++ .../spurious-check-concrete/nocex8/test.desc | 6 + .../spurious-check-concrete/nocex9/main.c | 11 ++ .../spurious-check-concrete/nocex9/test.desc | 6 + src/summarizer/summary_checker_kind.cpp | 1 + 262 files changed, 2494 insertions(+), 75 deletions(-) create mode 100644 regression/kiki-modular/Makefile rename regression/{modular => kiki-modular}/cex-struct1/main.c (100%) create mode 100644 regression/kiki-modular/cex-struct1/test.desc rename regression/{modular => kiki-modular}/cex-struct2/main.c (100%) create mode 100644 regression/kiki-modular/cex-struct2/test.desc rename regression/{modular => kiki-modular}/cex-struct3/main.c (100%) create mode 100644 regression/kiki-modular/cex-struct3/test.desc rename regression/{modular => kiki-modular}/cex1/main.c (100%) create mode 100644 regression/kiki-modular/cex1/test.desc rename regression/{modular => kiki-modular}/cex10/main.c (100%) create mode 100644 regression/kiki-modular/cex10/test.desc rename regression/{modular => kiki-modular}/cex13/main.c (100%) create mode 100644 regression/kiki-modular/cex13/test.desc rename regression/{modular => kiki-modular}/cex2/main.c (100%) create mode 100644 regression/kiki-modular/cex2/test.desc rename regression/{modular => kiki-modular}/cex3/main.c (100%) create mode 100644 regression/kiki-modular/cex3/test.desc rename regression/{modular => kiki-modular}/cex4/main.c (100%) create mode 100644 regression/kiki-modular/cex4/test.desc rename regression/{modular => kiki-modular}/cex5/main.c (100%) create mode 100644 regression/kiki-modular/cex5/test.desc rename regression/{modular => kiki-modular}/cex6/main.c (100%) create mode 100644 regression/kiki-modular/cex6/test.desc rename regression/{modular => kiki-modular}/cex7/main.c (100%) create mode 100644 regression/kiki-modular/cex7/test.desc rename regression/{modular => kiki-modular}/cex8/main.c (100%) create mode 100644 regression/kiki-modular/cex8/test.desc rename regression/{modular => kiki-modular}/cex9/main.c (100%) create mode 100644 regression/kiki-modular/cex9/test.desc rename regression/{modular => kiki-modular}/induction1/main.c (100%) rename regression/{modular/induction2 => kiki-modular/induction1}/test.desc (59%) rename regression/{modular => kiki-modular}/induction2/main.c (100%) create mode 100644 regression/kiki-modular/induction2/test.desc rename regression/{modular => kiki-modular}/induction3/main.c (100%) create mode 100644 regression/kiki-modular/induction3/test.desc rename regression/{modular => kiki-modular}/induction4/main.c (100%) rename regression/{modular/induction3 => kiki-modular/induction4}/test.desc (59%) rename regression/{modular => kiki-modular}/induction5/main.c (100%) rename regression/{modular => kiki-modular}/induction5/test.desc (52%) rename regression/{modular => kiki-modular}/induction7/main.c (100%) rename regression/{modular => kiki-modular}/induction7/test.desc (52%) rename regression/{modular => kiki-modular}/induction8/main.c (100%) create mode 100644 regression/kiki-modular/induction8/test.desc create mode 100644 regression/kiki-modular/inline-refine1/main.c create mode 100644 regression/kiki-modular/inline-refine1/test.desc rename regression/{modular/nocex2 => kiki-modular/inline-refine2}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine2/test.desc rename regression/{modular/nocex3 => kiki-modular/inline-refine3}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine3/test.desc rename regression/{modular/nocex4 => kiki-modular/inline-refine4}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine4/test.desc rename regression/{modular/nocex5 => kiki-modular/inline-refine5}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine5/test.desc rename regression/{modular/nocex6 => kiki-modular/inline-refine6}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine6/test.desc rename regression/{modular/nocex1 => kiki-modular/inline-refine7}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine7/test.desc rename regression/{modular/nocex8 => kiki-modular/inline-refine8}/main.c (100%) create mode 100644 regression/kiki-modular/inline-refine8/test.desc create mode 100644 regression/kiki-modular/inline-refine9/main.c create mode 100644 regression/kiki-modular/inline-refine9/test.desc rename regression/{modular => kiki-modular}/kind1/main.c (100%) create mode 100644 regression/kiki-modular/kind1/test.desc rename regression/{modular => kiki-modular}/kind2/main.c (100%) create mode 100644 regression/kiki-modular/kind2/test.desc rename regression/{modular => kiki-modular}/kind3/main.c (100%) create mode 100644 regression/kiki-modular/kind3/test.desc rename regression/{modular => kiki-modular}/kind4/main.c (100%) create mode 100644 regression/kiki-modular/kind4/test.desc rename regression/{modular => kiki-modular}/kind5/main.c (100%) create mode 100644 regression/kiki-modular/kind5/test.desc rename regression/{modular => kiki-modular}/kind6/main.c (100%) create mode 100644 regression/kiki-modular/kind6/test.desc rename regression/{modular/cex11 => kiki-modular/kind7}/main.c (100%) create mode 100644 regression/kiki-modular/kind7/test.desc rename regression/{modular/cex12 => kiki-modular/kind8}/main.c (100%) create mode 100644 regression/kiki-modular/kind8/test.desc rename regression/{modular => kiki-modular}/loop25/main.c (100%) create mode 100644 regression/kiki-modular/loop25/test.desc rename regression/{modular => kiki-modular}/loop27/main.c (100%) create mode 100644 regression/kiki-modular/loop27/test.desc rename regression/{modular => kiki-modular}/loop28/main.c (100%) create mode 100644 regression/kiki-modular/loop28/test.desc rename regression/{modular => kiki-modular}/nested11/main.c (100%) create mode 100644 regression/kiki-modular/nested11/test.desc rename regression/{modular => kiki-modular}/nested12/main.c (100%) rename regression/{modular => kiki-modular}/nested12/test.desc (52%) create mode 100644 regression/kiki-modular/nocex1/main.c create mode 100644 regression/kiki-modular/nocex1/test.desc create mode 100644 regression/kiki-modular/nocex2/main.c create mode 100644 regression/kiki-modular/nocex2/test.desc create mode 100644 regression/kiki-modular/nocex3/main.c create mode 100644 regression/kiki-modular/nocex3/test.desc create mode 100644 regression/kiki-modular/nocex4/main.c create mode 100644 regression/kiki-modular/nocex4/test.desc create mode 100644 regression/kiki-modular/nocex5/main.c create mode 100644 regression/kiki-modular/nocex5/test.desc create mode 100644 regression/kiki-modular/nocex6/main.c create mode 100644 regression/kiki-modular/nocex6/test.desc create mode 100644 regression/kiki-modular/nocex8/main.c create mode 100644 regression/kiki-modular/nocex8/test.desc create mode 100644 regression/kiki-modular/nocex9/main.c create mode 100644 regression/kiki-modular/nocex9/test.desc rename regression/{modular => kiki-modular}/s3_clnt_1/main.c (100%) create mode 100644 regression/kiki-modular/s3_clnt_1/test.desc rename regression/{modular => kiki-modular}/scope1/main.c (100%) create mode 100644 regression/kiki-modular/scope1/test.desc create mode 100644 regression/kiki-modular/unwind-refine1/main.c create mode 100644 regression/kiki-modular/unwind-refine1/test.desc delete mode 100644 regression/modular/kind1/test.desc delete mode 100644 regression/modular/kind2/test.desc delete mode 100644 regression/modular/kind3/test.desc delete mode 100644 regression/modular/kind4/test.desc delete mode 100644 regression/modular/kind5/test.desc delete mode 100644 regression/modular/kind6/test.desc delete mode 100644 regression/modular/loop25/test.desc delete mode 100644 regression/modular/loop28/test.desc delete mode 100644 regression/modular/nested11/test.desc delete mode 100644 regression/modular/s3_clnt_1/test.desc delete mode 100644 regression/modular/scope1/test.desc create mode 100644 regression/spurious-check-abstract/Makefile create mode 100644 regression/spurious-check-abstract/cex-struct1/main.c rename regression/{modular => spurious-check-abstract}/cex-struct1/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex-struct2/main.c rename regression/{modular => spurious-check-abstract}/cex-struct2/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex-struct3/main.c rename regression/{modular => spurious-check-abstract}/cex-struct3/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex1/main.c rename regression/{modular => spurious-check-abstract}/cex1/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex10/main.c rename regression/{modular => spurious-check-abstract}/cex10/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex13/main.c rename regression/{modular => spurious-check-abstract}/cex13/test.desc (100%) rename regression/{modular/cex13/main.c~ => spurious-check-abstract/cex2/main.c} (100%) rename regression/{modular => spurious-check-abstract}/cex2/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex3/main.c rename regression/{modular => spurious-check-abstract}/cex3/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex4/main.c rename regression/{modular => spurious-check-abstract}/cex4/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex5/main.c rename regression/{modular => spurious-check-abstract}/cex5/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex6/main.c rename regression/{modular => spurious-check-abstract}/cex6/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex7/main.c rename regression/{modular => spurious-check-abstract}/cex7/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex8/main.c rename regression/{modular => spurious-check-abstract}/cex8/test.desc (100%) create mode 100644 regression/spurious-check-abstract/cex9/main.c rename regression/{modular => spurious-check-abstract}/cex9/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex1/main.c rename regression/{modular => spurious-check-abstract}/nocex1/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex10/main.c create mode 100644 regression/spurious-check-abstract/nocex10/test.desc create mode 100644 regression/spurious-check-abstract/nocex2/main.c rename regression/{modular => spurious-check-abstract}/nocex2/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex3/main.c rename regression/{modular => spurious-check-abstract}/nocex3/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex4/main.c rename regression/{modular => spurious-check-abstract}/nocex4/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex5/main.c rename regression/{modular => spurious-check-abstract}/nocex5/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex6/main.c rename regression/{modular => spurious-check-abstract}/nocex6/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex8/main.c rename regression/{modular => spurious-check-abstract}/nocex8/test.desc (100%) create mode 100644 regression/spurious-check-abstract/nocex9/main.c create mode 100644 regression/spurious-check-abstract/nocex9/test.desc rename regression/{modular => spurious-check-complete}/Makefile (100%) create mode 100644 regression/spurious-check-complete/cex-struct1/main.c create mode 100644 regression/spurious-check-complete/cex-struct1/test.desc create mode 100644 regression/spurious-check-complete/cex-struct2/main.c create mode 100644 regression/spurious-check-complete/cex-struct2/test.desc create mode 100644 regression/spurious-check-complete/cex-struct3/main.c create mode 100644 regression/spurious-check-complete/cex-struct3/test.desc create mode 100644 regression/spurious-check-complete/cex1/main.c create mode 100644 regression/spurious-check-complete/cex1/test.desc create mode 100644 regression/spurious-check-complete/cex10/main.c create mode 100644 regression/spurious-check-complete/cex10/test.desc create mode 100644 regression/spurious-check-complete/cex13/main.c create mode 100644 regression/spurious-check-complete/cex13/test.desc create mode 100644 regression/spurious-check-complete/cex2/main.c create mode 100644 regression/spurious-check-complete/cex2/test.desc create mode 100644 regression/spurious-check-complete/cex3/main.c create mode 100644 regression/spurious-check-complete/cex3/test.desc create mode 100644 regression/spurious-check-complete/cex4/main.c create mode 100644 regression/spurious-check-complete/cex4/test.desc create mode 100644 regression/spurious-check-complete/cex5/main.c create mode 100644 regression/spurious-check-complete/cex5/test.desc create mode 100644 regression/spurious-check-complete/cex6/main.c create mode 100644 regression/spurious-check-complete/cex6/test.desc create mode 100644 regression/spurious-check-complete/cex7/main.c create mode 100644 regression/spurious-check-complete/cex7/test.desc create mode 100644 regression/spurious-check-complete/cex8/main.c create mode 100644 regression/spurious-check-complete/cex8/test.desc create mode 100644 regression/spurious-check-complete/cex9/main.c create mode 100644 regression/spurious-check-complete/cex9/test.desc create mode 100644 regression/spurious-check-complete/nocex1/main.c rename regression/{modular/induction1 => spurious-check-complete/nocex1}/test.desc (80%) create mode 100644 regression/spurious-check-complete/nocex10/main.c create mode 100644 regression/spurious-check-complete/nocex10/test.desc create mode 100644 regression/spurious-check-complete/nocex2/main.c rename regression/{modular/induction8 => spurious-check-complete/nocex2}/test.desc (80%) create mode 100644 regression/spurious-check-complete/nocex3/main.c rename regression/{modular/loop27 => spurious-check-complete/nocex3}/test.desc (80%) create mode 100644 regression/spurious-check-complete/nocex4/main.c rename regression/{modular/induction4 => spurious-check-complete/nocex4}/test.desc (80%) create mode 100644 regression/spurious-check-complete/nocex5/main.c create mode 100644 regression/spurious-check-complete/nocex5/test.desc create mode 100644 regression/spurious-check-complete/nocex6/main.c create mode 100644 regression/spurious-check-complete/nocex6/test.desc create mode 100644 regression/spurious-check-complete/nocex8/main.c create mode 100644 regression/spurious-check-complete/nocex8/test.desc create mode 100644 regression/spurious-check-complete/nocex9/main.c create mode 100644 regression/spurious-check-complete/nocex9/test.desc create mode 100644 regression/spurious-check-concrete/Makefile create mode 100644 regression/spurious-check-concrete/cex-struct1/main.c create mode 100644 regression/spurious-check-concrete/cex-struct1/test.desc create mode 100644 regression/spurious-check-concrete/cex-struct2/main.c create mode 100644 regression/spurious-check-concrete/cex-struct2/test.desc create mode 100644 regression/spurious-check-concrete/cex-struct3/main.c create mode 100644 regression/spurious-check-concrete/cex-struct3/test.desc create mode 100644 regression/spurious-check-concrete/cex1/main.c create mode 100644 regression/spurious-check-concrete/cex1/test.desc create mode 100644 regression/spurious-check-concrete/cex10/main.c create mode 100644 regression/spurious-check-concrete/cex10/test.desc create mode 100644 regression/spurious-check-concrete/cex13/main.c create mode 100644 regression/spurious-check-concrete/cex13/test.desc create mode 100644 regression/spurious-check-concrete/cex2/main.c create mode 100644 regression/spurious-check-concrete/cex2/test.desc create mode 100644 regression/spurious-check-concrete/cex3/main.c create mode 100644 regression/spurious-check-concrete/cex3/test.desc create mode 100644 regression/spurious-check-concrete/cex4/main.c create mode 100644 regression/spurious-check-concrete/cex4/test.desc create mode 100644 regression/spurious-check-concrete/cex5/main.c create mode 100644 regression/spurious-check-concrete/cex5/test.desc create mode 100644 regression/spurious-check-concrete/cex6/main.c create mode 100644 regression/spurious-check-concrete/cex6/test.desc create mode 100644 regression/spurious-check-concrete/cex7/main.c create mode 100644 regression/spurious-check-concrete/cex7/test.desc create mode 100644 regression/spurious-check-concrete/cex8/main.c create mode 100644 regression/spurious-check-concrete/cex8/test.desc create mode 100644 regression/spurious-check-concrete/cex9/main.c create mode 100644 regression/spurious-check-concrete/cex9/test.desc create mode 100644 regression/spurious-check-concrete/nocex1/main.c create mode 100644 regression/spurious-check-concrete/nocex1/test.desc create mode 100644 regression/spurious-check-concrete/nocex10/main.c create mode 100644 regression/spurious-check-concrete/nocex10/test.desc create mode 100644 regression/spurious-check-concrete/nocex2/main.c create mode 100644 regression/spurious-check-concrete/nocex2/test.desc create mode 100644 regression/spurious-check-concrete/nocex3/main.c create mode 100644 regression/spurious-check-concrete/nocex3/test.desc create mode 100644 regression/spurious-check-concrete/nocex4/main.c create mode 100644 regression/spurious-check-concrete/nocex4/test.desc create mode 100644 regression/spurious-check-concrete/nocex5/main.c create mode 100644 regression/spurious-check-concrete/nocex5/test.desc create mode 100644 regression/spurious-check-concrete/nocex6/main.c create mode 100644 regression/spurious-check-concrete/nocex6/test.desc create mode 100644 regression/spurious-check-concrete/nocex8/main.c create mode 100644 regression/spurious-check-concrete/nocex8/test.desc create mode 100644 regression/spurious-check-concrete/nocex9/main.c create mode 100644 regression/spurious-check-concrete/nocex9/test.desc diff --git a/regression/kiki-modular/Makefile b/regression/kiki-modular/Makefile new file mode 100644 index 000000000..d2ff74910 --- /dev/null +++ b/regression/kiki-modular/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/modular/cex-struct1/main.c b/regression/kiki-modular/cex-struct1/main.c similarity index 100% rename from regression/modular/cex-struct1/main.c rename to regression/kiki-modular/cex-struct1/main.c diff --git a/regression/kiki-modular/cex-struct1/test.desc b/regression/kiki-modular/cex-struct1/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex-struct2/main.c b/regression/kiki-modular/cex-struct2/main.c similarity index 100% rename from regression/modular/cex-struct2/main.c rename to regression/kiki-modular/cex-struct2/main.c diff --git a/regression/kiki-modular/cex-struct2/test.desc b/regression/kiki-modular/cex-struct2/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex-struct3/main.c b/regression/kiki-modular/cex-struct3/main.c similarity index 100% rename from regression/modular/cex-struct3/main.c rename to regression/kiki-modular/cex-struct3/main.c diff --git a/regression/kiki-modular/cex-struct3/test.desc b/regression/kiki-modular/cex-struct3/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex1/main.c b/regression/kiki-modular/cex1/main.c similarity index 100% rename from regression/modular/cex1/main.c rename to regression/kiki-modular/cex1/main.c diff --git a/regression/kiki-modular/cex1/test.desc b/regression/kiki-modular/cex1/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex10/main.c b/regression/kiki-modular/cex10/main.c similarity index 100% rename from regression/modular/cex10/main.c rename to regression/kiki-modular/cex10/main.c diff --git a/regression/kiki-modular/cex10/test.desc b/regression/kiki-modular/cex10/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex13/main.c b/regression/kiki-modular/cex13/main.c similarity index 100% rename from regression/modular/cex13/main.c rename to regression/kiki-modular/cex13/main.c diff --git a/regression/kiki-modular/cex13/test.desc b/regression/kiki-modular/cex13/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex2/main.c b/regression/kiki-modular/cex2/main.c similarity index 100% rename from regression/modular/cex2/main.c rename to regression/kiki-modular/cex2/main.c diff --git a/regression/kiki-modular/cex2/test.desc b/regression/kiki-modular/cex2/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex3/main.c b/regression/kiki-modular/cex3/main.c similarity index 100% rename from regression/modular/cex3/main.c rename to regression/kiki-modular/cex3/main.c diff --git a/regression/kiki-modular/cex3/test.desc b/regression/kiki-modular/cex3/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex4/main.c b/regression/kiki-modular/cex4/main.c similarity index 100% rename from regression/modular/cex4/main.c rename to regression/kiki-modular/cex4/main.c diff --git a/regression/kiki-modular/cex4/test.desc b/regression/kiki-modular/cex4/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex5/main.c b/regression/kiki-modular/cex5/main.c similarity index 100% rename from regression/modular/cex5/main.c rename to regression/kiki-modular/cex5/main.c diff --git a/regression/kiki-modular/cex5/test.desc b/regression/kiki-modular/cex5/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex6/main.c b/regression/kiki-modular/cex6/main.c similarity index 100% rename from regression/modular/cex6/main.c rename to regression/kiki-modular/cex6/main.c diff --git a/regression/kiki-modular/cex6/test.desc b/regression/kiki-modular/cex6/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex7/main.c b/regression/kiki-modular/cex7/main.c similarity index 100% rename from regression/modular/cex7/main.c rename to regression/kiki-modular/cex7/main.c diff --git a/regression/kiki-modular/cex7/test.desc b/regression/kiki-modular/cex7/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex8/main.c b/regression/kiki-modular/cex8/main.c similarity index 100% rename from regression/modular/cex8/main.c rename to regression/kiki-modular/cex8/main.c diff --git a/regression/kiki-modular/cex8/test.desc b/regression/kiki-modular/cex8/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/cex9/main.c b/regression/kiki-modular/cex9/main.c similarity index 100% rename from regression/modular/cex9/main.c rename to regression/kiki-modular/cex9/main.c diff --git a/regression/kiki-modular/cex9/test.desc b/regression/kiki-modular/cex9/test.desc new file mode 100644 index 000000000..b5b4ee326 --- /dev/null +++ b/regression/kiki-modular/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/modular/induction1/main.c b/regression/kiki-modular/induction1/main.c similarity index 100% rename from regression/modular/induction1/main.c rename to regression/kiki-modular/induction1/main.c diff --git a/regression/modular/induction2/test.desc b/regression/kiki-modular/induction1/test.desc similarity index 59% rename from regression/modular/induction2/test.desc rename to regression/kiki-modular/induction1/test.desc index 3bf2a93f1..e35a0daa7 100644 --- a/regression/modular/induction2/test.desc +++ b/regression/kiki-modular/induction1/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction --havoc +--k-induction --spurious-check complete ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction2/main.c b/regression/kiki-modular/induction2/main.c similarity index 100% rename from regression/modular/induction2/main.c rename to regression/kiki-modular/induction2/main.c diff --git a/regression/kiki-modular/induction2/test.desc b/regression/kiki-modular/induction2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/induction2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction3/main.c b/regression/kiki-modular/induction3/main.c similarity index 100% rename from regression/modular/induction3/main.c rename to regression/kiki-modular/induction3/main.c diff --git a/regression/kiki-modular/induction3/test.desc b/regression/kiki-modular/induction3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/induction3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction4/main.c b/regression/kiki-modular/induction4/main.c similarity index 100% rename from regression/modular/induction4/main.c rename to regression/kiki-modular/induction4/main.c diff --git a/regression/modular/induction3/test.desc b/regression/kiki-modular/induction4/test.desc similarity index 59% rename from regression/modular/induction3/test.desc rename to regression/kiki-modular/induction4/test.desc index 3bf2a93f1..e35a0daa7 100644 --- a/regression/modular/induction3/test.desc +++ b/regression/kiki-modular/induction4/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction --havoc +--k-induction --spurious-check complete ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/induction5/main.c b/regression/kiki-modular/induction5/main.c similarity index 100% rename from regression/modular/induction5/main.c rename to regression/kiki-modular/induction5/main.c diff --git a/regression/modular/induction5/test.desc b/regression/kiki-modular/induction5/test.desc similarity index 52% rename from regression/modular/induction5/test.desc rename to regression/kiki-modular/induction5/test.desc index 9a09cc686..4fb3efe5e 100644 --- a/regression/modular/induction5/test.desc +++ b/regression/kiki-modular/induction5/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--k-induction --spurious-check complete ^EXIT=10$ ^SIGNAL=0$ ^.*FAILURE$ diff --git a/regression/modular/induction7/main.c b/regression/kiki-modular/induction7/main.c similarity index 100% rename from regression/modular/induction7/main.c rename to regression/kiki-modular/induction7/main.c diff --git a/regression/modular/induction7/test.desc b/regression/kiki-modular/induction7/test.desc similarity index 52% rename from regression/modular/induction7/test.desc rename to regression/kiki-modular/induction7/test.desc index 9a09cc686..4fb3efe5e 100644 --- a/regression/modular/induction7/test.desc +++ b/regression/kiki-modular/induction7/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--k-induction --spurious-check complete ^EXIT=10$ ^SIGNAL=0$ ^.*FAILURE$ diff --git a/regression/modular/induction8/main.c b/regression/kiki-modular/induction8/main.c similarity index 100% rename from regression/modular/induction8/main.c rename to regression/kiki-modular/induction8/main.c diff --git a/regression/kiki-modular/induction8/test.desc b/regression/kiki-modular/induction8/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/induction8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine1/main.c b/regression/kiki-modular/inline-refine1/main.c new file mode 100644 index 000000000..5662b4933 --- /dev/null +++ b/regression/kiki-modular/inline-refine1/main.c @@ -0,0 +1,17 @@ +#include + +int inc(int x) +{ + return x+1; +} + +void main() +{ + int x = 0; + int y = 0; + + x = inc(x); + y = inc(y); + + assert(x==1); +} diff --git a/regression/kiki-modular/inline-refine1/test.desc b/regression/kiki-modular/inline-refine1/test.desc new file mode 100644 index 000000000..af2fdc37b --- /dev/null +++ b/regression/kiki-modular/inline-refine1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --no-propagation --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex2/main.c b/regression/kiki-modular/inline-refine2/main.c similarity index 100% rename from regression/modular/nocex2/main.c rename to regression/kiki-modular/inline-refine2/main.c diff --git a/regression/kiki-modular/inline-refine2/test.desc b/regression/kiki-modular/inline-refine2/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex3/main.c b/regression/kiki-modular/inline-refine3/main.c similarity index 100% rename from regression/modular/nocex3/main.c rename to regression/kiki-modular/inline-refine3/main.c diff --git a/regression/kiki-modular/inline-refine3/test.desc b/regression/kiki-modular/inline-refine3/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex4/main.c b/regression/kiki-modular/inline-refine4/main.c similarity index 100% rename from regression/modular/nocex4/main.c rename to regression/kiki-modular/inline-refine4/main.c diff --git a/regression/kiki-modular/inline-refine4/test.desc b/regression/kiki-modular/inline-refine4/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex5/main.c b/regression/kiki-modular/inline-refine5/main.c similarity index 100% rename from regression/modular/nocex5/main.c rename to regression/kiki-modular/inline-refine5/main.c diff --git a/regression/kiki-modular/inline-refine5/test.desc b/regression/kiki-modular/inline-refine5/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex6/main.c b/regression/kiki-modular/inline-refine6/main.c similarity index 100% rename from regression/modular/nocex6/main.c rename to regression/kiki-modular/inline-refine6/main.c diff --git a/regression/kiki-modular/inline-refine6/test.desc b/regression/kiki-modular/inline-refine6/test.desc new file mode 100644 index 000000000..dbc94b93b --- /dev/null +++ b/regression/kiki-modular/inline-refine6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 --k-induction --spurious-check concrete +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/modular/nocex1/main.c b/regression/kiki-modular/inline-refine7/main.c similarity index 100% rename from regression/modular/nocex1/main.c rename to regression/kiki-modular/inline-refine7/main.c diff --git a/regression/kiki-modular/inline-refine7/test.desc b/regression/kiki-modular/inline-refine7/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nocex8/main.c b/regression/kiki-modular/inline-refine8/main.c similarity index 100% rename from regression/modular/nocex8/main.c rename to regression/kiki-modular/inline-refine8/main.c diff --git a/regression/kiki-modular/inline-refine8/test.desc b/regression/kiki-modular/inline-refine8/test.desc new file mode 100644 index 000000000..75b577755 --- /dev/null +++ b/regression/kiki-modular/inline-refine8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/inline-refine9/main.c b/regression/kiki-modular/inline-refine9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/kiki-modular/inline-refine9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/kiki-modular/inline-refine9/test.desc b/regression/kiki-modular/inline-refine9/test.desc new file mode 100644 index 000000000..842d3328d --- /dev/null +++ b/regression/kiki-modular/inline-refine9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check concrete --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind1/main.c b/regression/kiki-modular/kind1/main.c similarity index 100% rename from regression/modular/kind1/main.c rename to regression/kiki-modular/kind1/main.c diff --git a/regression/kiki-modular/kind1/test.desc b/regression/kiki-modular/kind1/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind2/main.c b/regression/kiki-modular/kind2/main.c similarity index 100% rename from regression/modular/kind2/main.c rename to regression/kiki-modular/kind2/main.c diff --git a/regression/kiki-modular/kind2/test.desc b/regression/kiki-modular/kind2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind3/main.c b/regression/kiki-modular/kind3/main.c similarity index 100% rename from regression/modular/kind3/main.c rename to regression/kiki-modular/kind3/main.c diff --git a/regression/kiki-modular/kind3/test.desc b/regression/kiki-modular/kind3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind4/main.c b/regression/kiki-modular/kind4/main.c similarity index 100% rename from regression/modular/kind4/main.c rename to regression/kiki-modular/kind4/main.c diff --git a/regression/kiki-modular/kind4/test.desc b/regression/kiki-modular/kind4/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind5/main.c b/regression/kiki-modular/kind5/main.c similarity index 100% rename from regression/modular/kind5/main.c rename to regression/kiki-modular/kind5/main.c diff --git a/regression/kiki-modular/kind5/test.desc b/regression/kiki-modular/kind5/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind6/main.c b/regression/kiki-modular/kind6/main.c similarity index 100% rename from regression/modular/kind6/main.c rename to regression/kiki-modular/kind6/main.c diff --git a/regression/kiki-modular/kind6/test.desc b/regression/kiki-modular/kind6/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/cex11/main.c b/regression/kiki-modular/kind7/main.c similarity index 100% rename from regression/modular/cex11/main.c rename to regression/kiki-modular/kind7/main.c diff --git a/regression/kiki-modular/kind7/test.desc b/regression/kiki-modular/kind7/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/cex12/main.c b/regression/kiki-modular/kind8/main.c similarity index 100% rename from regression/modular/cex12/main.c rename to regression/kiki-modular/kind8/main.c diff --git a/regression/kiki-modular/kind8/test.desc b/regression/kiki-modular/kind8/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/kind8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop25/main.c b/regression/kiki-modular/loop25/main.c similarity index 100% rename from regression/modular/loop25/main.c rename to regression/kiki-modular/loop25/main.c diff --git a/regression/kiki-modular/loop25/test.desc b/regression/kiki-modular/loop25/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/loop25/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop27/main.c b/regression/kiki-modular/loop27/main.c similarity index 100% rename from regression/modular/loop27/main.c rename to regression/kiki-modular/loop27/main.c diff --git a/regression/kiki-modular/loop27/test.desc b/regression/kiki-modular/loop27/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/loop27/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop28/main.c b/regression/kiki-modular/loop28/main.c similarity index 100% rename from regression/modular/loop28/main.c rename to regression/kiki-modular/loop28/main.c diff --git a/regression/kiki-modular/loop28/test.desc b/regression/kiki-modular/loop28/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/loop28/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nested11/main.c b/regression/kiki-modular/nested11/main.c similarity index 100% rename from regression/modular/nested11/main.c rename to regression/kiki-modular/nested11/main.c diff --git a/regression/kiki-modular/nested11/test.desc b/regression/kiki-modular/nested11/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/nested11/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nested12/main.c b/regression/kiki-modular/nested12/main.c similarity index 100% rename from regression/modular/nested12/main.c rename to regression/kiki-modular/nested12/main.c diff --git a/regression/modular/nested12/test.desc b/regression/kiki-modular/nested12/test.desc similarity index 52% rename from regression/modular/nested12/test.desc rename to regression/kiki-modular/nested12/test.desc index 9a09cc686..4fb3efe5e 100644 --- a/regression/modular/nested12/test.desc +++ b/regression/kiki-modular/nested12/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--k-induction --spurious-check complete ^EXIT=10$ ^SIGNAL=0$ ^.*FAILURE$ diff --git a/regression/kiki-modular/nocex1/main.c b/regression/kiki-modular/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/kiki-modular/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/nocex1/test.desc b/regression/kiki-modular/nocex1/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex2/main.c b/regression/kiki-modular/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/kiki-modular/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/kiki-modular/nocex2/test.desc b/regression/kiki-modular/nocex2/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex3/main.c b/regression/kiki-modular/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/kiki-modular/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/nocex3/test.desc b/regression/kiki-modular/nocex3/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex4/main.c b/regression/kiki-modular/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/kiki-modular/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/kiki-modular/nocex4/test.desc b/regression/kiki-modular/nocex4/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex5/main.c b/regression/kiki-modular/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/kiki-modular/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/kiki-modular/nocex5/test.desc b/regression/kiki-modular/nocex5/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex6/main.c b/regression/kiki-modular/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/kiki-modular/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/kiki-modular/nocex6/test.desc b/regression/kiki-modular/nocex6/test.desc new file mode 100644 index 000000000..f0edbd788 --- /dev/null +++ b/regression/kiki-modular/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 --k-induction --spurious-check complete +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/kiki-modular/nocex8/main.c b/regression/kiki-modular/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/kiki-modular/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/kiki-modular/nocex8/test.desc b/regression/kiki-modular/nocex8/test.desc new file mode 100644 index 000000000..0544a23e5 --- /dev/null +++ b/regression/kiki-modular/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/nocex9/main.c b/regression/kiki-modular/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/kiki-modular/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/kiki-modular/nocex9/test.desc b/regression/kiki-modular/nocex9/test.desc new file mode 100644 index 000000000..312f3d84e --- /dev/null +++ b/regression/kiki-modular/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete --no-propagation +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/s3_clnt_1/main.c b/regression/kiki-modular/s3_clnt_1/main.c similarity index 100% rename from regression/modular/s3_clnt_1/main.c rename to regression/kiki-modular/s3_clnt_1/main.c diff --git a/regression/kiki-modular/s3_clnt_1/test.desc b/regression/kiki-modular/s3_clnt_1/test.desc new file mode 100644 index 000000000..62a74380a --- /dev/null +++ b/regression/kiki-modular/s3_clnt_1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --spurious-check complete +^EXIT=10$ +^SIGNAL=0$ +^.*FAILURE$ diff --git a/regression/modular/scope1/main.c b/regression/kiki-modular/scope1/main.c similarity index 100% rename from regression/modular/scope1/main.c rename to regression/kiki-modular/scope1/main.c diff --git a/regression/kiki-modular/scope1/test.desc b/regression/kiki-modular/scope1/test.desc new file mode 100644 index 000000000..e35a0daa7 --- /dev/null +++ b/regression/kiki-modular/scope1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--k-induction --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/kiki-modular/unwind-refine1/main.c b/regression/kiki-modular/unwind-refine1/main.c new file mode 100644 index 000000000..6852ba883 --- /dev/null +++ b/regression/kiki-modular/unwind-refine1/main.c @@ -0,0 +1,15 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + int c; + + while(x<10) ++x; + + int c; + while(c) ++y; + + assert(x==10); +} diff --git a/regression/kiki-modular/unwind-refine1/test.desc b/regression/kiki-modular/unwind-refine1/test.desc new file mode 100644 index 000000000..80195020a --- /dev/null +++ b/regression/kiki-modular/unwind-refine1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --k-induction --no-propagation --spurious-check complete +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind1/test.desc b/regression/modular/kind1/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind1/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind2/test.desc b/regression/modular/kind2/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind2/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind3/test.desc b/regression/modular/kind3/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind3/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind4/test.desc b/regression/modular/kind4/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind4/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind5/test.desc b/regression/modular/kind5/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind5/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/kind6/test.desc b/regression/modular/kind6/test.desc deleted file mode 100644 index 630457914..000000000 --- a/regression/modular/kind6/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop25/test.desc b/regression/modular/loop25/test.desc deleted file mode 100644 index 6755e58a3..000000000 --- a/regression/modular/loop25/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/loop28/test.desc b/regression/modular/loop28/test.desc deleted file mode 100644 index 34e465e33..000000000 --- a/regression/modular/loop28/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/nested11/test.desc b/regression/modular/nested11/test.desc deleted file mode 100644 index 34e465e33..000000000 --- a/regression/modular/nested11/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/modular/s3_clnt_1/test.desc b/regression/modular/s3_clnt_1/test.desc deleted file mode 100644 index 4d84485b2..000000000 --- a/regression/modular/s3_clnt_1/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---havoc --k-induction -^EXIT=10$ -^SIGNAL=0$ -^.*FAILURE$ diff --git a/regression/modular/scope1/test.desc b/regression/modular/scope1/test.desc deleted file mode 100644 index 34e465e33..000000000 --- a/regression/modular/scope1/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c ---k-induction -^EXIT=0$ -^SIGNAL=0$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-abstract/Makefile b/regression/spurious-check-abstract/Makefile new file mode 100644 index 000000000..3a92d5017 --- /dev/null +++ b/regression/spurious-check-abstract/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check abstract + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/spurious-check-abstract/cex-struct1/main.c b/regression/spurious-check-abstract/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/modular/cex-struct1/test.desc b/regression/spurious-check-abstract/cex-struct1/test.desc similarity index 100% rename from regression/modular/cex-struct1/test.desc rename to regression/spurious-check-abstract/cex-struct1/test.desc diff --git a/regression/spurious-check-abstract/cex-struct2/main.c b/regression/spurious-check-abstract/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/modular/cex-struct2/test.desc b/regression/spurious-check-abstract/cex-struct2/test.desc similarity index 100% rename from regression/modular/cex-struct2/test.desc rename to regression/spurious-check-abstract/cex-struct2/test.desc diff --git a/regression/spurious-check-abstract/cex-struct3/main.c b/regression/spurious-check-abstract/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-abstract/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/modular/cex-struct3/test.desc b/regression/spurious-check-abstract/cex-struct3/test.desc similarity index 100% rename from regression/modular/cex-struct3/test.desc rename to regression/spurious-check-abstract/cex-struct3/test.desc diff --git a/regression/spurious-check-abstract/cex1/main.c b/regression/spurious-check-abstract/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-abstract/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex1/test.desc b/regression/spurious-check-abstract/cex1/test.desc similarity index 100% rename from regression/modular/cex1/test.desc rename to regression/spurious-check-abstract/cex1/test.desc diff --git a/regression/spurious-check-abstract/cex10/main.c b/regression/spurious-check-abstract/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-abstract/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex10/test.desc b/regression/spurious-check-abstract/cex10/test.desc similarity index 100% rename from regression/modular/cex10/test.desc rename to regression/spurious-check-abstract/cex10/test.desc diff --git a/regression/spurious-check-abstract/cex13/main.c b/regression/spurious-check-abstract/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-abstract/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/modular/cex13/test.desc b/regression/spurious-check-abstract/cex13/test.desc similarity index 100% rename from regression/modular/cex13/test.desc rename to regression/spurious-check-abstract/cex13/test.desc diff --git a/regression/modular/cex13/main.c~ b/regression/spurious-check-abstract/cex2/main.c similarity index 100% rename from regression/modular/cex13/main.c~ rename to regression/spurious-check-abstract/cex2/main.c diff --git a/regression/modular/cex2/test.desc b/regression/spurious-check-abstract/cex2/test.desc similarity index 100% rename from regression/modular/cex2/test.desc rename to regression/spurious-check-abstract/cex2/test.desc diff --git a/regression/spurious-check-abstract/cex3/main.c b/regression/spurious-check-abstract/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-abstract/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex3/test.desc b/regression/spurious-check-abstract/cex3/test.desc similarity index 100% rename from regression/modular/cex3/test.desc rename to regression/spurious-check-abstract/cex3/test.desc diff --git a/regression/spurious-check-abstract/cex4/main.c b/regression/spurious-check-abstract/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-abstract/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/modular/cex4/test.desc b/regression/spurious-check-abstract/cex4/test.desc similarity index 100% rename from regression/modular/cex4/test.desc rename to regression/spurious-check-abstract/cex4/test.desc diff --git a/regression/spurious-check-abstract/cex5/main.c b/regression/spurious-check-abstract/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-abstract/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/modular/cex5/test.desc b/regression/spurious-check-abstract/cex5/test.desc similarity index 100% rename from regression/modular/cex5/test.desc rename to regression/spurious-check-abstract/cex5/test.desc diff --git a/regression/spurious-check-abstract/cex6/main.c b/regression/spurious-check-abstract/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-abstract/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/modular/cex6/test.desc b/regression/spurious-check-abstract/cex6/test.desc similarity index 100% rename from regression/modular/cex6/test.desc rename to regression/spurious-check-abstract/cex6/test.desc diff --git a/regression/spurious-check-abstract/cex7/main.c b/regression/spurious-check-abstract/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-abstract/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/modular/cex7/test.desc b/regression/spurious-check-abstract/cex7/test.desc similarity index 100% rename from regression/modular/cex7/test.desc rename to regression/spurious-check-abstract/cex7/test.desc diff --git a/regression/spurious-check-abstract/cex8/main.c b/regression/spurious-check-abstract/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-abstract/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex8/test.desc b/regression/spurious-check-abstract/cex8/test.desc similarity index 100% rename from regression/modular/cex8/test.desc rename to regression/spurious-check-abstract/cex8/test.desc diff --git a/regression/spurious-check-abstract/cex9/main.c b/regression/spurious-check-abstract/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-abstract/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/modular/cex9/test.desc b/regression/spurious-check-abstract/cex9/test.desc similarity index 100% rename from regression/modular/cex9/test.desc rename to regression/spurious-check-abstract/cex9/test.desc diff --git a/regression/spurious-check-abstract/nocex1/main.c b/regression/spurious-check-abstract/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-abstract/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/modular/nocex1/test.desc b/regression/spurious-check-abstract/nocex1/test.desc similarity index 100% rename from regression/modular/nocex1/test.desc rename to regression/spurious-check-abstract/nocex1/test.desc diff --git a/regression/spurious-check-abstract/nocex10/main.c b/regression/spurious-check-abstract/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-abstract/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-abstract/nocex10/test.desc b/regression/spurious-check-abstract/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-abstract/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-abstract/nocex2/main.c b/regression/spurious-check-abstract/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-abstract/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/modular/nocex2/test.desc b/regression/spurious-check-abstract/nocex2/test.desc similarity index 100% rename from regression/modular/nocex2/test.desc rename to regression/spurious-check-abstract/nocex2/test.desc diff --git a/regression/spurious-check-abstract/nocex3/main.c b/regression/spurious-check-abstract/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-abstract/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/modular/nocex3/test.desc b/regression/spurious-check-abstract/nocex3/test.desc similarity index 100% rename from regression/modular/nocex3/test.desc rename to regression/spurious-check-abstract/nocex3/test.desc diff --git a/regression/spurious-check-abstract/nocex4/main.c b/regression/spurious-check-abstract/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-abstract/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/modular/nocex4/test.desc b/regression/spurious-check-abstract/nocex4/test.desc similarity index 100% rename from regression/modular/nocex4/test.desc rename to regression/spurious-check-abstract/nocex4/test.desc diff --git a/regression/spurious-check-abstract/nocex5/main.c b/regression/spurious-check-abstract/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-abstract/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/modular/nocex5/test.desc b/regression/spurious-check-abstract/nocex5/test.desc similarity index 100% rename from regression/modular/nocex5/test.desc rename to regression/spurious-check-abstract/nocex5/test.desc diff --git a/regression/spurious-check-abstract/nocex6/main.c b/regression/spurious-check-abstract/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-abstract/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/modular/nocex6/test.desc b/regression/spurious-check-abstract/nocex6/test.desc similarity index 100% rename from regression/modular/nocex6/test.desc rename to regression/spurious-check-abstract/nocex6/test.desc diff --git a/regression/spurious-check-abstract/nocex8/main.c b/regression/spurious-check-abstract/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-abstract/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/modular/nocex8/test.desc b/regression/spurious-check-abstract/nocex8/test.desc similarity index 100% rename from regression/modular/nocex8/test.desc rename to regression/spurious-check-abstract/nocex8/test.desc diff --git a/regression/spurious-check-abstract/nocex9/main.c b/regression/spurious-check-abstract/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-abstract/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-abstract/nocex9/test.desc b/regression/spurious-check-abstract/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-abstract/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/modular/Makefile b/regression/spurious-check-complete/Makefile similarity index 100% rename from regression/modular/Makefile rename to regression/spurious-check-complete/Makefile diff --git a/regression/spurious-check-complete/cex-struct1/main.c b/regression/spurious-check-complete/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-complete/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct1/test.desc b/regression/spurious-check-complete/cex-struct1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex-struct2/main.c b/regression/spurious-check-complete/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct2/test.desc b/regression/spurious-check-complete/cex-struct2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex-struct3/main.c b/regression/spurious-check-complete/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-complete/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-complete/cex-struct3/test.desc b/regression/spurious-check-complete/cex-struct3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex1/main.c b/regression/spurious-check-complete/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-complete/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex1/test.desc b/regression/spurious-check-complete/cex1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex10/main.c b/regression/spurious-check-complete/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-complete/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex10/test.desc b/regression/spurious-check-complete/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex13/main.c b/regression/spurious-check-complete/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-complete/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex13/test.desc b/regression/spurious-check-complete/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex2/main.c b/regression/spurious-check-complete/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/spurious-check-complete/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex2/test.desc b/regression/spurious-check-complete/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex3/main.c b/regression/spurious-check-complete/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-complete/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex3/test.desc b/regression/spurious-check-complete/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex4/main.c b/regression/spurious-check-complete/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-complete/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex4/test.desc b/regression/spurious-check-complete/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex5/main.c b/regression/spurious-check-complete/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-complete/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-complete/cex5/test.desc b/regression/spurious-check-complete/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex6/main.c b/regression/spurious-check-complete/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-complete/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/cex6/test.desc b/regression/spurious-check-complete/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex7/main.c b/regression/spurious-check-complete/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-complete/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/cex7/test.desc b/regression/spurious-check-complete/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/spurious-check-complete/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex8/main.c b/regression/spurious-check-complete/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-complete/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex8/test.desc b/regression/spurious-check-complete/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/cex9/main.c b/regression/spurious-check-complete/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-complete/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-complete/cex9/test.desc b/regression/spurious-check-complete/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-complete/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-complete/nocex1/main.c b/regression/spurious-check-complete/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-complete/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/modular/induction1/test.desc b/regression/spurious-check-complete/nocex1/test.desc similarity index 80% rename from regression/modular/induction1/test.desc rename to regression/spurious-check-complete/nocex1/test.desc index 34e465e33..dc053e6ab 100644 --- a/regression/modular/induction1/test.desc +++ b/regression/spurious-check-complete/nocex1/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--havoc ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex10/main.c b/regression/spurious-check-complete/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-complete/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-complete/nocex10/test.desc b/regression/spurious-check-complete/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-complete/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-complete/nocex2/main.c b/regression/spurious-check-complete/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-complete/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/modular/induction8/test.desc b/regression/spurious-check-complete/nocex2/test.desc similarity index 80% rename from regression/modular/induction8/test.desc rename to regression/spurious-check-complete/nocex2/test.desc index 34e465e33..dc053e6ab 100644 --- a/regression/modular/induction8/test.desc +++ b/regression/spurious-check-complete/nocex2/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--havoc ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex3/main.c b/regression/spurious-check-complete/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-complete/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/modular/loop27/test.desc b/regression/spurious-check-complete/nocex3/test.desc similarity index 80% rename from regression/modular/loop27/test.desc rename to regression/spurious-check-complete/nocex3/test.desc index 34e465e33..dc053e6ab 100644 --- a/regression/modular/loop27/test.desc +++ b/regression/spurious-check-complete/nocex3/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--havoc ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex4/main.c b/regression/spurious-check-complete/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-complete/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/modular/induction4/test.desc b/regression/spurious-check-complete/nocex4/test.desc similarity index 80% rename from regression/modular/induction4/test.desc rename to regression/spurious-check-complete/nocex4/test.desc index 34e465e33..dc053e6ab 100644 --- a/regression/modular/induction4/test.desc +++ b/regression/spurious-check-complete/nocex4/test.desc @@ -1,6 +1,6 @@ CORE main.c ---k-induction +--havoc ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex5/main.c b/regression/spurious-check-complete/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-complete/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/spurious-check-complete/nocex5/test.desc b/regression/spurious-check-complete/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex6/main.c b/regression/spurious-check-complete/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-complete/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/spurious-check-complete/nocex6/test.desc b/regression/spurious-check-complete/nocex6/test.desc new file mode 100644 index 000000000..1f240717a --- /dev/null +++ b/regression/spurious-check-complete/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-complete/nocex8/main.c b/regression/spurious-check-complete/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-complete/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/spurious-check-complete/nocex8/test.desc b/regression/spurious-check-complete/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-complete/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-complete/nocex9/main.c b/regression/spurious-check-complete/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-complete/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-complete/nocex9/test.desc b/regression/spurious-check-complete/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-complete/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/Makefile b/regression/spurious-check-concrete/Makefile new file mode 100644 index 000000000..c26b769b2 --- /dev/null +++ b/regression/spurious-check-concrete/Makefile @@ -0,0 +1,20 @@ +default: tests.log + +FLAGS = --verbosity 10 --spurious-check concrete + +test: + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +tests.log: ../test.pl + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + +show: + @for dir in *; do \ + if [ -d "$$dir" ]; then \ + vim -o "$$dir/*.c" "$$dir/*.out"; \ + fi; \ + done; + +clean: + @rm -f *.log + @for dir in *; do rm -f $$dir/*.out; done; diff --git a/regression/spurious-check-concrete/cex-struct1/main.c b/regression/spurious-check-concrete/cex-struct1/main.c new file mode 100644 index 000000000..a9a9139fa --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct1/main.c @@ -0,0 +1,180 @@ + +#include +#include + +typedef struct{ int x; int y; int z; int w; int p; int q; int a; } foo; + +foo func_1(foo f); +foo func_2(foo f); +foo func_3(foo f); +foo func_4(foo f); +foo func_5(foo f); +foo func_6(foo f); +foo func_7(foo f); +foo func_8(foo f); + +foo func_1(foo f) +{ + f.x = 23 + 12; + f.x = 4 - 1; + if(f.w > f.x) {f.q = 21 * f.z;} else {f.q = 4 / f.p;} + f.a = f.a + 1; + f.z = f.y * f.x; + f.x = 11 - f.p; + f.y = 1 - 19; + f.y = f.w - 14; + return f; +} + +/**********************************************************************/ + +foo func_2(foo f) +{ + if(f.y < f.q) {f.x = f.w / 24;} else {f.w = 18 / 3;} + f.w = f.p * f.q; + if(f.q < 6) {f.y = 25 - f.x;} else {f.q = f.y / f.w;} + f.a = f.a + 1; + if(f.x <= f.x) {f.z = 9 + f.x;} else {f.y = 1 + f.x;} + f.x = f.q / 25; + f.w = f.x - f.y; + f.x = 9 + f.y; + f.y = 24 - 2; + f.w = 10 + f.z; + return f; +} + +/**********************************************************************/ + +foo func_3(foo f) +{ + if(f.p == 12) {f.w = 22 + 7;} else {f.p = f.x + f.q;} + f.y = f.p + f.x; + f.w = 6 * f.y; + if(f.z <= 20) {f.q = 21 / 24;} else {f.w = f.q * f.w;} + f.w = f.y - 10; + f.q = 2 - 20; + f.x = f.x / 10; + f.p = 16 * 23; + f.w = 18 - 13; + f.w = f.p - 2; + f.z = 7 + f.w; + f.a = f.a + 1; + f.x = 16 / f.x; + return f; +} + +/**********************************************************************/ + +foo func_4(foo f) +{ + f.w = f.y + f.y; + f.q = 18 * 13; + f.y = f.z * f.y; + f.x = 1 * 25; + f.y = 4 / f.q; + f.x = f.p / 9; + f.a = f.a + 1; + if(f.x >= 7) {f.q = f.q / 8;} else {if(f.x < 9) {f.z = 16 + f.p;} else {f.x = f.w - 18;}} + f.y = 2 * 4; + f.q = 16 + f.x; + f.q = 16 * f.w; + if(f.w > 14) {f.q = 20 + 20;} else {f.x = 25 + 9;} + return f; +} + +/**********************************************************************/ + +foo func_5(foo f) +{ + f.a = f.a + 1; + if(f.x == 13) {f.p = 13 * f.x;} else {f.w = f.q / f.w;} + f.y = 21 * f.x; + f.p = 4 * 25; + if(f.p >= 24) {f.x = 11 * 6;} else {f.p = 9 * f.p;} + f.x = 3 + f.z; + f.x = f.x + 22; + f.z = 8 / 8; + f.q = 18 + f.x; + f.x = 5 * f.z; + f.y = f.w + f.w; + return f; +} + +/**********************************************************************/ + +foo func_6(foo f) +{ + f.y = f.w / f.q; + f.p = f.x + 8; + f.a = f.a + 1; + f.z = f.w * 20; + if(f.x < f.p) {f.x = f.z + 2;} else {f.y = f.y * f.x;} + if(f.y <= f.y) {f.q = f.y * f.w;} else {f.q = f.w - 13;} + f.p = 9 + f.y; + f.q = f.y / 14; + f.p = 18 / f.z; + if(f.w < f.p) {f.w = 17 * f.p;} else {f.p = f.y - 10;} + f.p = 3 / 14; + f.q = 10 - f.w; + if(f.y > 12) {f.z = f.q - 24;} else {f.x = f.z / 17;} + return f; +} + +/**********************************************************************/ + +foo func_7(foo f) +{ + f.q = f.q * 23; + f.w = 25 - f.w; + if(f.q < f.y) {f.w = 21 + f.z;} else {f.p = 16 - 1;} + f.y = f.z * f.z; + f.x = 5 * 16; + f.x = 11 + f.x; + f.z = f.y - f.y; + f.q = 11 - f.z; + f.a = f.a + 1; + return f; +} + +/**********************************************************************/ + +foo func_8(foo f) +{ + f.w = f.y - 15; + f.q = f.x + f.z; + f.y = 5 / 1; + if(f.q < 3) {f.p = 6 * f.x; } else {f.w = 8 + 21;} + f.p = 19 / f.p; + f.z = 4 / f.z; + if(f.x < 12) {f.p = f.y - 4; } else {f.x = 15 + 2;} + f.q = 19 / f.w; + if(f.p > 24) {f.z = 5 / 9;} else {f.w = f.x + f.z;} + f.z = f.z + 1; + f.a = f.a + 1; + if(f.z > f.y) {f.y = f.y + f.x;} else {f.y = 13 - 18;} + return f; +} + + +/**********************************************************************/ + +int main() +{ + foo f0, f1, f2, f3, f4, f5, f6, f7, f8; + +// __CPROVER_assume((f0.a >= 0) && (f0.a <= 9)); + f0.a = 0; + + f1 = func_1(f0); +/* f2 = func_2(f1); + f3 = func_3(f2); + f4 = func_4(f3); + f5 = func_5(f4); + f6 = func_6(f5); + f7 = func_7(f6); + f8 = func_8(f7);*/ + + assert(/*(f8.x + f8.y + f8.z + f8.w + f8.p + f8.q > 0) &&*/ (f1.a != 1)); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct1/test.desc b/regression/spurious-check-concrete/cex-struct1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex-struct2/main.c b/regression/spurious-check-concrete/cex-struct2/main.c new file mode 100644 index 000000000..a5d7032f4 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct2/main.c @@ -0,0 +1,22 @@ + +#include +#include + +typedef struct{ int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + + assert((f1.a) != 1); + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct2/test.desc b/regression/spurious-check-concrete/cex-struct2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex-struct3/main.c b/regression/spurious-check-concrete/cex-struct3/main.c new file mode 100644 index 000000000..1715861bf --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct3/main.c @@ -0,0 +1,23 @@ + +#include +#include + +typedef struct{ int x; int a; } foo; + +foo func_1(foo f) +{ + f.a = f.a + 1; + f.x = f.a; + return f; +} + +int main() +{ + foo f0, f1; + f0.a = 0; + + f1 = func_1(f0); + assert(f1.a != 1); // unsafe assertion + + return 0; +} diff --git a/regression/spurious-check-concrete/cex-struct3/test.desc b/regression/spurious-check-concrete/cex-struct3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex-struct3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex1/main.c b/regression/spurious-check-concrete/cex1/main.c new file mode 100644 index 000000000..53451cf50 --- /dev/null +++ b/regression/spurious-check-concrete/cex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y<=0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex1/test.desc b/regression/spurious-check-concrete/cex1/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex10/main.c b/regression/spurious-check-concrete/cex10/main.c new file mode 100644 index 000000000..5908a46cc --- /dev/null +++ b/regression/spurious-check-concrete/cex10/main.c @@ -0,0 +1,21 @@ +#include + +int error(int k){ + assert(k != 3); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex10/test.desc b/regression/spurious-check-concrete/cex10/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex13/main.c b/regression/spurious-check-concrete/cex13/main.c new file mode 100644 index 000000000..242217d45 --- /dev/null +++ b/regression/spurious-check-concrete/cex13/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 4; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex13/test.desc b/regression/spurious-check-concrete/cex13/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex13/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex2/main.c b/regression/spurious-check-concrete/cex2/main.c new file mode 100644 index 000000000..cd18d78fc --- /dev/null +++ b/regression/spurious-check-concrete/cex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex2/test.desc b/regression/spurious-check-concrete/cex2/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex3/main.c b/regression/spurious-check-concrete/cex3/main.c new file mode 100644 index 000000000..952a3137b --- /dev/null +++ b/regression/spurious-check-concrete/cex3/main.c @@ -0,0 +1,16 @@ + +#include + +int foo(int x) +{ + assert(x!=5); +} + +int main(int argc, char** argv) +{ + int y; + if(y > 0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex3/test.desc b/regression/spurious-check-concrete/cex3/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex4/main.c b/regression/spurious-check-concrete/cex4/main.c new file mode 100644 index 000000000..8463fe610 --- /dev/null +++ b/regression/spurious-check-concrete/cex4/main.c @@ -0,0 +1,11 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 5; + assert(y != 5); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex4/test.desc b/regression/spurious-check-concrete/cex4/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex5/main.c b/regression/spurious-check-concrete/cex5/main.c new file mode 100644 index 000000000..0eabc5796 --- /dev/null +++ b/regression/spurious-check-concrete/cex5/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>0) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex5/test.desc b/regression/spurious-check-concrete/cex5/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex6/main.c b/regression/spurious-check-concrete/cex6/main.c new file mode 100644 index 000000000..80de0138d --- /dev/null +++ b/regression/spurious-check-concrete/cex6/main.c @@ -0,0 +1,20 @@ + +#include + +void foo(int x) +{ + assert(x<1); +} + +int main() +{ + int y; + + while(y < 2){ + y++; + foo(y); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex6/test.desc b/regression/spurious-check-concrete/cex6/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex7/main.c b/regression/spurious-check-concrete/cex7/main.c new file mode 100644 index 000000000..8ab3e91f9 --- /dev/null +++ b/regression/spurious-check-concrete/cex7/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<4); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/cex7/test.desc b/regression/spurious-check-concrete/cex7/test.desc new file mode 100644 index 000000000..696fce3b5 --- /dev/null +++ b/regression/spurious-check-concrete/cex7/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 3 +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex8/main.c b/regression/spurious-check-concrete/cex8/main.c new file mode 100644 index 000000000..3bb7e471c --- /dev/null +++ b/regression/spurious-check-concrete/cex8/main.c @@ -0,0 +1,20 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex8/test.desc b/regression/spurious-check-concrete/cex8/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/cex9/main.c b/regression/spurious-check-concrete/cex9/main.c new file mode 100644 index 000000000..675f72f26 --- /dev/null +++ b/regression/spurious-check-concrete/cex9/main.c @@ -0,0 +1,24 @@ +#include + +int error(int k){ + assert(k != 2); +} + +int inc(int x){ + return (x+1); +}; + +int inc_copy(int x){ + return (x+1); +}; + +int main(){ + int num = 0; + num = inc_copy(num); + num = inc(num); + error(num); + return 0; +} + + + diff --git a/regression/spurious-check-concrete/cex9/test.desc b/regression/spurious-check-concrete/cex9/test.desc new file mode 100644 index 000000000..429367b44 --- /dev/null +++ b/regression/spurious-check-concrete/cex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=10$ +^SIGNAL=0$ +^VERIFICATION FAILED$ diff --git a/regression/spurious-check-concrete/nocex1/main.c b/regression/spurious-check-concrete/nocex1/main.c new file mode 100644 index 000000000..6faa3e65d --- /dev/null +++ b/regression/spurious-check-concrete/nocex1/main.c @@ -0,0 +1,15 @@ +#include + +int foo(int x) +{ + assert(x>5); +} + +int main(int argc, char** argv) +{ + int y; + if(y>10) foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex1/test.desc b/regression/spurious-check-concrete/nocex1/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex1/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex10/main.c b/regression/spurious-check-concrete/nocex10/main.c new file mode 100644 index 000000000..5e3d522a4 --- /dev/null +++ b/regression/spurious-check-concrete/nocex10/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10); + + assert(x==11); +} diff --git a/regression/spurious-check-concrete/nocex10/test.desc b/regression/spurious-check-concrete/nocex10/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-concrete/nocex10/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/nocex2/main.c b/regression/spurious-check-concrete/nocex2/main.c new file mode 100644 index 000000000..100ddaec6 --- /dev/null +++ b/regression/spurious-check-concrete/nocex2/main.c @@ -0,0 +1,16 @@ + +#include + +void foo(int x) +{ + assert(x==5); +} + +int main(int argc, char** argv) +{ + int y = 5; + foo(y); + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex2/test.desc b/regression/spurious-check-concrete/nocex2/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex3/main.c b/regression/spurious-check-concrete/nocex3/main.c new file mode 100644 index 000000000..257e8fc7a --- /dev/null +++ b/regression/spurious-check-concrete/nocex3/main.c @@ -0,0 +1,18 @@ + +#include + +int bar(){ + return 1; +} + +void foo(int x) { + assert(x != 5); +} + +int main() { + int y = bar(); + foo(y); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex3/test.desc b/regression/spurious-check-concrete/nocex3/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex3/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex4/main.c b/regression/spurious-check-concrete/nocex4/main.c new file mode 100644 index 000000000..4e96e8e89 --- /dev/null +++ b/regression/spurious-check-concrete/nocex4/main.c @@ -0,0 +1,20 @@ + +#include + +int bar(int k){ + return (k+1); +} + +void foo(int x) { + x = bar(x); + assert(x>5); +} + +int main() { + int y; + __CPROVER_assume(y<100000); + if(y > 10) foo(y); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex4/test.desc b/regression/spurious-check-concrete/nocex4/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex4/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex5/main.c b/regression/spurious-check-concrete/nocex5/main.c new file mode 100644 index 000000000..b225c7ce8 --- /dev/null +++ b/regression/spurious-check-concrete/nocex5/main.c @@ -0,0 +1,30 @@ + +#include + +int foobar(int a){ + int i; + if(i < a) + return i+a; + else + return i-a; +} + +int bar(int k){ + return (k+1); +} + +void foo(int x, int s) { + int m; + s = foobar(m); + x = bar(x); + assert(x>5); +} + +int main() { + int y,k; + __CPROVER_assume(y<100000); + if(y > 10) foo(y,k); + return 0; +} + + diff --git a/regression/spurious-check-concrete/nocex5/test.desc b/regression/spurious-check-concrete/nocex5/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex5/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex6/main.c b/regression/spurious-check-concrete/nocex6/main.c new file mode 100644 index 000000000..309c06dd9 --- /dev/null +++ b/regression/spurious-check-concrete/nocex6/main.c @@ -0,0 +1,15 @@ + +#include + +int main(int argc, char** argv) +{ + int y = 1; + + while(y<30){ + y++; + assert(y<5); + } + + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex6/test.desc b/regression/spurious-check-concrete/nocex6/test.desc new file mode 100644 index 000000000..1f240717a --- /dev/null +++ b/regression/spurious-check-concrete/nocex6/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --unwind 2 +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/regression/spurious-check-concrete/nocex8/main.c b/regression/spurious-check-concrete/nocex8/main.c new file mode 100644 index 000000000..d9ee4be0c --- /dev/null +++ b/regression/spurious-check-concrete/nocex8/main.c @@ -0,0 +1,16 @@ + +#include + +void fail(void) +{ + assert(0); +} + +int main(void) +{ + int tmp = 0; + if(tmp) + fail(); + return 0; +} + diff --git a/regression/spurious-check-concrete/nocex8/test.desc b/regression/spurious-check-concrete/nocex8/test.desc new file mode 100644 index 000000000..dc053e6ab --- /dev/null +++ b/regression/spurious-check-concrete/nocex8/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/spurious-check-concrete/nocex9/main.c b/regression/spurious-check-concrete/nocex9/main.c new file mode 100644 index 000000000..47067bc2f --- /dev/null +++ b/regression/spurious-check-concrete/nocex9/main.c @@ -0,0 +1,11 @@ +#include + +void main() +{ + int x = 0; + int y = 0; + + while(y<10) { x++; } + + assert(x==11); +} diff --git a/regression/spurious-check-concrete/nocex9/test.desc b/regression/spurious-check-concrete/nocex9/test.desc new file mode 100644 index 000000000..329fdf02a --- /dev/null +++ b/regression/spurious-check-concrete/nocex9/test.desc @@ -0,0 +1,6 @@ +CORE +main.c +--havoc --no-propagation +^EXIT=5$ +^SIGNAL=0$ +^VERIFICATION INCONCLUSIVE$ diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp index 790102fef..2500ecce8 100644 --- a/src/summarizer/summary_checker_kind.cpp +++ b/src/summarizer/summary_checker_kind.cpp @@ -62,6 +62,7 @@ property_checkert::resultt summary_checker_kindt::operator()( if((options.get_option("spurious-check") != "concrete") && (options.get_option("spurious-check") != "abstract")) { + //TODO: update only those that were refined by unwinding SSA_dependency_graphs(goto_model, ns); } } From eeee97a77bd2ca2a1ffa2f73cc174f8575f3e98f Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 17 Sep 2016 17:29:59 +0100 Subject: [PATCH 77/90] partial upgrade to new cbmc version --- src/Makefile | 10 +- src/domains/util.cpp | 2 +- src/ssa/ssa_value_set.cpp | 6 +- src/summarizer/Makefile | 11 +- src/summarizer/preprocessing_util.cpp | 252 ++++++++++---------- src/summarizer/summarizer_parse_options.cpp | 194 +++++++-------- src/summarizer/summarizer_parse_options.h | 5 +- src/summarizer/summary_db.cpp | 2 +- 8 files changed, 243 insertions(+), 239 deletions(-) diff --git a/src/Makefile b/src/Makefile index a6060ad36..4032fef26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,7 @@ include config.inc -SUBDIRS = summarizer ssa solver domains functions +SUBDIRS = summarizer ssa domains +#solver functions all: summarizer #deltagit @@ -13,10 +14,9 @@ $(SUBDIRS): # Dependencies -summarizer: domains ssa solver -deltacheck: ssa solver functions html -deltagit: html -summarizer: ssa solver functions domains +summarizer: domains ssa +#deltacheck: ssa solver functions html +#deltagit: html clean: $(patsubst %, %_clean, $(SUBDIRS)) diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 97e856df5..ab34a1263 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -157,7 +157,7 @@ void extend_expr_types(exprt &expr) } else assert(false); } - std::cerr << "failed expr: " << expr << std::endl; + std::cerr << "failed expr: " << expr.pretty() << std::endl; assert(false); } diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index 950de1f3f..c3522207a 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -251,7 +251,7 @@ void ssa_value_domaint::assign_rhs_rec( { mp_integer pointer_offset=pointer_offset_size(it->type().subtype(), ns); if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); } } @@ -263,7 +263,7 @@ void ssa_value_domaint::assign_rhs_rec( { mp_integer pointer_offset=pointer_offset_size(rhs.type().subtype(), ns); if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); } } @@ -341,7 +341,7 @@ void ssa_value_domaint::assign_rhs_rec_address_of( offset=true; mp_integer pointer_offset=pointer_offset_size(rhs.type(), ns); if(pointer_offset<1) pointer_offset=1; - a=merge_alignment(a, integer2long(pointer_offset)); + a=merge_alignment(a, integer2ulong(pointer_offset)); } assign_rhs_rec_address_of(dest, to_index_expr(rhs).array(), ns, offset, a); diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 07ae39e51..ff5758c4a 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -1,6 +1,6 @@ include ../config.inc -SRC = summarizer_base.cpp \ +SRC = summarizer_base.cpp summarizer_languages.cpp \ summarizer_fw.cpp summarizer_bw.cpp \ summarizer_fw_term.cpp summarizer_bw_term.cpp \ summarizer_fw_contexts.cpp \ @@ -11,7 +11,8 @@ SRC = summarizer_base.cpp \ cover_goals_ext.cpp horn_encoding.cpp \ summary_db.cpp summary.cpp ssa_db.cpp \ array_abstraction.cpp preprocessing_util.cpp \ - instrument_goto.cpp function_signature.cpp + instrument_goto.cpp +#function_signature.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ @@ -29,6 +30,7 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ DELTA_OBJ+=\ $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ + $(CBMC)/src/goto-instrument/loop_utils$(OBJEXT) \ ../ssa/local_ssa$(OBJEXT) \ ../ssa/malloc_ssa$(OBJEXT) \ ../ssa/ssa_domain$(OBJEXT) \ @@ -44,11 +46,6 @@ DELTA_OBJ+=\ ../ssa/ssa_unwinder$(OBJEXT)\ ../ssa/unwindable_local_ssa$(OBJEXT)\ ../ssa/ssa_value_set$(OBJEXT) \ - ../functions/summary$(OBJEXT) \ - ../functions/get_function$(OBJEXT) \ - ../functions/path_util$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../functions/index$(OBJEXT) \ ../domains/fixed_point$(OBJEXT) \ ../domains/ssa_fixed_point$(OBJEXT) \ ../domains/tpolyhedra_domain$(OBJEXT) \ diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp index beb1b3516..c07175d5a 100644 --- a/src/summarizer/preprocessing_util.cpp +++ b/src/summarizer/preprocessing_util.cpp @@ -1,12 +1,13 @@ #include #include #include -#include #include +#include #include "summarizer_parse_options.h" + /*******************************************************************\ Function: summarizer_parse_optionst::inline_main @@ -95,13 +96,13 @@ void summarizer_parse_optionst::nondet_locals(goto_modelt &goto_model) { if(i_it->is_decl()) { - const code_declt& decl = to_code_decl(i_it->code); + const code_declt& decl = to_code_decl(i_it->code); side_effect_expr_nondett nondet(decl.symbol().type()); - goto_programt::targett t = f_it->second.body.insert_after(i_it); - t->make_assignment(); - code_assignt c(decl.symbol(),nondet); - t->code.swap(c); - t->source_location = i_it->source_location; + goto_programt::targett t = f_it->second.body.insert_after(i_it); + t->make_assignment(); + code_assignt c(decl.symbol(),nondet); + t->code.swap(c); + t->source_location = i_it->source_location; } } } @@ -120,61 +121,64 @@ Function: goto_unwind \*******************************************************************/ -void summarizer_parse_optionst::goto_unwind(goto_modelt &goto_model, unsigned k) +void summarizer_parse_optionst::unwind(goto_modelt &goto_model, unsigned k) { - - typedef std::vector > loopst; +goto_unwind(goto_model.goto_functions, k); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - goto_programt &body=f_it->second.body; +#if 0 +typedef std::vector > loopst; + +Forall_goto_functions(f_it, goto_model.goto_functions) +{ +goto_programt &body=f_it->second.body; - loopst loops; - Forall_goto_program_instructions(i_it, body) - { - if(i_it->is_backwards_goto()) - { - goto_programt::targett loop_head = i_it->get_target(); - goto_programt::targett loop_exit = i_it; - bool has_goto_into_loop = false; +loopst loops; +Forall_goto_program_instructions(i_it, body) +{ +if(i_it->is_backwards_goto()) +{ +goto_programt::targett loop_head = i_it->get_target(); +goto_programt::targett loop_exit = i_it; +bool has_goto_into_loop = false; - goto_programt::targett it = loop_head; - if(it!=loop_exit) it++; - for(; it!=loop_exit; it++) - { - for( std::set::iterator - s_it = it->incoming_edges.begin(); - s_it!=it->incoming_edges.end(); ++s_it) - { - if((*s_it)->is_goto() && - (*s_it)->location_number < loop_head->location_number) - { - has_goto_into_loop = true; - break; - } - } - if(has_goto_into_loop) break; - } - if(has_goto_into_loop) - { - status() << "Unwinding jump into loop" << eom; - loops.push_back(loopst::value_type(++loop_exit,loop_head)); - } - } - } +goto_programt::targett it = loop_head; +if(it!=loop_exit) it++; +for(; it!=loop_exit; it++) +{ +for( std::set::iterator +s_it = it->incoming_edges.begin(); +s_it!=it->incoming_edges.end(); ++s_it) +{ +if((*s_it)->is_goto() && +(*s_it)->location_number < loop_head->location_number) +{ +has_goto_into_loop = true; +break; +} +} +if(has_goto_into_loop) break; +} +if(has_goto_into_loop) +{ +status() << "Unwinding jump into loop" << eom; +loops.push_back(loopst::value_type(++loop_exit,loop_head)); +} +} +} - for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) - { - std::vector iteration_points; - unwind(body,l_it->second,l_it->first,k,iteration_points); - assert(iteration_points.size()==2); - goto_programt::targett t=body.insert_before(l_it->first); - t->make_goto(); - t->targets.push_back(iteration_points.front()); - } - } - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); +for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) +{ +std::vector iteration_points; +unwind(body,l_it->second,l_it->first,k,iteration_points); +assert(iteration_points.size()==2); +goto_programt::targett t=body.insert_before(l_it->first); +t->make_goto(); +t->targets.push_back(iteration_points.front()); +} +} +goto_model.goto_functions.update(); +goto_model.goto_functions.compute_loop_numbers(); +#endif } /*******************************************************************\ @@ -191,75 +195,75 @@ Function: remove_multiple_dereferences void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen) { - if(expr.id()==ID_member) - { - member_exprt &member_expr = to_member_expr(expr); - if(member_expr.compound().id()==ID_dereference) - { - dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); - remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); - if(deref_seen) - { - symbolt new_symbol; - new_symbol.type=member_expr.type(); - new_symbol.name="$deref"+i2string(var_counter++); - new_symbol.base_name=new_symbol.name; - new_symbol.pretty_name=new_symbol.name; - goto_model.symbol_table.add(new_symbol); - goto_programt::targett t_new = body.insert_before(t); - t_new->make_assignment(); - t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); - expr = new_symbol.symbol_expr(); - for(std::set::iterator t_it = - t->incoming_edges.begin(); - t_it != t->incoming_edges.end(); ++t_it) - { - (*t_it)->targets.clear(); - (*t_it)->targets.push_back(t_new); - } - body.compute_location_numbers(); - body.compute_target_numbers(); - body.compute_incoming_edges(); - } - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); +if(expr.id()==ID_member) +{ +member_exprt &member_expr = to_member_expr(expr); +if(member_expr.compound().id()==ID_dereference) +{ +dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); +remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); +if(deref_seen) +{ +symbolt new_symbol; +new_symbol.type=member_expr.type(); +new_symbol.name="$deref"+i2string(var_counter++); +new_symbol.base_name=new_symbol.name; +new_symbol.pretty_name=new_symbol.name; +goto_model.symbol_table.add(new_symbol); +goto_programt::targett t_new = body.insert_before(t); +t_new->make_assignment(); +t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); +expr = new_symbol.symbol_expr(); +for(std::set::iterator t_it = + t->incoming_edges.begin(); +t_it != t->incoming_edges.end(); ++t_it) +{ +(*t_it)->targets.clear(); +(*t_it)->targets.push_back(t_new); +} +body.compute_location_numbers(); +body.compute_target_numbers(); +body.compute_incoming_edges(); +} +} +else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); +} +else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); } void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) { - unsigned var_counter = 0; - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(i_it->is_goto()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - i_it->guard, - var_counter, false); - } - else if(i_it->is_assign()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).lhs(), - var_counter, false); - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).rhs(), - var_counter, false); - } - } - } +unsigned var_counter = 0; +namespacet ns(goto_model.symbol_table); +Forall_goto_functions(f_it, goto_model.goto_functions) +{ +Forall_goto_program_instructions(i_it, f_it->second.body) +{ +if(i_it->is_goto()) +{ +remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, false); +} +else if(i_it->is_assign()) +{ +remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); +remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); +} +} +} } diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index 492c3d1aa..7b4bc4a1d 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -70,8 +70,9 @@ Function: summarizer_parse_optionst::summarizer_parse_optionst \*******************************************************************/ summarizer_parse_optionst::summarizer_parse_optionst(int argc, const char **argv): - parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit("2LS " CBMC_VERSION, cmdline) +parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), + language_uit(cmdline, ui_message_handler), + ui_message_handler(cmdline, "2LS " SUMMARIZER_VERSION) { } @@ -278,18 +279,18 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) if(cmdline.isset("lexicographic-ranking-function")) { options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); + cmdline.get_value("lexicographic-ranking-function")); } else options.set_option("lexicographic-ranking-function",3); if(cmdline.isset("max-inner-ranking-iterations")) { options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); + cmdline.get_value("max-inner-ranking-iterations")); } else options.set_option("max-inner-ranking-iterations",20); - // do k-induction refinement + // do k-induction refinement if(cmdline.isset("k-induction")) { options.set_option("std-invariants", true); @@ -299,7 +300,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("unwind",UINT_MAX); } - // do incremental bmc + // do incremental bmc if(cmdline.isset("incremental-bmc")) { options.set_option("incremental-bmc", true); @@ -337,7 +338,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) // instrumentation / output if(cmdline.isset("instrument-output")) options.set_option("instrument-output", - cmdline.get_value("instrument-output")); + cmdline.get_value("instrument-output")); #ifdef SHOW_CALLING_CONTEXTS @@ -347,7 +348,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) throw "--show-calling-contexts only possible with --intervals"; options.set_option("show-calling-contexts", true); options.set_option("do-not-analyze-functions", - cmdline.get_value("show-calling-contexts")); + cmdline.get_value("show-calling-contexts")); } #endif @@ -393,11 +394,10 @@ int summarizer_parse_optionst::doit() // status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; - register_language(new_ansi_c_language); - register_language(new_cpp_language); - goto_modelt goto_model; + register_languages(); + if(get_goto_program(options, goto_model)) return 6; @@ -498,13 +498,13 @@ int summarizer_parse_optionst::doit() summary_checker_baset *summary_checker = NULL; if(!options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_ait(options); + summary_checker = new summary_checker_ait(options); if(options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_kindt(options); + summary_checker = new summary_checker_kindt(options); if(!options.get_bool_option("k-induction") && options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_bmct(options); + summary_checker = new summary_checker_bmct(options); summary_checker->set_message_handler(get_message_handler()); summary_checker->simplify=!cmdline.isset("no-simplify"); @@ -533,11 +533,11 @@ int summarizer_parse_optionst::doit() } else { - #ifdef _MSC_VER +#ifdef _MSC_VER std::ofstream out(widen(out_file).c_str()); - #else +#else std::ofstream out(out_file.c_str()); - #endif +#endif if(!out) { @@ -606,20 +606,20 @@ int summarizer_parse_optionst::doit() return 8; } - #if 0 +#if 0 // let's log some more statistics debug() << "Memory consumption:" << messaget::endl; memory_info(debug()); debug() << eom; - #endif +#endif } void summarizer_parse_optionst::type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns) + const typet &type, + expr_statst &stats, + const namespacet &ns) { if(type.id()==ID_symbol) @@ -662,8 +662,8 @@ Function: summarizer_parse_optionst::expr_stats_rec void summarizer_parse_optionst::expr_stats_rec( - const exprt &expr, - expr_statst &stats) + const exprt &expr, + expr_statst &stats) { if(expr.id()==ID_side_effect) @@ -709,7 +709,7 @@ Function: summarizer_parse_optionst::show_stats \*******************************************************************/ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) + std::ostream &out) { const namespacet ns(goto_model.symbol_table); @@ -735,9 +735,9 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, #endif for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) { nr_instructions++; const goto_programt::instructiont &instruction=*i_it; @@ -746,32 +746,32 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, switch(instruction.type) { - case ASSIGN: - { - const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); - } - break; - case ASSUME: - expr_stats_rec(instruction.guard, stats); - break; - case ASSERT: - expr_stats_rec(instruction.guard, stats); - break; - case GOTO: - expr_stats_rec(instruction.guard, stats); - break; + case ASSIGN: + { + const code_assignt &assign=to_code_assign(instruction.code); + expr_stats_rec(assign.lhs(), stats); + expr_stats_rec(assign.rhs(), stats); + } + break; + case ASSUME: + expr_stats_rec(instruction.guard, stats); + break; + case ASSERT: + expr_stats_rec(instruction.guard, stats); + break; + case GOTO: + expr_stats_rec(instruction.guard, stats); + break; - case DECL: - // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); + case DECL: + // someone declaring an array + type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - break; + break; - default: - // skip - break; + default: + // skip + break; } // switch } // forall instructions } // forall functions @@ -877,7 +877,7 @@ bool summarizer_parse_optionst::get_goto_program( status() << "Reading GOTO program from file" << eom; if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) + goto_model, get_message_handler())) return true; config.set_from_symbol_table(goto_model.symbol_table); @@ -898,11 +898,11 @@ bool summarizer_parse_optionst::get_goto_program( std::string filename=cmdline.args[0]; - #ifdef _MSC_VER +#ifdef _MSC_VER std::ifstream infile(widen(filename).c_str()); - #else +#else std::ifstream infile(filename.c_str()); - #endif +#endif if(!infile) { @@ -947,7 +947,7 @@ bool summarizer_parse_optionst::get_goto_program( return true; } - #if 0 +#if 0 irep_idt entry_point=goto_model.goto_functions.entry_point(); if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) @@ -955,7 +955,7 @@ bool summarizer_parse_optionst::get_goto_program( error() << "No entry point; please provide a main function" << eom; return true; } - #endif +#endif status() << "Generating GOTO Program" << eom; @@ -1026,9 +1026,9 @@ bool summarizer_parse_optionst::process_goto_program( Forall_goto_functions(f_it, goto_model.goto_functions) if(f_it->first!=ID__start && f_it->second.body.instructions.size()<=2*(limit/2)) - { + { f_it->second.body.clear(); - } + } } // add generic checks @@ -1044,7 +1044,7 @@ bool summarizer_parse_optionst::process_goto_program( #if UNWIND_GOTO_INTO_LOOP - goto_unwind(goto_model,2); + unwind(goto_model,2); #endif remove_skip(goto_model.goto_functions); @@ -1057,7 +1057,7 @@ bool summarizer_parse_optionst::process_goto_program( if(goto_inline(goto_model, ui_message_handler)) { report_unknown(); - return 5; + return 5; } } @@ -1068,7 +1068,7 @@ bool summarizer_parse_optionst::process_goto_program( nondet_locals(goto_model); #if 1 - //TODO: find a better place for that + //TODO: find a better place for that replace_malloc(goto_model,""); #endif @@ -1178,7 +1178,7 @@ void summarizer_parse_optionst::report_properties( const property_checkert::property_mapt &property_map) { for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { @@ -1210,8 +1210,8 @@ void summarizer_parse_optionst::report_properties( if(cmdline.isset("json-cex") && it->second.result==property_checkert::FAIL) output_json_cex(options, - goto_model, it->second.error_trace, - id2string(it->first)); + goto_model, it->second.error_trace, + id2string(it->first)); } if(!cmdline.isset("property")) @@ -1222,7 +1222,7 @@ void summarizer_parse_optionst::report_properties( unsigned unknown=0; for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { @@ -1263,13 +1263,13 @@ void summarizer_parse_optionst::report_success() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="SUCCESS"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="SUCCESS"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1302,12 +1302,12 @@ void summarizer_parse_optionst::show_counterexample( break; case ui_message_handlert::XML_UI: - { - xmlt xml; - convert(ns, error_trace, xml); - std::cout << xml << std::endl; - } - break; + { + xmlt xml; + convert(ns, error_trace, xml); + std::cout << xml << std::endl; + } + break; default: assert(false); @@ -1406,13 +1406,13 @@ void summarizer_parse_optionst::report_failure() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="FAILURE"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="FAILURE"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1441,13 +1441,13 @@ void summarizer_parse_optionst::report_unknown() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="UNKNOWN"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="UNKNOWN"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1501,12 +1501,12 @@ void summarizer_parse_optionst::help() " --show-symbol-table show symbol table\n" " --show-goto-functions show goto program\n" " --arch set architecture (default: " - << configt::this_architecture() << ")\n" + << configt::this_architecture() << ")\n" " --os set operating system (default: " - << configt::this_operating_system() << ")\n" - #ifdef _WIN32 + << configt::this_operating_system() << ")\n" +#ifdef _WIN32 " --gcc use GCC as preprocessor\n" - #endif +#endif " --no-arch don't set up an architecture\n" " --no-library disable built-in abstract C library\n" " --round-to-nearest IEEE floating point rounding mode (default)\n" diff --git a/src/summarizer/summarizer_parse_options.h b/src/summarizer/summarizer_parse_options.h index 944755af9..8e9e821f7 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/summarizer/summarizer_parse_options.h @@ -73,6 +73,9 @@ class summarizer_parse_optionst: const std::string &extra_options); protected: + ui_message_handlert ui_message_handler; + virtual void register_languages(); + void get_command_line_options(optionst &options); bool get_goto_program( @@ -145,7 +148,7 @@ class summarizer_parse_optionst: void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void goto_unwind(goto_modelt &goto_model, unsigned k); + void unwind(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, bool &valid); diff --git a/src/summarizer/summary_db.cpp b/src/summarizer/summary_db.cpp index 4e7dde39e..7517780a7 100644 --- a/src/summarizer/summary_db.cpp +++ b/src/summarizer/summary_db.cpp @@ -62,7 +62,7 @@ void summary_dbt::read(const std::string &id) { current=id; - summary=jsont::json_object(); + summary.make_object(); parse_json(file_name(id), get_message_handler(), summary); } From 2d17dbb554f457e5c1c61c7228d4506a61144023 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 17 Sep 2016 20:40:41 +0100 Subject: [PATCH 78/90] soon compiles with CBMC master --- install.sh | 4 +- .../interprocedural/partialinline1/test.desc | 2 +- .../interprocedural/partialinline2/test.desc | 2 +- .../precond_contextsensitive2/test.desc | 2 +- src/summarizer/preprocessing_util.cpp | 236 +++++++++--------- src/summarizer/summarizer_parse_options.cpp | 2 +- src/summarizer/summarizer_parse_options.h | 2 +- 7 files changed, 123 insertions(+), 127 deletions(-) diff --git a/install.sh b/install.sh index af86688af..9c2717e29 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,7 @@ -git clone https://github.com/diffblue/cbmc +git clone https://github.com/peterschrammel/cbmc cd cbmc CBMC=`pwd` -git checkout e4a5a611f569c72f97c0e099e56a827c9a55d2aa +git checkout e347c69bb1071f174f611278f5e001fd6070884a cd src make minisat2-download make diff --git a/regression/interprocedural/partialinline1/test.desc b/regression/interprocedural/partialinline1/test.desc index 46a1a642f..0eb76f70f 100644 --- a/regression/interprocedural/partialinline1/test.desc +++ b/regression/interprocedural/partialinline1/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 5 +--inline-partial 7 ^EXIT=10$ ^SIGNAL=0$ ^** 1 of 2 failed$ diff --git a/regression/interprocedural/partialinline2/test.desc b/regression/interprocedural/partialinline2/test.desc index caccafb2c..7e9e73c85 100644 --- a/regression/interprocedural/partialinline2/test.desc +++ b/regression/interprocedural/partialinline2/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 6 +--inline-partial 8 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/preconditions/precond_contextsensitive2/test.desc b/regression/preconditions/precond_contextsensitive2/test.desc index 5bd0c35e6..f757b9ab1 100644 --- a/regression/preconditions/precond_contextsensitive2/test.desc +++ b/regression/preconditions/precond_contextsensitive2/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[main\]: argc <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc) <= -2$ +^\[_start\]: argc' <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc') <= -2$ diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp index c07175d5a..0be64ff76 100644 --- a/src/summarizer/preprocessing_util.cpp +++ b/src/summarizer/preprocessing_util.cpp @@ -121,64 +121,60 @@ Function: goto_unwind \*******************************************************************/ -void summarizer_parse_optionst::unwind(goto_modelt &goto_model, unsigned k) +void summarizer_parse_optionst::unwind_goto_into_loop(goto_modelt &goto_model, unsigned k) { -goto_unwind(goto_model.goto_functions, k); + typedef std::vector > loopst; -#if 0 -typedef std::vector > loopst; - -Forall_goto_functions(f_it, goto_model.goto_functions) -{ -goto_programt &body=f_it->second.body; + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; -loopst loops; -Forall_goto_program_instructions(i_it, body) -{ -if(i_it->is_backwards_goto()) -{ -goto_programt::targett loop_head = i_it->get_target(); -goto_programt::targett loop_exit = i_it; -bool has_goto_into_loop = false; + loopst loops; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_backwards_goto()) + { + goto_programt::targett loop_head = i_it->get_target(); + goto_programt::targett loop_exit = i_it; + bool has_goto_into_loop = false; -goto_programt::targett it = loop_head; -if(it!=loop_exit) it++; -for(; it!=loop_exit; it++) -{ -for( std::set::iterator -s_it = it->incoming_edges.begin(); -s_it!=it->incoming_edges.end(); ++s_it) -{ -if((*s_it)->is_goto() && -(*s_it)->location_number < loop_head->location_number) -{ -has_goto_into_loop = true; -break; -} -} -if(has_goto_into_loop) break; -} -if(has_goto_into_loop) -{ -status() << "Unwinding jump into loop" << eom; -loops.push_back(loopst::value_type(++loop_exit,loop_head)); -} -} -} + goto_programt::targett it = loop_head; + if(it!=loop_exit) it++; + for(; it!=loop_exit; it++) + { + for( std::set::iterator + s_it = it->incoming_edges.begin(); + s_it!=it->incoming_edges.end(); ++s_it) + { + if((*s_it)->is_goto() && + (*s_it)->location_number < loop_head->location_number) + { + has_goto_into_loop = true; + break; + } + } + if(has_goto_into_loop) break; + } + if(has_goto_into_loop) + { + status() << "Unwinding jump into loop" << eom; + loops.push_back(loopst::value_type(++loop_exit,loop_head)); + } + } + } -for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) -{ -std::vector iteration_points; -unwind(body,l_it->second,l_it->first,k,iteration_points); -assert(iteration_points.size()==2); -goto_programt::targett t=body.insert_before(l_it->first); -t->make_goto(); -t->targets.push_back(iteration_points.front()); -} -} -goto_model.goto_functions.update(); -goto_model.goto_functions.compute_loop_numbers(); -#endif + for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) + { + std::vector iteration_points; + unwind(body,l_it->second,l_it->first,k,iteration_points); + assert(iteration_points.size()==2); + goto_programt::targett t=body.insert_before(l_it->first); + t->make_goto(); + t->targets.push_back(iteration_points.front()); + } + } + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); } /*******************************************************************\ @@ -195,75 +191,75 @@ Function: remove_multiple_dereferences void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen) { -if(expr.id()==ID_member) -{ -member_exprt &member_expr = to_member_expr(expr); -if(member_expr.compound().id()==ID_dereference) -{ -dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); -remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); -if(deref_seen) -{ -symbolt new_symbol; -new_symbol.type=member_expr.type(); -new_symbol.name="$deref"+i2string(var_counter++); -new_symbol.base_name=new_symbol.name; -new_symbol.pretty_name=new_symbol.name; -goto_model.symbol_table.add(new_symbol); -goto_programt::targett t_new = body.insert_before(t); -t_new->make_assignment(); -t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); -expr = new_symbol.symbol_expr(); -for(std::set::iterator t_it = - t->incoming_edges.begin(); -t_it != t->incoming_edges.end(); ++t_it) -{ -(*t_it)->targets.clear(); -(*t_it)->targets.push_back(t_new); -} -body.compute_location_numbers(); -body.compute_target_numbers(); -body.compute_incoming_edges(); -} -} -else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); -} -else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); + if(expr.id()==ID_member) + { + member_exprt &member_expr = to_member_expr(expr); + if(member_expr.compound().id()==ID_dereference) + { + dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); + remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); + if(deref_seen) + { + symbolt new_symbol; + new_symbol.type=member_expr.type(); + new_symbol.name="$deref"+i2string(var_counter++); + new_symbol.base_name=new_symbol.name; + new_symbol.pretty_name=new_symbol.name; + goto_model.symbol_table.add(new_symbol); + goto_programt::targett t_new = body.insert_before(t); + t_new->make_assignment(); + t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); + expr = new_symbol.symbol_expr(); + for(std::set::iterator t_it = + t->incoming_edges.begin(); + t_it != t->incoming_edges.end(); ++t_it) + { + (*t_it)->targets.clear(); + (*t_it)->targets.push_back(t_new); + } + body.compute_location_numbers(); + body.compute_target_numbers(); + body.compute_incoming_edges(); + } + } + else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); + } + else + Forall_operands(o_it,expr) + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); } void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) { -unsigned var_counter = 0; -namespacet ns(goto_model.symbol_table); -Forall_goto_functions(f_it, goto_model.goto_functions) -{ -Forall_goto_program_instructions(i_it, f_it->second.body) -{ -if(i_it->is_goto()) -{ -remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - i_it->guard, - var_counter, false); -} -else if(i_it->is_assign()) -{ -remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).lhs(), - var_counter, false); -remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).rhs(), - var_counter, false); -} -} -} + unsigned var_counter = 0; + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_goto()) + { + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, false); + } + else if(i_it->is_assign()) + { + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); + } + } + } } diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index 7b4bc4a1d..100a2e674 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -1044,7 +1044,7 @@ bool summarizer_parse_optionst::process_goto_program( #if UNWIND_GOTO_INTO_LOOP - unwind(goto_model,2); + unwind_goto_into_loop(goto_model,2); #endif remove_skip(goto_model.goto_functions); diff --git a/src/summarizer/summarizer_parse_options.h b/src/summarizer/summarizer_parse_options.h index 8e9e821f7..0b32d24f1 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/summarizer/summarizer_parse_options.h @@ -148,7 +148,7 @@ class summarizer_parse_optionst: void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void unwind(goto_modelt &goto_model, unsigned k); + void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, bool &valid); From af9b84549b2e5bf9187f9f1c0ccdc9c6f5c8d9b3 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 18 Sep 2016 15:56:07 +0100 Subject: [PATCH 79/90] bounds simplification --- src/domains/Makefile | 3 +- src/domains/simplify_bounds.cpp | 454 ++++++++++++++++++++ src/domains/simplify_bounds.h | 32 ++ src/domains/simplify_bounds_class.h | 66 +++ src/domains/simplify_transformer.cpp | 254 +++++++++++ src/domains/simplify_transformer.h | 35 ++ src/domains/simplify_transformer_class.h | 59 +++ src/domains/tpolyhedra_domain.cpp | 3 + src/summarizer/Makefile | 3 +- src/summarizer/summarizer_parse_options.cpp | 2 +- 10 files changed, 908 insertions(+), 3 deletions(-) create mode 100644 src/domains/simplify_bounds.cpp create mode 100644 src/domains/simplify_bounds.h create mode 100644 src/domains/simplify_bounds_class.h create mode 100644 src/domains/simplify_transformer.cpp create mode 100644 src/domains/simplify_transformer.h create mode 100644 src/domains/simplify_transformer_class.h diff --git a/src/domains/Makefile b/src/domains/Makefile index 8ba93df10..65bc40679 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -11,7 +11,8 @@ SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp + strategy_solver_predabs.cpp \ + simplify_transformer.cpp simplify_bounds.cpp #solver_enumeration.cpp include $(CBMC)/src/config.inc diff --git a/src/domains/simplify_bounds.cpp b/src/domains/simplify_bounds.cpp new file mode 100644 index 000000000..cfa64de81 --- /dev/null +++ b/src/domains/simplify_bounds.cpp @@ -0,0 +1,454 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_bounds.h" +#include "simplify_bounds_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_boundst::get_min_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_min_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_min_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_smallest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_min_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_max_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::get_max_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_max_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_max_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_largest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_max_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_min_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::simplify_rec(exprt &expr) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_le) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=true_exprt(); + result=false; + } + else + result=clean_up_typecast(expr,rhs_value); + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_ge) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_lt) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_gt) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else + { + Forall_operands(it, expr) + if(!simplify_rec(*it)) + result=false; + } +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_typecast(exprt &expr, + const mp_integer &rhs_value) +{ + if(expr.id()==ID_le && expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast) + { + const typet &inner_type=expr.op0().op0().op0().type(); + if(-rhs_value>get_smallest(inner_type)) + { + expr=binary_relation_exprt(expr.op0().op0().op0(), ID_ge, + from_integer(-rhs_value,inner_type)); + return true; + } + } + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_implications(exprt &expr) +{ + bool result=true; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(!clean_up_implications(*it)) + result=false; + exprt::operandst::iterator it=expr.operands().begin(); + while(it!=expr.operands().end()) + { + if(it->is_true()) + it=expr.operands().erase(it); + else + ++it; + } + if(expr.operands().empty()) + expr=true_exprt(); + else if(expr.operands().size()==1) + expr=expr.op0(); + } + else if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + { + expr=true_exprt(); + result=false; + } + } + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::regroup_implications(exprt &expr) +{ + std::map implication_map; + exprt::operandst r; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(it->id()==ID_implies) + implication_map[it->op0()].push_back(it->op1()); + else + r.push_back(*it); + } + else + return true; + for(auto &i : implication_map) + r.push_back(implies_exprt(i.first, conjunction(i.second))); + expr=conjunction(r); + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::operator()(exprt &expr) +{ + bool result=simplify_rec(expr); + if(!clean_up_implications(expr)) + result=false; + if(!regroup_implications(expr)) + result=false; + return result; +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_bounds(exprt &expr, + const namespacet &ns) +{ + return simplify_boundst(ns)(expr); +} + +/*******************************************************************\ + +Function: simplify_bounds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_bounds(const exprt &src, + const namespacet &ns) +{ + exprt tmp=src; + simplify_boundst simplify_bounds(ns); + simplify_bounds(tmp); + return tmp; +} + diff --git a/src/domains/simplify_bounds.h b/src/domains/simplify_bounds.h new file mode 100644 index 000000000..6799b76a2 --- /dev/null +++ b/src/domains/simplify_bounds.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_BOUNDS_H +#define CPROVER_SIMPLIFY_BOUNDS_H + +#include + +class exprt; +class namespacet; + +// +// simplify bounds +// +// true: did nothing +// false: simplified something +// + +bool simplify_bounds( + exprt &expr, + const namespacet &ns); + +exprt simplify_bounds( + const exprt &src, + const namespacet &ns); + +#endif diff --git a/src/domains/simplify_bounds_class.h b/src/domains/simplify_bounds_class.h new file mode 100644 index 000000000..3510f42ed --- /dev/null +++ b/src/domains/simplify_bounds_class.h @@ -0,0 +1,66 @@ +/*******************************************************************\ + +Module: Bounds Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_BOUNDS_CLASS_H +#define CPROVER_SIMPLIFY_BOUNDS_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_boundst +{ +public: + explicit simplify_boundst(const namespacet &_ns): + ns(_ns) + { + } + + virtual ~simplify_boundst() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv; + } + inline static mp_integer get_largest(const typet &type) + { + if(type.id()==ID_signedbv) return to_signedbv_type(type).largest(); + else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).largest(); + assert(false); + } + inline static mp_integer get_smallest(const typet &type) + { + if(type.id()==ID_signedbv) return to_signedbv_type(type).smallest(); + else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).smallest(); + assert(false); + } + +protected: + const namespacet &ns; + + bool simplify_rec(exprt &expr); + bool get_min_bound(const exprt &expr, mp_integer &value); + bool get_max_bound(const exprt &expr, mp_integer &value); + + bool clean_up_implications(exprt &expr); + bool regroup_implications(exprt &expr); + bool clean_up_typecast(exprt &expr, const mp_integer &value); + +}; + +#endif diff --git a/src/domains/simplify_transformer.cpp b/src/domains/simplify_transformer.cpp new file mode 100644 index 000000000..87f43673d --- /dev/null +++ b/src/domains/simplify_transformer.cpp @@ -0,0 +1,254 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_transformer.h" +#include "simplify_transformer_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_transformert::collect_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void simplify_transformert::collect_node(const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy) +{ + if(expr.id()==ID_equal) + { + const equal_exprt &e = to_equal_expr(expr); + + bool rhs_is_constant=e.rhs().id()==ID_constant; + bool rhs_is_symbol=e.rhs().id()==ID_symbol || + e.rhs().id()==ID_nondet_symbol; + bool rhs_is_frozen=rhs_is_symbol && + frozen_symbols.find(e.rhs().get(ID_identifier))!=frozen_symbols.end(); + bool lhs_is_constant=e.lhs().id()==ID_constant; + bool lhs_is_symbol=e.lhs().id()==ID_symbol || + e.lhs().id()==ID_nondet_symbol; + bool lhs_is_frozen=lhs_is_symbol && + frozen_symbols.find(e.lhs().get(ID_identifier))!=frozen_symbols.end(); + + exprt lhs, rhs; + lhs.make_nil(); + rhs.make_nil(); + //stupid matching + if((rhs_is_frozen || rhs_is_constant || !frozen_only) && + lhs_is_symbol && !lhs_is_frozen) + { + lhs=e.lhs(); + rhs=e.rhs(); + } + if((lhs_is_frozen || lhs_is_constant || !frozen_only) && + rhs_is_symbol && !rhs_is_frozen) + { + rhs=e.lhs(); + lhs=e.rhs(); + } + if(rhs.is_not_nil() && lhs.is_not_nil()) + { + if(make_copy) //make lazy copy + { + replace_mapt _subst = substitutions; + substitutions = _subst; + } + substitutions[lhs]=rhs; + } + } + +#ifdef DEBUGX + std::cout << "COLLECT: " << from_expr(ns, "", expr) << std::endl; + for(const auto &it : substitutions) + std::cout << from_expr(ns, "", it.first) + << "---> " << from_expr(ns, "", it.second) + << "\n"; +#endif +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_node(exprt &expr, + const replace_mapt &substitutions) +{ + return replace_expr(substitutions, expr); +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_rec(exprt &expr, + replace_mapt &substitutions) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_and) + { + //first propagate from frozen symbols + bool res=false; + do + { + Forall_operands(it, expr) + collect_node(*it, substitutions, true, false); + + Forall_operands(it, expr) + if(!simplify_rec(*it, substitutions)) + result=false; + + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + + //simplify remaining equalities + Forall_operands(it, expr) + collect_node(*it, substitutions, false, false); + + res=false; + do + { + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + } + +#if 0 //for later extension to disjunctions + //TODO: handle negation, too + else if(expr.id()==ID_or || expr.id()==ID_implies) + { + Forall_operands(it, expr) + { + collect_node(*it, substitutions, true); + if(!simplify_rec(*it, substitutions)) + result=false; + + bool res=false; + do + { + res=simplify_node(*it, substitutions); + if(!res) result=false; + } + while(!res); + } + } +#endif + +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_transformert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::operator()(exprt &expr) +{ + replace_mapt substitutions; + return simplify_rec(expr, substitutions); +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify(exprt &expr, + const std::set &frozen_vars, + const namespacet &ns) +{ + return simplify_transformert(ns, frozen_vars)(expr); +} + +/*******************************************************************\ + +Function: simplify_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_transformer(const exprt &src, + const std::set &frozen_vars, + const namespacet &ns) +{ +#ifdef DEBUGX + std::cout << "FROZEN:"; + for(const auto &it : frozen_vars) + std::cout << " " << it; + std::cout << "\n"; +#endif + + exprt tmp=src; + simplify_transformert(ns, frozen_vars)(tmp); + return tmp; +} + diff --git a/src/domains/simplify_transformer.h b/src/domains/simplify_transformer.h new file mode 100644 index 000000000..32a815c8c --- /dev/null +++ b/src/domains/simplify_transformer.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_TRANSFORMER_H +#define CPROVER_SIMPLIFY_TRANSFORMER_H + +#include +#include + +class exprt; +class namespacet; + +// +// simplify transformers by best-effort intermediate variable elimination +// +// true: did nothing +// false: simplified something +// + +bool simplify( + exprt &expr, + const std::set &frozen_vars, //do not eliminate these + const namespacet &ns); + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, //do not eliminate these + const namespacet &ns); + +#endif diff --git a/src/domains/simplify_transformer_class.h b/src/domains/simplify_transformer_class.h new file mode 100644 index 000000000..ccae118b4 --- /dev/null +++ b/src/domains/simplify_transformer_class.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Transformer Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H +#define CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_transformert +{ +public: + explicit simplify_transformert(const namespacet &_ns, + const std::set &_frozen_symbols): + ns(_ns), + frozen_symbols(_frozen_symbols) + { + } + + virtual ~simplify_transformert() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + // main recursion + bool simplify_rec(exprt &expr, replace_mapt &substitutions); + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv; + } + +protected: + const namespacet &ns; + const std::set &frozen_symbols; + + void collect_node( + const exprt &expr, replace_mapt &substitutions, + bool frozen_only, + bool make_copy); + bool simplify_node(exprt &expr, const replace_mapt &substitutions); + +}; + +#endif diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 800ea8ef7..115699565 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -8,6 +8,8 @@ #include #include +#include "simplify_bounds.h" + #define SYMB_BOUND_VAR "symb_bound#" #define ENABLE_HEURISTICS @@ -593,6 +595,7 @@ void tpolyhedra_domaint::project_on_vars(valuet &value, } } result = conjunction(c); + simplify_bounds(result, ns); } /*******************************************************************\ diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index ff5758c4a..2b4bd9dfb 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -70,7 +70,8 @@ DELTA_OBJ+=\ ../domains/ranking_solver_enumeration$(OBJEXT) \ ../domains/linrank_domain$(OBJEXT) \ ../domains/lexlinrank_solver_enumeration$(OBJEXT) \ - ../domains/lexlinrank_domain$(OBJEXT) + ../domains/lexlinrank_domain$(OBJEXT) \ + ../domains/simplify_bounds$(OBJEXT) # ../domains/solver_enumeration$(OBJEXT) # ../domains/strategy_solver_binsearch2$(OBJEXT) diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index 7b4bc4a1d..487f29ef1 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -54,7 +54,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "show.h" #include "horn_encoding.h" -#define UNWIND_GOTO_INTO_LOOP 1 +#define UNWIND_GOTO_INTO_LOOP 0 #define REMOVE_MULTIPLE_DEREFERENCES 1 /*******************************************************************\ From faf3f1850d4ba9f44ef54571cd5fb82aabbcf606 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 17 Sep 2016 20:40:41 +0100 Subject: [PATCH 80/90] merge --- install.sh | 13 ++ .../interprocedural/partialinline1/test.desc | 2 +- .../interprocedural/partialinline2/test.desc | 2 +- .../precond_contextsensitive2/test.desc | 2 +- src/summarizer/preprocessing_util.cpp | 122 +++++++++--------- src/summarizer/summarizer_parse_options.cpp | 7 +- src/summarizer/summarizer_parse_options.h | 2 +- 7 files changed, 82 insertions(+), 68 deletions(-) create mode 100755 install.sh diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..9c2717e29 --- /dev/null +++ b/install.sh @@ -0,0 +1,13 @@ +git clone https://github.com/peterschrammel/cbmc +cd cbmc +CBMC=`pwd` +git checkout e347c69bb1071f174f611278f5e001fd6070884a +cd src +make minisat2-download +make +cd ../../src +cp config.inc.template config.inc +sed -i.bak "s#CBMC = ~/my-cbmc#CBMC = $CBMC#g" config.inc +make +cd .. +echo "The executable is src/summarizer/2ls" diff --git a/regression/interprocedural/partialinline1/test.desc b/regression/interprocedural/partialinline1/test.desc index 46a1a642f..0eb76f70f 100644 --- a/regression/interprocedural/partialinline1/test.desc +++ b/regression/interprocedural/partialinline1/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 5 +--inline-partial 7 ^EXIT=10$ ^SIGNAL=0$ ^** 1 of 2 failed$ diff --git a/regression/interprocedural/partialinline2/test.desc b/regression/interprocedural/partialinline2/test.desc index caccafb2c..7e9e73c85 100644 --- a/regression/interprocedural/partialinline2/test.desc +++ b/regression/interprocedural/partialinline2/test.desc @@ -1,6 +1,6 @@ CORE main.c ---inline-partial 6 +--inline-partial 8 ^EXIT=0$ ^SIGNAL=0$ ^VERIFICATION SUCCESSFUL$ diff --git a/regression/preconditions/precond_contextsensitive2/test.desc b/regression/preconditions/precond_contextsensitive2/test.desc index 5bd0c35e6..f757b9ab1 100644 --- a/regression/preconditions/precond_contextsensitive2/test.desc +++ b/regression/preconditions/precond_contextsensitive2/test.desc @@ -3,4 +3,4 @@ main.c --context-sensitive --preconditions ^EXIT=5$ ^SIGNAL=0$ -^\[main\]: argc <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc) <= -2$ +^\[_start\]: argc' <= 268435456 && -((signed __CPROVER_bitvector\[33\])argc') <= -2$ diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp index beb1b3516..ccc335388 100644 --- a/src/summarizer/preprocessing_util.cpp +++ b/src/summarizer/preprocessing_util.cpp @@ -1,12 +1,13 @@ #include #include #include -#include #include +#include #include "summarizer_parse_options.h" + /*******************************************************************\ Function: summarizer_parse_optionst::inline_main @@ -120,9 +121,8 @@ Function: goto_unwind \*******************************************************************/ -void summarizer_parse_optionst::goto_unwind(goto_modelt &goto_model, unsigned k) +void summarizer_parse_optionst::unwind_goto_into_loop(goto_modelt &goto_model, unsigned k) { - typedef std::vector > loopst; Forall_goto_functions(f_it, goto_model.goto_functions) @@ -136,30 +136,30 @@ void summarizer_parse_optionst::goto_unwind(goto_modelt &goto_model, unsigned k) { goto_programt::targett loop_head = i_it->get_target(); goto_programt::targett loop_exit = i_it; - bool has_goto_into_loop = false; + bool has_goto_into_loop = false; - goto_programt::targett it = loop_head; - if(it!=loop_exit) it++; - for(; it!=loop_exit; it++) - { - for( std::set::iterator - s_it = it->incoming_edges.begin(); - s_it!=it->incoming_edges.end(); ++s_it) - { - if((*s_it)->is_goto() && - (*s_it)->location_number < loop_head->location_number) - { - has_goto_into_loop = true; - break; - } - } - if(has_goto_into_loop) break; - } - if(has_goto_into_loop) - { - status() << "Unwinding jump into loop" << eom; - loops.push_back(loopst::value_type(++loop_exit,loop_head)); - } + goto_programt::targett it = loop_head; + if(it!=loop_exit) it++; + for(; it!=loop_exit; it++) + { + for( std::set::iterator + s_it = it->incoming_edges.begin(); + s_it!=it->incoming_edges.end(); ++s_it) + { + if((*s_it)->is_goto() && + (*s_it)->location_number < loop_head->location_number) + { + has_goto_into_loop = true; + break; + } + } + if(has_goto_into_loop) break; + } + if(has_goto_into_loop) + { + status() << "Unwinding jump into loop" << eom; + loops.push_back(loopst::value_type(++loop_exit,loop_head)); + } } } @@ -200,31 +200,31 @@ void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_m remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); if(deref_seen) { - symbolt new_symbol; - new_symbol.type=member_expr.type(); - new_symbol.name="$deref"+i2string(var_counter++); - new_symbol.base_name=new_symbol.name; - new_symbol.pretty_name=new_symbol.name; - goto_model.symbol_table.add(new_symbol); - goto_programt::targett t_new = body.insert_before(t); - t_new->make_assignment(); - t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); - expr = new_symbol.symbol_expr(); + symbolt new_symbol; + new_symbol.type=member_expr.type(); + new_symbol.name="$deref"+i2string(var_counter++); + new_symbol.base_name=new_symbol.name; + new_symbol.pretty_name=new_symbol.name; + goto_model.symbol_table.add(new_symbol); + goto_programt::targett t_new = body.insert_before(t); + t_new->make_assignment(); + t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); + expr = new_symbol.symbol_expr(); for(std::set::iterator t_it = - t->incoming_edges.begin(); - t_it != t->incoming_edges.end(); ++t_it) - { - (*t_it)->targets.clear(); - (*t_it)->targets.push_back(t_new); - } - body.compute_location_numbers(); - body.compute_target_numbers(); - body.compute_incoming_edges(); + t->incoming_edges.begin(); + t_it != t->incoming_edges.end(); ++t_it) + { + (*t_it)->targets.clear(); + (*t_it)->targets.push_back(t_new); + } + body.compute_location_numbers(); + body.compute_target_numbers(); + body.compute_incoming_edges(); } } else Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); + remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); } else Forall_operands(o_it,expr) @@ -241,24 +241,24 @@ void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_m { if(i_it->is_goto()) { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - i_it->guard, - var_counter, false); + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, false); } else if(i_it->is_assign()) { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).lhs(), - var_counter, false); - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).rhs(), - var_counter, false); + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); + remove_multiple_dereferences(goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); } } } diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index ebe580703..e3570333e 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -70,8 +70,9 @@ Function: summarizer_parse_optionst::summarizer_parse_optionst \*******************************************************************/ summarizer_parse_optionst::summarizer_parse_optionst(int argc, const char **argv): - parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit("2LS " CBMC_VERSION, cmdline) +parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), + language_uit(cmdline, ui_message_handler), + ui_message_handler(cmdline, "2LS " SUMMARIZER_VERSION) { } @@ -1042,7 +1043,7 @@ bool summarizer_parse_optionst::process_goto_program( #if UNWIND_GOTO_INTO_LOOP - goto_unwind(goto_model,2); + unwind_goto_into_loop(goto_model,2); #endif remove_skip(goto_model.goto_functions); diff --git a/src/summarizer/summarizer_parse_options.h b/src/summarizer/summarizer_parse_options.h index d7e165bdb..3e35f4b59 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/summarizer/summarizer_parse_options.h @@ -147,7 +147,7 @@ class summarizer_parse_optionst: void inline_main(goto_modelt &goto_model); void propagate_constants(goto_modelt &goto_model); void nondet_locals(goto_modelt &goto_model); - void goto_unwind(goto_modelt &goto_model, unsigned k); + void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, bool &valid); From 2d3559b21b0fc3a2935c38b24024798fc65ddcc5 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sat, 17 Sep 2016 17:29:59 +0100 Subject: [PATCH 81/90] merge --- src/Makefile | 7 +- src/domains/util.cpp | 2 +- src/ssa/ssa_value_set.cpp | 6 +- src/summarizer/Makefile | 11 +- src/summarizer/preprocessing_util.cpp | 12 +- src/summarizer/summarizer_parse_options.cpp | 187 ++++++++++---------- src/summarizer/summarizer_parse_options.h | 3 + src/summarizer/summary_db.cpp | 2 +- 8 files changed, 115 insertions(+), 115 deletions(-) diff --git a/src/Makefile b/src/Makefile index bae5bbc03..4032fef26 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,6 +1,7 @@ include config.inc -SUBDIRS = summarizer ssa solver domains functions +SUBDIRS = summarizer ssa domains +#solver functions all: summarizer #deltagit @@ -14,8 +15,8 @@ $(SUBDIRS): # Dependencies summarizer: domains ssa -deltacheck: ssa solver functions html -deltagit: html +#deltacheck: ssa solver functions html +#deltagit: html clean: $(patsubst %, %_clean, $(SUBDIRS)) diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 80d45d499..d6d219713 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -156,7 +156,7 @@ void extend_expr_types(exprt &expr) } else assert(false); } - std::cerr << "failed expr: " << expr << std::endl; + std::cerr << "failed expr: " << expr.pretty() << std::endl; assert(false); } diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index 950de1f3f..c3522207a 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -251,7 +251,7 @@ void ssa_value_domaint::assign_rhs_rec( { mp_integer pointer_offset=pointer_offset_size(it->type().subtype(), ns); if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); } } @@ -263,7 +263,7 @@ void ssa_value_domaint::assign_rhs_rec( { mp_integer pointer_offset=pointer_offset_size(rhs.type().subtype(), ns); if(pointer_offset<1) pointer_offset=1; - unsigned a=merge_alignment(integer2long(pointer_offset), alignment); + unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); } } @@ -341,7 +341,7 @@ void ssa_value_domaint::assign_rhs_rec_address_of( offset=true; mp_integer pointer_offset=pointer_offset_size(rhs.type(), ns); if(pointer_offset<1) pointer_offset=1; - a=merge_alignment(a, integer2long(pointer_offset)); + a=merge_alignment(a, integer2ulong(pointer_offset)); } assign_rhs_rec_address_of(dest, to_index_expr(rhs).array(), ns, offset, a); diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 878ad3397..6f99abd33 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -1,6 +1,6 @@ include ../config.inc -SRC = summarizer_base.cpp \ +SRC = summarizer_base.cpp summarizer_languages.cpp \ summarizer_fw.cpp summarizer_bw.cpp \ summarizer_fw_term.cpp summarizer_bw_term.cpp \ summarizer_bw_cex.cpp summarizer_bw_cex_complete.cpp \ @@ -15,7 +15,8 @@ SRC = summarizer_base.cpp \ cover_goals_ext.cpp horn_encoding.cpp \ summary_db.cpp summary.cpp ssa_db.cpp \ array_abstraction.cpp preprocessing_util.cpp \ - instrument_goto.cpp function_signature.cpp + instrument_goto.cpp +#function_signature.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ @@ -33,6 +34,7 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ DELTA_OBJ+=\ $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ + $(CBMC)/src/goto-instrument/loop_utils$(OBJEXT) \ ../ssa/local_ssa$(OBJEXT) \ ../ssa/malloc_ssa$(OBJEXT) \ ../ssa/ssa_domain$(OBJEXT) \ @@ -54,11 +56,6 @@ DELTA_OBJ+=\ ../ssa/ssa_dependency_graph$(OBJEXT) \ ../ssa/ssa_refiner_monolithic$(OBJEXT) \ ../ssa/ssa_refiner_selective$(OBJEXT) \ - ../functions/summary$(OBJEXT) \ - ../functions/get_function$(OBJEXT) \ - ../functions/path_util$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../functions/index$(OBJEXT) \ ../domains/fixed_point$(OBJEXT) \ ../domains/ssa_fixed_point$(OBJEXT) \ ../domains/tpolyhedra_domain$(OBJEXT) \ diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp index ccc335388..0be64ff76 100644 --- a/src/summarizer/preprocessing_util.cpp +++ b/src/summarizer/preprocessing_util.cpp @@ -96,13 +96,13 @@ void summarizer_parse_optionst::nondet_locals(goto_modelt &goto_model) { if(i_it->is_decl()) { - const code_declt& decl = to_code_decl(i_it->code); + const code_declt& decl = to_code_decl(i_it->code); side_effect_expr_nondett nondet(decl.symbol().type()); - goto_programt::targett t = f_it->second.body.insert_after(i_it); - t->make_assignment(); - code_assignt c(decl.symbol(),nondet); - t->code.swap(c); - t->source_location = i_it->source_location; + goto_programt::targett t = f_it->second.body.insert_after(i_it); + t->make_assignment(); + code_assignt c(decl.symbol(),nondet); + t->code.swap(c); + t->source_location = i_it->source_location; } } } diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index e3570333e..dd0ed96d9 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -294,18 +294,18 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) if(cmdline.isset("lexicographic-ranking-function")) { options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); + cmdline.get_value("lexicographic-ranking-function")); } else options.set_option("lexicographic-ranking-function",3); if(cmdline.isset("max-inner-ranking-iterations")) { options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); + cmdline.get_value("max-inner-ranking-iterations")); } else options.set_option("max-inner-ranking-iterations",20); - // do k-induction refinement + // do k-induction refinement if(cmdline.isset("k-induction")) { options.set_option("std-invariants", true); @@ -315,7 +315,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("unwind",UINT_MAX); } - // do incremental bmc + // do incremental bmc if(cmdline.isset("incremental-bmc")) { options.set_option("incremental-bmc", true); @@ -341,7 +341,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) // instrumentation / output if(cmdline.isset("instrument-output")) options.set_option("instrument-output", - cmdline.get_value("instrument-output")); + cmdline.get_value("instrument-output")); #ifdef SHOW_CALLING_CONTEXTS @@ -351,7 +351,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) throw "--show-calling-contexts only possible with --intervals"; options.set_option("show-calling-contexts", true); options.set_option("do-not-analyze-functions", - cmdline.get_value("show-calling-contexts")); + cmdline.get_value("show-calling-contexts")); } #endif @@ -397,11 +397,10 @@ int summarizer_parse_optionst::doit() // status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; - register_language(new_ansi_c_language); - register_language(new_cpp_language); - goto_modelt goto_model; + register_languages(); + if(get_goto_program(options, goto_model)) return 6; @@ -502,13 +501,13 @@ int summarizer_parse_optionst::doit() summary_checker_baset *summary_checker = NULL; if(!options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_ait(options); + summary_checker = new summary_checker_ait(options); if(options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_kindt(options); + summary_checker = new summary_checker_kindt(options); if(!options.get_bool_option("k-induction") && options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_bmct(options); + summary_checker = new summary_checker_bmct(options); summary_checker->set_message_handler(get_message_handler()); summary_checker->simplify=!cmdline.isset("no-simplify"); @@ -537,11 +536,11 @@ int summarizer_parse_optionst::doit() } else { - #ifdef _MSC_VER +#ifdef _MSC_VER std::ofstream out(widen(out_file).c_str()); - #else +#else std::ofstream out(out_file.c_str()); - #endif +#endif if(!out) { @@ -605,20 +604,20 @@ int summarizer_parse_optionst::doit() return 8; } - #if 0 +#if 0 // let's log some more statistics debug() << "Memory consumption:" << messaget::endl; memory_info(debug()); debug() << eom; - #endif +#endif } void summarizer_parse_optionst::type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns) + const typet &type, + expr_statst &stats, + const namespacet &ns) { if(type.id()==ID_symbol) @@ -661,8 +660,8 @@ Function: summarizer_parse_optionst::expr_stats_rec void summarizer_parse_optionst::expr_stats_rec( - const exprt &expr, - expr_statst &stats) + const exprt &expr, + expr_statst &stats) { if(expr.id()==ID_side_effect) @@ -708,7 +707,7 @@ Function: summarizer_parse_optionst::show_stats \*******************************************************************/ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) + std::ostream &out) { const namespacet ns(goto_model.symbol_table); @@ -734,9 +733,9 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, #endif for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) { nr_instructions++; const goto_programt::instructiont &instruction=*i_it; @@ -745,32 +744,32 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, switch(instruction.type) { - case ASSIGN: - { - const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); - } - break; - case ASSUME: - expr_stats_rec(instruction.guard, stats); - break; - case ASSERT: - expr_stats_rec(instruction.guard, stats); - break; - case GOTO: - expr_stats_rec(instruction.guard, stats); - break; + case ASSIGN: + { + const code_assignt &assign=to_code_assign(instruction.code); + expr_stats_rec(assign.lhs(), stats); + expr_stats_rec(assign.rhs(), stats); + } + break; + case ASSUME: + expr_stats_rec(instruction.guard, stats); + break; + case ASSERT: + expr_stats_rec(instruction.guard, stats); + break; + case GOTO: + expr_stats_rec(instruction.guard, stats); + break; - case DECL: - // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); + case DECL: + // someone declaring an array + type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - break; + break; - default: - // skip - break; + default: + // skip + break; } // switch } // forall instructions } // forall functions @@ -876,7 +875,7 @@ bool summarizer_parse_optionst::get_goto_program( status() << "Reading GOTO program from file" << eom; if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) + goto_model, get_message_handler())) return true; config.set_from_symbol_table(goto_model.symbol_table); @@ -897,11 +896,11 @@ bool summarizer_parse_optionst::get_goto_program( std::string filename=cmdline.args[0]; - #ifdef _MSC_VER +#ifdef _MSC_VER std::ifstream infile(widen(filename).c_str()); - #else +#else std::ifstream infile(filename.c_str()); - #endif +#endif if(!infile) { @@ -946,7 +945,7 @@ bool summarizer_parse_optionst::get_goto_program( return true; } - #if 0 +#if 0 irep_idt entry_point=goto_model.goto_functions.entry_point(); if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) @@ -954,7 +953,7 @@ bool summarizer_parse_optionst::get_goto_program( error() << "No entry point; please provide a main function" << eom; return true; } - #endif +#endif status() << "Generating GOTO Program" << eom; @@ -1025,9 +1024,9 @@ bool summarizer_parse_optionst::process_goto_program( Forall_goto_functions(f_it, goto_model.goto_functions) if(f_it->first!=ID__start && f_it->second.body.instructions.size()<=2*(limit/2)) - { + { f_it->second.body.clear(); - } + } } // add generic checks @@ -1056,7 +1055,7 @@ bool summarizer_parse_optionst::process_goto_program( if(goto_inline(goto_model, ui_message_handler)) { report_unknown(); - return 5; + return 5; } } @@ -1067,7 +1066,7 @@ bool summarizer_parse_optionst::process_goto_program( nondet_locals(goto_model); #if 1 - //TODO: find a better place for that + //TODO: find a better place for that replace_malloc(goto_model,""); #endif @@ -1177,7 +1176,7 @@ void summarizer_parse_optionst::report_properties( const property_checkert::property_mapt &property_map) { for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { @@ -1209,8 +1208,8 @@ void summarizer_parse_optionst::report_properties( if(cmdline.isset("json-cex") && it->second.result==property_checkert::FAIL) output_json_cex(options, - goto_model, it->second.error_trace, - id2string(it->first)); + goto_model, it->second.error_trace, + id2string(it->first)); } if(!cmdline.isset("property")) @@ -1221,7 +1220,7 @@ void summarizer_parse_optionst::report_properties( unsigned unknown=0; for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); + it=property_map.begin(); it!=property_map.end(); it++) { @@ -1262,13 +1261,13 @@ void summarizer_parse_optionst::report_success() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="SUCCESS"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="SUCCESS"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1301,12 +1300,12 @@ void summarizer_parse_optionst::show_counterexample( break; case ui_message_handlert::XML_UI: - { - xmlt xml; - convert(ns, error_trace, xml); - std::cout << xml << std::endl; - } - break; + { + xmlt xml; + convert(ns, error_trace, xml); + std::cout << xml << std::endl; + } + break; default: assert(false); @@ -1405,13 +1404,13 @@ void summarizer_parse_optionst::report_failure() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="FAILURE"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="FAILURE"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1440,13 +1439,13 @@ void summarizer_parse_optionst::report_unknown() break; case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="UNKNOWN"; - std::cout << xml; - std::cout << std::endl; - } - break; + { + xmlt xml("cprover-status"); + xml.data="UNKNOWN"; + std::cout << xml; + std::cout << std::endl; + } + break; default: assert(false); @@ -1501,12 +1500,12 @@ void summarizer_parse_optionst::help() " --show-symbol-table show symbol table\n" " --show-goto-functions show goto program\n" " --arch set architecture (default: " - << configt::this_architecture() << ")\n" + << configt::this_architecture() << ")\n" " --os set operating system (default: " - << configt::this_operating_system() << ")\n" - #ifdef _WIN32 + << configt::this_operating_system() << ")\n" +#ifdef _WIN32 " --gcc use GCC as preprocessor\n" - #endif +#endif " --no-arch don't set up an architecture\n" " --no-library disable built-in abstract C library\n" " --round-to-nearest IEEE floating point rounding mode (default)\n" diff --git a/src/summarizer/summarizer_parse_options.h b/src/summarizer/summarizer_parse_options.h index 3e35f4b59..f06b469b2 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/summarizer/summarizer_parse_options.h @@ -75,6 +75,9 @@ class summarizer_parse_optionst: const std::string &extra_options); protected: + ui_message_handlert ui_message_handler; + virtual void register_languages(); + void get_command_line_options(optionst &options); bool get_goto_program( diff --git a/src/summarizer/summary_db.cpp b/src/summarizer/summary_db.cpp index 4e7dde39e..7517780a7 100644 --- a/src/summarizer/summary_db.cpp +++ b/src/summarizer/summary_db.cpp @@ -62,7 +62,7 @@ void summary_dbt::read(const std::string &id) { current=id; - summary=jsont::json_object(); + summary.make_object(); parse_json(file_name(id), get_message_handler(), summary); } From f32b3c2be7cf3bd1937775e42907e14e03d37913 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 18 Sep 2016 15:56:07 +0100 Subject: [PATCH 82/90] bounds simplification --- src/domains/Makefile | 3 +- src/domains/simplify_bounds.cpp | 454 ++++++++++++++++++++ src/domains/simplify_bounds.h | 32 ++ src/domains/simplify_bounds_class.h | 66 +++ src/domains/simplify_transformer.cpp | 254 +++++++++++ src/domains/simplify_transformer.h | 35 ++ src/domains/simplify_transformer_class.h | 59 +++ src/domains/tpolyhedra_domain.cpp | 3 + src/summarizer/Makefile | 3 +- src/summarizer/summarizer_parse_options.cpp | 2 +- 10 files changed, 908 insertions(+), 3 deletions(-) create mode 100644 src/domains/simplify_bounds.cpp create mode 100644 src/domains/simplify_bounds.h create mode 100644 src/domains/simplify_bounds_class.h create mode 100644 src/domains/simplify_transformer.cpp create mode 100644 src/domains/simplify_transformer.h create mode 100644 src/domains/simplify_transformer_class.h diff --git a/src/domains/Makefile b/src/domains/Makefile index 50b4978fd..f6107d0dc 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -11,7 +11,8 @@ SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp + strategy_solver_predabs.cpp \ + simplify_transformer.cpp simplify_bounds.cpp #solver_enumeration.cpp include $(CBMC)/src/config.inc diff --git a/src/domains/simplify_bounds.cpp b/src/domains/simplify_bounds.cpp new file mode 100644 index 000000000..cfa64de81 --- /dev/null +++ b/src/domains/simplify_bounds.cpp @@ -0,0 +1,454 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_bounds.h" +#include "simplify_bounds_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_boundst::get_min_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_min_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_min_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_smallest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_min_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_max_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::get_max_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_max_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_max_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_largest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_max_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_min_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::simplify_rec(exprt &expr) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_le) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=true_exprt(); + result=false; + } + else + result=clean_up_typecast(expr,rhs_value); + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_ge) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_lt) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_gt) + { + binary_relation_exprt &e = to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else + { + Forall_operands(it, expr) + if(!simplify_rec(*it)) + result=false; + } +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_typecast(exprt &expr, + const mp_integer &rhs_value) +{ + if(expr.id()==ID_le && expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast) + { + const typet &inner_type=expr.op0().op0().op0().type(); + if(-rhs_value>get_smallest(inner_type)) + { + expr=binary_relation_exprt(expr.op0().op0().op0(), ID_ge, + from_integer(-rhs_value,inner_type)); + return true; + } + } + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_implications(exprt &expr) +{ + bool result=true; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(!clean_up_implications(*it)) + result=false; + exprt::operandst::iterator it=expr.operands().begin(); + while(it!=expr.operands().end()) + { + if(it->is_true()) + it=expr.operands().erase(it); + else + ++it; + } + if(expr.operands().empty()) + expr=true_exprt(); + else if(expr.operands().size()==1) + expr=expr.op0(); + } + else if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + { + expr=true_exprt(); + result=false; + } + } + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::regroup_implications(exprt &expr) +{ + std::map implication_map; + exprt::operandst r; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(it->id()==ID_implies) + implication_map[it->op0()].push_back(it->op1()); + else + r.push_back(*it); + } + else + return true; + for(auto &i : implication_map) + r.push_back(implies_exprt(i.first, conjunction(i.second))); + expr=conjunction(r); + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::operator()(exprt &expr) +{ + bool result=simplify_rec(expr); + if(!clean_up_implications(expr)) + result=false; + if(!regroup_implications(expr)) + result=false; + return result; +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_bounds(exprt &expr, + const namespacet &ns) +{ + return simplify_boundst(ns)(expr); +} + +/*******************************************************************\ + +Function: simplify_bounds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_bounds(const exprt &src, + const namespacet &ns) +{ + exprt tmp=src; + simplify_boundst simplify_bounds(ns); + simplify_bounds(tmp); + return tmp; +} + diff --git a/src/domains/simplify_bounds.h b/src/domains/simplify_bounds.h new file mode 100644 index 000000000..6799b76a2 --- /dev/null +++ b/src/domains/simplify_bounds.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_BOUNDS_H +#define CPROVER_SIMPLIFY_BOUNDS_H + +#include + +class exprt; +class namespacet; + +// +// simplify bounds +// +// true: did nothing +// false: simplified something +// + +bool simplify_bounds( + exprt &expr, + const namespacet &ns); + +exprt simplify_bounds( + const exprt &src, + const namespacet &ns); + +#endif diff --git a/src/domains/simplify_bounds_class.h b/src/domains/simplify_bounds_class.h new file mode 100644 index 000000000..3510f42ed --- /dev/null +++ b/src/domains/simplify_bounds_class.h @@ -0,0 +1,66 @@ +/*******************************************************************\ + +Module: Bounds Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_BOUNDS_CLASS_H +#define CPROVER_SIMPLIFY_BOUNDS_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_boundst +{ +public: + explicit simplify_boundst(const namespacet &_ns): + ns(_ns) + { + } + + virtual ~simplify_boundst() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv; + } + inline static mp_integer get_largest(const typet &type) + { + if(type.id()==ID_signedbv) return to_signedbv_type(type).largest(); + else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).largest(); + assert(false); + } + inline static mp_integer get_smallest(const typet &type) + { + if(type.id()==ID_signedbv) return to_signedbv_type(type).smallest(); + else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).smallest(); + assert(false); + } + +protected: + const namespacet &ns; + + bool simplify_rec(exprt &expr); + bool get_min_bound(const exprt &expr, mp_integer &value); + bool get_max_bound(const exprt &expr, mp_integer &value); + + bool clean_up_implications(exprt &expr); + bool regroup_implications(exprt &expr); + bool clean_up_typecast(exprt &expr, const mp_integer &value); + +}; + +#endif diff --git a/src/domains/simplify_transformer.cpp b/src/domains/simplify_transformer.cpp new file mode 100644 index 000000000..87f43673d --- /dev/null +++ b/src/domains/simplify_transformer.cpp @@ -0,0 +1,254 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_transformer.h" +#include "simplify_transformer_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_transformert::collect_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void simplify_transformert::collect_node(const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy) +{ + if(expr.id()==ID_equal) + { + const equal_exprt &e = to_equal_expr(expr); + + bool rhs_is_constant=e.rhs().id()==ID_constant; + bool rhs_is_symbol=e.rhs().id()==ID_symbol || + e.rhs().id()==ID_nondet_symbol; + bool rhs_is_frozen=rhs_is_symbol && + frozen_symbols.find(e.rhs().get(ID_identifier))!=frozen_symbols.end(); + bool lhs_is_constant=e.lhs().id()==ID_constant; + bool lhs_is_symbol=e.lhs().id()==ID_symbol || + e.lhs().id()==ID_nondet_symbol; + bool lhs_is_frozen=lhs_is_symbol && + frozen_symbols.find(e.lhs().get(ID_identifier))!=frozen_symbols.end(); + + exprt lhs, rhs; + lhs.make_nil(); + rhs.make_nil(); + //stupid matching + if((rhs_is_frozen || rhs_is_constant || !frozen_only) && + lhs_is_symbol && !lhs_is_frozen) + { + lhs=e.lhs(); + rhs=e.rhs(); + } + if((lhs_is_frozen || lhs_is_constant || !frozen_only) && + rhs_is_symbol && !rhs_is_frozen) + { + rhs=e.lhs(); + lhs=e.rhs(); + } + if(rhs.is_not_nil() && lhs.is_not_nil()) + { + if(make_copy) //make lazy copy + { + replace_mapt _subst = substitutions; + substitutions = _subst; + } + substitutions[lhs]=rhs; + } + } + +#ifdef DEBUGX + std::cout << "COLLECT: " << from_expr(ns, "", expr) << std::endl; + for(const auto &it : substitutions) + std::cout << from_expr(ns, "", it.first) + << "---> " << from_expr(ns, "", it.second) + << "\n"; +#endif +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_node(exprt &expr, + const replace_mapt &substitutions) +{ + return replace_expr(substitutions, expr); +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_rec(exprt &expr, + replace_mapt &substitutions) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_and) + { + //first propagate from frozen symbols + bool res=false; + do + { + Forall_operands(it, expr) + collect_node(*it, substitutions, true, false); + + Forall_operands(it, expr) + if(!simplify_rec(*it, substitutions)) + result=false; + + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + + //simplify remaining equalities + Forall_operands(it, expr) + collect_node(*it, substitutions, false, false); + + res=false; + do + { + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + } + +#if 0 //for later extension to disjunctions + //TODO: handle negation, too + else if(expr.id()==ID_or || expr.id()==ID_implies) + { + Forall_operands(it, expr) + { + collect_node(*it, substitutions, true); + if(!simplify_rec(*it, substitutions)) + result=false; + + bool res=false; + do + { + res=simplify_node(*it, substitutions); + if(!res) result=false; + } + while(!res); + } + } +#endif + +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_transformert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::operator()(exprt &expr) +{ + replace_mapt substitutions; + return simplify_rec(expr, substitutions); +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify(exprt &expr, + const std::set &frozen_vars, + const namespacet &ns) +{ + return simplify_transformert(ns, frozen_vars)(expr); +} + +/*******************************************************************\ + +Function: simplify_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_transformer(const exprt &src, + const std::set &frozen_vars, + const namespacet &ns) +{ +#ifdef DEBUGX + std::cout << "FROZEN:"; + for(const auto &it : frozen_vars) + std::cout << " " << it; + std::cout << "\n"; +#endif + + exprt tmp=src; + simplify_transformert(ns, frozen_vars)(tmp); + return tmp; +} + diff --git a/src/domains/simplify_transformer.h b/src/domains/simplify_transformer.h new file mode 100644 index 000000000..32a815c8c --- /dev/null +++ b/src/domains/simplify_transformer.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_TRANSFORMER_H +#define CPROVER_SIMPLIFY_TRANSFORMER_H + +#include +#include + +class exprt; +class namespacet; + +// +// simplify transformers by best-effort intermediate variable elimination +// +// true: did nothing +// false: simplified something +// + +bool simplify( + exprt &expr, + const std::set &frozen_vars, //do not eliminate these + const namespacet &ns); + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, //do not eliminate these + const namespacet &ns); + +#endif diff --git a/src/domains/simplify_transformer_class.h b/src/domains/simplify_transformer_class.h new file mode 100644 index 000000000..ccae118b4 --- /dev/null +++ b/src/domains/simplify_transformer_class.h @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Transformer Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H +#define CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_transformert +{ +public: + explicit simplify_transformert(const namespacet &_ns, + const std::set &_frozen_symbols): + ns(_ns), + frozen_symbols(_frozen_symbols) + { + } + + virtual ~simplify_transformert() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + // main recursion + bool simplify_rec(exprt &expr, replace_mapt &substitutions); + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv; + } + +protected: + const namespacet &ns; + const std::set &frozen_symbols; + + void collect_node( + const exprt &expr, replace_mapt &substitutions, + bool frozen_only, + bool make_copy); + bool simplify_node(exprt &expr, const replace_mapt &substitutions); + +}; + +#endif diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 1990c48d7..263c464e1 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -8,6 +8,8 @@ #include #include +#include "simplify_bounds.h" + #define SYMB_BOUND_VAR "symb_bound#" #define ENABLE_HEURISTICS @@ -588,6 +590,7 @@ void tpolyhedra_domaint::project_on_vars(valuet &value, } } result = conjunction(c); + simplify_bounds(result, ns); } /*******************************************************************\ diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 6f99abd33..600dc3a7c 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -81,7 +81,8 @@ DELTA_OBJ+=\ ../domains/ranking_solver_enumeration$(OBJEXT) \ ../domains/linrank_domain$(OBJEXT) \ ../domains/lexlinrank_solver_enumeration$(OBJEXT) \ - ../domains/lexlinrank_domain$(OBJEXT) + ../domains/lexlinrank_domain$(OBJEXT) \ + ../domains/simplify_bounds$(OBJEXT) # ../domains/solver_enumeration$(OBJEXT) # ../domains/strategy_solver_binsearch2$(OBJEXT) diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/summarizer/summarizer_parse_options.cpp index dd0ed96d9..f6561aa03 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/summarizer/summarizer_parse_options.cpp @@ -54,7 +54,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "show.h" #include "horn_encoding.h" -#define UNWIND_GOTO_INTO_LOOP 1 +#define UNWIND_GOTO_INTO_LOOP 0 #define REMOVE_MULTIPLE_DEREFERENCES 1 /*******************************************************************\ From 87b3310f0fe0529ae6bbe4d503263e5adabc57f0 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Sun, 18 Sep 2016 16:43:46 +0100 Subject: [PATCH 83/90] missing summarizer_languages file --- src/summarizer/summarizer_languages.cpp | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/summarizer/summarizer_languages.cpp diff --git a/src/summarizer/summarizer_languages.cpp b/src/summarizer/summarizer_languages.cpp new file mode 100644 index 000000000..5973c3e93 --- /dev/null +++ b/src/summarizer/summarizer_languages.cpp @@ -0,0 +1,34 @@ +/*******************************************************************\ + +Module: Language Registration + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include +#include +#include + +#include "summarizer_parse_options.h" + +/*******************************************************************\ + +Function: summarizer_parse_optionst::register_languages + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_parse_optionst::register_languages() +{ + register_language(new_ansi_c_language); +// register_language(new_cpp_language); +// register_language(new_java_bytecode_language); +} From 3420966683bb8d2e8cce4f32f268f62ee0d4529b Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Tue, 4 Apr 2017 18:52:58 +0530 Subject: [PATCH 84/90] after rebase --- .../2ls_languages.cpp} | 6 +- .../deltacheck_main.cpp => 2ls/2ls_main.cpp} | 30 +- .../2ls_parse_options.cpp} | 781 +++--- .../2ls_parse_options.h} | 106 +- src/2ls/Makefile | 60 + src/{summarizer => 2ls}/cover_goals_ext.cpp | 144 +- src/{summarizer => 2ls}/cover_goals_ext.h | 50 +- src/2ls/dynamic_cfg.cpp | 301 +++ src/2ls/dynamic_cfg.h | 105 + src/2ls/graphml_witness_ext.cpp | 148 ++ src/2ls/graphml_witness_ext.h | 44 + src/{summarizer => 2ls}/horn_encoding.cpp | 39 +- src/{summarizer => 2ls}/horn_encoding.h | 4 +- src/{summarizer => 2ls}/instrument_goto.cpp | 154 +- src/{summarizer => 2ls}/instrument_goto.h | 26 +- src/2ls/preprocessing_util.cpp | 468 ++++ src/2ls/show.cpp | 609 +++++ src/{summarizer => 2ls}/show.h | 22 +- src/{summarizer => 2ls}/summarizer_bw_cex.cpp | 2 +- src/{summarizer => 2ls}/summarizer_bw_cex.h | 4 +- .../summarizer_bw_cex_ai.cpp | 2 +- .../summarizer_bw_cex_ai.h | 2 +- .../summarizer_bw_cex_all.cpp | 0 .../summarizer_bw_cex_all.h | 2 +- .../summarizer_bw_cex_complete.cpp | 2 +- .../summarizer_bw_cex_complete.h | 2 +- .../summarizer_bw_cex_concrete.cpp | 2 +- .../summarizer_bw_cex_concrete.h | 2 +- .../summarizer_bw_cex_wp.cpp | 2 +- .../summarizer_bw_cex_wp.h | 2 +- src/2ls/summary_checker_ai.cpp | 180 ++ src/{summarizer => 2ls}/summary_checker_ai.h | 9 +- .../summary_checker_base.cpp | 8 +- .../summary_checker_base.h | 8 +- src/2ls/summary_checker_bmc.cpp | 59 + src/{summarizer => 2ls}/summary_checker_bmc.h | 9 +- src/2ls/summary_checker_kind.cpp | 76 + .../summary_checker_kind.h | 9 +- src/2ls/version.h | 14 + src/Makefile | 36 +- src/config.inc.template | 5 +- src/deltacheck/deltacheck_parse_options.cpp | 347 --- src/deltacheck/deltacheck_parse_options.h | 52 - src/deltacheck/get_source.cpp | 140 -- src/deltacheck/get_source.h | 35 - src/deltacheck/old/call_graph.cpp | 49 - src/deltacheck/old/call_graph.h | 17 - src/deltacheck/old/canonicalize.cpp | 161 -- src/deltacheck/old/canonicalize.h | 18 - src/deltacheck/old/cgraph_builder.cpp | 167 -- src/deltacheck/old/cgraph_builder.h | 69 - src/deltacheck/old/collect_symbols.cpp | 23 - src/deltacheck/old/collect_symbols.h | 20 - src/deltacheck/old/data_flow.cpp | 316 --- src/deltacheck/old/data_flow.h | 59 - src/deltacheck/old/delta_check.cpp | 449 ---- src/deltacheck/old/delta_check.h | 20 - src/deltacheck/old/dependencies.cpp | 42 - src/deltacheck/old/dependencies.h | 23 - src/deltacheck/old/discover_predicates.cpp | 56 - src/deltacheck/old/discover_predicates.h | 16 - src/deltacheck/old/function_delta.cpp | 127 - src/deltacheck/old/function_delta.h | 24 - src/deltacheck/old/function_file_map.cpp | 54 - src/deltacheck/old/function_file_map.h | 23 - src/deltacheck/old/function_transformer.cpp | 478 ---- src/deltacheck/old/function_transformer.h | 21 - src/deltacheck/old/get_goto_program.cpp | 68 - src/deltacheck/old/get_goto_program.h | 20 - src/deltacheck/old/modular_analysis.h | 246 -- src/deltacheck/old/modular_code_analysis.cpp | 196 -- src/deltacheck/old/modular_code_analysis.h | 67 - src/deltacheck/old/modular_fptr_analysis.cpp | 275 --- src/deltacheck/old/modular_fptr_analysis.h | 63 - .../old/modular_globals_analysis.cpp | 134 -- src/deltacheck/old/modular_globals_analysis.h | 47 - src/deltacheck/old/predicates.cpp | 70 - src/deltacheck/old/predicates.h | 58 - src/deltacheck/old/reporting.cpp | 233 -- src/deltacheck/old/reporting.h | 28 - src/deltacheck/old/statement_transformer.cpp | 201 -- src/deltacheck/old/statement_transformer.h | 59 - src/deltacheck/old/summarization.cpp | 327 --- src/deltacheck/old/summarization.h | 24 - src/deltacheck/old/xml_conversion.cpp | 37 - src/deltacheck/old/xml_conversion.h | 4 - src/deltacheck/properties.cpp | 361 --- src/deltacheck/properties.h | 52 - src/deltacheck/rename.cpp | 189 -- src/deltacheck/rename.h | 34 - src/deltacheck/report_source_code.h | 40 - src/deltacheck/show.cpp | 395 ---- src/deltacheck/show.h | 49 - src/deltacheck/source_diff.cpp | 243 -- src/deltacheck/source_diff.h | 13 - src/deltacheck/ssa_fixed_point.cpp | 411 ---- src/deltacheck/ssa_fixed_point.h | 88 - src/deltacheck/statistics.cpp | 142 -- src/deltacheck/statistics.h | 49 - .../summarize_function_pointers.cpp | 79 - src/deltacheck/summarize_function_pointers.h | 35 - src/deltacheck/version.h | 1 - src/deltagit/deltagit_config.cpp | 44 - src/deltagit/deltagit_config.h | 29 - src/deltagit/deltagit_parse_options.cpp | 206 -- src/deltagit/deltagit_parse_options.h | 30 - src/deltagit/do_job.cpp | 330 --- src/deltagit/do_job.h | 17 - src/deltagit/git_branch.cpp | 65 - src/deltagit/git_branch.h | 37 - src/deltagit/git_log.cpp | 87 - src/deltagit/git_log.h | 42 - src/deltagit/init.cpp | 236 -- src/deltagit/init.h | 14 - src/deltagit/job_status.cpp | 298 --- src/deltagit/job_status.h | 72 - src/deltagit/log_scale.cpp | 4 - src/deltagit/log_scale.h | 1 - src/deltagit/log_scale.png | Bin 3342 -> 0 bytes src/deltagit/reanalyse.cpp | 90 - src/deltagit/reanalyse.h | 15 - src/deltagit/reset.cpp | 70 - src/deltagit/reset.h | 15 - src/deltagit/revisions_report.h | 17 - src/deltagit/revisions_report_header.html | 137 -- src/deltagit/shell_escape.cpp | 59 - src/deltagit/shell_escape.h | 13 - src/deltagit/show_jobs.cpp | 48 - src/deltagit/show_jobs.h | 16 - src/deltagit/update.cpp | 45 - src/deltagit/update.h | 14 - src/dist-bin | 9 - src/domains/Makefile | 25 +- src/domains/domain.cpp | 83 +- src/domains/domain.h | 120 +- src/domains/domain_refinement.h | 39 - src/domains/domain_refinement_variables.cpp | 20 - src/domains/equality_domain.cpp | 404 ++-- src/domains/equality_domain.h | 59 +- src/domains/fixed_point.cpp | 140 -- src/domains/fixed_point.h | 53 - src/domains/incremental_solver.cpp | 136 +- src/domains/incremental_solver.h | 185 +- src/domains/lexlinrank_domain.cpp | 671 ++++-- src/domains/lexlinrank_domain.h | 97 +- src/domains/lexlinrank_solver_enumeration.cpp | 368 +-- src/domains/lexlinrank_solver_enumeration.h | 31 +- src/domains/linrank_domain.cpp | 477 ++-- src/domains/linrank_domain.h | 96 +- src/domains/predabs_domain.cpp | 252 +- src/domains/predabs_domain.h | 67 +- src/domains/predicate.cpp | 225 -- src/domains/predicate.h | 89 - src/domains/ranking_solver_enumeration.cpp | 245 +- src/domains/ranking_solver_enumeration.h | 37 +- src/domains/simplify_bounds.cpp | 454 ---- src/domains/simplify_bounds.h | 32 - src/domains/simplify_bounds_class.h | 66 - src/domains/simplify_transformer.cpp | 254 -- src/domains/simplify_transformer.h | 35 - src/domains/simplify_transformer_class.h | 59 - src/domains/solver_enumeration.cpp | 126 - src/domains/solver_enumeration.h | 23 - src/domains/ssa_analyzer.cpp | 427 ++-- src/domains/ssa_analyzer.h | 139 +- src/domains/ssa_fixed_point.cpp | 79 - src/domains/ssa_fixed_point.h | 16 - src/domains/strategy_solver_base.cpp | 7 + src/domains/strategy_solver_base.h | 36 +- src/domains/strategy_solver_binsearch.cpp | 264 ++- src/domains/strategy_solver_binsearch.h | 33 +- src/domains/strategy_solver_binsearch2.cpp | 235 +- src/domains/strategy_solver_binsearch2.h | 22 +- src/domains/strategy_solver_binsearch3.cpp | 268 +-- src/domains/strategy_solver_binsearch3.h | 26 +- src/domains/strategy_solver_enumeration.cpp | 155 +- src/domains/strategy_solver_enumeration.h | 31 +- src/domains/strategy_solver_equality.cpp | 100 +- src/domains/strategy_solver_equality.h | 26 +- src/domains/strategy_solver_predabs.cpp | 126 +- src/domains/strategy_solver_predabs.h | 25 +- src/domains/template_domain.cpp | 1300 ---------- src/domains/template_generator_base.cpp | 624 +++-- src/domains/template_generator_base.h | 150 +- .../template_generator_callingcontext.cpp | 93 +- .../template_generator_callingcontext.h | 33 +- src/domains/template_generator_ranking.cpp | 123 +- src/domains/template_generator_ranking.h | 39 +- src/domains/template_generator_summary.cpp | 77 +- src/domains/template_generator_summary.h | 35 +- src/domains/tpolyhedra_domain.cpp | 932 ++++---- src/domains/tpolyhedra_domain.h | 98 +- src/domains/util.cpp | 544 +++-- src/domains/util.h | 23 +- src/functions/call_graph.cpp | 60 - src/functions/call_graph.h | 66 - src/functions/get_function.h | 42 - src/functions/index.h | 54 - src/functions/path_util.cpp | 98 - src/functions/path_util.h | 18 - src/functions/summary.cpp | 10 - src/functions/summary.h | 31 - src/html/html_escape.cpp | 62 - src/html/html_escape.h | 19 - src/html/logo.cpp | 4 - src/html/logo.h | 1 - src/html/syntax_highlighting.cpp | 306 --- src/html/syntax_highlighting.h | 35 - src/html/to_c_string.perl | 17 - src/solver/Makefile | 20 +- src/solver/fixed_point.cpp | 137 -- src/solver/fixed_point.h | 53 - src/solver/predicate.cpp | 202 -- src/solver/predicate.h | 85 - src/solver/solver.cpp | 954 -------- src/solver/solver.h | 182 -- .../summarizer_base.cpp | 287 +-- src/solver/summarizer_base.h | 137 ++ src/solver/summarizer_bw.cpp | 541 +++++ src/solver/summarizer_bw.h | 86 + src/solver/summarizer_bw_term.cpp | 497 ++++ src/solver/summarizer_bw_term.h | 80 + src/solver/summarizer_fw.cpp | 234 ++ src/solver/summarizer_fw.h | 57 + src/solver/summarizer_fw_contexts.cpp | 176 ++ src/solver/summarizer_fw_contexts.h | 59 + src/solver/summarizer_fw_term.cpp | 372 +++ src/solver/summarizer_fw_term.h | 63 + src/solver/summary.cpp | 197 ++ src/{summarizer => solver}/summary.h | 37 +- src/solver/summary_db.cpp | 111 + src/{summarizer => solver}/summary_db.h | 8 +- src/ssa/Makefile | 21 +- src/ssa/address_canonizer.cpp | 16 +- src/ssa/address_canonizer.h | 4 +- src/ssa/assignments.cpp | 33 +- src/ssa/assignments.h | 19 +- src/ssa/const_propagator.cpp | 467 ---- src/ssa/const_propagator.h | 101 - src/ssa/guard_domain.cpp | 178 -- src/ssa/guard_domain.h | 113 - src/ssa/guard_map.cpp | 20 +- src/ssa/guard_map.h | 26 +- src/ssa/local_ssa.cpp | 639 ++--- src/ssa/local_ssa.h | 105 +- src/ssa/malloc_ssa.cpp | 120 +- src/ssa/malloc_ssa.h | 12 +- src/ssa/replace_symbol_ext.cpp | 82 - src/ssa/replace_symbol_ext.h | 20 - src/ssa/simplify_ssa.cpp | 6 +- src/ssa/simplify_ssa.h | 4 +- src/ssa/split_loopheads.cpp | 48 - src/ssa/split_loopheads.h | 8 - src/ssa/ssa_build_goto_trace.cpp | 311 ++- src/ssa/ssa_build_goto_trace.h | 19 +- src/{summarizer => ssa}/ssa_db.cpp | 2 +- src/ssa/ssa_db.h | 97 + src/ssa/ssa_dependency_graph.cpp | 11 +- src/ssa/ssa_dependency_graph.h | 10 +- src/ssa/ssa_dereference.cpp | 101 +- src/ssa/ssa_dereference.h | 8 +- src/ssa/ssa_domain.cpp | 22 +- src/ssa/ssa_domain.h | 16 +- src/ssa/ssa_inliner.cpp | 1108 ++++----- src/ssa/ssa_inliner.h | 229 +- src/ssa/ssa_object.cpp | 58 +- src/ssa/ssa_object.h | 30 +- src/ssa/ssa_refiner_monolithic.cpp | 30 - src/ssa/ssa_refiner_monolithic.h | 44 - src/ssa/ssa_refiner_selective.h | 2 +- src/ssa/ssa_slicer.cpp | 179 -- src/ssa/ssa_slicer.h | 44 - src/ssa/ssa_unwinder.cpp | 2105 ++++++++--------- src/ssa/ssa_unwinder.h | 316 +-- src/ssa/ssa_unwinder_old.cpp | 1556 ------------ src/ssa/ssa_unwinder_old.h | 186 -- src/ssa/ssa_value_set.cpp | 143 +- src/ssa/ssa_value_set.h | 39 +- src/ssa/translate_union_member.cpp | 5 +- src/ssa/translate_union_member.h | 4 +- src/ssa/unwindable_local_ssa.cpp | 290 ++- src/ssa/unwindable_local_ssa.h | 63 +- src/storefront/Makefile | 27 - src/storefront/data.cpp | 84 - src/storefront/data.h | 40 - src/storefront/file_view.cpp | 147 -- src/storefront/file_view.h | 14 - src/storefront/property_view.cpp | 30 - src/storefront/property_view.h | 14 - src/storefront/storefront_main.cpp | 38 - src/storefront/storefront_parse_options.cpp | 121 - src/storefront/storefront_parse_options.h | 29 - src/storefront/trace_view.cpp | 29 - src/storefront/trace_view.h | 14 - src/summarizer/2ls-wrapper.sh | 81 - src/summarizer/Makefile | 36 +- src/summarizer/function_signature.cpp | 78 - src/summarizer/function_signature.h | 17 - src/summarizer/preprocessing_util.cpp | 265 --- src/summarizer/show.cpp | 7 +- src/summarizer/ssa_db.h | 5 +- src/summarizer/summarizer.cpp | 125 - src/summarizer/summarizer.h | 45 - src/summarizer/summarizer_base.h | 36 +- src/summarizer/summarizer_bw_term.cpp | 453 ---- src/summarizer/summarizer_bw_term.h | 77 - src/summarizer/summarizer_fw.cpp | 18 - src/summarizer/summarizer_fw_contexts.cpp | 174 -- src/summarizer/summarizer_fw_contexts.h | 59 - src/summarizer/summarizer_fw_term.cpp | 340 --- src/summarizer/summarizer_fw_term.h | 61 - src/summarizer/summarizer_parseoptions.cpp | 1372 ----------- src/summarizer/summarizer_parseoptions.h | 128 - src/summarizer/summary.cpp | 49 - src/summarizer/summary_checker.cpp | 220 -- src/summarizer/summary_checker.h | 51 - src/summarizer/summary_checker_ai.cpp | 223 -- src/summarizer/summary_checker_bmc.cpp | 90 - src/summarizer/summary_checker_kind.cpp | 105 - src/summarizer/summary_db.cpp | 2 +- src/summarizer/version.h | 2 +- 321 files changed, 13686 insertions(+), 29280 deletions(-) rename src/{summarizer/summarizer_languages.cpp => 2ls/2ls_languages.cpp} (82%) rename src/{deltacheck/deltacheck_main.cpp => 2ls/2ls_main.cpp} (60%) rename src/{summarizer/summarizer_parse_options.cpp => 2ls/2ls_parse_options.cpp} (69%) rename src/{summarizer/summarizer_parse_options.h => 2ls/2ls_parse_options.h} (64%) create mode 100644 src/2ls/Makefile rename src/{summarizer => 2ls}/cover_goals_ext.cpp (56%) rename src/{summarizer => 2ls}/cover_goals_ext.h (76%) create mode 100644 src/2ls/dynamic_cfg.cpp create mode 100644 src/2ls/dynamic_cfg.h create mode 100644 src/2ls/graphml_witness_ext.cpp create mode 100644 src/2ls/graphml_witness_ext.h rename src/{summarizer => 2ls}/horn_encoding.cpp (91%) rename src/{summarizer => 2ls}/horn_encoding.h (79%) rename src/{summarizer => 2ls}/instrument_goto.cpp (50%) rename src/{summarizer => 2ls}/instrument_goto.h (68%) create mode 100644 src/2ls/preprocessing_util.cpp create mode 100644 src/2ls/show.cpp rename src/{summarizer => 2ls}/show.h (78%) rename src/{summarizer => 2ls}/summarizer_bw_cex.cpp (98%) rename src/{summarizer => 2ls}/summarizer_bw_cex.h (95%) rename src/{summarizer => 2ls}/summarizer_bw_cex_ai.cpp (99%) rename src/{summarizer => 2ls}/summarizer_bw_cex_ai.h (98%) rename src/{summarizer => 2ls}/summarizer_bw_cex_all.cpp (100%) rename src/{summarizer => 2ls}/summarizer_bw_cex_all.h (98%) rename src/{summarizer => 2ls}/summarizer_bw_cex_complete.cpp (99%) rename src/{summarizer => 2ls}/summarizer_bw_cex_complete.h (98%) rename src/{summarizer => 2ls}/summarizer_bw_cex_concrete.cpp (99%) rename src/{summarizer => 2ls}/summarizer_bw_cex_concrete.h (98%) rename src/{summarizer => 2ls}/summarizer_bw_cex_wp.cpp (99%) rename src/{summarizer => 2ls}/summarizer_bw_cex_wp.h (98%) create mode 100644 src/2ls/summary_checker_ai.cpp rename src/{summarizer => 2ls}/summary_checker_ai.h (77%) rename src/{summarizer => 2ls}/summary_checker_base.cpp (99%) rename src/{summarizer => 2ls}/summary_checker_base.h (97%) create mode 100644 src/2ls/summary_checker_bmc.cpp rename src/{summarizer => 2ls}/summary_checker_bmc.h (73%) create mode 100644 src/2ls/summary_checker_kind.cpp rename src/{summarizer => 2ls}/summary_checker_kind.h (73%) create mode 100644 src/2ls/version.h delete mode 100644 src/deltacheck/deltacheck_parse_options.cpp delete mode 100644 src/deltacheck/deltacheck_parse_options.h delete mode 100644 src/deltacheck/get_source.cpp delete mode 100644 src/deltacheck/get_source.h delete mode 100644 src/deltacheck/old/call_graph.cpp delete mode 100644 src/deltacheck/old/call_graph.h delete mode 100644 src/deltacheck/old/canonicalize.cpp delete mode 100644 src/deltacheck/old/canonicalize.h delete mode 100644 src/deltacheck/old/cgraph_builder.cpp delete mode 100644 src/deltacheck/old/cgraph_builder.h delete mode 100644 src/deltacheck/old/collect_symbols.cpp delete mode 100644 src/deltacheck/old/collect_symbols.h delete mode 100644 src/deltacheck/old/data_flow.cpp delete mode 100644 src/deltacheck/old/data_flow.h delete mode 100644 src/deltacheck/old/delta_check.cpp delete mode 100644 src/deltacheck/old/delta_check.h delete mode 100644 src/deltacheck/old/dependencies.cpp delete mode 100644 src/deltacheck/old/dependencies.h delete mode 100644 src/deltacheck/old/discover_predicates.cpp delete mode 100644 src/deltacheck/old/discover_predicates.h delete mode 100644 src/deltacheck/old/function_delta.cpp delete mode 100644 src/deltacheck/old/function_delta.h delete mode 100644 src/deltacheck/old/function_file_map.cpp delete mode 100644 src/deltacheck/old/function_file_map.h delete mode 100644 src/deltacheck/old/function_transformer.cpp delete mode 100644 src/deltacheck/old/function_transformer.h delete mode 100644 src/deltacheck/old/get_goto_program.cpp delete mode 100644 src/deltacheck/old/get_goto_program.h delete mode 100644 src/deltacheck/old/modular_analysis.h delete mode 100644 src/deltacheck/old/modular_code_analysis.cpp delete mode 100644 src/deltacheck/old/modular_code_analysis.h delete mode 100644 src/deltacheck/old/modular_fptr_analysis.cpp delete mode 100644 src/deltacheck/old/modular_fptr_analysis.h delete mode 100644 src/deltacheck/old/modular_globals_analysis.cpp delete mode 100644 src/deltacheck/old/modular_globals_analysis.h delete mode 100644 src/deltacheck/old/predicates.cpp delete mode 100644 src/deltacheck/old/predicates.h delete mode 100644 src/deltacheck/old/reporting.cpp delete mode 100644 src/deltacheck/old/reporting.h delete mode 100644 src/deltacheck/old/statement_transformer.cpp delete mode 100644 src/deltacheck/old/statement_transformer.h delete mode 100644 src/deltacheck/old/summarization.cpp delete mode 100644 src/deltacheck/old/summarization.h delete mode 100644 src/deltacheck/old/xml_conversion.cpp delete mode 100644 src/deltacheck/old/xml_conversion.h delete mode 100644 src/deltacheck/properties.cpp delete mode 100644 src/deltacheck/properties.h delete mode 100644 src/deltacheck/rename.cpp delete mode 100644 src/deltacheck/rename.h delete mode 100644 src/deltacheck/report_source_code.h delete mode 100644 src/deltacheck/show.cpp delete mode 100644 src/deltacheck/show.h delete mode 100644 src/deltacheck/source_diff.cpp delete mode 100644 src/deltacheck/source_diff.h delete mode 100644 src/deltacheck/ssa_fixed_point.cpp delete mode 100644 src/deltacheck/ssa_fixed_point.h delete mode 100644 src/deltacheck/statistics.cpp delete mode 100644 src/deltacheck/statistics.h delete mode 100644 src/deltacheck/summarize_function_pointers.cpp delete mode 100644 src/deltacheck/summarize_function_pointers.h delete mode 100644 src/deltacheck/version.h delete mode 100644 src/deltagit/deltagit_config.cpp delete mode 100644 src/deltagit/deltagit_config.h delete mode 100644 src/deltagit/deltagit_parse_options.cpp delete mode 100644 src/deltagit/deltagit_parse_options.h delete mode 100644 src/deltagit/do_job.cpp delete mode 100644 src/deltagit/do_job.h delete mode 100644 src/deltagit/git_branch.cpp delete mode 100644 src/deltagit/git_branch.h delete mode 100644 src/deltagit/git_log.cpp delete mode 100644 src/deltagit/git_log.h delete mode 100644 src/deltagit/init.cpp delete mode 100644 src/deltagit/init.h delete mode 100644 src/deltagit/job_status.cpp delete mode 100644 src/deltagit/job_status.h delete mode 100644 src/deltagit/log_scale.cpp delete mode 100644 src/deltagit/log_scale.h delete mode 100644 src/deltagit/log_scale.png delete mode 100644 src/deltagit/reanalyse.cpp delete mode 100644 src/deltagit/reanalyse.h delete mode 100644 src/deltagit/reset.cpp delete mode 100644 src/deltagit/reset.h delete mode 100644 src/deltagit/revisions_report.h delete mode 100644 src/deltagit/revisions_report_header.html delete mode 100644 src/deltagit/shell_escape.cpp delete mode 100644 src/deltagit/shell_escape.h delete mode 100644 src/deltagit/show_jobs.cpp delete mode 100644 src/deltagit/show_jobs.h delete mode 100644 src/deltagit/update.cpp delete mode 100644 src/deltagit/update.h delete mode 100755 src/dist-bin delete mode 100644 src/domains/domain_refinement.h delete mode 100644 src/domains/domain_refinement_variables.cpp delete mode 100644 src/domains/fixed_point.cpp delete mode 100644 src/domains/fixed_point.h delete mode 100644 src/domains/predicate.cpp delete mode 100644 src/domains/predicate.h delete mode 100644 src/domains/simplify_bounds.cpp delete mode 100644 src/domains/simplify_bounds.h delete mode 100644 src/domains/simplify_bounds_class.h delete mode 100644 src/domains/simplify_transformer.cpp delete mode 100644 src/domains/simplify_transformer.h delete mode 100644 src/domains/simplify_transformer_class.h delete mode 100644 src/domains/solver_enumeration.cpp delete mode 100644 src/domains/solver_enumeration.h delete mode 100644 src/domains/ssa_fixed_point.cpp delete mode 100644 src/domains/ssa_fixed_point.h delete mode 100644 src/domains/template_domain.cpp delete mode 100644 src/functions/call_graph.cpp delete mode 100644 src/functions/call_graph.h delete mode 100644 src/functions/get_function.h delete mode 100644 src/functions/index.h delete mode 100644 src/functions/path_util.cpp delete mode 100644 src/functions/path_util.h delete mode 100644 src/functions/summary.cpp delete mode 100644 src/functions/summary.h delete mode 100644 src/html/html_escape.cpp delete mode 100644 src/html/html_escape.h delete mode 100644 src/html/logo.cpp delete mode 100644 src/html/logo.h delete mode 100644 src/html/syntax_highlighting.cpp delete mode 100644 src/html/syntax_highlighting.h delete mode 100755 src/html/to_c_string.perl delete mode 100644 src/solver/fixed_point.cpp delete mode 100644 src/solver/fixed_point.h delete mode 100644 src/solver/predicate.cpp delete mode 100644 src/solver/predicate.h delete mode 100644 src/solver/solver.cpp delete mode 100644 src/solver/solver.h rename src/{summarizer => solver}/summarizer_base.cpp (64%) create mode 100644 src/solver/summarizer_base.h create mode 100644 src/solver/summarizer_bw.cpp create mode 100644 src/solver/summarizer_bw.h create mode 100644 src/solver/summarizer_bw_term.cpp create mode 100644 src/solver/summarizer_bw_term.h create mode 100644 src/solver/summarizer_fw.cpp create mode 100644 src/solver/summarizer_fw.h create mode 100644 src/solver/summarizer_fw_contexts.cpp create mode 100644 src/solver/summarizer_fw_contexts.h create mode 100644 src/solver/summarizer_fw_term.cpp create mode 100644 src/solver/summarizer_fw_term.h create mode 100644 src/solver/summary.cpp rename src/{summarizer => solver}/summary.h (84%) create mode 100644 src/solver/summary_db.cpp rename src/{summarizer => solver}/summary_db.h (83%) delete mode 100644 src/ssa/const_propagator.cpp delete mode 100644 src/ssa/const_propagator.h delete mode 100644 src/ssa/guard_domain.cpp delete mode 100644 src/ssa/guard_domain.h delete mode 100644 src/ssa/replace_symbol_ext.cpp delete mode 100644 src/ssa/replace_symbol_ext.h delete mode 100644 src/ssa/split_loopheads.cpp delete mode 100644 src/ssa/split_loopheads.h rename src/{summarizer => ssa}/ssa_db.cpp (93%) create mode 100644 src/ssa/ssa_db.h delete mode 100644 src/ssa/ssa_refiner_monolithic.cpp delete mode 100644 src/ssa/ssa_refiner_monolithic.h delete mode 100644 src/ssa/ssa_slicer.cpp delete mode 100644 src/ssa/ssa_slicer.h delete mode 100644 src/ssa/ssa_unwinder_old.cpp delete mode 100644 src/ssa/ssa_unwinder_old.h delete mode 100644 src/storefront/Makefile delete mode 100644 src/storefront/data.cpp delete mode 100644 src/storefront/data.h delete mode 100644 src/storefront/file_view.cpp delete mode 100644 src/storefront/file_view.h delete mode 100644 src/storefront/property_view.cpp delete mode 100644 src/storefront/property_view.h delete mode 100644 src/storefront/storefront_main.cpp delete mode 100644 src/storefront/storefront_parse_options.cpp delete mode 100644 src/storefront/storefront_parse_options.h delete mode 100644 src/storefront/trace_view.cpp delete mode 100644 src/storefront/trace_view.h delete mode 100644 src/summarizer/2ls-wrapper.sh delete mode 100644 src/summarizer/function_signature.cpp delete mode 100644 src/summarizer/function_signature.h delete mode 100644 src/summarizer/preprocessing_util.cpp delete mode 100644 src/summarizer/summarizer.cpp delete mode 100644 src/summarizer/summarizer.h delete mode 100644 src/summarizer/summarizer_bw_term.cpp delete mode 100644 src/summarizer/summarizer_bw_term.h delete mode 100644 src/summarizer/summarizer_fw_contexts.cpp delete mode 100644 src/summarizer/summarizer_fw_contexts.h delete mode 100644 src/summarizer/summarizer_fw_term.cpp delete mode 100644 src/summarizer/summarizer_fw_term.h delete mode 100644 src/summarizer/summarizer_parseoptions.cpp delete mode 100644 src/summarizer/summarizer_parseoptions.h delete mode 100644 src/summarizer/summary_checker.cpp delete mode 100644 src/summarizer/summary_checker.h delete mode 100644 src/summarizer/summary_checker_ai.cpp delete mode 100644 src/summarizer/summary_checker_bmc.cpp delete mode 100644 src/summarizer/summary_checker_kind.cpp diff --git a/src/summarizer/summarizer_languages.cpp b/src/2ls/2ls_languages.cpp similarity index 82% rename from src/summarizer/summarizer_languages.cpp rename to src/2ls/2ls_languages.cpp index 5973c3e93..0b23d3539 100644 --- a/src/summarizer/summarizer_languages.cpp +++ b/src/2ls/2ls_languages.cpp @@ -12,11 +12,11 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include "summarizer_parse_options.h" +#include "2ls_parse_options.h" /*******************************************************************\ -Function: summarizer_parse_optionst::register_languages +Function: twols_parse_optionst::register_languages Inputs: @@ -26,7 +26,7 @@ Function: summarizer_parse_optionst::register_languages \*******************************************************************/ -void summarizer_parse_optionst::register_languages() +void twols_parse_optionst::register_languages() { register_language(new_ansi_c_language); // register_language(new_cpp_language); diff --git a/src/deltacheck/deltacheck_main.cpp b/src/2ls/2ls_main.cpp similarity index 60% rename from src/deltacheck/deltacheck_main.cpp rename to src/2ls/2ls_main.cpp index c369d3d58..1d4955202 100644 --- a/src/deltacheck/deltacheck_main.cpp +++ b/src/2ls/2ls_main.cpp @@ -1,18 +1,20 @@ /*******************************************************************\ -Module: Main Module +Module: 2LS Main Module -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ #include -#include "deltacheck_parse_options.h" +#include "2ls_parse_options.h" + +#ifdef _MSC_VER /*******************************************************************\ -Function: main +Function: wmain Inputs: @@ -22,17 +24,31 @@ Function: main \*******************************************************************/ -#ifdef _MSC_VER int wmain(int argc, const wchar_t **argv_wide) { const char **argv=narrow_argv(argc, argv_wide); - deltacheck_parse_optionst parse_options(argc, argv); + twols_parse_optionst parse_options(argc, argv); return parse_options.main(); } + #else + +/*******************************************************************\ + +Function: main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + int main(int argc, const char **argv) { - deltacheck_parse_optionst parse_options(argc, argv); + twols_parse_optionst parse_options(argc, argv); return parse_options.main(); } + #endif diff --git a/src/summarizer/summarizer_parse_options.cpp b/src/2ls/2ls_parse_options.cpp similarity index 69% rename from src/summarizer/summarizer_parse_options.cpp rename to src/2ls/2ls_parse_options.cpp index 52a4c475e..b1363d104 100644 --- a/src/summarizer/summarizer_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -1,12 +1,12 @@ /*******************************************************************\ -Module: Summarizer Command Line Options Processing +Module: 2LS Command Line Options Processing -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#include +#include #include #include #include @@ -28,13 +28,12 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include +#include #include #include -#include #include #include #include -#include "array_abstraction.h" #include @@ -43,23 +42,28 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "version.h" -#include "../ssa/malloc_ssa.h" +#include -#include "summarizer_parse_options.h" -#include "summary_db.h" +#include "graphml_witness_ext.h" +#include + +#include "2ls_parse_options.h" #include "summary_checker_ai.h" #include "summary_checker_bmc.h" #include "summary_checker_kind.h" -#include "../ssa/split_loopheads.h" #include "show.h" #include "horn_encoding.h" -#define UNWIND_GOTO_INTO_LOOP 0 +#define UNWIND_GOTO_INTO_LOOP 1 #define REMOVE_MULTIPLE_DEREFERENCES 1 +#define IGNORE_RECURSION 1 +#define IGNORE_THREADS 1 +#define EXPLICIT_NONDET_LOCALS 0 +#define FILTER_ASSERTIONS 1 /*******************************************************************\ -Function: summarizer_parse_optionst::summarizer_parse_optionst +Function: twols_parse_optionst::twols_parse_optionst Inputs: @@ -69,16 +73,18 @@ Function: summarizer_parse_optionst::summarizer_parse_optionst \*******************************************************************/ -summarizer_parse_optionst::summarizer_parse_optionst(int argc, const char **argv): -parse_options_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit(cmdline, ui_message_handler), - ui_message_handler(cmdline, "2LS " SUMMARIZER_VERSION) +twols_parse_optionst::twols_parse_optionst(int argc, const char **argv): +parse_options_baset(TWOLS_OPTIONS, argc, argv), +language_uit(cmdline, ui_message_handler), + ui_message_handler(cmdline, "2LS " TWOLS_VERSION), + recursion_detected(false), + threads_detected(false) { } - + /*******************************************************************\ -Function: summarizer_parse_optionst::eval_verbosity +Function: twols_parse_optionst::eval_verbosity Inputs: @@ -88,11 +94,11 @@ Function: summarizer_parse_optionst::eval_verbosity \*******************************************************************/ -void summarizer_parse_optionst::eval_verbosity() +void twols_parse_optionst::eval_verbosity() { // this is our default verbosity int v=messaget::M_STATISTICS; - + if(cmdline.isset("verbosity")) { v=unsafe_string2int(cmdline.get_value("verbosity")); @@ -101,13 +107,13 @@ void summarizer_parse_optionst::eval_verbosity() else if(v>10) v=10; } - + ui_message_handler.set_verbosity(v); } /*******************************************************************\ -Function: summarizer_parse_optionst::get_command_line_options +Function: twols_parse_optionst::get_command_line_options Inputs: @@ -117,7 +123,7 @@ Function: summarizer_parse_optionst::get_command_line_options \*******************************************************************/ -void summarizer_parse_optionst::get_command_line_options(optionst &options) +void twols_parse_optionst::get_command_line_options(optionst &options) { if(config.set(cmdline)) { @@ -143,63 +149,13 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) if(cmdline.isset("inline")) options.set_option("inline", true); - if(cmdline.isset("spurious-check")) - options.set_option("spurious-check", cmdline.get_value("spurious-check")); - else - options.set_option("spurious-check", "all"); - if(cmdline.isset("slice") && cmdline.isset("inline")) options.set_option("slice", true); else options.set_option("slice", false); - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check overflow on floats - if(cmdline.isset("float-overflow-check")) - options.set_option("float-overflow-check", true); - else - options.set_option("float-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // check for memory leaks - if(cmdline.isset("memory-leak-check")) - options.set_option("memory-leak-check", true); - else - options.set_option("memory-leak-check", false); + // all checks supported by goto_check + GOTO_CHECK_PARSE_OPTIONS(cmdline, options); // check assertions if(cmdline.isset("no-assertions")) @@ -207,12 +163,6 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) else options.set_option("assertions", true); - // SSA equality propagation - if(cmdline.isset("ssa-propagation")) - options.set_option("ssa-propagation", true); - else - options.set_option("ssa-propagation", false); - // use assumptions if(cmdline.isset("no-assumptions")) options.set_option("assumptions", false); @@ -225,11 +175,6 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) else options.set_option("refine", false); - if(cmdline.isset("unit-check")) - options.set_option("unit-check", true); - else - options.set_option("unit-check", false); - // compute standard invariants (include value at loop entry) if(cmdline.isset("std-invariants")) options.set_option("std-invariants", true); @@ -247,7 +192,7 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("equalities", true); options.set_option("std-invariants", true); } - else + else { if(cmdline.isset("zones")) options.set_option("zones", true); @@ -255,12 +200,12 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) options.set_option("qzones", true); else if(cmdline.isset("octagons")) options.set_option("octagons", true); - else //if(cmdline.isset("intervals")) //default + else // if(cmdline.isset("intervals")) // default options.set_option("intervals", true); - if(cmdline.isset("enum-solver")) + if(cmdline.isset("enum-solver")) options.set_option("enum-solver", true); - else //if(cmdline.isset("binsearch-solver")) //default + else // if(cmdline.isset("binsearch-solver")) // default options.set_option("binsearch-solver", true); } @@ -290,44 +235,55 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) { options.set_option("monolithic-ranking-function", true); } - else options.set_option("monolithic-ranking-function", false); + else + options.set_option("monolithic-ranking-function", false); if(cmdline.isset("lexicographic-ranking-function")) { - options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); + options.set_option( + "lexicographic-ranking-function", + cmdline.get_value("lexicographic-ranking-function")); } - else options.set_option("lexicographic-ranking-function",3); + else + options.set_option("lexicographic-ranking-function", 5); if(cmdline.isset("max-inner-ranking-iterations")) { - options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); + options.set_option( + "max-inner-ranking-iterations", + cmdline.get_value("max-inner-ranking-iterations")); } - else options.set_option("max-inner-ranking-iterations",20); + else + options.set_option("max-inner-ranking-iterations", 50); // do k-induction refinement if(cmdline.isset("k-induction")) { options.set_option("std-invariants", true); options.set_option("k-induction", true); -// options.set_option("inline", true); + options.set_option("inline", true); if(!cmdline.isset("unwind")) - options.set_option("unwind",UINT_MAX); + options.set_option("unwind", UINT_MAX); } // do incremental bmc if(cmdline.isset("incremental-bmc")) { options.set_option("incremental-bmc", true); -// options.set_option("inline", true); + options.set_option("inline", true); options.set_option("havoc", true); if(!cmdline.isset("unwind")) - options.set_option("unwind",UINT_MAX); + options.set_option("unwind", UINT_MAX); } + // check for spuriousness of assertion failures + if(cmdline.isset("no-spurious-check")) + options.set_option("spurious-check", false); + else + options.set_option("spurious-check", true); + // all properties (default) - if(cmdline.isset("no-all-properties")) + if(cmdline.isset("stop-on-fail")) options.set_option("all-properties", false); else options.set_option("all-properties", true); @@ -343,13 +299,15 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) { options.set_option("competition-mode", true); options.set_option("all-properties", false); + options.set_option("inline", true); } // instrumentation / output if(cmdline.isset("instrument-output")) - options.set_option("instrument-output", - cmdline.get_value("instrument-output")); - + options.set_option( + "instrument-output", + cmdline.get_value("instrument-output")); + #ifdef SHOW_CALLING_CONTEXTS if(cmdline.isset("show-calling-contexts")) @@ -357,22 +315,23 @@ void summarizer_parse_optionst::get_command_line_options(optionst &options) if(!options.get_bool_option("intervals")) throw "--show-calling-contexts only possible with --intervals"; options.set_option("show-calling-contexts", true); - options.set_option("do-not-analyze-functions", - cmdline.get_value("show-calling-contexts")); + options.set_option( + "do-not-analyze-functions", + cmdline.get_value("show-calling-contexts")); } #endif if(cmdline.isset("show-trace")) options.set_option("show-trace", true); - if(cmdline.isset("graphml-cex")) - options.set_option("graphml-cex", cmdline.get_value("graphml-cex")); + if(cmdline.isset("graphml-witness")) + options.set_option("graphml-witness", cmdline.get_value("graphml-witness")); if(cmdline.isset("json-cex")) options.set_option("json-cex", cmdline.get_value("json-cex")); } /*******************************************************************\ -Function: summarizer_parse_optionst::doit +Function: twols_parse_optionst::doit Inputs: @@ -382,14 +341,14 @@ Function: summarizer_parse_optionst::doit \*******************************************************************/ -int summarizer_parse_optionst::doit() +int twols_parse_optionst::doit() { if(cmdline.isset("version")) { - std::cout << SUMMARIZER_VERSION << std::endl; + std::cout << TWOLS_VERSION << std::endl; return 0; } - + // // command line options // @@ -398,11 +357,12 @@ int summarizer_parse_optionst::doit() get_command_line_options(options); eval_verbosity(); - + // // Print a banner // - status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; + status() << "2LS version " TWOLS_VERSION " (based on CBMC " CBMC_VERSION ")" + << eom; goto_modelt goto_model; @@ -418,7 +378,7 @@ int summarizer_parse_optionst::doit() } // options for various debug outputs - + if(cmdline.isset("show-ssa")) { bool simplify=!cmdline.isset("no-simplify"); @@ -427,14 +387,6 @@ int summarizer_parse_optionst::doit() return 7; } - if(cmdline.isset("show-fixed-points")) - { - bool simplify=!cmdline.isset("no-simplify"); - irep_idt function=cmdline.get_value("function"); - show_fixed_points(goto_model, function, simplify, std::cout, ui_message_handler); - return 7; - } - if(cmdline.isset("show-defs")) { irep_idt function=cmdline.get_value("function"); @@ -455,7 +407,7 @@ int summarizer_parse_optionst::doit() show_guards(goto_model, function, std::cout, ui_message_handler); return 7; } - + if(cmdline.isset("show-value-sets")) { irep_idt function=cmdline.get_value("function"); @@ -463,15 +415,33 @@ int summarizer_parse_optionst::doit() return 7; } - if(cmdline.isset("show-invariants")) + if(cmdline.isset("show-invariants")) { options.set_option("show-invariants", true); } +#if IGNORE_RECURSION + if(recursion_detected) + { + status() << "Recursion not supported" << eom; + report_unknown(); + return 5; + } +#endif + +#if IGNORE_THREADS + if(threads_detected) + { + status() << "Threads not supported" << eom; + report_unknown(); + return 5; + } +#endif + if(cmdline.isset("context-sensitive")) { options.set_option("context-sensitive", true); - status() << "Context-sensitive analysis from " << + status() << "Context-sensitive analysis from " << goto_model.goto_functions.entry_point() << eom; } @@ -481,7 +451,7 @@ int summarizer_parse_optionst::doit() status() << "Do not ignore array contents" << eom; } - //TODO: check option inconsistencies, ignored options etc + // TODO: check option inconsistencies, ignored options etc if(options.get_bool_option("havoc")) status() << "Havocking loops and function calls" << eom; else if(options.get_bool_option("equalities")) @@ -494,39 +464,45 @@ int summarizer_parse_optionst::doit() status() << "Using zones domain"; else if(options.get_bool_option("octagons")) status() << "Using octagons domain"; - else assert(false); + else + assert(false); + if(options.get_bool_option("enum-solver")) status() << " with enumeration solver"; else if(options.get_bool_option("binsearch-solver")) status() << " with binary search solver"; - else assert(false); + else + assert(false); + status() << eom; } - + try { - summary_checker_baset *summary_checker = NULL; - if(!options.get_bool_option("k-induction") && + std::unique_ptr checker; + if(!options.get_bool_option("k-induction") && + !options.get_bool_option("incremental-bmc")) + checker=std::unique_ptr( + new summary_checker_ait(options)); + if(options.get_bool_option("k-induction") && !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_ait(options); - if(options.get_bool_option("k-induction") && - !options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_kindt(options); - if(!options.get_bool_option("k-induction") && - options.get_bool_option("incremental-bmc")) - summary_checker = new summary_checker_bmct(options); - - summary_checker->set_message_handler(get_message_handler()); - summary_checker->simplify=!cmdline.isset("no-simplify"); - summary_checker->fixed_point=!cmdline.isset("no-fixed-point"); + checker=std::unique_ptr( + new summary_checker_kindt(options)); + if(!options.get_bool_option("k-induction") && + options.get_bool_option("incremental-bmc")) + checker=std::unique_ptr( + new summary_checker_bmct(options)); + + checker->set_message_handler(get_message_handler()); + checker->simplify=!cmdline.isset("no-simplify"); + checker->fixed_point=!cmdline.isset("no-fixed-point"); int retval; if(cmdline.isset("show-vcc")) { std::cout << "VERIFICATION CONDITIONS:\n\n"; - summary_checker->show_vcc=true; - (*summary_checker)(goto_model); - delete summary_checker; + checker->show_vcc=true; + (*checker)(goto_model); return 0; } @@ -534,9 +510,9 @@ int summarizer_parse_optionst::doit() { status() << "Horn-clause encoding" << eom; namespacet ns(symbol_table); - + std::string out_file=cmdline.get_value("horn-encoding"); - + if(out_file=="-") { horn_encoding(goto_model, std::cout); @@ -548,62 +524,66 @@ int summarizer_parse_optionst::doit() #else std::ofstream out(out_file.c_str()); #endif - + if(!out) { error() << "Failed to open output file " << out_file << eom; - delete summary_checker; return 1; } - + horn_encoding(goto_model, out); } - - delete summary_checker; + return 0; } - - bool report_assertions = + + bool report_assertions= !options.get_bool_option("preconditions") && !options.get_bool_option("termination"); // do actual analysis - switch((*summary_checker)(goto_model)) + switch((*checker)(goto_model)) { case property_checkert::PASS: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); + if(report_assertions) + report_properties(options, goto_model, checker->property_map); report_success(); - retval = 0; + if(cmdline.isset("graphml-witness") && + !options.get_bool_option("termination")) + output_graphml_proof(options, goto_model, *checker); + retval=0; break; - + case property_checkert::FAIL: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); + if(report_assertions) + report_properties(options, goto_model, checker->property_map); report_failure(); - retval = 10; + if(cmdline.isset("graphml-witness")) + { + output_graphml_cex(options, goto_model, *checker); + } + retval=10; break; case property_checkert::UNKNOWN: - if(report_assertions) - report_properties(options,goto_model, summary_checker->property_map); - retval = 5; + if(report_assertions) + report_properties(options, goto_model, checker->property_map); + retval=5; report_unknown(); break; - + default: assert(false); } if(cmdline.isset("instrument-output")) { - summary_checker->instrument_and_output(goto_model); + checker->instrument_and_output(goto_model); } - delete summary_checker; return retval; } - + catch(const std::string error_msg) { error() << error_msg << messaget::eom; @@ -616,7 +596,7 @@ int summarizer_parse_optionst::doit() return 8; } -#if 0 +#if 0 // let's log some more statistics debug() << "Memory consumption:" << messaget::endl; memory_info(debug()); @@ -624,43 +604,52 @@ int summarizer_parse_optionst::doit() #endif } +/*******************************************************************\ + +Function: twols_parse_optionst::type_stats_rec + + Inputs: + + Outputs: + Purpose: -void summarizer_parse_optionst::type_stats_rec( +\*******************************************************************/ + +void twols_parse_optionst::type_stats_rec( const typet &type, expr_statst &stats, const namespacet &ns) { - if(type.id()==ID_symbol) type_stats_rec(ns.follow(type), stats, ns); - - if(type.id()==ID_pointer || type.id()==ID_array) - { - stats.has_array=true; - - const typet &subtype=ns.follow(type.subtype()); - - if(subtype.id()==ID_signedbv || + + if(type.id()==ID_pointer || type.id()==ID_array) + { + stats.has_array=true; + + const typet &subtype=ns.follow(type.subtype()); + + if(subtype.id()==ID_signedbv || subtype.id()==ID_unsignedbv) { - stats.has_string=(to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); - } - } - + stats.has_string= + (to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); + } + } + if(type.has_subtypes()) { - forall_subtypes(it, type) - { - type_stats_rec(*it, stats, ns); - } - } + forall_subtypes(it, type) + { + type_stats_rec(*it, stats, ns); + } + } } - /*******************************************************************\ -Function: summarizer_parse_optionst::expr_stats_rec +Function: twols_parse_optionst::expr_stats_rec Inputs: @@ -670,12 +659,10 @@ Function: summarizer_parse_optionst::expr_stats_rec \*******************************************************************/ - -void summarizer_parse_optionst::expr_stats_rec( +void twols_parse_optionst::expr_stats_rec( const exprt &expr, expr_statst &stats) { - if(expr.id()==ID_side_effect) { const side_effect_exprt &side_effect_expr=to_side_effect_expr(expr); @@ -691,24 +678,19 @@ void summarizer_parse_optionst::expr_stats_rec( } } - if(expr.id()==ID_symbol ) - { - - } - if(expr.has_operands()) { forall_operands(it, expr) { - expr_stats_rec(*it, stats); + expr_stats_rec(*it, stats); } } } - + /*******************************************************************\ -Function: summarizer_parse_optionst::show_stats +Function: twols_parse_optionst::show_stats Inputs: @@ -718,10 +700,10 @@ Function: summarizer_parse_optionst::show_stats \*******************************************************************/ -void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) +void twols_parse_optionst::show_stats( + const goto_modelt &goto_model, + std::ostream &out) { - const namespacet ns(goto_model.symbol_table); expr_statst stats; @@ -733,14 +715,15 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, // analyze all the functions forall_goto_functions(f_it, goto_model.goto_functions) { - if(!f_it->second.body_available()) continue; + if(!f_it->second.body_available()) + continue; ++nr_functions; const goto_programt &goto_program=f_it->second.body; #if 0 - statistics() << "function size of " << f_it->first << ": " + statistics() << "function size of " << f_it->first << ": " << goto_program.instructions.size() << eom; #endif @@ -752,15 +735,16 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, nr_instructions++; const goto_programt::instructiont &instruction=*i_it; - if(i_it->is_backwards_goto()) nr_loops++; + if(i_it->is_backwards_goto()) + nr_loops++; switch(instruction.type) { case ASSIGN: { const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); + expr_stats_rec(assign.lhs(), stats); + expr_stats_rec(assign.rhs(), stats); } break; case ASSUME: @@ -772,13 +756,14 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, case GOTO: expr_stats_rec(instruction.guard, stats); break; - + case DECL: // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - - break; - + type_stats_rec( + to_code_decl(instruction.code).symbol().type(), stats, ns); + + break; + default: // skip break; @@ -786,10 +771,10 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, } // forall instructions } // forall functions - out << " =============== STATS =============== " << std::endl; - out << " nr of instructions: " << nr_instructions << std::endl; - out << " nr of functions: " << nr_functions << std::endl; - out << " nr of loops: " << nr_loops << std::endl; + out << "=============== STATS=============== " << std::endl; + out << " nr of instructions: " << nr_instructions << std::endl; + out << " nr of functions: " << nr_functions << std::endl; + out << " nr of loops: " << nr_loops << std::endl; out << " malloc: " << (stats.has_malloc ? "YES" : "NO") << std::endl; out << " arrays: " << (stats.has_array ? "YES" : "NO") << std::endl; out << " strings: " << (stats.has_string ? "YES" : "NO") << std::endl; @@ -798,7 +783,7 @@ void summarizer_parse_optionst::show_stats(const goto_modelt &goto_model, /*******************************************************************\ -Function: summarizer_parse_optionst::set_properties +Function: twols_parse_optionst::set_properties Inputs: @@ -808,7 +793,7 @@ Function: summarizer_parse_optionst::set_properties \*******************************************************************/ -bool summarizer_parse_optionst::set_properties(goto_modelt &goto_model) +bool twols_parse_optionst::set_properties(goto_modelt &goto_model) { try { @@ -827,18 +812,18 @@ bool summarizer_parse_optionst::set_properties(goto_modelt &goto_model) error() << e << eom; return true; } - + catch(int) { return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::require_entry +Function: twols_parse_optionst::require_entry Inputs: @@ -847,19 +832,20 @@ Function: summarizer_parse_optionst::require_entry Purpose: \*******************************************************************/ - -void summarizer_parse_optionst::require_entry( + +void twols_parse_optionst::require_entry( const goto_modelt &goto_model) { irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(goto_model.symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) + + if(goto_model.symbol_table.symbols.find(entry_point)== + symbol_table.symbols.end()) throw "The program has no entry point; please complete linking"; } /*******************************************************************\ -Function: summarizer_parse_optionst::get_goto_program +Function: twols_parse_optionst::get_goto_program Inputs: @@ -868,8 +854,8 @@ Function: summarizer_parse_optionst::get_goto_program Purpose: \*******************************************************************/ - -bool summarizer_parse_optionst::get_goto_program( + +bool twols_parse_optionst::get_goto_program( const optionst &options, goto_modelt &goto_model) { @@ -886,10 +872,10 @@ bool summarizer_parse_optionst::get_goto_program( { status() << "Reading GOTO program from file" << eom; - if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) + if(read_goto_binary( + cmdline.args[0], goto_model, get_message_handler())) return true; - + config.set_from_symbol_table(goto_model.symbol_table); if(cmdline.isset("show-symbol-table")) @@ -905,48 +891,51 @@ bool summarizer_parse_optionst::get_goto_program( error() << "Please give one source file only" << eom; return true; } - + std::string filename=cmdline.args[0]; - + #ifdef _MSC_VER std::ifstream infile(widen(filename).c_str()); #else std::ifstream infile(filename.c_str()); #endif - + if(!infile) { error() << "failed to open input file `" << filename << "'" << eom; return true; } - + languaget *language=get_language_from_filename(filename); - + if(language==NULL) { - error() << "failed to figure out type of file `" << filename << "'" << eom; + error() << "failed to figure out type of file `" << filename << "'" + << eom; return true; } - + language->set_message_handler(get_message_handler()); - + status("Parsing", filename); - + if(language->parse(infile, filename)) { error() << "PARSING ERROR" << eom; return true; } - + language->show_parse(std::cout); return true; } else { - - if(parse()) return true; - if(typecheck()) return true; - if(final()) return true; + if(parse()) + return true; + if(typecheck()) + return true; + if(final()) + return true; // we no longer need any parse trees or language files clear_parse(); @@ -959,14 +948,14 @@ bool summarizer_parse_optionst::get_goto_program( #if 0 irep_idt entry_point=goto_model.goto_functions.entry_point(); - + if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) { error() << "No entry point; please provide a main function" << eom; return true; } #endif - + status() << "Generating GOTO Program" << eom; goto_convert(symbol_table, goto_model, ui_message_handler); @@ -991,24 +980,24 @@ bool summarizer_parse_optionst::get_goto_program( error() << e << eom; return true; } - + catch(int) { return true; } - + catch(std::bad_alloc) { error() << "Out of memory" << eom; return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::process_goto_program +Function: twols_parse_optionst::process_goto_program Inputs: @@ -1017,22 +1006,26 @@ Function: summarizer_parse_optionst::process_goto_program Purpose: \*******************************************************************/ - -bool summarizer_parse_optionst::process_goto_program( + +bool twols_parse_optionst::process_goto_program( const optionst &options, goto_modelt &goto_model) { try { + status() << "Function Pointer Removal" << eom; + remove_function_pointers( + goto_model, cmdline.isset("pointer-check")); + // do partial inlining if(options.get_bool_option("inline-partial")) { - unsigned limit = options.get_unsigned_int_option("inline-partial"); + unsigned limit=options.get_unsigned_int_option("inline-partial"); status() << "Performing partial inlining (" << limit << ")" << eom; goto_partial_inline(goto_model, ui_message_handler, limit/2); - //TODO: where is limit multiplied by 2??? + // TODO: where is limit multiplied by 2??? - //remove inlined functions + // remove inlined functions Forall_goto_functions(f_it, goto_model.goto_functions) if(f_it->first!=ID__start && f_it->second.body.instructions.size()<=2*(limit/2)) @@ -1040,50 +1033,53 @@ bool summarizer_parse_optionst::process_goto_program( f_it->second.body.clear(); } } - - // add generic checks - status() << "Generic Property Instrumentation" << eom; - goto_check(options, goto_model); - status() << "Function Pointer Removal" << eom; - remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); +#if IGNORE_THREADS + threads_detected=has_threads(goto_model); +#endif // remove returns (must be done after function pointer removal) remove_returns(goto_model); - - -#if UNWIND_GOTO_INTO_LOOP - unwind_goto_into_loop(goto_model,2); -#endif - - remove_skip(goto_model.goto_functions); - goto_model.goto_functions.update(); // now do full inlining, if requested if(options.get_bool_option("inline")) { status() << "Performing full inlining" << eom; - if(goto_inline(goto_model, ui_message_handler)) - { - report_unknown(); - return 5; - } + const namespacet ns(goto_model.symbol_table); + goto_inlinet goto_inline( + goto_model.goto_functions, ns, ui_message_handler); + goto_inline(); +#if IGNORE_RECURSION + recursion_detected=goto_inline.recursion_detected(); +#endif } - //preprocessing to improve the structure of the SSA for the unwinder +#if REMOVE_MULTIPLE_DEREFERENCES + remove_multiple_dereferences(goto_model); +#endif + + // add generic checks + status() << "Generic Property Instrumentation" << eom; + goto_check(options, goto_model); + +#if UNWIND_GOTO_INTO_LOOP + unwind_goto_into_loop(goto_model, 2); +#endif + + remove_skip(goto_model.goto_functions); + goto_model.goto_functions.update(); + + // preprocessing to improve the structure of the SSA for the unwinder split_loopheads(goto_model); - //explicitly initialize all local variables +#if EXPLICIT_NONDET_LOCALS + // explicitly initialize all local variables nondet_locals(goto_model); - -#if 1 - //TODO: find a better place for that - replace_malloc(goto_model,""); #endif -#if REMOVE_MULTIPLE_DEREFERENCES - remove_multiple_dereferences(goto_model); +#if 1 + // TODO: find a better place for that + replace_malloc(goto_model, ""); #endif // recalculate numbers, etc. @@ -1092,12 +1088,21 @@ bool summarizer_parse_optionst::process_goto_program( // add loop ids goto_model.goto_functions.compute_loop_numbers(); - //inline __CPROVER_initialize and main + // inline __CPROVER_initialize and main if(cmdline.isset("inline-main")) { - inline_main(goto_model); + inline_main(goto_model); + } + + if(!cmdline.isset("independent-properties")) + { + add_assumptions_after_assertions(goto_model); } +#ifdef FILTER_ASSERTIONS + filter_assertions(goto_model); +#endif + if(!cmdline.isset("no-propagation")) { status() << "Constant Propagation" << eom; @@ -1116,14 +1121,6 @@ bool summarizer_parse_optionst::process_goto_program( return true; } - // do array abstraction - if(cmdline.isset("array-abstraction")) - { - status() << "Performing array abstraction" << eom; - array_abstraction(goto_model.symbol_table, ui_message_handler, - goto_model.goto_functions); - } - label_properties(goto_model); if(cmdline.isset("show-properties")) @@ -1155,24 +1152,24 @@ bool summarizer_parse_optionst::process_goto_program( error() << e << eom; return true; } - + catch(int) { return true; } - + catch(std::bad_alloc) { error() << "Out of memory" << eom; return true; } - + return false; } /*******************************************************************\ -Function: summarizer_parse_optionst::report_properties +Function: twols_parse_optionst::report_properties Inputs: @@ -1182,7 +1179,7 @@ Function: summarizer_parse_optionst::report_properties \*******************************************************************/ -void summarizer_parse_optionst::report_properties( +void twols_parse_optionst::report_properties( const optionst &options, const goto_modelt &goto_model, const property_checkert::property_mapt &property_map) @@ -1192,14 +1189,22 @@ void summarizer_parse_optionst::report_properties( it!=property_map.end(); it++) { - if(it->first=="") //TODO: some properties do not show up in initialize_property_map - continue; +#if 1 + // TODO: some properties do not show up in initialize_property_map + if(it->first=="") + continue; +#endif + + if(!options.get_bool_option("all-properties") && + it->second.result!=property_checkert::FAIL) + continue; if(get_ui()==ui_message_handlert::XML_UI) { xmlt xml_result("result"); xml_result.set_attribute("property", id2string(it->first)); - xml_result.set_attribute("status", property_checkert::as_string(it->second.result)); + xml_result.set_attribute( + "status", property_checkert::as_string(it->second.result)); std::cout << xml_result << "\n"; } else @@ -1214,14 +1219,13 @@ void summarizer_parse_optionst::report_properties( if(cmdline.isset("show-trace") && it->second.result==property_checkert::FAIL) show_counterexample(goto_model, it->second.error_trace); - if(cmdline.isset("graphml-cex") && - it->second.result==property_checkert::FAIL) - output_graphml_cex(options,goto_model, it->second.error_trace); if(cmdline.isset("json-cex") && it->second.result==property_checkert::FAIL) - output_json_cex(options, - goto_model, it->second.error_trace, - id2string(it->first)); + output_json_cex( + options, + goto_model, + it->second.error_trace, + id2string(it->first)); } if(!cmdline.isset("property")) @@ -1241,19 +1245,19 @@ void summarizer_parse_optionst::report_properties( if(it->second.result==property_checkert::FAIL) failed++; } - + status() << "** " << unknown << " of " << property_map.size() << " unknown" - << eom; + << eom; status() << "** " << failed << " of " << property_map.size() << " failed" - << eom; + << eom; } } /*******************************************************************\ -Function: summarizer_parse_optionst::report_success +Function: twols_parse_optionst::report_success Inputs: @@ -1263,7 +1267,7 @@ Function: summarizer_parse_optionst::report_success \*******************************************************************/ -void summarizer_parse_optionst::report_success() +void twols_parse_optionst::report_success() { result() << "VERIFICATION SUCCESSFUL" << eom; @@ -1271,7 +1275,7 @@ void summarizer_parse_optionst::report_success() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: { xmlt xml("cprover-status"); @@ -1280,7 +1284,7 @@ void summarizer_parse_optionst::report_success() std::cout << std::endl; } break; - + default: assert(false); } @@ -1288,7 +1292,7 @@ void summarizer_parse_optionst::report_success() /*******************************************************************\ -Function: summarizer_parse_optionst::show_counterexample +Function: twols_parse_optionst::show_counterexample Inputs: @@ -1298,7 +1302,7 @@ Function: summarizer_parse_optionst::show_counterexample \*******************************************************************/ -void summarizer_parse_optionst::show_counterexample( +void twols_parse_optionst::show_counterexample( const goto_modelt &goto_model, const goto_tracet &error_trace) { @@ -1310,7 +1314,7 @@ void summarizer_parse_optionst::show_counterexample( std::cout << std::endl << "Counterexample:" << std::endl; show_goto_trace(std::cout, ns, error_trace); break; - + case ui_message_handlert::XML_UI: { xmlt xml; @@ -1318,7 +1322,7 @@ void summarizer_parse_optionst::show_counterexample( std::cout << xml << std::endl; } break; - + default: assert(false); } @@ -1326,7 +1330,7 @@ void summarizer_parse_optionst::show_counterexample( /*******************************************************************\ -Function: summarizer_parse_optionst::output_graphml_cex +Function: twols_parse_optionst::output_graphml_cex Inputs: @@ -1336,31 +1340,71 @@ Function: summarizer_parse_optionst::output_graphml_cex \*******************************************************************/ -void summarizer_parse_optionst::output_graphml_cex( +void twols_parse_optionst::output_graphml_cex( const optionst &options, const goto_modelt &goto_model, - const goto_tracet &error_trace) + const summary_checker_baset &summary_checker) +{ + for(const auto &p : summary_checker.property_map) + { + if(p.second.result!=property_checkert::FAIL) + continue; + + const namespacet ns(goto_model.symbol_table); + const std::string graphml=options.get_option("graphml-witness"); + if(!graphml.empty()) + { + graphml_witnesst graphml_witness(ns); + graphml_witness(p.second.error_trace); + + if(graphml=="-") + write_graphml(graphml_witness.graph(), std::cout); + else + { + std::ofstream out(graphml.c_str()); + write_graphml(graphml_witness.graph(), out); + } + } + break; + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::output_graphml_proof + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::output_graphml_proof( + const optionst &options, + const goto_modelt &goto_model, + const summary_checker_baset &summary_checker) { const namespacet ns(goto_model.symbol_table); - const std::string graphml=options.get_option("graphml-cex"); + const std::string graphml=options.get_option("graphml-witness"); if(!graphml.empty()) { - graphmlt cex_graph; - convert(ns, error_trace, cex_graph); + graphml_witness_extt graphml_witness(ns); + graphml_witness(summary_checker); if(graphml=="-") - write_graphml(cex_graph, std::cout); + write_graphml(graphml_witness.graph(), std::cout); else { std::ofstream out(graphml.c_str()); - write_graphml(cex_graph, out); + write_graphml(graphml_witness.graph(), out); } } } - /*******************************************************************\ -Function: summarizer_parse_optionst::output_json_cex +Function: twols_parse_optionst::output_json_cex Inputs: @@ -1370,7 +1414,7 @@ Function: summarizer_parse_optionst::output_json_cex \*******************************************************************/ -void summarizer_parse_optionst::output_json_cex( +void twols_parse_optionst::output_json_cex( const optionst &options, const goto_modelt &goto_model, const goto_tracet &error_trace, @@ -1381,22 +1425,23 @@ void summarizer_parse_optionst::output_json_cex( const namespacet ns(goto_model.symbol_table); jsont json_trace; convert(ns, error_trace, json_trace); - + if(options.get_option("json-cex")=="-") { std::cout << json_trace; } else { - std::ofstream out((options.get_option("json-cex")+"-"+property_id+".json").c_str()); + std::ofstream out( + (options.get_option("json-cex")+"-"+property_id+".json").c_str()); out << json_trace << '\n'; } - } + } } /*******************************************************************\ -Function: summarizer_parse_optionst::report_failure +Function: twols_parse_optionst::report_failure Inputs: @@ -1406,7 +1451,7 @@ Function: summarizer_parse_optionst::report_failure \*******************************************************************/ -void summarizer_parse_optionst::report_failure() +void twols_parse_optionst::report_failure() { result() << "VERIFICATION FAILED" << eom; @@ -1414,7 +1459,7 @@ void summarizer_parse_optionst::report_failure() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: { xmlt xml("cprover-status"); @@ -1423,7 +1468,7 @@ void summarizer_parse_optionst::report_failure() std::cout << std::endl; } break; - + default: assert(false); } @@ -1431,7 +1476,7 @@ void summarizer_parse_optionst::report_failure() /*******************************************************************\ -Function: summarizer_parse_optionst::report_unknown +Function: twols_parse_optionst::report_unknown Inputs: @@ -1441,7 +1486,7 @@ Function: summarizer_parse_optionst::report_unknown \*******************************************************************/ -void summarizer_parse_optionst::report_unknown() +void twols_parse_optionst::report_unknown() { result() << "VERIFICATION INCONCLUSIVE" << eom; @@ -1449,7 +1494,7 @@ void summarizer_parse_optionst::report_unknown() { case ui_message_handlert::PLAIN: break; - + case ui_message_handlert::XML_UI: { xmlt xml("cprover-status"); @@ -1458,7 +1503,7 @@ void summarizer_parse_optionst::report_unknown() std::cout << std::endl; } break; - + default: assert(false); } @@ -1466,7 +1511,7 @@ void summarizer_parse_optionst::report_unknown() /*******************************************************************\ -Function: summarizer_parse_optionst::help +Function: twols_parse_optionst::help Inputs: @@ -1476,17 +1521,16 @@ Function: summarizer_parse_optionst::help \*******************************************************************/ -void summarizer_parse_optionst::help() +void twols_parse_optionst::help() { std::cout << "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2015 * *\n" + "* * 2LS " TWOLS_VERSION "-Copyright (C) 2014-2017 * *\n" // NOLINT(*) "* * (based on CBMC " CBMC_VERSION " "; - std::cout << "(" << (sizeof(void *)*8) << "-bit version))"; - + std::cout << " * *\n"; - + std::cout << "* * Daniel Kroening, Peter Schrammel * *\n" "* * University of Oxford * *\n" @@ -1494,15 +1538,15 @@ void summarizer_parse_optionst::help() "\n" "Usage: Purpose:\n" "\n" - " summarizer [-?] [-h] [--help] show help\n" - " summarizer file.c ... source file names\n" + " 2ls [-?] [-h] [--help] show help\n" + " 2ls file.c ... source file names\n" "\n" "Frontend options:\n" " -I path set include path (C/C++)\n" " -D macro define preprocessor macro (C/C++)\n" " --preprocess stop after preprocessing\n" " --16, --32, --64 set width of int\n" - " --LP64, --ILP64, --LLP64,\n" + " --LP64, --ILP64, --LLP64, \n" " --ILP32, --LP32 set width of int, long and pointers\n" " --little-endian allow little-endian word-byte conversions\n" " --big-endian allow big-endian word-byte conversions\n" @@ -1511,47 +1555,36 @@ void summarizer_parse_optionst::help() " --show-parse-tree show parse tree\n" " --show-symbol-table show symbol table\n" " --show-goto-functions show goto program\n" - " --arch set architecture (default: " - << configt::this_architecture() << ")\n" - " --os set operating system (default: " - << configt::this_operating_system() << ")\n" + " --arch set architecture (default: " << configt::this_architecture() << ")\n" // NOLINT(*) + " --os set operating system (default: " << configt::this_operating_system() << ")\n" // NOLINT(*) #ifdef _WIN32 " --gcc use GCC as preprocessor\n" #endif " --no-arch don't set up an architecture\n" " --no-library disable built-in abstract C library\n" - " --round-to-nearest IEEE floating point rounding mode (default)\n" + " --round-to-nearest IEEE floating point rounding mode (default)\n" // NOLINT(*) " --round-to-plus-inf IEEE floating point rounding mode\n" " --round-to-minus-inf IEEE floating point rounding mode\n" " --round-to-zero IEEE floating point rounding mode\n" "\n" "Program instrumentation options:\n" - " --bounds-check enable array bounds checks\n" - " --pointer-check enable pointer checks\n" - " --array-abstraction use array and string abstraction for bounds checks\n" - " --div-by-zero-check enable division by zero checks\n" - " --memory-leak-check enable memory leak checks\n" - " --signed-overflow-check enable arithmetic over- and underflow checks\n" - " --unsigned-overflow-check enable arithmetic over- and underflow checks\n" - " --nan-check check floating-point for NaN\n" + GOTO_CHECK_HELP " --error-label label check that label is unreachable\n" " --show-properties show the properties\n" " --no-assertions ignore user assertions\n" " --no-assumptions ignore user assumptions\n" " --inline inline all functions into main\n" - " --inline-partial nr inline functions smaller than the given nr of instructions\n" - " --instrument-output f output inferred information in goto-binary f\n" + " --inline-partial nr inline functions smaller than the given nr of instructions\n" // NOLINT(*) "\n" "Backend options:\n" " --all-functions check each function as entry point\n" - " --no-all-properties stop on first failing assertion\n" - " --context-sensitive context-sensitive analysis from entry point\n" - " --termination compute ranking functions to prove termination\n" + " --stop-on-fail stop on first failing assertion\n" + " --context-sensitive context-sensitive analysis from entry point\n" // NOLINT(*) + " --termination compute ranking functions to prove termination\n" // NOLINT(*) " --k-induction use k-induction\n" - " --unit-check check each function (similar to a unit test)\n" " --incremental-bmc use incremental-bmc\n" " --preconditions compute preconditions\n" - " --sufficient sufficient preconditions (default: necessary)\n" + " --sufficient sufficient preconditions (default: necessary)\n" // NOLINT(*) " --havoc havoc loops and function calls\n" " --intervals use interval domain\n" " --equalities use equalities and disequalities domain\n" diff --git a/src/summarizer/summarizer_parse_options.h b/src/2ls/2ls_parse_options.h similarity index 64% rename from src/summarizer/summarizer_parse_options.h rename to src/2ls/2ls_parse_options.h index f06b469b2..d564b1279 100644 --- a/src/summarizer/summarizer_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -1,13 +1,13 @@ /*******************************************************************\ -Module: Command Line Parsing +Module: 2LS Command Line Parsing -Author: Daniel Kroening, kroening@kroening.com +Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_PARSE_OPTIONS_H -#define CPROVER_SUMMARIZER_PARSE_OPTIONS_H +#ifndef CPROVER_2LS_2LS_2LS_PARSE_OPTIONS_H +#define CPROVER_2LS_2LS_2LS_PARSE_OPTIONS_H #include #include @@ -15,20 +15,19 @@ Author: Daniel Kroening, kroening@kroening.com #include +#include + class goto_modelt; class optionst; #include "summary_checker_base.h" -#define SUMMARIZER_OPTIONS \ +#define TWOLS_OPTIONS \ "(xml-ui)" \ "(function):" \ "D:I:" \ "(depth):(context-bound):(unwind):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)(memory-leak-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)" \ - "(float-overflow-check)(nan-check)" \ - "(array-abstraction)(refine)" \ + GOTO_CHECK_OPTIONS \ "(non-incremental)" \ "(no-assertions)(no-assumptions)" \ "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ @@ -46,21 +45,19 @@ class optionst; "(lexicographic-ranking-function):(monolithic-ranking-function)" \ "(max-inner-ranking-iterations):" \ "(preconditions)(sufficient)" \ - "(instrument-output):" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-fixed-points)(show-stats)" \ + "(show-locs)(show-vcc)(show-properties)(show-trace)(show-stats)" \ "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ - "(show-calling-contexts):" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ - "(no-spurious-check)(unit-check)" \ + "(no-spurious-check)(all-functions)" \ "(no-simplify)(no-fixed-point)" \ - "(graphml-cex):(json-cex):" \ - "(spurious-check):(no-all-properties)" \ - "(competition-mode)(slice)(no-propagation)(ssa-propagation)" \ + "(graphml-witness):(json-cex):" \ + "(no-spurious-check)(stop-on-fail)" \ + "(competition-mode)(slice)(no-propagation)(independent-properties)" \ "(no-unwinding-assertions)" // the last line is for CBMC-regression testing only -class summarizer_parse_optionst: +class twols_parse_optionst: public parse_options_baset, public language_uit { @@ -68,15 +65,17 @@ class summarizer_parse_optionst: virtual int doit(); virtual void help(); - summarizer_parse_optionst(int argc, const char **argv); - summarizer_parse_optionst( + twols_parse_optionst(int argc, const char **argv); + twols_parse_optionst( int argc, const char **argv, const std::string &extra_options); protected: ui_message_handlert ui_message_handler; - virtual void register_languages(); + bool recursion_detected; + bool threads_detected; + virtual void register_languages(); void get_command_line_options(optionst &options); @@ -87,7 +86,7 @@ class summarizer_parse_optionst: bool process_goto_program( const optionst &options, goto_modelt &goto_model); - + bool set_properties(goto_modelt &); void report_success(); @@ -96,7 +95,7 @@ class summarizer_parse_optionst: void report_properties( const optionst &options, const goto_modelt &, - const summary_checker_baset::property_mapt &); + const summary_checker_baset::property_mapt &); void show_counterexample( const goto_modelt &, @@ -105,46 +104,54 @@ class summarizer_parse_optionst: void output_graphml_cex( const optionst &options, const goto_modelt &, - const class goto_tracet &); + const summary_checker_baset &summary_checker); + + void output_graphml_proof( + const optionst &options, + const goto_modelt &goto_model, + const summary_checker_baset &summary_checker); void output_json_cex( const optionst &options, const goto_modelt &goto_model, const goto_tracet &error_trace, const std::string &property_id); - - struct expr_statst { + + struct expr_statst + { bool has_malloc; bool has_string; bool has_array; bool has_pointer; - - expr_statst() - : has_malloc(false) - , has_string(false) - , has_array(false) - , has_pointer(false) - {} - }; - + + expr_statst(): + has_malloc(false), + has_string(false), + has_array(false), + has_pointer(false) + { + } + }; + void type_stats_rec( const typet &type, expr_statst &stats, const namespacet &ns); - + void expr_stats_rec( const exprt &expr, - expr_statst &stats); - + expr_statst &stats); + void show_stats( const goto_modelt &, - std::ostream &); - + std::ostream &); + void eval_verbosity(); void report_unknown(); - void require_entry( - const goto_modelt &goto_model); + void require_entry(const goto_modelt &goto_model); + + bool has_threads(const goto_modelt &goto_model); // diverse preprocessing void inline_main(goto_modelt &goto_model); @@ -152,10 +159,21 @@ class summarizer_parse_optionst: void nondet_locals(goto_modelt &goto_model); void unwind_goto_into_loop(goto_modelt &goto_model, unsigned k); void replace_types_rec(const replace_symbolt &replace_const, exprt &expr); - exprt evaluate_casts_in_constants(const exprt &expr, const typet& parent_type, - bool &valid); + exprt evaluate_casts_in_constants( + const exprt &expr, + const typet& parent_type, + bool &valid); void remove_multiple_dereferences(goto_modelt &goto_model); - void remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen); + void remove_multiple_dereferences( + goto_modelt &goto_model, + goto_programt &body, + goto_programt::targett t, + exprt &expr, + unsigned &var_counter, + bool deref_seen); + void add_assumptions_after_assertions(goto_modelt &goto_model); + void filter_assertions(goto_modelt &goto_model); + void split_loopheads(goto_modelt &goto_model); }; #endif diff --git a/src/2ls/Makefile b/src/2ls/Makefile new file mode 100644 index 000000000..fcd7b93c7 --- /dev/null +++ b/src/2ls/Makefile @@ -0,0 +1,60 @@ +include ../config.inc +include $(CBMC)/src/config.inc +include $(CBMC)/src/common + +SRC = 2ls_main.cpp 2ls_parse_options.cpp \ + 2ls_languages.cpp \ + show.cpp summary_checker_base.cpp \ + summary_checker_ai.cpp summary_checker_bmc.cpp \ + summary_checker_kind.cpp \ + cover_goals_ext.cpp horn_encoding.cpp \ + preprocessing_util.cpp \ + instrument_goto.cpp dynamic_cfg.cpp \ + summarizer_bw_cex_ai.cpp summarizer_bw_cex_complete.cpp summarizer_bw_cex.cpp \ + summarizer_bw_cex_all.cpp summarizer_bw_cex_concrete.cpp summarizer_bw_cex_wp.cpp \ + graphml_witness_ext.cpp \ + + +OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ + $(CBMC)/src/linking/linking$(LIBEXT) \ + $(CBMC)/src/assembler/assembler$(LIBEXT) \ + $(CBMC)/src/big-int/big-int$(LIBEXT) \ + $(CBMC)/src/goto-programs/goto-programs$(LIBEXT) \ + $(CBMC)/src/goto-symex/goto-symex$(LIBEXT) \ + $(CBMC)/src/analyses/analyses$(LIBEXT) \ + $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ + $(CBMC)/src/langapi/langapi$(LIBEXT) \ + $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ + $(CBMC)/src/json/json$(LIBEXT) \ + $(CBMC)/src/solvers/solvers$(LIBEXT) \ + $(CBMC)/src/util/util$(LIBEXT) \ + $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ + ../domains/domains$(LIBEXT) \ + ../ssa/ssa$(LIBEXT) \ + ../solver/solver$(LIBEXT) \ + +CP_CXXFLAGS+= $(TWOLSFLAGS) + +INCLUDES= -I $(CBMC)/src -I .. + +LIBS = + +CLEANFILES = 2ls$(EXEEXT) $(DELTA_OBJ) + +all: 2ls$(EXEEXT) + +ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) + OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) + CP_CXXFLAGS += -DHAVE_CPP +endif + +ifneq ($(wildcard $(CBMC)/src/java/Makefile),) + OBJ += $(CBMC)/src/java/java$(LIBEXT) + CXXFLAGS += -DHAVE_JAVA +endif + +############################################################################### + +2ls$(EXEEXT): $(OBJ) + $(LINKBIN) + diff --git a/src/summarizer/cover_goals_ext.cpp b/src/2ls/cover_goals_ext.cpp similarity index 56% rename from src/summarizer/cover_goals_ext.cpp rename to src/2ls/cover_goals_ext.cpp index dc154a671..7331999df 100644 --- a/src/summarizer/cover_goals_ext.cpp +++ b/src/2ls/cover_goals_ext.cpp @@ -1,5 +1,6 @@ /*******************************************************************\ + Module: Cover a set of goals incrementally Author: Daniel Kroening, kroening@kroening.com @@ -9,7 +10,8 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include "../ssa/ssa_build_goto_trace.h" +#include + #include "cover_goals_ext.h" /*******************************************************************\ @@ -53,10 +55,10 @@ void cover_goals_extt::mark() _number_covered++; } } - + /*******************************************************************\ -Function: cover_goals_extt::constaint +Function: cover_goals_extt::constraint Inputs: @@ -118,21 +120,21 @@ Function: cover_goals_extt::operator() void cover_goals_extt::operator()() { _iterations=_number_covered=0; - + decision_proceduret::resultt dec_result; - + // We use incremental solving, so need to freeze some variables - // to prevent them from being eliminated. + // to prevent them from being eliminated. freeze_goal_variables(); do { // We want (at least) one of the remaining goals, please! _iterations++; - + constraint(); - - dec_result = solver(); + + dec_result=solver(); switch(dec_result) { @@ -141,12 +143,13 @@ void cover_goals_extt::operator()() case decision_proceduret::D_SATISFIABLE: // mark the goals we got - mark(); - + mark(); + // notify assignment(); - if(!all_properties) return; //exit on first failure if requested + if(!all_properties) + return; // exit on first failure if requested break; default: @@ -172,72 +175,71 @@ Function: cover_goals_extt::assignment void cover_goals_extt::assignment() { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) + // check loop head choices in model + bool invariants_involved=false; + if(spurious_check) { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.l_get(g_it->condition).is_true()) + for(exprt::operandst::const_iterator l_it=loophead_selects.begin(); + l_it!=loophead_selects.end(); l_it++) { -#if 1 - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(g_it->cond_expression); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); -#else // THE ASSERTIONS THAT FAIL COULD BE RATHER ARBITRARY SINCE THE FORMULA - // IS NOT "ROOTED" IN AN INITIAL STATE. - assert((g_it->cond_expression).id() == ID_not); - exprt conjunct_expr = (g_it->cond_expression).op0(); -#if 0 - std::cout << "FAILED EXPR: " - << from_expr(SSA.ns, "", conjunct_expr) << std::endl; -#endif - - if(conjunct_expr.id() != ID_and) - { - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(g_it->cond_expression); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); - } - else + if(solver.get(l_it->op0()).is_true()) { - //filter out assertion instances that are not violated - exprt::operandst failed_exprs; - for(exprt::operandst::const_iterator c_it = - conjunct_expr.operands().begin(); - c_it != conjunct_expr.operands().end(); c_it++) - { - literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_false()) - { - failed_exprs.push_back(*c_it); -#if 0 - std::cout << "failed_expr: " - << from_expr(SSA.ns, "", *c_it) << std::endl; -#endif - } - } - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); + invariants_involved=true; + break; } -#endif } - if(property_map[it->first].result == property_checkert::FAIL) + } + if(!invariants_involved || !spurious_check) + { + std::list::const_iterator g_it=goals.begin(); + for(goal_mapt::const_iterator it=goal_map.begin(); + it!=goal_map.end(); it++, g_it++) { - if(build_error_trace) - { - ssa_build_goto_tracet build_goto_trace(SSA,solver.get_solver()); - build_goto_trace(property_map[it->first].error_trace); + if(property_map[it->first].result==property_checkert::UNKNOWN && + solver.l_get(g_it->condition).is_true()) + { + if(spurious_check) + { + assert((g_it->cond_expression).id() == ID_not); + exprt conjunct_expr = (g_it->cond_expression).op0(); + + if(conjunct_expr.id() != ID_and) + { + solver.pop_context(); //otherwise this would interfere with necessary preconditions + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result = summarizer_bw_cex.check(); + solver.new_context(); + } + else + { + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it = + conjunct_expr.operands().begin(); + c_it != conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal = solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_false()) + failed_exprs.push_back(*c_it); + } + solver.pop_context(); //otherwise this would interfere with necessary preconditions + for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); + if(property_map[it->first].result == + property_checkert::FAIL) + break; } + solver.new_context(); } - if(!all_properties && - property_map[it->first].result == property_checkert::FAIL) - break; } - - _iterations++; //statistics + else + property_map[it->first].result = property_checkert::FAIL; + } + } + + _iterations++; // statistics + } } - + diff --git a/src/summarizer/cover_goals_ext.h b/src/2ls/cover_goals_ext.h similarity index 76% rename from src/summarizer/cover_goals_ext.h rename to src/2ls/cover_goals_ext.h index a2673bebb..c3a7b781c 100644 --- a/src/summarizer/cover_goals_ext.h +++ b/src/2ls/cover_goals_ext.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_COVER_GOALS_H -#define CPROVER_SUMMARIZER_COVER_GOALS_H +#ifndef CPROVER_2LS_2LS_COVER_GOALS_EXT_H +#define CPROVER_2LS_2LS_COVER_GOALS_EXT_H #include #include @@ -28,7 +28,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//cover goals extended with spuriousness check +// cover goals extended with spuriousness check struct goalt { @@ -40,7 +40,7 @@ struct goalt { description=id2string(instruction.source_location.get_comment()); } - + goalt() { } @@ -48,6 +48,27 @@ struct goalt class cover_goals_extt:public messaget { +/* +public: + explicit inline cover_goals_extt( + unwindable_local_SSAt &_SSA, + incremental_solvert &_solver, + const exprt::operandst& _loophead_selects, + property_checkert::property_mapt &_property_map, + bool _spurious_check, bool _all_properties, bool _build_error_trace, + summarizer_bw_cex_baset &_summarizer_bw_cex): + SSA(_SSA), + solver(_solver), + property_map(_property_map), + spurious_check(_spurious_check), + all_properties(_all_properties), + build_error_trace(_build_error_trace), + loophead_selects(_loophead_selects), + summarizer_bw_cex(_summarizer_bw_cex) + { + } +*/ + public: explicit inline cover_goals_extt(unwindable_local_SSAt &_SSA, incremental_solvert &_solver, @@ -62,8 +83,8 @@ class cover_goals_extt:public messaget all_properties(_all_properties), build_error_trace(_build_error_trace), summarizer_bw_cex(_summarizer_bw_cex) - {} - +{} + virtual ~cover_goals_extt(); void operator()(); @@ -75,7 +96,7 @@ class cover_goals_extt:public messaget literalt condition; bool covered; exprt cond_expression; - + cover_goalt():covered(false) { } @@ -83,8 +104,8 @@ class cover_goals_extt:public messaget typedef std::list goalst; goalst goals; - - typedef std::map goal_mapt; + + typedef std::map goal_mapt; goal_mapt goal_map; // statistics @@ -93,17 +114,17 @@ class cover_goals_extt:public messaget { return _number_covered; } - + inline unsigned iterations() const { return _iterations; } - + inline goalst::size_type size() const { return goals.size(); } - + // managing the goals inline void add(const literalt condition) @@ -111,7 +132,7 @@ class cover_goals_extt:public messaget goals.push_back(cover_goalt()); goals.back().condition=condition; } - + inline void add(const exprt cond_expression) { goals.push_back(cover_goalt()); @@ -124,7 +145,8 @@ class cover_goals_extt:public messaget unsigned _number_covered, _iterations; incremental_solvert &solver; property_checkert::property_mapt &property_map; - bool all_properties, build_error_trace; + bool spurious_check, all_properties, build_error_trace; + exprt::operandst loophead_selects; summarizer_bw_cex_baset &summarizer_bw_cex; // this method is called for each satisfying assignment diff --git a/src/2ls/dynamic_cfg.cpp b/src/2ls/dynamic_cfg.cpp new file mode 100644 index 000000000..807ec5ed3 --- /dev/null +++ b/src/2ls/dynamic_cfg.cpp @@ -0,0 +1,301 @@ +/*******************************************************************\ + +Module: Dynamic Control Flow Graph + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "dynamic_cfg.h" + +#include + +/*******************************************************************\ + +Function: dynamic_cfgt::operator() + + Inputs: + + Outputs: + + Purpose: generates the dynamic CFG + +\*******************************************************************/ + +void dynamic_cfgt::operator()( + const ssa_local_unwindert &ssa_unwinder, + const unwindable_local_SSAt &ssa, + const summaryt &summary) +{ + const goto_programt &goto_program=ssa.goto_function.body; + build_cfg(goto_program, ssa_unwinder); + + assumptionst assumptions; + build_from_invariants(ssa, summary, assumptions); + add_assumptions(assumptions); +} + +/*******************************************************************\ + +Function: operator== + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool operator==(const dynamic_cfg_idt &a, const dynamic_cfg_idt &b) +{ + return a.pc==b.pc && a.iteration_stack==b.iteration_stack; +} + +/*******************************************************************\ + +Function: dynamic_cfgt::add_assumptions + + Inputs: + + Outputs: + + Purpose: annotates the nodes with assumptions + +\*******************************************************************/ + +void dynamic_cfgt::add_assumptions(const assumptionst &assumptions) +{ + for(const auto &a : assumptions) + { + (*this)[a.first].assumption=a.second; + } +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_cfg + + Inputs: + + Outputs: + + Purpose: extracts assumptions from invariants + +\*******************************************************************/ + +void dynamic_cfgt::build_cfg( + const goto_programt &goto_program, + const ssa_local_unwindert &ssa_unwinder) +{ + std::vector iteration_stack; + std::vector loop_node_stack; + std::vector max_iteration_stack; + std::map > incoming_edges; + + forall_goto_program_instructions(it, goto_program) + { + node_indext node=add_node(); + nodes[node].id.pc=it; + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=false; + nodes[node].assumption=nil_exprt(); + + // this is the end of a loop + // (sink self-loops are translated into assume false in the SSA) + if(it->is_backwards_goto() && + it->get_target()!=it) + { +#if 0 + // Sanity checks + const ssa_local_unwindert::loopt *loop=nullptr; + if(!ssa_unwinder.find_loop(it->get_target()->location_number, loop)) + { + std::cout << "NON-LOOP BACKEDGE? " << it->location_number + << " --> " << it->get_target()->location_number << std::endl; + assert(false); + } + assert(!iteration_stack.empty()); + assert(!max_iteration_stack.empty()); +#endif + + // max_unwind reached + if(iteration_stack.back()==max_iteration_stack.back()) + { + iteration_stack.pop_back(); + max_iteration_stack.pop_back(); + + // add back-edge + add_edge(node, loop_node_stack.back()); + + loop_node_stack.pop_back(); + } + // max_unwind not reached + else + { + // add edges for end of loop + const std::set &iedges=incoming_edges[it]; + for(const auto &from : iedges) + add_edge(from, node); + incoming_edges.erase(it); + + // jump back to loop head + it=it->get_target(); + iteration_stack.back()++; + node_indext new_node=add_node(); + nodes[new_node].id.iteration_stack=iteration_stack; + nodes[new_node].id.pc=it; + nodes[new_node].is_loop_head=false; + nodes[new_node].assumption=nil_exprt(); + + // add forward edge to unwound loop head + add_edge(node, new_node); + + // remember forward gotos + if(it->is_goto() && !it->is_backwards_goto()) + incoming_edges[it->get_target()].insert(new_node); + goto_programt::const_targett next=it; ++next; + if(next!=goto_program.instructions.end() && + (!it->is_goto() || !it->guard.is_true())) + incoming_edges[next].insert(new_node); + + continue; + } + } + // reconstruct sink self-loops + else if(it->is_backwards_goto() && + it->get_target()==it) + { + nodes[node].is_loop_head=true; + add_edge(node, node); + continue; + } + + // remember forward gotos + if(it->is_goto() && !it->is_backwards_goto()) + incoming_edges[it->get_target()].insert(node); + goto_programt::const_targett next=it; ++next; + if(next!=goto_program.instructions.end() && + (!it->is_goto() || !it->guard.is_true())) + incoming_edges[next].insert(node); + + // this is a loop head + const ssa_local_unwindert::loopt *loop=nullptr; + if(ssa_unwinder.find_loop(it->location_number, loop)) + { + iteration_stack.push_back(0); + loop_node_stack.push_back(node); + assert(loop->current_unwinding>=0); + max_iteration_stack.push_back(loop->current_unwinding); + nodes[node].id.iteration_stack=iteration_stack; + nodes[node].is_loop_head=true; + } + + const std::set &iedges=incoming_edges[it]; + for(const auto &from : iedges) + add_edge(from, node); + incoming_edges.erase(it); + } +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_from_invariant + + Inputs: + + Outputs: + + Purpose: extracts assumption from invariant + +\*******************************************************************/ + +void dynamic_cfgt::build_from_invariant( + const unwindable_local_SSAt &ssa, + const exprt &invariant, + assumptionst &assumptions) +{ + dynamic_cfg_idt id; + if(invariant.op0().id()==ID_symbol) + ssa.get_full_ssa_name( + to_symbol_expr(invariant.op0()), + id.pc, + id.iteration_stack); + else if(invariant.op0().id()==ID_and && + invariant.op0().op0().id()==ID_symbol) + ssa.get_full_ssa_name( + to_symbol_expr(invariant.op0().op0()), + id.pc, + id.iteration_stack); + else + assert(false); + + for(auto &a : assumptions) + { + // update existing + if(a.first==id) + { + exprt::operandst e; + if(a.second.id()==ID_and) + e=a.second.operands(); + else + e.push_back(a.second); + + exprt cexpr=invariant.op1(); // copy + clean_expr(cexpr); + e.push_back(cexpr); + a.second=conjunction(e); + return; + } + } + + // create new + assumptions.push_back(assumptiont()); + assumptions.back().first=id; + assumptions.back().second=invariant.op1(); // copy + clean_expr(assumptions.back().second); +} + +/*******************************************************************\ + +Function: dynamic_cfgt::build_from_invariants + + Inputs: + + Outputs: + + Purpose: extracts assumptions from invariants + +\*******************************************************************/ + +void dynamic_cfgt::build_from_invariants( + const unwindable_local_SSAt &ssa, + const summaryt &summary, + assumptionst &assumptions) +{ + if(summary.fw_invariant.is_nil() || + summary.fw_invariant.is_true()) + return; + + // expected format /\_i g_i=> inv_i + if(summary.fw_invariant.id()==ID_implies) + { + build_from_invariant( + ssa, summary.fw_invariant, + assumptions); + } + else if(summary.fw_invariant.id()==ID_and) + { + for(unsigned i=0; i +#include +#include + +#include +#include +#include + + +/*******************************************************************\ + + Class: dynamic_cfgt + + Purpose: + +\*******************************************************************/ + +struct dynamic_cfg_edget +{ +}; + +struct dynamic_cfg_idt +{ + goto_programt::const_targett pc; + std::vector iteration_stack; + // TODO: thread id + + std::string to_string() const + { + std::ostringstream sid; + sid << i2string(pc->location_number); + for(const auto &i : iteration_stack) + sid << "." < +{ + dynamic_cfg_idt id; + bool is_loop_head; + exprt assumption; +}; + +class dynamic_cfgt:public graph +{ +public: + inline dynamic_cfg_nodet& operator[](const dynamic_cfg_idt &id) + { + for(auto &n : nodes) + { + if(n.id==id) + return n; + } + + node_indext node=add_node(); + nodes[node].id=id; + return nodes[node]; + } + + inline const nodet &operator[](node_indext n) const + { + return nodes[n]; + } + + // TODO: generalise this to non-inlined programs + void operator()( + const ssa_local_unwindert &ssa_unwinder, + const unwindable_local_SSAt &ssa, + const summaryt &summary); + +protected: + typedef std::pair assumptiont; + typedef std::vector assumptionst; + + void build_cfg( + const goto_programt &goto_program, + const ssa_local_unwindert &ssa_unwinder); + + void build_from_invariants( + const unwindable_local_SSAt &ssa, + const summaryt &summary, + assumptionst &assumptions); + void build_from_invariant( + const unwindable_local_SSAt &ssa, + const exprt &invariant, + assumptionst &assumptions); + + void add_assumptions(const assumptionst &assumptions); +}; + +#endif // CPROVER_2LS_SUMMARIZER_DYNAMIC_CFG_H diff --git a/src/2ls/graphml_witness_ext.cpp b/src/2ls/graphml_witness_ext.cpp new file mode 100644 index 000000000..11fa4bc3b --- /dev/null +++ b/src/2ls/graphml_witness_ext.cpp @@ -0,0 +1,148 @@ +/*******************************************************************\ + +Module: SSA CFA extension for GraphML output + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "graphml_witness_ext.h" + +/*******************************************************************\ + +Function: graphml_witness_extt::operator() + + Inputs: + + Outputs: + + Purpose: proof witness + TODO: works only for inlined programs + +\*******************************************************************/ + +void graphml_witness_extt::operator()( + const summary_checker_baset &summary_checker) +{ + irep_idt function_name=ID__start; + const unwindable_local_SSAt &ssa= + static_cast( + summary_checker.ssa_db.get(function_name)); + summaryt summary; + if(summary_checker.summary_db.exists(function_name)) + summary=summary_checker.summary_db.get(function_name); + const ssa_local_unwindert &ssa_unwinder= + summary_checker.ssa_unwinder.get(function_name); + + graphml.key_values["sourcecodelang"]="C"; + + dynamic_cfgt cfg; + cfg(ssa_unwinder, ssa, summary); + + // CFG to CFA + const graphmlt::node_indext sink=graphml.add_node(); + graphml[sink].node_name="sink"; + graphml[sink].thread_nr=0; + graphml[sink].is_violation=false; + graphml[sink].has_invariant=false; + + std::vector index_map; + index_map.resize(cfg.size()); + for(std::size_t i=0; isource_location; + + if(source_location.is_nil() || + source_location.get_file().empty() || + source_location.get_file()=="" || + source_location.get_line().empty()) + { + index_map[i]=sink; + continue; + } + + const graphmlt::node_indext node=add_node(cfg[i]); + index_map[i]=node; + } + for(std::size_t i=0; ifunction); + } + return node; +} + +/*******************************************************************\ + +Function: graphml_witness_extt::add_edge + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void graphml_witness_extt::add_edge( + const graphmlt::node_indext &from, + const dynamic_cfg_nodet &from_cfg_node, + const graphmlt::node_indext &to, + const dynamic_cfg_nodet &to_cfg_node) +{ + const source_locationt &source_location=from_cfg_node.id.pc->source_location; + + xmlt edge("edge"); + edge.set_attribute("source", graphml[from].node_name); + edge.set_attribute("target", graphml[to].node_name); + + { + xmlt &data_f=edge.new_element("data"); + data_f.set_attribute("key", "originfile"); + data_f.data=id2string(source_location.get_file()); + + xmlt &data_l=edge.new_element("data"); + data_l.set_attribute("key", "startline"); + data_l.data=id2string(source_location.get_line()); + + if(to_cfg_node.is_loop_head) + { + xmlt &data_l=edge.new_element("data"); + data_l.set_attribute("key", "enterLoopHead"); + data_l.data="true"; + } + } + + graphml[to].in[from].xml_node=edge; + graphml[from].out[to].xml_node=edge; +} diff --git a/src/2ls/graphml_witness_ext.h b/src/2ls/graphml_witness_ext.h new file mode 100644 index 000000000..df62a2b6d --- /dev/null +++ b/src/2ls/graphml_witness_ext.h @@ -0,0 +1,44 @@ +/*******************************************************************\ + +Module: SSA CFA extension for GraphML output + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_2LS_GRAPHML_WITNESS_EXT_H +#define CPROVER_2LS_2LS_GRAPHML_WITNESS_EXT_H + +#include + +#include + +#include "dynamic_cfg.h" +#include "summary_checker_base.h" + + +class graphml_witness_extt:public graphml_witnesst +{ +public: + explicit graphml_witness_extt(const namespacet &ns): + graphml_witnesst(ns) {} + + // correctness witness + void operator()(const summary_checker_baset &summary_checker); + +protected: + graphmlt::node_indext add_node( + std::map &loc_to_node, + goto_programt::const_targett it); + + void add_edge( + const graphmlt::node_indext &from, + const dynamic_cfg_nodet &from_cfg_node, + const graphmlt::node_indext &to, + const dynamic_cfg_nodet &to_cfg_node); + + graphmlt::node_indext add_node( + const dynamic_cfg_nodet &cfg_node); +}; + +#endif // CPROVER_2LS_SUMMARIZER_GRAPHML_WITNESS_EXT_H diff --git a/src/summarizer/horn_encoding.cpp b/src/2ls/horn_encoding.cpp similarity index 91% rename from src/summarizer/horn_encoding.cpp rename to src/2ls/horn_encoding.cpp index 481f336ee..fb986a691 100644 --- a/src/summarizer/horn_encoding.cpp +++ b/src/2ls/horn_encoding.cpp @@ -4,26 +4,16 @@ Module: Horn-clause Encoding Author: Daniel Kroening -Date: June 2015 - \*******************************************************************/ #include #include -#include "../ssa/local_ssa.h" +#include #include "horn_encoding.h" -/*******************************************************************\ - - Class: horn_encodingt - - Purpose: - -\*******************************************************************/ - class horn_encodingt { public: @@ -36,16 +26,16 @@ class horn_encodingt smt2_conv(ns, "", "Horn-clause encoding", "", smt2_convt::Z3, _out) { } - + void operator()(); protected: const goto_functionst &goto_functions; const namespacet ns; std::ostream &out; - + smt2_convt smt2_conv; - + void translate(const goto_functionst::function_mapt::const_iterator); }; @@ -94,7 +84,7 @@ void horn_encodingt::translate( local_SSAt local_SSA(f_it->second, ns, ""); const goto_programt &body=f_it->second.body; - + // first generate the predicates for all locations for(goto_programt::instructionst::const_iterator loc=body.instructions.begin(); @@ -109,7 +99,8 @@ void horn_encodingt::translate( o_it!=local_SSA.ssa_objects.objects.end(); o_it++) { - if(o_it!=local_SSA.ssa_objects.objects.begin()) out << ' '; + if(o_it!=local_SSA.ssa_objects.objects.begin()) + out << ' '; out << '('; smt2_conv.convert_expr(o_it->symbol_expr()); out << ' '; @@ -119,10 +110,10 @@ void horn_encodingt::translate( out << ") Bool)\n"; } - + out << '\n'; - // now encode transitions + // now encode transitions for(goto_programt::instructionst::const_iterator loc=body.instructions.begin(); loc!=body.instructions.end(); @@ -141,7 +132,8 @@ void horn_encodingt::translate( o_it!=local_SSA.ssa_objects.objects.end(); o_it++) { - if(o_it!=local_SSA.ssa_objects.objects.begin()) out << ' '; + if(o_it!=local_SSA.ssa_objects.objects.begin()) + out << ' '; out << '('; smt2_conv.convert_expr(o_it->symbol_expr()); out << ' '; @@ -150,9 +142,9 @@ void horn_encodingt::translate( } out << ")\n"; - out << " (=> (h-" << f_it->first << '-' + out << " (=> (h-" << f_it->first << '-' << loc->location_number; - + for(ssa_objectst::objectst::const_iterator o_it=local_SSA.ssa_objects.objects.begin(); o_it!=local_SSA.ssa_objects.objects.end(); @@ -161,7 +153,7 @@ void horn_encodingt::translate( out << ' '; smt2_conv.convert_expr(o_it->symbol_expr()); } - + out << ")\n "; if(loc->is_goto()) @@ -208,7 +200,6 @@ void horn_encodingt::translate( } else { - #if 0 for(local_SSAt::nodet::constraintst::const_iterator it=node.constraints.begin(); @@ -229,7 +220,7 @@ void horn_encodingt::translate( } #endif } - + out << ")))"; // =>, forall, assert out << '\n'; diff --git a/src/summarizer/horn_encoding.h b/src/2ls/horn_encoding.h similarity index 79% rename from src/summarizer/horn_encoding.h rename to src/2ls/horn_encoding.h index 33475adfb..a266ed46a 100644 --- a/src/summarizer/horn_encoding.h +++ b/src/2ls/horn_encoding.h @@ -6,8 +6,8 @@ Module: Horn-clause Encoding \*******************************************************************/ -#ifndef CPROVER_HORN_ENCODING_H -#define CPROVER_HORN_ENCODING_H +#ifndef CPROVER_2LS_2LS_HORN_ENCODING_H +#define CPROVER_2LS_2LS_HORN_ENCODING_H #include diff --git a/src/summarizer/instrument_goto.cpp b/src/2ls/instrument_goto.cpp similarity index 50% rename from src/summarizer/instrument_goto.cpp rename to src/2ls/instrument_goto.cpp index 7cd4b4f3f..3e550c6e1 100644 --- a/src/summarizer/instrument_goto.cpp +++ b/src/2ls/instrument_goto.cpp @@ -2,13 +2,13 @@ Module: Instrument Goto Program with Inferred Information -Author: Peter Schrammel +Author: Peter Schrammel, Björn Wachter \*******************************************************************/ #include -#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -16,21 +16,33 @@ Author: Peter Schrammel #include "instrument_goto.h" +/*******************************************************************\ + +Function: find_loop_by_guard + + Inputs: + + Outputs: + Purpose: -local_SSAt::locationt find_loop_by_guard(const local_SSAt &SSA, - const symbol_exprt &guard) +\*******************************************************************/ + +local_SSAt::locationt find_loop_by_guard( + const local_SSAt &SSA, + const symbol_exprt &guard) { - std::string gstr = id2string(guard.get_identifier()); - unsigned pos1 = gstr.find("#")+1; - unsigned pos2 = gstr.find("%",pos1); - unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); + std::string gstr=id2string(guard.get_identifier()); + unsigned pos1=gstr.find("#")+1; + unsigned pos2=gstr.find("%", pos1); + unsigned n=safe_string2unsigned(gstr.substr(pos1, pos2)); - local_SSAt::nodest::const_iterator n_it =SSA.nodes.begin(); + local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); - for(; n_it != SSA.nodes.end(); n_it++) + for(; n_it!=SSA.nodes.end(); n_it++) { - if(n_it->location->location_number == n) { + if(n_it->location->location_number==n) + { // find end of loop break; } @@ -42,44 +54,63 @@ local_SSAt::locationt find_loop_by_guard(const local_SSAt &SSA, return n_it->loophead->location; } +/*******************************************************************\ + +Function: instrument_gotot::instrument_instruction + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ void instrument_gotot::instrument_instruction( const exprt &expr, goto_programt &dest, goto_programt::targett &target) { - goto_programt::targett where=target; - std::cout << "target " << target->type << " : " << target->source_location << std::endl; +#ifdef DEBUG + std::cout << "target " << target->type << " : " + << target->source_location << std::endl; +#endif - for(;;++where) + for(; ; ++where) { if(where->is_goto() && where->get_target()==target) break; } - goto_programt tmp; - + goto_programt::targett assumption=tmp.add_instruction(); assumption->make_assumption(expr); assumption->source_location=target->source_location; assumption->source_location.set_comment("invariant generated by 2LS"); - dest.insert_before_swap(where, tmp); + dest.insert_before_swap(where, tmp); - #ifdef DEBUG - std::cout << "instrumenting instruction " << std::endl; - #endif +#ifdef DEBUG + std::cout << "instrumenting instruction " << std::endl; +#endif +} +extern void purify_identifiers(exprt &expr); - //dest.update(); -} +/*******************************************************************\ + +Function: instrument_gotot::instrument_body -extern -void purify_identifiers(exprt &expr); + Inputs: + Outputs: + + Purpose: + +\*******************************************************************/ void instrument_gotot::instrument_body( const local_SSAt &SSA, @@ -87,9 +118,9 @@ void instrument_gotot::instrument_body( goto_functionst::goto_functiont &function ) { - //expected format (/\_j g_j) => inv - const exprt &impl = expr.op0(); - exprt inv = expr.op1(); //copy + // expected format (/\_j g_j)=> inv + const exprt &impl=expr.op0(); + exprt inv=expr.op1(); // copy std::cout << "Invariant " << from_expr(inv) << std::endl; @@ -98,39 +129,49 @@ void instrument_gotot::instrument_body( local_SSAt::locationt loc; if(impl.id()==ID_symbol) { - loc = find_loop_by_guard(SSA,to_symbol_expr(impl)); + loc=find_loop_by_guard(SSA, to_symbol_expr(impl)); } else if(impl.id()==ID_and) { assert(impl.op0().id()==ID_symbol); - loc = find_loop_by_guard(SSA,to_symbol_expr(impl.op0())); - + loc=find_loop_by_guard(SSA, to_symbol_expr(impl.op0())); } - else assert(false); - + else + assert(false); Forall_goto_program_instructions(it, function.body) + { if(it==loc) { - instrument_instruction(inv, function.body, it); break; } + } } +/*******************************************************************\ + +Function: instrument_gotot::instrument_function + + Inputs: + Outputs: + + Purpose: + +\*******************************************************************/ void instrument_gotot::instrument_function( const irep_idt &function_name, goto_functionst::goto_functiont &function) { #ifdef DEBUG - std::cout << "instrumenting function " << function_name << std::endl; + std::cout << "instrumenting function " << function_name << std::endl; #endif if(!summary_db.exists(function_name)) return; - + const summaryt summary=summary_db.get(function_name); if(!ssa_db.exists(function_name)) @@ -138,10 +179,12 @@ void instrument_gotot::instrument_function( const local_SSAt &SSA=ssa_db.get(function_name); - if(summary.fw_invariant.is_nil()) return; - if(summary.fw_invariant.is_true()) return; + if(summary.fw_invariant.is_nil()) + return; + if(summary.fw_invariant.is_true()) + return; - //expected format /\_i g_i => inv_i + // expected format /\_i g_i=> inv_i if(summary.fw_invariant.id()==ID_implies) { instrument_body(SSA, summary.fw_invariant, function); @@ -154,26 +197,37 @@ void instrument_gotot::instrument_function( instrument_body(SSA, summary.fw_invariant.operands()[i], function); } } - else assert(false); + else + assert(false); } +/*******************************************************************\ + +Function: instrument_gotot::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void instrument_gotot::operator()(goto_modelt &goto_model) { - goto_functionst - &goto_functions=goto_model.goto_functions; - - typedef goto_functions_templatet::function_mapt + goto_functionst &goto_functions=goto_model.goto_functions; + + typedef goto_functions_templatet::function_mapt function_mapt; - - function_mapt - &function_map=goto_functions.function_map; - - for(function_mapt::iterator + + function_mapt &function_map=goto_functions.function_map; + + for(function_mapt::iterator fit=function_map.begin(); fit!=function_map.end(); ++fit) - { - instrument_function(fit->first, fit->second); + { + instrument_function(fit->first, fit->second); } goto_model.goto_functions.update(); diff --git a/src/summarizer/instrument_goto.h b/src/2ls/instrument_goto.h similarity index 68% rename from src/summarizer/instrument_goto.h rename to src/2ls/instrument_goto.h index dbc3a1b41..c121994e0 100644 --- a/src/summarizer/instrument_goto.h +++ b/src/2ls/instrument_goto.h @@ -2,29 +2,30 @@ Module: Instrument Goto Program with Inferred Information -Author: Peter Schrammel +Author: Peter Schrammel, Björn Wachter \*******************************************************************/ -#ifndef CPROVER_INSTRUMENT_GOTO_H -#define CPROVER_INSTRUMENT_GOTO_H +#ifndef CPROVER_2LS_2LS_INSTRUMENT_GOTO_H +#define CPROVER_2LS_2LS_INSTRUMENT_GOTO_H #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" -#include "ssa_db.h" -#include "summary_db.h" +#include +#include +#include +#include class instrument_gotot:public messaget { public: - inline instrument_gotot(optionst &_options, - ssa_dbt &_ssa_db, - summary_dbt &_summary_db): + inline instrument_gotot( + optionst &_options, + ssa_dbt &_ssa_db, + summary_dbt &_summary_db): options(_options), - ssa_db(_ssa_db),summary_db(_summary_db) + ssa_db(_ssa_db), summary_db(_summary_db) { } @@ -35,7 +36,7 @@ class instrument_gotot:public messaget ssa_dbt &ssa_db; summary_dbt &summary_db; - + void instrument_function( const irep_idt &function_name, goto_functionst::goto_functiont &function); @@ -49,7 +50,6 @@ class instrument_gotot:public messaget const exprt &expr, goto_programt &dest, goto_programt::targett &target); - }; #endif diff --git a/src/2ls/preprocessing_util.cpp b/src/2ls/preprocessing_util.cpp new file mode 100644 index 000000000..590827693 --- /dev/null +++ b/src/2ls/preprocessing_util.cpp @@ -0,0 +1,468 @@ +/*******************************************************************\ + +Module: 2LS Command Line Options Processing + +Author: Peter Schrammel + +\*******************************************************************/ + +#include +#include +#include + +#include +#include + +#include "2ls_parse_options.h" + + +/*******************************************************************\ + +Function: twols_parse_optionst::inline_main + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::inline_main(goto_modelt &goto_model) +{ + goto_programt &main=goto_model.goto_functions.function_map[ID__start].body; + goto_programt::targett target=main.instructions.begin(); + while(target!=main.instructions.end()) + { + if(target->is_function_call()) + { + const code_function_callt &code_function_call= + to_code_function_call(target->code); + irep_idt fname=code_function_call.function().get(ID_identifier); + + debug() << "Inlining " << fname << eom; + + goto_programt tmp; + tmp.copy_from(goto_model.goto_functions.function_map[fname].body); + (--tmp.instructions.end())->make_skip(); + goto_model.goto_functions.function_map.erase(fname); + + goto_programt::targett next_target(target); + target->make_skip(); + next_target++; + main.instructions.splice(next_target, tmp.instructions); + target=next_target; + } + else + target++; + } + + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::propagate_constants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void twols_parse_optionst::propagate_constants(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + constant_propagator_ait(f_it->second, ns); + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::nondet_locals + + Inputs: + + Outputs: + + Purpose: explicitly assign a nondet_symbol to local variables + this is required by the unwinder, which would be unable + to recognise in which scope variables have been declared + +\*******************************************************************/ + +void twols_parse_optionst::nondet_locals(goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_decl()) + { + const code_declt& decl=to_code_decl(i_it->code); + + goto_programt::const_targett next=i_it; ++next; + if(next!=f_it->second.body.instructions.end() && + next->is_assign() && + to_code_assign(next->code).lhs().id()==ID_symbol && + to_symbol_expr(to_code_assign(next->code).lhs())==decl.symbol()) + continue; + + side_effect_expr_nondett nondet(decl.symbol().type()); + goto_programt::targett t=f_it->second.body.insert_after(i_it); + t->make_assignment(); + code_assignt c(decl.symbol(), nondet); + t->code.swap(c); + t->source_location=i_it->source_location; + } + } + } + goto_model.goto_functions.update(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::unwind_goto_into_loop + + Inputs: + + Outputs: + + Purpose: unwind all loops + +\*******************************************************************/ + +void twols_parse_optionst::unwind_goto_into_loop( + goto_modelt &goto_model, + unsigned k) +{ + typedef std::vector > loopst; + + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &body=f_it->second.body; + + loopst loops; + Forall_goto_program_instructions(i_it, body) + { + if(i_it->is_backwards_goto()) + { + goto_programt::targett loop_head=i_it->get_target(); + goto_programt::targett loop_exit=i_it; + bool has_goto_into_loop=false; + + goto_programt::targett it=loop_head; + if(it!=loop_exit) + it++; + for(; it!=loop_exit; it++) + { + for( std::set::iterator + s_it=it->incoming_edges.begin(); + s_it!=it->incoming_edges.end(); ++s_it) + { + if((*s_it)->is_goto() && + (*s_it)->location_numberlocation_number) + { + has_goto_into_loop=true; + break; + } + } + if(has_goto_into_loop) + break; + } + if(has_goto_into_loop) + { + status() << "Unwinding jump into loop" << eom; + loops.push_back(loopst::value_type(++loop_exit, loop_head)); + } + } + } + + for(loopst::iterator l_it=loops.begin(); l_it!=loops.end(); ++l_it) + { + std::vector iteration_points; + + goto_unwindt goto_unwind; + goto_unwind.unwind( + body, + l_it->second, + l_it->first, + k, + goto_unwindt::PARTIAL, iteration_points); + + assert(iteration_points.size()==2); + goto_programt::targett t=body.insert_before(l_it->first); + t->make_goto(); + t->targets.push_back(iteration_points.front()); + } + } + goto_model.goto_functions.update(); + goto_model.goto_functions.compute_loop_numbers(); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_multiple_dereferences + + Inputs: + + Outputs: + + Purpose: temporary fix to circumvent ssa_dereference problem + +\*******************************************************************/ + +void twols_parse_optionst::remove_multiple_dereferences( + goto_modelt &goto_model, + goto_programt &body, + goto_programt::targett t, + exprt &expr, + unsigned &var_counter, + bool deref_seen) +{ + if(expr.id()==ID_member) + { + member_exprt &member_expr=to_member_expr(expr); + if(member_expr.compound().id()==ID_dereference) + { + dereference_exprt &deref_expr=to_dereference_expr(member_expr.compound()); + remove_multiple_dereferences( + goto_model, body, t, deref_expr.pointer(), var_counter, true); + if(deref_seen) + { + symbolt new_symbol; + new_symbol.type=member_expr.type(); + new_symbol.name="$deref"+i2string(var_counter++); + new_symbol.base_name=new_symbol.name; + new_symbol.pretty_name=new_symbol.name; + goto_model.symbol_table.add(new_symbol); + goto_programt::targett t_new=body.insert_before(t); + t_new->make_assignment(); + t_new->code=code_assignt(new_symbol.symbol_expr(), member_expr); + expr=new_symbol.symbol_expr(); + for(std::set::iterator t_it= + t->incoming_edges.begin(); + t_it!=t->incoming_edges.end(); ++t_it) + { + if((*t_it)->is_goto() && (*t_it)->get_target()==t) + { + (*t_it)->targets.clear(); + (*t_it)->targets.push_back(t_new); + } + } + body.compute_location_numbers(); + body.compute_target_numbers(); + body.compute_incoming_edges(); + } + } + else + Forall_operands(o_it, expr) + remove_multiple_dereferences( + goto_model, body, t, *o_it, var_counter, deref_seen); + } + else + Forall_operands(o_it, expr) + remove_multiple_dereferences( + goto_model, body, t, *o_it, var_counter, deref_seen); +} + +/*******************************************************************\ + +Function: twols_parse_optionst::remove_multiple_dereferences + + Inputs: + + Outputs: + + Purpose: temporary fix to circumvent ssa_dereference problem + +\*******************************************************************/ + +void twols_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) +{ + unsigned var_counter=0; + namespacet ns(goto_model.symbol_table); + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_goto()) + { + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + i_it->guard, + var_counter, + false); + } + else if(i_it->is_assign()) + { + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).lhs(), + var_counter, false); + remove_multiple_dereferences( + goto_model, + f_it->second.body, + i_it, + to_code_assign(i_it->code).rhs(), + var_counter, false); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::add_assumptions_after_assertions + + Inputs: + + Outputs: + + Purpose: assumes assertions after checking them + +\*******************************************************************/ + +void twols_parse_optionst::add_assumptions_after_assertions( + goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(i_it->is_assert() && !i_it->guard.is_true()) + { + goto_programt::targett t_new=f_it->second.body.insert_after(i_it); + t_new->make_assumption(i_it->guard); + f_it->second.body.compute_location_numbers(); + f_it->second.body.compute_target_numbers(); + f_it->second.body.compute_incoming_edges(); + } + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::has_threads + + Inputs: + + Outputs: + + Purpose: checks whether the program has threads + +\*******************************************************************/ + +bool twols_parse_optionst::has_threads(const goto_modelt &goto_model) +{ + namespacet ns(goto_model.symbol_table); + forall_goto_functions(f_it, goto_model.goto_functions) + { + const goto_programt& program=f_it->second.body; + + forall_goto_program_instructions(i_it, program) + { + const goto_programt::instructiont &instruction=*i_it; + + if(instruction.is_function_call()) + { + const code_function_callt &fct=to_code_function_call(instruction.code); + if(fct.function().id()==ID_symbol) + { + const symbol_exprt &fsym=to_symbol_expr(fct.function()); + + if(ns.lookup(fsym.get_identifier()).base_name=="pthread_create") + return true; + } + } + } + } + return false; +} + +/*******************************************************************\ + +Function: twols_parse_optionst::filter_assertions + + Inputs: + + Outputs: + + Purpose: filter certain assertions for SV-COMP + +\*******************************************************************/ + +void twols_parse_optionst::filter_assertions(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + goto_programt &program=f_it->second.body; + + Forall_goto_program_instructions(i_it, program) + { + if(!i_it->is_assert()) + continue; + + if(i_it->source_location.get_comment()=="free argument is dynamic object") + i_it->make_skip(); + } + } +} + +/*******************************************************************\ + +Function: twols_parse_optionst::split_loopheads + +Inputs: + +Outputs: + +Purpose: insert skip at jump targets if they are goto, + assume or assert instructions + +\*******************************************************************/ + +void twols_parse_optionst::split_loopheads(goto_modelt &goto_model) +{ + Forall_goto_functions(f_it, goto_model.goto_functions) + { + Forall_goto_program_instructions(i_it, f_it->second.body) + { + if(!i_it->is_backwards_goto()) + continue; + goto_programt::targett loophead=i_it->get_target(); + if(i_it->guard.is_true() && !loophead->is_assert()) + continue; + + // inserts the skip + goto_programt::targett new_loophead= + f_it->second.body.insert_before(loophead); + new_loophead->make_skip(); + new_loophead->source_location=loophead->source_location; + new_loophead->function=i_it->function; + + // update jumps to loophead + for(std::set::iterator j_it= + loophead->incoming_edges.begin(); + j_it!=loophead->incoming_edges.end(); j_it++) + { + if(!(*j_it)->is_goto() || (*j_it)->get_target()!=loophead) + continue; + (*j_it)->targets.clear(); + (*j_it)->targets.push_back(new_loophead); + } + } + } +} diff --git a/src/2ls/show.cpp b/src/2ls/show.cpp new file mode 100644 index 000000000..c81d0db95 --- /dev/null +++ b/src/2ls/show.cpp @@ -0,0 +1,609 @@ +/*******************************************************************\ + +Module: Showing various debugging information + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "show.h" + +/*******************************************************************\ + +Function: show_assignments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_assignments( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + std::ostream &out) +{ + ssa_objectst ssa_objects(goto_function, ns); + ssa_value_ait ssa_value_ai(goto_function, ns); + assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + assignments.output(ns, goto_function.body, out); +} + +/*******************************************************************\ + +Function: show_assignments + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_assignments( + const goto_modelt &goto_model, + const irep_idt &function, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_assignments(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_assignments(f_it->second, ns, out); + + out << "\n"; + } + } +} + +/*******************************************************************\ + +Function: show_defs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_defs( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + std::ostream &out) +{ + ssa_objectst ssa_objects(goto_function, ns); + ssa_value_ait ssa_value_ai(goto_function, ns); + assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); + ssa_ait ssa_analysis(assignments); + ssa_analysis(goto_function, ns); + ssa_analysis.output(ns, goto_function.body, out); +} + +/*******************************************************************\ + +Function: show_defs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_defs( + const goto_modelt &goto_model, + const irep_idt &function, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_defs(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_defs(f_it->second, ns, out); + + out << "\n"; + } + } +} + +/*******************************************************************\ + +Function: show_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_guards( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + std::ostream &out) +{ + guard_mapt guard_map(goto_function.body); + guard_map.output(goto_function.body, out); +} + +/*******************************************************************\ + +Function: show_guards + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_guards( + const goto_modelt &goto_model, + const irep_idt &function, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_guards(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_guards(f_it->second, ns, out); + + out << "\n"; + } + } +} + +/*******************************************************************\ + +Function: show_ssa + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_ssa( + const goto_functionst::goto_functiont &goto_function, + bool simplify, + const namespacet &ns, + std::ostream &out) +{ + local_SSAt local_SSA(goto_function, ns); + if(simplify) + ::simplify(local_SSA, ns); + local_SSA.output_verbose(out); +} + +/*******************************************************************\ + +Function: show_ssa + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_ssa( + const goto_modelt &goto_model, + const irep_idt &function, + bool simplify, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + out << ">>>> Function " << function << "\n"; + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_ssa(f_it->second, simplify, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + if(f_it->first=="assert") + continue; + if(f_it->first=="__CPROVER_assume") + continue; + + out << ">>>> Function " << f_it->first << "\n"; + + show_ssa(f_it->second, simplify, ns, out); + + out << "\n"; + } + } +} + +/*******************************************************************\ + +Function: print_symbol_values + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void print_symbol_values( + const local_SSAt &SSA, + prop_convt &solver, + std::ostream &out, + const exprt &expr) +{ + if(expr.id()==ID_symbol) + { + out << from_expr(SSA.ns, "", expr) << "==" + << from_expr(SSA.ns, "", solver.get(expr)) << "\n"; + return; + } + for(exprt::operandst::const_iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) + { + print_symbol_values(SSA, solver, out, *it); + } +} + +/*******************************************************************\ + +Function: show_raw_countermodel + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_raw_countermodel( + const irep_idt &property_id, + const local_SSAt &SSA, + prop_convt &solver, + std::ostream &out, + message_handlert &message_handler) +{ + out << "\n*** Error trace for property " << property_id << "\n"; + for(local_SSAt::nodest::const_iterator n_it= + SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) + { + print_symbol_values(SSA, solver, out, *e_it); + // out << from_expr(SSA.ns, "", e_it->op0()) << "==" << + // from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; + } + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) + { + print_symbol_values(SSA, solver, out, *c_it); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + print_symbol_values(SSA, solver, out, *a_it); + } + } + out << "\n"; +} + +/*******************************************************************\ + +Function: find_loc_by_guard + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +local_SSAt::locationt find_loc_by_guard( + const local_SSAt &SSA, + const symbol_exprt &guard) +{ + std::string gstr=id2string(guard.get_identifier()); + unsigned pos1=gstr.find("#")+1; + unsigned pos2=gstr.find("%", pos1); + unsigned n=safe_string2unsigned(gstr.substr(pos1, pos2)); + return SSA.get_location(n); +} + +/*******************************************************************\ + +Function: purify_identifiers + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void purify_identifiers(exprt &expr) +{ + if(expr.id()==ID_symbol) + { + std::string idstr=id2string(to_symbol_expr(expr).get_identifier()); + to_symbol_expr(expr).set_identifier(idstr.substr(0, idstr.find("#"))); + } + for(unsigned i=0; i inv + const exprt &impl=expr.op0(); + exprt inv=expr.op1(); // copy + local_SSAt::locationt loc; + if(impl.id()==ID_symbol) + { + loc=find_loc_by_guard(SSA, to_symbol_expr(impl)); + } + else if(impl.id()==ID_and) + { + assert(impl.op0().id()==ID_symbol); + loc=find_loc_by_guard(SSA, to_symbol_expr(impl.op0())); + } + else + assert(false); + + out << "\n** invariant: " << loc->source_location << "\n"; + purify_identifiers(inv); + out << " " << from_expr(SSA.ns, "", inv) << "\n"; +} + +/*******************************************************************\ + +Function: show_invariants + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_invariants( + const local_SSAt &SSA, + const summaryt &summary, + std::ostream &out) +{ + if(summary.fw_invariant.is_nil()) + return; + if(summary.fw_invariant.is_true()) + return; + + // expected format /\_i g_i=> inv_i + if(summary.fw_invariant.id()==ID_implies) + { + show_invariant(SSA, summary.fw_invariant, out); + } + else if(summary.fw_invariant.id()==ID_and) + { + for(unsigned i=0; i symbols; + out << "\n*** SSA Symbols " << "\n"; + for(local_SSAt::nodest::const_iterator n_it= + SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) + { + find_symbols(*e_it, symbols); + } + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) + { + find_symbols(*c_it, symbols); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + find_symbols(*a_it, symbols); + } + find_symbols(n_it->enabling_expr, symbols); + } + + for(std::set::const_iterator it=symbols.begin(); + it!=symbols.end(); it++) + { + out << from_type(SSA.ns, "", it->type()) << " " << + from_expr(SSA.ns, "", *it) << ";\n"; + } + out << "\n"; +} + +/*******************************************************************\ + +Function: show_value_set + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_set( + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns, + std::ostream &out) +{ + ssa_objectst ssa_objects(goto_function, ns); + ssa_value_ait ssa_value_ai(goto_function, ns); + ssa_value_ai.output(ns, goto_function, out); +} + +/*******************************************************************\ + +Function: show_value_sets + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void show_value_sets( + const goto_modelt &goto_model, + const irep_idt &function, + std::ostream &out, + message_handlert &message_handler) +{ + const namespacet ns(goto_model.symbol_table); + + if(!function.empty()) + { + goto_functionst::function_mapt::const_iterator + f_it=goto_model.goto_functions.function_map.find(function); + if(f_it==goto_model.goto_functions.function_map.end()) + out << "function " << function << " not found\n"; + else + show_value_set(f_it->second, ns, out); + } + else + { + forall_goto_functions(f_it, goto_model.goto_functions) + { + out << ">>>> Function " << f_it->first << "\n"; + + show_value_set(f_it->second, ns, out); + + out << "\n"; + } + } +} + diff --git a/src/summarizer/show.h b/src/2ls/show.h similarity index 78% rename from src/summarizer/show.h rename to src/2ls/show.h index 7dda411df..e30bb0565 100644 --- a/src/summarizer/show.h +++ b/src/2ls/show.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_SHOW_H -#define CPROVER_SUMMARIZER_SHOW_H +#ifndef CPROVER_2LS_2LS_SHOW_H +#define CPROVER_2LS_2LS_SHOW_H #include #include @@ -16,7 +16,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "summary.h" +#include class message_handlert; @@ -48,31 +48,25 @@ void show_assignments( void show_guards( const goto_modelt &, const irep_idt &function, + //bool simplify, std::ostream &, message_handlert &); -void show_fixed_points( - const goto_modelt &, - const irep_idt &function, - bool simplify, - std::ostream &, - message_handlert &); - -//shows raw error trace +// shows raw error trace void show_raw_countermodel( const irep_idt &property_id, - const local_SSAt &SSA, + const local_SSAt &SSA, prop_convt &solver, std::ostream &, message_handlert &); void show_invariants( - const local_SSAt &SSA, + const local_SSAt &SSA, const summaryt &summary, std::ostream &out); void show_ssa_symbols( - const local_SSAt &SSA, + const local_SSAt &SSA, std::ostream &out); #endif diff --git a/src/summarizer/summarizer_bw_cex.cpp b/src/2ls/summarizer_bw_cex.cpp similarity index 98% rename from src/summarizer/summarizer_bw_cex.cpp rename to src/2ls/summarizer_bw_cex.cpp index 424e11248..4422d49a8 100644 --- a/src/summarizer/summarizer_bw_cex.cpp +++ b/src/2ls/summarizer_bw_cex.cpp @@ -15,7 +15,7 @@ Author: Kumar Madhukar, Peter Schrammel #include #include "summarizer_bw_cex.h" -#include "summary_db.h" +#include "../solver/summary_db.h" #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" diff --git a/src/summarizer/summarizer_bw_cex.h b/src/2ls/summarizer_bw_cex.h similarity index 95% rename from src/summarizer/summarizer_bw_cex.h rename to src/2ls/summarizer_bw_cex.h index 8da077347..3fe5c4edb 100644 --- a/src/summarizer/summarizer_bw_cex.h +++ b/src/2ls/summarizer_bw_cex.h @@ -16,9 +16,9 @@ Author: Kumar Madhukar, Peter Schrammel #include "../ssa/ssa_unwinder.h" #include "../ssa/ssa_refiner_selective.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" -#include "summarizer_bw.h" +#include "../solver/summarizer_bw.h" class summarizer_bw_cex_baset : public summarizer_bwt { diff --git a/src/summarizer/summarizer_bw_cex_ai.cpp b/src/2ls/summarizer_bw_cex_ai.cpp similarity index 99% rename from src/summarizer/summarizer_bw_cex_ai.cpp rename to src/2ls/summarizer_bw_cex_ai.cpp index 6d676e175..5f3472321 100644 --- a/src/summarizer/summarizer_bw_cex_ai.cpp +++ b/src/2ls/summarizer_bw_cex_ai.cpp @@ -15,7 +15,7 @@ Author: Peter Schrammel #include #include "summarizer_bw_cex_ai.h" -#include "summary_db.h" +#include "../solver/summary_db.h" #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" diff --git a/src/summarizer/summarizer_bw_cex_ai.h b/src/2ls/summarizer_bw_cex_ai.h similarity index 98% rename from src/summarizer/summarizer_bw_cex_ai.h rename to src/2ls/summarizer_bw_cex_ai.h index a41b9c122..6862ce2c5 100644 --- a/src/summarizer/summarizer_bw_cex_ai.h +++ b/src/2ls/summarizer_bw_cex_ai.h @@ -14,7 +14,7 @@ Author: Peter Schrammel #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" #include diff --git a/src/summarizer/summarizer_bw_cex_all.cpp b/src/2ls/summarizer_bw_cex_all.cpp similarity index 100% rename from src/summarizer/summarizer_bw_cex_all.cpp rename to src/2ls/summarizer_bw_cex_all.cpp diff --git a/src/summarizer/summarizer_bw_cex_all.h b/src/2ls/summarizer_bw_cex_all.h similarity index 98% rename from src/summarizer/summarizer_bw_cex_all.h rename to src/2ls/summarizer_bw_cex_all.h index b04dae0fb..1c81cf46a 100644 --- a/src/summarizer/summarizer_bw_cex_all.h +++ b/src/2ls/summarizer_bw_cex_all.h @@ -15,7 +15,7 @@ Author: Kumar Madhukar, Peter Schrammel #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" #include "summarizer_bw_cex.h" #include "summarizer_bw_cex_concrete.h" diff --git a/src/summarizer/summarizer_bw_cex_complete.cpp b/src/2ls/summarizer_bw_cex_complete.cpp similarity index 99% rename from src/summarizer/summarizer_bw_cex_complete.cpp rename to src/2ls/summarizer_bw_cex_complete.cpp index 9ba0d6ecb..9ec87e5f1 100644 --- a/src/summarizer/summarizer_bw_cex_complete.cpp +++ b/src/2ls/summarizer_bw_cex_complete.cpp @@ -14,7 +14,7 @@ Author: Madhukar Kumar, Peter Schrammel #include #include -#include "summary_db.h" +#include "../solver/summary_db.h" #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" diff --git a/src/summarizer/summarizer_bw_cex_complete.h b/src/2ls/summarizer_bw_cex_complete.h similarity index 98% rename from src/summarizer/summarizer_bw_cex_complete.h rename to src/2ls/summarizer_bw_cex_complete.h index 8646c409d..c1ecf12e1 100644 --- a/src/summarizer/summarizer_bw_cex_complete.h +++ b/src/2ls/summarizer_bw_cex_complete.h @@ -15,7 +15,7 @@ Author: Peter Schrammel #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" #include diff --git a/src/summarizer/summarizer_bw_cex_concrete.cpp b/src/2ls/summarizer_bw_cex_concrete.cpp similarity index 99% rename from src/summarizer/summarizer_bw_cex_concrete.cpp rename to src/2ls/summarizer_bw_cex_concrete.cpp index f03435ef0..2fda14c0e 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.cpp +++ b/src/2ls/summarizer_bw_cex_concrete.cpp @@ -25,7 +25,7 @@ Author: Kumar Madhukar, Peter Schrammel #include #include "summarizer_bw_cex_concrete.h" -#include "summary_db.h" +#include "../solver/summary_db.h" #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" diff --git a/src/summarizer/summarizer_bw_cex_concrete.h b/src/2ls/summarizer_bw_cex_concrete.h similarity index 98% rename from src/summarizer/summarizer_bw_cex_concrete.h rename to src/2ls/summarizer_bw_cex_concrete.h index 40b7177dd..49ccd3070 100644 --- a/src/summarizer/summarizer_bw_cex_concrete.h +++ b/src/2ls/summarizer_bw_cex_concrete.h @@ -15,7 +15,7 @@ Author: Kumar Madhukar, Peter Schrammel #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" #include diff --git a/src/summarizer/summarizer_bw_cex_wp.cpp b/src/2ls/summarizer_bw_cex_wp.cpp similarity index 99% rename from src/summarizer/summarizer_bw_cex_wp.cpp rename to src/2ls/summarizer_bw_cex_wp.cpp index 86b0b6e54..1883ee7a1 100644 --- a/src/summarizer/summarizer_bw_cex_wp.cpp +++ b/src/2ls/summarizer_bw_cex_wp.cpp @@ -14,7 +14,7 @@ Author: Madhukar Kumar, Peter Schrammel #include #include -#include "summary_db.h" +#include "../solver/summary_db.h" #include "../domains/ssa_analyzer.h" #include "../domains/template_generator_summary.h" diff --git a/src/summarizer/summarizer_bw_cex_wp.h b/src/2ls/summarizer_bw_cex_wp.h similarity index 98% rename from src/summarizer/summarizer_bw_cex_wp.h rename to src/2ls/summarizer_bw_cex_wp.h index d3afb8906..32c4e9ef7 100644 --- a/src/summarizer/summarizer_bw_cex_wp.h +++ b/src/2ls/summarizer_bw_cex_wp.h @@ -15,7 +15,7 @@ Author: Madhukar Kumar, Peter Schrammel #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_unwinder.h" #include "../ssa/local_ssa.h" -#include "ssa_db.h" +#include "../ssa/ssa_db.h" #include diff --git a/src/2ls/summary_checker_ai.cpp b/src/2ls/summary_checker_ai.cpp new file mode 100644 index 000000000..6cda09787 --- /dev/null +++ b/src/2ls/summary_checker_ai.cpp @@ -0,0 +1,180 @@ +/*******************************************************************\ + +Module: Summary Checker for AI + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_ai.h" +#include + +#define TERM_CEX 1 + +/*******************************************************************\ + +Function: summary_checker_ait::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns); + + ssa_unwinder.init(false, false); + + unsigned unwind=options.get_unsigned_int_option("unwind"); + if(unwind>0) + { + status() << "Unwinding" << messaget::eom; + + ssa_unwinder.init_localunwinders(); + + ssa_unwinder.unwind_all(unwind); + } + + // properties + initialize_property_map(goto_model.goto_functions); + + bool preconditions=options.get_bool_option("preconditions"); + bool termination=options.get_bool_option("termination"); + bool trivial_domain=options.get_bool_option("havoc"); + if(!trivial_domain || termination) + { + // forward analysis + summarize(goto_model, true, termination); + } + if(!trivial_domain && preconditions) + { + // backward analysis + summarize(goto_model, false, termination); + } + + if(preconditions) + { + report_statistics(); + report_preconditions(); + return property_checkert::UNKNOWN; + } + + if(termination) + { + report_statistics(); + return report_termination(); + } + +#ifdef SHOW_CALLINGCONTEXTS + if(options.get_bool_option("show-calling-contexts")) + return property_checkert::UNKNOWN; +#endif + + property_checkert::resultt result=check_properties(); + report_statistics(); + return result; +} + + +/*******************************************************************\ + +Function: summary_checker_ait::report_preconditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_checker_ait::report_preconditions() +{ + result() << eom; + result() << "** " << (options.get_bool_option("sufficient") ? + "Sufficient" : "Necessary") + << " preconditions: " << eom; + ssa_dbt::functionst &functions=ssa_db.functions(); + for(ssa_dbt::functionst::iterator it=functions.begin(); + it!=functions.end(); it++) + { + exprt precondition; + bool computed=summary_db.exists(it->first); + if(computed) + precondition=summary_db.get(it->first).bw_precondition; + if(precondition.is_nil()) + computed=false; + result() << eom << "[" << it->first << "]: " << + (!computed?"not computed":from_expr(it->second->ns, "", precondition)) + << eom; + } +} + +/*******************************************************************\ + +Function: summary_checker_ait::report_termination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_ait::report_termination() +{ + result() << eom; + result() << "** Termination: " << eom; + bool not_computed=true; + bool all_terminate=true; + bool one_nonterminate=false; + ssa_dbt::functionst &functions=ssa_db.functions(); + for(ssa_dbt::functionst::iterator it=functions.begin(); + it!=functions.end(); it++) + { + threevalt terminates=YES; + bool computed=summary_db.exists(it->first); + if(computed) + { + terminates=summary_db.get(it->first).terminates; + not_computed=false; + } + all_terminate=all_terminate && (terminates==YES); + one_nonterminate=one_nonterminate || (terminates==NO); + result() << "[" << it->first << "]: " + << (!computed ? "not computed" : threeval2string(terminates)) << eom; + } + if(not_computed) + return property_checkert::UNKNOWN; + if(all_terminate) + return property_checkert::PASS; + if(one_nonterminate) + { +#if TERM_CEX + if(options.get_option("graphml-witness")!="" && + !functions.empty()) + { + property_map.clear(); + incremental_solvert &solver=ssa_db.get_solver(functions.begin()->first); + if(solver()==decision_proceduret::D_SATISFIABLE) + { + irep_idt pid="non-termination"; + property_map[pid].result=property_checkert::FAIL; + ssa_build_goto_tracet build_goto_trace( + *functions.begin()->second, solver.get_solver(), true); + build_goto_trace(property_map[pid].error_trace); + } + } +#endif + return property_checkert::FAIL; + } + return property_checkert::UNKNOWN; +} diff --git a/src/summarizer/summary_checker_ai.h b/src/2ls/summary_checker_ai.h similarity index 77% rename from src/summarizer/summary_checker_ai.h rename to src/2ls/summary_checker_ai.h index 33eafe538..84eab2bf0 100644 --- a/src/summarizer/summary_checker_ai.h +++ b/src/2ls/summary_checker_ai.h @@ -6,25 +6,24 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_AI_H -#define CPROVER_SUMMARY_CHECKER_AI_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_AI_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_AI_H #include "summary_checker_base.h" class summary_checker_ait:public summary_checker_baset { public: - inline summary_checker_ait(optionst &_options): + explicit summary_checker_ait(optionst &_options): summary_checker_baset(_options) { } - + virtual resultt operator()(const goto_modelt &); protected: void report_preconditions(); property_checkert::resultt report_termination(); - }; #endif diff --git a/src/summarizer/summary_checker_base.cpp b/src/2ls/summary_checker_base.cpp similarity index 99% rename from src/summarizer/summary_checker_base.cpp rename to src/2ls/summary_checker_base.cpp index b63857933..1418b632d 100644 --- a/src/summarizer/summary_checker_base.cpp +++ b/src/2ls/summary_checker_base.cpp @@ -38,10 +38,10 @@ Author: Peter Schrammel #include "summarizer_bw_cex_wp.h" #include "summarizer_bw_cex_all.h" -#include "summarizer_fw.h" -#include "summarizer_fw_term.h" -#include "summarizer_bw.h" -#include "summarizer_bw_term.h" +#include "../solver/summarizer_fw.h" +#include "../solver/summarizer_fw_term.h" +#include "../solver/summarizer_bw.h" +#include "../solver/summarizer_bw_term.h" #ifdef SHOW_CALLING_CONTEXTS #include "summarizer_fw_contexts.h" diff --git a/src/summarizer/summary_checker_base.h b/src/2ls/summary_checker_base.h similarity index 97% rename from src/summarizer/summary_checker_base.h rename to src/2ls/summary_checker_base.h index 6f28f5338..4daefa5b5 100644 --- a/src/summarizer/summary_checker_base.h +++ b/src/2ls/summary_checker_base.h @@ -19,8 +19,8 @@ Author: Peter Schrammel #include "../ssa/ssa_unwinder.h" #include "../ssa/ssa_inliner.h" #include "../domains/incremental_solver.h" -#include "ssa_db.h" -#include "summary_db.h" +#include "../ssa/ssa_db.h" +#include "../solver/summary_db.h" #include "summarizer_bw_cex.h" class summary_checker_baset:public property_checkert @@ -53,14 +53,14 @@ class summary_checker_baset:public property_checkert absolute_timet start_time; time_periodt sat_time; -protected: optionst &options; - ssa_dbt ssa_db; summary_dbt summary_db; ssa_unwindert ssa_unwinder; ssa_inlinert ssa_inliner; +protected: + irep_idt entry_function; summarizer_bw_cex_baset::reasont reason; diff --git a/src/2ls/summary_checker_bmc.cpp b/src/2ls/summary_checker_bmc.cpp new file mode 100644 index 000000000..38e9e925e --- /dev/null +++ b/src/2ls/summary_checker_bmc.cpp @@ -0,0 +1,59 @@ +/*******************************************************************\ + +Module: Summary Checker for BMC + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_bmc.h" + + +/*******************************************************************\ + +Function: summary_checker_bmct::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_bmct::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns); + + ssa_unwinder.init(false, true); + + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind=0; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << messaget::eom; + summary_db.mark_recompute_all(); + ssa_unwinder.unwind_all(unwind); + result=check_properties(); + if(result==property_checkert::PASS) + { + status() << "incremental BMC proof found after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + else if(result==property_checkert::FAIL) + { + status() << "incremental BMC counterexample found after " + << unwind << " unwinding(s)" << messaget::eom; + break; + } + } + report_statistics(); + return result; +} diff --git a/src/summarizer/summary_checker_bmc.h b/src/2ls/summary_checker_bmc.h similarity index 73% rename from src/summarizer/summary_checker_bmc.h rename to src/2ls/summary_checker_bmc.h index 8345a41fe..5885f6b36 100644 --- a/src/summarizer/summary_checker_bmc.h +++ b/src/2ls/summary_checker_bmc.h @@ -6,21 +6,20 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_BMC_H -#define CPROVER_SUMMARY_CHECKER_BMC_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_BMC_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_BMC_H #include "summary_checker_base.h" class summary_checker_bmct:public summary_checker_baset { public: - inline summary_checker_bmct(optionst &_options): + explicit summary_checker_bmct(optionst &_options): summary_checker_baset(_options) { } - - virtual resultt operator()(const goto_modelt &); + virtual resultt operator()(const goto_modelt &); }; #endif diff --git a/src/2ls/summary_checker_kind.cpp b/src/2ls/summary_checker_kind.cpp new file mode 100644 index 000000000..5c13a3a92 --- /dev/null +++ b/src/2ls/summary_checker_kind.cpp @@ -0,0 +1,76 @@ +/*******************************************************************\ + +Module: Summary Checker for k-induction + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "summary_checker_kind.h" + +#define GIVE_UP_INVARIANTS 4 + +/*******************************************************************\ + +Function: summary_checker_kindt::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summary_checker_kindt::operator()( + const goto_modelt &goto_model) +{ + const namespacet ns(goto_model.symbol_table); + + SSA_functions(goto_model, ns); + + ssa_unwinder.init(true, false); + + property_checkert::resultt result=property_checkert::UNKNOWN; + unsigned max_unwind=options.get_unsigned_int_option("unwind"); + status() << "Max-unwind is " << max_unwind << eom; + ssa_unwinder.init_localunwinders(); + + for(unsigned unwind=0; unwind<=max_unwind; unwind++) + { + status() << "Unwinding (k=" << unwind << ")" << eom; + + // TODO: recompute only functions with loops + summary_db.mark_recompute_all(); + + ssa_unwinder.unwind_all(unwind); + + result=check_properties(); + bool magic_limit_not_reached= + unwind -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "deltacheck_parse_options.h" -#include "version.h" -#include "analyzer.h" -#include "change_impact.h" -#include "../functions/path_util.h" - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::deltacheck_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -deltacheck_parse_optionst::deltacheck_parse_optionst( - int argc, const char **argv): - parse_options_baset(DELTACHECK_OPTIONS, argc, argv), - xml_interfacet(cmdline), - ui_message_handler( - cmdline.isset("xml-ui")?ui_message_handlert::XML_UI:ui_message_handlert::PLAIN, - "DeltaCheck " DELTACHECK_VERSION) -{ -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::eval_verbosity - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::eval_verbosity() -{ - // our default verbosity - int v=messaget::M_STATISTICS; - - if(cmdline.isset("verbosity")) - { - v=unsafe_string2int(cmdline.get_value("verbosity")); - if(v<0) - v=0; - else if(v>10) - v=10; - } - - ui_message_handler.set_verbosity(v); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::get_command_line_options - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::get_command_line_options(optionst &options) -{ - if(config.set(cmdline)) - { - usage_error(); - exit(1); - } - - if(cmdline.isset("debug-level")) - options.set_option("debug-level", cmdline.get_value("debug-level")); - - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // do we do inlining? - if(cmdline.isset("no-inlining")) - options.set_option("partial-inlining", false); - else - options.set_option("partial-inlining", true); - - // check assertions - options.set_option("assertions", true); - - // use assumptions - options.set_option("assumptions", true); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::register_langauges - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_parse_optionst::register_languages() -{ - register_language(new_ansi_c_language); - register_language(new_cpp_language); -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int deltacheck_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - register_languages(); - - // command line options - - optionst options; - get_command_line_options(options); - set_message_handler(ui_message_handler); - eval_verbosity(); - - try - { - options.set_option("simplify", true); - options.set_option("assertions", true); - options.set_option("assumptions", true); - - if(cmdline.isset("function")) - options.set_option("function", cmdline.get_value("function")); - - if(cmdline.args.size()!=2) - { - usage_error(); - return 10; - } - - if(cmdline.isset("description-old")) - options.set_option("description-old", cmdline.get_value("description-old")); - else - options.set_option("description-old", cmdline.args[0]); - - if(cmdline.isset("description-new")) - options.set_option("description-new", cmdline.get_value("description-new")); - else - options.set_option("description-new", cmdline.args[1]); - - status() << "Reading first GOTO program from file" << eom; - - goto_modelt goto_model1; - - if(read_goto_binary(cmdline.args[0], - goto_model1, get_message_handler())) - return 10; - - status() << "Reading second GOTO program from file" << eom; - - goto_modelt goto_model2; - - if(read_goto_binary(cmdline.args[1], - goto_model2, get_message_handler())) - return 10; - - if(cmdline.isset("show-diff")) - { - change_impactt change_impact; - change_impact.set_message_handler(get_message_handler()); - - change_impact.diff(goto_model1, goto_model2); - change_impact.output_diff(std::cout); - } - else if(cmdline.isset("show-change-impact")) - { - change_impactt change_impact; - change_impact.set_message_handler(get_message_handler()); - - status() << "Computing syntactic difference" << eom; - change_impact.diff(goto_model1, goto_model2); - - status() << "Change-impact analysis" << eom; - change_impact.change_impact(goto_model2); - - change_impact.output_change_impact(std::cout); - } - else - { - std::string path1=get_directory(cmdline.args[0]); - std::string path2=get_directory(cmdline.args[1]); - - deltacheck_analyzer( - path1, goto_model1, - path2, goto_model2, - options, get_message_handler()); - } - - return 0; - } - - catch(const char *e) - { - error() << e << eom; - return 13; - } - - catch(const std::string &e) - { - error() << e << eom; - return 13; - } - - catch(int) - { - return 13; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return 14; - } - - return 0; -} - -/*******************************************************************\ - -Function: deltacheck_parse_optionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void deltacheck_parse_optionst::help() -{ - std::cout << - "\n" - "* * DELTACHECK " DELTACHECK_VERSION " - Copyright (C) 2011-2015 * *\n" - "* * based on CBMC " CBMC_VERSION " * *\n" - "* * Daniel Kroening * *\n" - "* * Oxford University, Computer Science Department * *\n" - "* * kroening@kroening.com * *\n" - "\n" - "Usage: Purpose:\n" - "\n" - " deltacheck [-?] [-h] [--help] show help\n" - " deltacheck prog1 prog2 delta check two programs\n" - "\n" - "Delta checking options:\n" - " --show-change-impact show syntactic change-impact\n" - " --description-old text description of old version\n" - " --description-new text description of new version\n" - "\n" - "Safety checks:\n" - " --bounds-check add array bounds checks\n" - " --div-by-zero-check add division by zero checks\n" - " --pointer-check add pointer checks\n" - " --signed-overflow-check add arithmetic over- and underflow checks\n" - " --unsigned-overflow-check add arithmetic over- and underflow checks\n" - " --nan-check add floating-point NaN checks\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - " --xml-interface stdio-XML interface\n" - "\n"; -} diff --git a/src/deltacheck/deltacheck_parse_options.h b/src/deltacheck/deltacheck_parse_options.h deleted file mode 100644 index dbeb2fa6d..000000000 --- a/src/deltacheck/deltacheck_parse_options.h +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H -#define CPROVER_DELTACHECK_PARSE_OPTIONS_H - -#include -#include -#include - -#include -#include - -#define DELTACHECK_OPTIONS \ - "(function):" \ - "(debug-level):" \ - "(xml-ui)(xml-interface)" \ - "(verbosity):(version)(index):(description-old):(description-new):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)(nan-check)" \ - "(show-ssa)(show-defs)(show-guards)(show-fixed-points)" \ - "(show-properties)(show-change-impact)(show-diff)" \ - "(no-inline)(sat)" - -class deltacheck_parse_optionst: - public parse_options_baset, - public xml_interfacet, - public messaget -{ -public: - virtual int doit(); - virtual void help(); - - deltacheck_parse_optionst( - int argc, const char **argv); - -protected: - virtual void register_languages(); - - virtual void get_command_line_options(optionst &options); - - void eval_verbosity(); - - ui_message_handlert ui_message_handler; -}; - -#endif diff --git a/src/deltacheck/get_source.cpp b/src/deltacheck/get_source.cpp deleted file mode 100644 index 228a3f77c..000000000 --- a/src/deltacheck/get_source.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/*******************************************************************\ - -Module: Get Source Code - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include - -#include "../functions/path_util.h" -#include "get_source.h" - -/*******************************************************************\ - -Function: fast_forward - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fast_forward(unsigned lines, std::istream &in) -{ - for(unsigned int i=0; i::max(), '\n'); -} - -/*******************************************************************\ - -Function: get_source - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_source( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - std::list &dest, - message_handlert &message_handler) -{ - messaget message(message_handler); - const irep_idt &file=location.get_file(); - - if(file=="") return; - if(goto_program.instructions.empty()) return; - - // split up path_prefix into directories - std::list directories; - - { - std::string tmp; - for(unsigned i=0; i=0; i--) - { - std::string prefix; - for(std::list::const_iterator - p_it=directories.begin(); - p_it!=directories.end(); - p_it++) - prefix+=*p_it; - - full_path= - make_relative_path(prefix, id2string(file)); - - if(access(full_path.c_str(), R_OK)==0) break; // found! - } - - std::ifstream in; - in.open(full_path.c_str()); - - if(!in) - { - message.error() << "failed to open source `" - << file << "'" << messaget::eom; - if(!directories.empty()) - message.error() << "also tried prefixes of `" << path_prefix << "'" - << messaget::eom; - dest.push_back(linet(file, 1, "/* failed to open source file */")); - dest.push_back(linet(file, 2, "/* "+full_path+" */")); - return; - } - - unsigned first_line=safe_string2unsigned(id2string(location.get_line())); - - if(first_line!=0) - fast_forward(first_line-1, in); - - // get last line of function - - const source_locationt &last=goto_program.instructions.back().source_location; - - if(last.get_file()!=file) - { - // Hm, function ends in a different file than it starts. - // Possible, but unusual. - return; - } - - unsigned end_line=safe_string2unsigned(id2string(last.get_line())); - - for(unsigned line_no=first_line; line_no<=end_line; line_no++) - { - std::string s; - if(!std::getline(in, s)) break; - dest.push_back(linet(file, line_no, s)); - } - -} diff --git a/src/deltacheck/get_source.h b/src/deltacheck/get_source.h deleted file mode 100644 index 081f40815..000000000 --- a/src/deltacheck/get_source.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Extract Source Code - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GET_SOURCE_H -#define CPROVER_GET_SOURCE_H - -#include -#include - -#include -#include - -struct linet -{ - explicit linet():line_no(0) { } - linet(const irep_idt &_file, unsigned _line_no, const std::string &_line): - file(_file), line_no(_line_no), line(_line) { } - irep_idt file; - unsigned line_no; - std::string line; -}; - -void get_source( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - std::list &dest, - message_handlert &message_handler); - -#endif diff --git a/src/deltacheck/old/call_graph.cpp b/src/deltacheck/old/call_graph.cpp deleted file mode 100644 index f7d7501b5..000000000 --- a/src/deltacheck/old/call_graph.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: Call Graph - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "call_graph.h" - -/*******************************************************************\ - -Function: summary_to_call_graph - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_to_call_graph(const xmlt &xml, call_grapht &dest) -{ - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - irep_idt caller=f_it->get_attribute("id"); - - for(xmlt::elementst::const_iterator - c_it=f_it->elements.begin(); - c_it!=f_it->elements.end(); - c_it++) - { - if(c_it->name=="called") - { - irep_idt callee=c_it->get_attribute("id"); - dest.add(caller, callee); - } - } - } - } -} diff --git a/src/deltacheck/old/call_graph.h b/src/deltacheck/old/call_graph.h deleted file mode 100644 index 25c5079ec..000000000 --- a/src/deltacheck/old/call_graph.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CALL_GRAPH_H -#define CPROVER_DELTACHECK_CALL_GRAPH_H - -#include -#include - -void summary_to_call_graph(const xmlt &xml, call_grapht &dest); - -#endif diff --git a/src/deltacheck/old/canonicalize.cpp b/src/deltacheck/old/canonicalize.cpp deleted file mode 100644 index 18ce4b2d0..000000000 --- a/src/deltacheck/old/canonicalize.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/*******************************************************************\ - -Module: Partial Canonicalization of a Predicate - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include "canonicalize.h" - -/*******************************************************************\ - -Function: canonicalize_rec - -Inputs: - -Outputs: - -Purpose: - -\*******************************************************************/ - -void canonicalize_rec(exprt &expr, bool &negation) -{ - if(expr.id()==ID_not) - { - if(expr.operands().size()==1) - { - exprt tmp; - tmp.swap(expr.op0()); - negation=!negation; - canonicalize_rec(tmp, negation); - expr.swap(tmp); - } - } - else if(expr.id()==ID_notequal) - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_equal); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_ge) // we only use le and lt - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_lt); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_gt) // we only use le and lt - { - if(expr.operands().size()==2) - { - negation=!negation; - expr.id(ID_le); - canonicalize_rec(expr, negation); - } - } - else if(expr.id()==ID_le || expr.id()==ID_lt) - { - if(expr.operands().size()==2) - { - - } - } - else if(expr.id()==ID_equal) - { - // we order the operands with < - assert(expr.operands().size()==2); - - if(expr.op0() -#include - -#include - -#include "cgraph_builder.h" - -cgraph_buildert::cgraph_buildert() -{ -} - -cgraph_buildert::~cgraph_buildert() -{ -} - -void -cgraph_buildert::analyze_module(const symbol_tablet& symbol_table, - const goto_functionst& functions) -{ - Forall_analyses(it, analyses) - { - (*it)->set_symbol_table(symbol_table); - } - - forall_goto_functions(it, functions) - { - const goto_functionst::goto_functiont& function = it->second; - - if (function.body_available) - { - std::cout << "=================================" << std::endl; - std::cout << "Analyzing function: " << it->first << std::endl; - analyze_function(it->first, function); - } - } - - std::cout << "===== AFTER COLLECTION =====" << std::endl; - print_analyses(std::cout); - - compute_fixpoints(); - - std::cout << "===== AFTER FIXPOINT =====" << std::endl; - print_analyses(std::cout); - - remove_invisible(); - - std::cout << "===== AFTER FILTERING =====" << std::endl; - print_analyses(std::cout); -} - -void -cgraph_buildert::analyze_function( - irep_idt current_function, - const goto_functionst::goto_functiont& function) -{ - Forall_analyses(it, analyses) - { - (*it)->enter_function(current_function); - forall_goto_program_instructions(it2, function.body) - { - (*it)->visit(*it2); - } - (*it)->exit_function(); - } -} - -void -cgraph_buildert::print_analyses(std::ostream& out) const -{ - forall_analyses(it, analyses) - { - out << " *** Analysis: " << (*it)->get_analysis_id() << std::endl; - (*it)->print(out); - out << std::endl; - } -} - -void -cgraph_buildert::compute_fixpoints() -{ - Forall_analyses(it, analyses) - { - (*it)->compute_fixpoint(); - } -} - -void -cgraph_buildert::remove_invisible() -{ - Forall_analyses(it, analyses) - { - (*it)->remove_invisible(); - } -} - -void -cgraph_buildert::serialize(const std::string& orig_file) -{ - forall_analyses(it, analyses) - { - std::string analysis_file = orig_file + (*it)->get_default_suffix(); - std::ofstream out(analysis_file.c_str()); - if (!out) - throw "Failed to write partial analysis result: " + analysis_file; - - (*it)->serialize(out); - - if (!out) { - out.close(); - throw "Failed to write the partial call graph file: " + analysis_file; - } - out.close(); - } -} - -void -cgraph_buildert::deserialize(const std::string& orig_file) -{ - Forall_analyses(it, analyses) - { - std::string analysis_file = orig_file + (*it)->get_default_suffix(); - std::ifstream in(analysis_file.c_str()); - if (!in) - throw "Failed to read the partial call graph file: " + analysis_file; - - (*it)->deserialize(in); - - if (!in) - { - in.close(); - throw "Failed to read the partial call graph file: " + analysis_file; - } - in.close(); - } -} - -void -cgraph_buildert::deserialize_list(std::istream& in) -{ - std::string line_str; - - while (getline(in, line_str)) - { - if (!line_str.empty()) - deserialize(line_str); - } - - if (in.bad()) - { - throw "Failed to read the list of call graph files."; - } - - std::cout << "===== AFTER LOAD =====" << std::endl; - print_analyses(std::cout); - compute_fixpoints(); - std::cout << "===== AFTER FIXPOINT =====" << std::endl; - print_analyses(std::cout); -} diff --git a/src/deltacheck/old/cgraph_builder.h b/src/deltacheck/old/cgraph_builder.h deleted file mode 100644 index 8cbe1dfb4..000000000 --- a/src/deltacheck/old/cgraph_builder.h +++ /dev/null @@ -1,69 +0,0 @@ -/*******************************************************************\ - -Module: Call graph builder, builds and stores partial call graphs -(per C file). - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CGRAPH_BUILDER_H -#define CPROVER_DELTACHECK_CGRAPH_BUILDER_H - -#include - -#include - -#include -#include - -#include "modular_code_analysis.h" - -#define Forall_analyses(it, analyses) \ - for(analysest::iterator it=(analyses).begin(); \ - it!=(analyses).end(); ++it) - -#define forall_analyses(it, analyses) \ - for(analysest::const_iterator it=(analyses).begin(); \ - it!=(analyses).end(); ++it) - -class cgraph_buildert { -public: - cgraph_buildert(); - ~cgraph_buildert(); - - void analyze_module(const symbol_tablet &symbol_table, - const goto_functionst& functions); - void analyze_function(irep_idt current_function, - const goto_functionst::goto_functiont& function); - - void add_analysis(modular_code_analysist* analysis) - { - analyses.push_back(analysis); - } - - void add_analyses(modular_code_analysist** analyses) - { - while (*analyses != 0) { - add_analysis(*analyses++); - } - } - - void serialize(const std::string& orig_file); - - void deserialize(const std::string& orig_file); - - void deserialize_list(std::istream& in); - -private: - typedef std::vector analysest; - - analysest analyses; - - void print_analyses(std::ostream& out) const; - void compute_fixpoints(); - void remove_invisible(); -}; - -#endif - diff --git a/src/deltacheck/old/collect_symbols.cpp b/src/deltacheck/old/collect_symbols.cpp deleted file mode 100644 index 9d4e2b7b2..000000000 --- a/src/deltacheck/old/collect_symbols.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_COLLECT_SYMBOLS_H -#define CPROVER_DELTACHECK_COLLECT_SYMBOLS_H - -#include - -#include - -void collect_symbols( - const goto_functionst::goto_functiont &goto_function, - find_symbols_sett &dest) -{ - -} - -#endif diff --git a/src/deltacheck/old/collect_symbols.h b/src/deltacheck/old/collect_symbols.h deleted file mode 100644 index b8c40bd1d..000000000 --- a/src/deltacheck/old/collect_symbols.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_COLLECT_SYMBOLS_H -#define CPROVER_DELTACHECK_COLLECT_SYMBOLS_H - -#include - -#include - -void collect_symbols( - const goto_functionst::goto_functiont &, - find_symbols_sett &); - -#endif diff --git a/src/deltacheck/old/data_flow.cpp b/src/deltacheck/old/data_flow.cpp deleted file mode 100644 index 3da5948e1..000000000 --- a/src/deltacheck/old/data_flow.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include "data_flow.h" - -/*******************************************************************\ - -Function: data_flowt::rename - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt data_flowt::rename( - kindt kind, - const exprt &src, - goto_programt::const_targett t) -{ - exprt tmp=src; - - if(tmp.id()==ID_symbol) - { - irep_idt identifier=to_symbol_expr(tmp).get_identifier(); - irep_idt new_identifier= - id2string(identifier)+"#"+i2string(t->location_number)+"v"+i2string(version)+ - (kind==OUT?"O":kind==OUT_TAKEN?"Ot":"I"); - to_symbol_expr(tmp).set_identifier(new_identifier); - } - else - { - Forall_operands(it, tmp) - *it=rename(kind, *it, t); - } - - return tmp; -} - -/*******************************************************************\ - -Function: data_flowt::guard - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -symbol_exprt data_flowt::guard(goto_programt::const_targett t) -{ - irep_idt id= - "data_flow::\\guard"+i2string(t->location_number)+"v"+i2string(version); - return symbol_exprt(id, bool_typet()); -} - -/*******************************************************************\ - -Function: data_flowt::out_is_in - - Inputs: - - Outputs: - - Purpose: adds the skip transformer for location t - -\*******************************************************************/ - -void data_flowt::out_is_in(goto_programt::const_targett t) -{ - // this says that v_OUT = v_IN - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); -} - -/*******************************************************************\ - -Function: data_flowt::transformer - - Inputs: - - Outputs: - - Purpose: adds the transformer for location t - -\*******************************************************************/ - -void data_flowt::transformer(goto_programt::const_targett t) -{ - if(t->is_assign()) - { - const code_assignt &assignment=to_code_assign(t->code); - - std::set assigned; - - if(assignment.lhs().id()==ID_symbol) - { - const exprt &lhs=assignment.lhs(); - const exprt &rhs=assignment.rhs(); - assigned.insert(lhs); - solver.set_equal(rename(OUT, lhs, t), rename(IN, rhs, t)); - } - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - if(assigned.find(*o_it)!=assigned.end()) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); - } - else if(t->is_assert()) - { - //if(assert_to_assume) - // solver.set_to_true(rename(OUT, t->guard, t)); - - out_is_in(t); - } - else if(t->is_function_call()) - { - const code_function_callt &function_call= - to_code_function_call(t->code); - - std::set assigned; - - if(function_call.lhs().id()==ID_symbol) - { - const exprt &lhs=function_call.lhs(); - assigned.insert(lhs); - } - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - if(assigned.find(*o_it)!=assigned.end()) - solver.set_equal(rename(OUT, *o_it, t), rename(IN, *o_it, t)); - } - else - out_is_in(t); - - // Do guard. This is the OR of the incoming edges. -// for( - -} - -/*******************************************************************\ - -Function: data_flowt::collect_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::collect_objects(const exprt &src) -{ - forall_operands(it, src) - collect_objects(*it); - - if(src.id()==ID_symbol) - objects.insert(src); -} - -/*******************************************************************\ - -Function: data_flowt::collect_objects - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::collect_objects(const goto_programt &goto_program) -{ - forall_goto_program_instructions(it, goto_program) - { - collect_objects(it->guard); - collect_objects(it->code); - } -} - -/*******************************************************************\ - -Function: data_flowt::join - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool data_flowt::join(goto_programt::const_targett t) -{ - // find facts that are true at all predecessors of t - // and make them true at t - - // build 'in' vector - solvert::var_sett in; - in.reserve(objects.size()); - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - { - in.push_back(rename(IN, *o_it, t)); - } - - // build list of matching 'out' vectors - std::list src; - - const loct &loc=loc_map[t]; - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - { - goto_programt::const_targett succ=*s_it; - goto_programt::const_targett succ_next=*s_it; - succ_next++; - - src.push_back(solvert::var_sett()); - solvert::var_sett &out=src.back(); - out.reserve(objects.size()); - - // connect branches to correct output - kindt kind= - (succ->is_goto() && succ_next!=t)?OUT_TAKEN:OUT; - - for(objectst::const_iterator - o_it=objects.begin(); - o_it!=objects.end(); - o_it++) - { - out.push_back(rename(kind, *o_it, succ)); - } - - } - - return solver.join(src, in); -} - -/*******************************************************************\ - -Function: data_flowt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void data_flowt::operator()(const goto_programt &goto_program) -{ - // collect objects to track - collect_objects(goto_program); - - // collect locations - forall_goto_program_instructions(it, goto_program) - { - loct &loc=loc_map[it]; - - // build successors - goto_program.get_successors(it, loc.succ); - - // build predecessors - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - loc_map[*s_it].pred.push_back(it); - } - - // add the transformers for all instructions - forall_goto_program_instructions(it, goto_program) - transformer(it); - - // now do data flow equations - forall_goto_program_instructions(it, goto_program) - work_queue.push_back(it); - - while(!work_queue.empty()) - { - goto_programt::const_targett t=work_queue.back(); - work_queue.pop_back(); - - if(join(t)) - { - const loct &loc=loc_map[t]; - for(goto_programt::const_targetst::const_iterator - s_it=loc.succ.begin(); s_it!=loc.succ.end(); s_it++) - work_queue.push_back(*s_it); - } - } -} diff --git a/src/deltacheck/old/data_flow.h b/src/deltacheck/old/data_flow.h deleted file mode 100644 index ab3e1fbcf..000000000 --- a/src/deltacheck/old/data_flow.h +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "solver.h" - -class data_flowt -{ -public: - explicit data_flowt(solvert &_solver): - version(0), - assert_to_assume(false), - solver(_solver) - { - } - - unsigned version; - bool assert_to_assume; - - void operator()(const goto_programt &); - -protected: - solvert &solver; - - typedef enum { OUT, OUT_TAKEN, IN } kindt; - - exprt rename(kindt kind, const exprt &src, goto_programt::const_targett t); - typet rename(kindt kind, const typet &src, goto_programt::const_targett t); - - class symbol_exprt guard(goto_programt::const_targett t); - - void transformer(goto_programt::const_targett t); - void out_is_in(goto_programt::const_targett t); - - void collect_objects(const goto_programt &); - void collect_objects(const exprt &); - - typedef std::vector work_queuet; - work_queuet work_queue; - - struct loct - { - goto_programt::const_targetst succ, pred; - }; - - bool join(goto_programt::const_targett t); - - typedef std::map loc_mapt; - loc_mapt loc_map; - - typedef std::set objectst; - objectst objects; -}; diff --git a/src/deltacheck/old/delta_check.cpp b/src/deltacheck/old/delta_check.cpp deleted file mode 100644 index 8b94bf563..000000000 --- a/src/deltacheck/old/delta_check.cpp +++ /dev/null @@ -1,449 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -#include "index.h" -#include "ssa_data_flow.h" -#include "html_report.h" -#include "get_function.h" -#include "delta_check.h" - -/*******************************************************************\ - -Function: delta_check_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void delta_check_all( - const indext &index1, - const indext &index2, - std::ostream &report, - message_handlert &message_handler) -{ - // we do this by file in index2 - - messaget message(message_handler); - - get_functiont get_function1(index1); - get_function1.set_message_handler(message_handler); - - for(indext::file_to_functiont::const_iterator - file_it=index2.file_to_function.begin(); - file_it!=index2.file_to_function.end(); - file_it++) - { - message.status() << "Processing \"" << file_it->first << "\"" - << messaget::eom; - - // read the file - goto_modelt model2; - read_goto_binary(id2string(file_it->first), model2, message_handler); - - const std::set &functions=file_it->second; - - // now do all functions from model2 - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - const goto_functionst::goto_functiont *index2_fkt= - &model2.goto_functions.function_map.find(id)->second; - - // get corresponding index1 function, if available - - const goto_functionst::goto_functiont *index1_fkt= - get_function1(id); - - if(index1_fkt!=NULL) - { - message.status("Delta Checking \""+id2string(id)+"\""); - - report << "

Function " << id << " in " << file_it->first - << "

\n"; - - #if 0 - function_delta(id, *index1_fkt, *index2_fkt, report, message_handler); - #endif - } - } - } -} - -/*******************************************************************\ - -Function: delta_check - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void delta_check( - const indext &index1, - const indext &index2, - message_handlert &message_handler) -{ - messaget message(message_handler); - - std::string report_file_name="deltacheck.html"; - std::ofstream out(report_file_name.c_str()); - - if(!out) - { - message.error() << "failed to write to \"" - << report_file_name << "\"" << messaget::eom; - return; - } - - message.status() << "Writing report into \"" - << report_file_name << "\"" << messaget::eom; - - html_report_header(out, index1, index2); - - delta_check_all(index1, index2, out, message_handler); - - html_report_footer(out, index1, index2); -} - -#if 0 -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "get_goto_program.h" -#include "xml_conversion.h" -#include "summarization.h" -#include "dependencies.h" -#include "function_transformer.h" - -//#include "cgraph_builder.h" -//#include "modular_fptr_analysis.h" -//#include "modular_globals_analysis.h" - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls_rec( - const namespacet &ns, - const goto_functionst &goto_functions, - const exprt &function, - std::set &called_functions) -{ - if(function.id()==ID_symbol) - { - irep_idt id=to_symbol_expr(function).get_identifier(); - const symbolt &symbol=ns.lookup(id); - if(!symbol.is_file_local) - called_functions.insert(id); - } - else if(function.id()==ID_dereference) - { - } - else if(function.id()==ID_if) - { - } -} - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - std::ostream &out) -{ - std::set called_functions; - - forall_goto_program_instructions(it, goto_function.body) - { - if(it->is_function_call()) - { - const exprt &function=to_code_function_call(it->code).function(); - - summarize_function_calls_rec( - ns, goto_functions, function, called_functions); - } - } - - for(std::set::const_iterator - it=called_functions.begin(); - it!=called_functions.end(); - it++) - { - out << " "; - out << "\n"; - } -} - -/*******************************************************************\ - -Function: summarize_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function( - const namespacet &ns, - const goto_functionst &goto_functions, - const symbolt &symbol, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - out << "\n"; - - if(symbol.location.is_not_nil() && - symbol.location.get_file()!="") - out << " " << xml(symbol.location); - - summarize_function_calls(ns, goto_functions, goto_function, out); - - function_transformer(ns, goto_functions, goto_function, message_handler, out); - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: dump_exported_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_exported_functions( - const namespacet &ns, - const goto_functionst &goto_functions, - message_handlert &message_handler, - std::ostream &out) -{ - out << "\n"; - - // do this for each function - forall_goto_functions(f_it, goto_functions) - { - if(!f_it->second.body_available) - continue; - - if(has_prefix(id2string(f_it->first), CPROVER_PREFIX)) - continue; - - const symbolt &symbol=ns.lookup(f_it->first); - - if(symbol.is_file_local) - continue; - - messaget message(message_handler); - message.status("Summarizing "+id2string(f_it->first)); - - summarize_function( - ns, goto_functions, symbol, f_it->second, message_handler, out); - } - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: dump_state_variables - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_state_variables( - const symbol_tablet &symbol_table, - std::ostream &out) -{ - out << "\n"; - - forall_symbols(s_it, symbol_table.symbols) - { - const symbolt &symbol=s_it->second; - - if(has_prefix(id2string(symbol.name), CPROVER_PREFIX)) - continue; - - if(symbol.type.id()==ID_code || - symbol.is_type) - continue; - - if(symbol.is_file_local) - continue; - - out << "\n"; - - if(symbol.location.is_not_nil() && symbol.location.get_file()!="") - out << xml(symbol.location); - - out << "\n"; - } - - out << "\n"; - out << "\n"; -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const symbol_tablet &symbol_table, - const goto_functionst &goto_functions, - const optionst &options, - message_handlert &message_handler, - std::ostream &out) -{ - // first collect non-static function symbols that - // have a body - - namespacet ns(symbol_table); - - dump_exported_functions(ns, goto_functions, message_handler, out); - - dump_state_variables(symbol_table, out); - - #if 0 - cgraph_buildert cg_builder; - modular_fptr_analysist fptr_analysis; - modular_globals_analysist globals_analysis; - - cg_builder.add_analysis(&fptr_analysis); - cg_builder.add_analysis(&globals_analysis); - - cg_builder.analyze_module(symbol_table, goto_functions); - cg_builder.serialize(file_name); - #endif -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - // first check dependencies - if(!options.get_bool_option("force") && - dependencies(function_file_map, file_name, message_handler)==FRESH) - return; - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - //goto_functions.output(ns, std::cout); - - std::string summary_file_name=file_name+".summary"; - std::ofstream summary_file(summary_file_name.c_str(), - std::ios::binary|std::ios::trunc|std::ios::out); - - if(!summary_file) - throw std::string("failed to write summary file"); - - summary_file << "\n"; - - ::summarization( - function_file_map, - symbol_table, - goto_functions, - options, - message_handler, - summary_file); - - summary_file << "\n"; - - messaget message(message_handler); - message.status("Summary written as "+summary_file_name); -} - -#endif diff --git a/src/deltacheck/old/delta_check.h b/src/deltacheck/old/delta_check.h deleted file mode 100644 index 9c1596f7b..000000000 --- a/src/deltacheck/old/delta_check.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTA_CHECK_H -#define CPROVER_DELTA_CHECK_H - -class message_handlert; -class indext; - -void delta_check( - const indext &index1, - const indext &index2, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/dependencies.cpp b/src/deltacheck/old/dependencies.cpp deleted file mode 100644 index cccabbd46..000000000 --- a/src/deltacheck/old/dependencies.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Dependency Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include "dependencies.h" - -/*******************************************************************\ - -Function: dependencies - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -dependency_statet dependencies( - const function_file_mapt &function_file_map, - const std::string &file_name, - message_handlert &message_handler) -{ - std::string summary_file=file_name+".summary"; - - std::ifstream in(summary_file.c_str()); - - if(!in) return STALE; - - xmlt xml; - parse_xml(in, summary_file, message_handler, xml); - - return STALE; -} diff --git a/src/deltacheck/old/dependencies.h b/src/deltacheck/old/dependencies.h deleted file mode 100644 index ae308f2b7..000000000 --- a/src/deltacheck/old/dependencies.h +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: Dependency Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_DEPENDENCIES_H -#define CPROVER_DELTACHECK_DEPENDENCIES_H - -#include - -#include "function_file_map.h" - -typedef enum { STALE, FRESH } dependency_statet; - -dependency_statet dependencies( - const function_file_mapt &function_file_map, - const std::string &file_name, - class message_handlert &message_handler); - -#endif diff --git a/src/deltacheck/old/discover_predicates.cpp b/src/deltacheck/old/discover_predicates.cpp deleted file mode 100644 index 0a2004caf..000000000 --- a/src/deltacheck/old/discover_predicates.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/*******************************************************************\ - -Module: Predicate Discovery - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "discover_predicates.h" -#include "canonicalize.h" - -/*******************************************************************\ - -Function: discover_predicates - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#include - -std::list discover_predicates( - const exprt &src, - const namespacet &ns) -{ - if(src.id()==ID_and || src.id()==ID_or) - { - std::list result; - - forall_operands(it, src) - { - std::list tmp=discover_predicates(*it, ns); - result.insert(result.end(), tmp.begin(), tmp.end()); - } - - return result; - } - else if(src.id()==ID_not) - { - assert(src.operands().size()==1); - return discover_predicates(src.op0(), ns); - } - else - { - exprt tmp=src; - bool negation; - canonicalize(tmp, negation, ns); - std::list result; - result.push_back(tmp); - return result; - } -} diff --git a/src/deltacheck/old/discover_predicates.h b/src/deltacheck/old/discover_predicates.h deleted file mode 100644 index 2330b2920..000000000 --- a/src/deltacheck/old/discover_predicates.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Predicate Discovery - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -std::list discover_predicates( - const exprt &src, - const namespacet &ns); diff --git a/src/deltacheck/old/function_delta.cpp b/src/deltacheck/old/function_delta.cpp deleted file mode 100644 index ba9e6dafa..000000000 --- a/src/deltacheck/old/function_delta.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "solver.h" -#include "function_delta.h" - -class function_deltat:public messaget -{ -public: - explicit function_deltat( - std::ostream &_out):out(_out), ns(symbol_table), solver(ns) - { - } - - void operator()( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2); - -protected: - std::ostream &out; - symbol_tablet symbol_table; - namespacet ns; - solvert solver; - - exprt rename_rhs(const exprt &src, goto_programt::const_targett t, unsigned v); - exprt rename_lhs(const exprt &src, goto_programt::const_targett t, unsigned v); - - void encode(const goto_functionst::goto_functiont &goto_function, unsigned v); -}; - -/*******************************************************************\ - -Function: function_deltat::encode - - Inputs: - - Outputs: - - Purpose: builds data-flow equations - -\*******************************************************************/ - -void function_deltat::encode( - const goto_functionst::goto_functiont &f, unsigned v) -{ - const goto_programt &body=f.body; - - forall_goto_program_instructions(i_it, body) - { - const goto_programt::instructiont &instruction=*i_it; - - if(instruction.is_goto()) - { - } - else if(instruction.is_assert()) - { - } - else if(instruction.is_assume()) - { - } - else - { - // treat like 'skip' - } - } -} - -/*******************************************************************\ - -Function: function_deltat::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_deltat::operator()( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2) -{ - if(!f2.body.has_assertion()) - { - status("New version has no properties"); - return; - } - - out << "

Function " << id << "

\n"; - - // encode both programs - encode(f1, 1); - encode(f2, 2); -} - -/*******************************************************************\ - -Function: function_delta - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_delta( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2, - std::ostream &out, - message_handlert &message_handler) -{ - function_deltat function_delta(out); - function_delta.set_message_handler(message_handler); - function_delta(id, f1, f2); -} - diff --git a/src/deltacheck/old/function_delta.h b/src/deltacheck/old/function_delta.h deleted file mode 100644 index 6dfdc7785..000000000 --- a/src/deltacheck/old/function_delta.h +++ /dev/null @@ -1,24 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_FUNCTION_DELTA_H -#define CPROVER_FUNCTION_DELTA_H - -#include - -#include -#include - -void function_delta( - const irep_idt &id, - const goto_functionst::goto_functiont &f1, - const goto_functionst::goto_functiont &f2, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/function_file_map.cpp b/src/deltacheck/old/function_file_map.cpp deleted file mode 100644 index 6870464ea..000000000 --- a/src/deltacheck/old/function_file_map.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Map from function names to the file - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "function_file_map.h" - -/*******************************************************************\ - -Function: build_function_file_map - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void build_function_file_map( - const std::list &files, - message_handlert &message_handler, - function_file_mapt &function_file_map) -{ - for(std::list::const_iterator - file_it=files.begin(); - file_it!=files.end(); - file_it++) - { - xmlt xml; - parse_xml(*file_it+".summary", message_handler, xml); - - irep_idt file=*file_it; - - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - irep_idt id=f_it->get_attribute("id"); - function_file_map[id]=file; - } - } - } -} diff --git a/src/deltacheck/old/function_file_map.h b/src/deltacheck/old/function_file_map.h deleted file mode 100644 index 85dc54e1f..000000000 --- a/src/deltacheck/old/function_file_map.h +++ /dev/null @@ -1,23 +0,0 @@ -/*******************************************************************\ - -Module: Map from function names to the file - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_FUNCTION_FILE_MAP_H -#define CPROVER_DELTACHECK_FUNCTION_FILE_MAP_H - -#include - -#include - -typedef std::map function_file_mapt; - -void build_function_file_map( - const std::list &files, - class message_handlert &message_handler, - function_file_mapt &function_file_map); - -#endif diff --git a/src/deltacheck/old/function_transformer.cpp b/src/deltacheck/old/function_transformer.cpp deleted file mode 100644 index d500b44ac..000000000 --- a/src/deltacheck/old/function_transformer.cpp +++ /dev/null @@ -1,478 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include - -#include "function_transformer.h" -#include "statement_transformer.h" -#include "collect_symbols.h" -#include "discover_predicates.h" -#include "predicates.h" - -class function_transformert -{ -public: - class locationt - { - public: - // the BDD for the guard - BDD guard; - - goto_programt::const_targett target; - unsigned PC; - }; - - typedef std::vector locationst; - locationst locations; - - typedef std::map location_mapt; - location_mapt location_map; - - unsigned target_PC(goto_programt::const_targett t) - { - location_mapt::const_iterator it=location_map.find(t); - assert(it!=location_map.end()); - return it->second; - } - - function_transformert( - const namespacet &_ns, - const goto_functionst &_goto_functions, - Cudd &_mgr, - message_handlert &_message_handler): - predicates(_ns), - ns(_ns), - goto_functions(_goto_functions), - mgr(_mgr), - message(_message_handler) - { - } - - void operator() (const goto_functionst::goto_functiont &); - - void output(std::ostream &out) const; - - predicatest predicates; - -protected: - const namespacet &ns; - const goto_functionst &goto_functions; - Cudd &mgr; - messaget message; - - void xml(BDD, std::ostream &) const; - - void setup_state_map(const goto_functionst::goto_functiont &goto_function) - { - unsigned PC=0; - locations.resize(goto_function.body.instructions.size()); - - forall_goto_program_instructions(i_it, goto_function.body) - { - locations[PC].target=i_it; - locations[PC].guard=!mgr.bddOne(); - locations[PC].PC=PC; - location_map[i_it]=PC++; - } - } - - void add(const std::list &); - - void discover_predicates( - const goto_functionst::goto_functiont &goto_function); - - void make_entry_state() - { - assert(!locations.empty()); - locations[0].guard=mgr.bddOne(); - } - - std::stack queue; - - void get_successors(unsigned PC); - - void merge(unsigned PC, BDD guard); -}; - -/*******************************************************************\ - -Function: function_transformert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::operator()( - const goto_functionst::goto_functiont &goto_function) -{ - setup_state_map(goto_function); - discover_predicates(goto_function); - message.debug("Predicates: "+predicates.make_list()); - - if(locations.empty()) return; - - // setup entry state, and put into queue - make_entry_state(); - queue.push(0); - - while(!queue.empty()) - { - unsigned PC=queue.top(); - queue.pop(); - - // compute successors and propagate - get_successors(PC); - } -} - -/*******************************************************************\ - -Function: function_transformert::get_successors - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::get_successors(unsigned PC) -{ - // end of function readched? - if(PC>=locations.size()) return; - - const locationt &from=locations[PC]; - - const goto_programt::instructiont &instruction= - *from.target; - - statement_transformert statement_transformer(predicates, mgr, ns); - - if(instruction.is_goto()) - { - // guarded? - if(instruction.guard.is_false()) - merge(PC+1, from.guard); - else - { - if(!instruction.guard.is_true()) - { - BDD new_guard= - statement_transformer.guard_not(from.guard, instruction.guard); - merge(PC+1, new_guard); - } - - // targets - for(goto_programt::instructiont::targetst::const_iterator - it=instruction.targets.begin(); - it!=instruction.targets.end(); - it++) - { - BDD new_guard= - statement_transformer.guard(from.guard, instruction.guard); - - merge(target_PC(*it), new_guard); - } - } - } - else if(instruction.is_assign()) - { - const code_assignt &code_assign=to_code_assign(instruction.code); - BDD new_guard= - statement_transformer.assign(from.guard, code_assign); - merge(PC+1, new_guard); - } - else if(instruction.is_function_call()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_assume()) - { - BDD new_guard= - statement_transformer.guard(new_guard, instruction.guard); - - merge(PC+1, new_guard); - } - else if(instruction.is_assert()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_other()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_start_thread()) - { - // targets - for(goto_programt::instructiont::targetst::const_iterator - it=instruction.targets.begin(); - it!=instruction.targets.end(); - it++) - { - merge(target_PC(*it), from.guard); - } - } - else if(instruction.is_end_thread()) - { - // no successor - } - else if(instruction.is_end_function()) - { - // no successor - } - else if(instruction.is_atomic_begin()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_atomic_end()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_return()) - { - BDD new_guard=from.guard; - // process return value - - // go to end-of-function - merge(locations.size()-1, new_guard); - } - else if(instruction.is_decl()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_dead()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_throw()) - { - // complex successor - } - else if(instruction.is_catch()) - { - BDD new_guard=from.guard; - merge(PC+1, new_guard); - } - else if(instruction.is_skip() || - instruction.is_location()) - { - merge(PC+1, from.guard); - } - else - { - // treat like skip - merge(PC+1, from.guard); - } -} - -/*******************************************************************\ - -Function: function_transformert::merge - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::merge( - unsigned PC, - BDD new_guard) -{ - // end of function? - if(PC>=locations.size()) return; - - locationt &to=locations[PC]; - BDD old_guard=to.guard; - to.guard|=new_guard; // merge - - if(to.guard!=old_guard) - { - std::cout << "New state at PC " << PC << std::endl; - to.guard.print(predicates.size()*2); - queue.push(PC); // fixpoint not yet reached - } -} - -/*******************************************************************\ - -Function: function_transformert::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::add( - const std::list &new_predicates) -{ - for(std::list::const_iterator - it=new_predicates.begin(); - it!=new_predicates.end(); - it++) - { - predicates.add(mgr, *it); - } -} - -/*******************************************************************\ - -Function: function_transformert::discover_predicates - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::discover_predicates( - const goto_functionst::goto_functiont &goto_function) -{ - forall_goto_program_instructions(i_it, goto_function.body) - { - const goto_programt::instructiont &instruction=*i_it; - - if(instruction.is_assert()) - { - add(::discover_predicates(instruction.guard, ns)); - } - } -} - -/*******************************************************************\ - -Function: function_transformert::xml - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::xml(BDD bdd, std::ostream &out) const -{ - // dump DNF - assert((unsigned)mgr.ReadSize()>=predicates.size()*2); - - out << " " << std::endl; - - CUDD_VALUE_TYPE value; - int *cube; - DdGen *gen; - - Cudd_ForeachCube(mgr.getManager(), bdd.getNode(), gen, cube, value) - { - assert(gen!=NULL); - assert(cube!=NULL); - - out << " " << std::endl; - for(unsigned p=0; p"; - out << "" << std::endl; - if(cube[i]==0) out << ""; - out << std::endl; - break; - - case 2: // don't care - break; - - default: - assert(false); - } - } - out << " " << std::endl; - } - - out << " " << std::endl; -} - -/*******************************************************************\ - -Function: function_transformert::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformert::output(std::ostream &out) const -{ - out << " " << std::endl; - - // dump DNF - xml(locations.back().guard, out); - - out << " " << std::endl; -} - -/*******************************************************************\ - -Function: transformer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void function_transformer( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - Cudd mgr; - function_transformert function_transformer( - ns, goto_functions, mgr, message_handler); - - function_transformer(goto_function); - - function_transformer.output(out); -} diff --git a/src/deltacheck/old/function_transformer.h b/src/deltacheck/old/function_transformer.h deleted file mode 100644 index 098a064fa..000000000 --- a/src/deltacheck/old/function_transformer.h +++ /dev/null @@ -1,21 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include - -void function_transformer( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out); diff --git a/src/deltacheck/old/get_goto_program.cpp b/src/deltacheck/old/get_goto_program.cpp deleted file mode 100644 index 255afb85c..000000000 --- a/src/deltacheck/old/get_goto_program.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/*******************************************************************\ - -Module: Goto Program Preparation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -#include - -#include "get_goto_program.h" - -/*******************************************************************\ - -Function: get_goto_program - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void get_goto_program( - const std::string &file_name, - const optionst &options, - symbol_tablet &symbol_table, - goto_functionst &goto_functions, - message_handlert &message_handler) -{ - messaget message(message_handler); - message.status("Reading goto-program"); - - if(read_goto_binary( - file_name, - symbol_table, goto_functions, message_handler)) - throw std::string("failed to read goto binary ")+file_name; - - config.ansi_c.set_from_symbol_table(symbol_table); - - message.status("Preparing goto-program"); - - // finally add the library - link_to_library( - symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - // do partial inlining - goto_partial_inline(goto_functions, ns, message_handler); - - // add checks - goto_check(ns, options, goto_functions); - - // recalculate numbers, etc. - goto_functions.update(); - - // add loop ids - goto_functions.compute_loop_numbers(); -} - diff --git a/src/deltacheck/old/get_goto_program.h b/src/deltacheck/old/get_goto_program.h deleted file mode 100644 index 8749e600a..000000000 --- a/src/deltacheck/old/get_goto_program.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Goto Program Preparation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include - -void get_goto_program( - const std::string &file_name, - const optionst &options, - symbol_tablet &symbol_table, - goto_functionst &goto_functions, - message_handlert &message_handler); diff --git a/src/deltacheck/old/modular_analysis.h b/src/deltacheck/old/modular_analysis.h deleted file mode 100644 index c909e7681..000000000 --- a/src/deltacheck/old/modular_analysis.h +++ /dev/null @@ -1,246 +0,0 @@ -/*******************************************************************\ - -Module: Partial fixed-point analysis to be used for function-pointer alias -analysis per C file. - -The analysis accepts rules (subsumption among variables), values (constants), -and distinguishes visible and invisible variables. After fixpoint computation -the invisible variables can be removed. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARTIAL_ANALYSIS_H -#define CPROVER_DELTACHECK_PARTIAL_ANALYSIS_H - -#include - -#include - -template -class modular_analysist { -public: - typedef variableT variablet; - typedef valueT valuet; - typedef typename hash_set_cont valuest; - typedef typename hash_set_cont variablest; - typedef typename hash_map_cont value_mapt; - typedef typename hash_map_cont rulest; - - modular_analysist() {} - - bool add_rule(const variablet& subsumes, - const variablet& subsumed) - { - insert_variable(inverse_rules, subsumed, subsumes); - return insert_variable(rules, subsumes, subsumed); - } - - bool add_value(const variablet& variable, const valuet& value) - { - typename value_mapt::iterator it = value_map.find(variable); - - if (it == value_map.end()) - it = value_map.insert(typename value_mapt::value_type( - variable, valuest())).first; - - unsigned count = it->second.size(); - it->second.insert(value); - - return count != it->second.size(); - } - - bool add_values(const variablet& variable, const valuest& values) - { - typename value_mapt::iterator it = value_map.find(variable); - - if (it == value_map.end()) - it = value_map.insert( - typename value_mapt::value_type(variable, valuest())).first; - - unsigned count = it->second.size(); - it->second.insert(values.begin(), values.end()); - - return count != it->second.size(); - } - - void set_visible(const variablet& variable) - { - visible_variables.insert(variable); - } - - void compute_fixpoint() - { - variablest queue; - - // Only variables occurring as rhs of a rule need to be considered - for (typename rulest::const_iterator - it = rules.begin(); - it != rules.end(); - ++it) - { - queue.insert(it->first); - } - - // Iterate until saturation - while (!queue.empty()) - { - variablet current = *queue.begin(); - queue.erase(queue.begin()); - - bool changed = update_variable(current); - - if (changed) - plan_dependants(queue, current); - } - } - - void remove_invisible() - { - remove_invisible_from_values(); - remove_invisible_from_rules(rules); - remove_invisible_from_rules(inverse_rules); - } - - const value_mapt& get_value_map() const { return value_map; } - -private: - - bool insert_variable(rulest& rules, const variablet& key, - const variablet& value) - { - typename rulest::iterator it = rules.find(key); - - if (it == rules.end()) - it = rules.insert(typename rulest::value_type(key, variablest())).first; - - unsigned count = it->second.size(); - it->second.insert(value); - - return count != it->second.size(); - } - - void remove_invisible_from_values() - { - for (typename value_mapt::const_iterator it = value_map.begin(); - it != value_map.end();) - { - if (visible_variables.find(it->first) == visible_variables.end()) - it = value_map.erase(it); - else - ++it; - } - } - - void remove_invisible_from_rules(rulest& rules) - { - for (typename rulest::iterator it = rules.begin(); - it != rules.end();) - { - if (visible_variables.find(it->first) == visible_variables.end()) - { - // Invisible key -> remove the whole entry - it = rules.erase(it); - } - else - { - // Visible key -> remove invisible values - variablest& variables = it->second; - for (typename variablest::const_iterator it2 = variables.begin(); - it2 != variables.end();) - { - if (visible_variables.find(*it2) == visible_variables.end()) - it2 = variables.erase(it2); - else - ++it2; - } - - // All values removed -> remove the whole entry anyway - if (variables.size() == 0) - it = rules.erase(it); - else - ++it; - } - } - } - - bool update_variable(const variablet& variable) - { - bool changed = false; - - typename rulest::const_iterator it = rules.find(variable); - - if (it == inverse_rules.end()) - return false; - - // Merge the values and rules - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - if (*it2 != variable) { - if (visible_variables.find(*it2) == visible_variables.end()) { - changed |= merge_rules(variable, *it2); - } - changed |= merge_values(variable, *it2); - } - } - - return changed; - } - - bool merge_rules(const variablet& dest, const variablet& src) - { - typename rulest::const_iterator it = rules.find(src); - - if (it == rules.end()) - return false; - - bool changed = false; - - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - changed |= add_rule(dest, *it2); - } - return changed; - } - - bool merge_values(const variablet& dest, const variablet& src) - { - typename value_mapt::const_iterator it = value_map.find(src); - - if (it == value_map.end()) - return false; - - return add_values(dest, it->second); - } - - void plan_dependants(variablest& queue, const variablet& variable) - { - typename rulest::const_iterator it = inverse_rules.find(variable); - - if (it == inverse_rules.end()) - return; - - for (typename variablest::const_iterator it2 = it->second.begin(); - it2 != it->second.end(); - ++it2) - { - if (*it2 != variable) - queue.insert(*it2); - } - } - -protected: - value_mapt value_map; - rulest rules; - rulest inverse_rules; - variablest visible_variables; -}; - -#endif - diff --git a/src/deltacheck/old/modular_code_analysis.cpp b/src/deltacheck/old/modular_code_analysis.cpp deleted file mode 100644 index b3834cd81..000000000 --- a/src/deltacheck/old/modular_code_analysis.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/*******************************************************************\ - -Module: Ancestor of modular (i.e., per C file) fixpoint analysis -of goto-programs. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include "modular_code_analysis.h" - -modular_code_analysist::modular_code_analysist() : symbol_table(NULL) -{ -} - -modular_code_analysist::~modular_code_analysist() -{ -} - -void -modular_code_analysist::visit(const goto_programt::instructiont& instr) -{ - switch(instr.type) { - case ASSUME: - accept_assume(to_code_assume(instr.code)); - break; - case ASSIGN: - accept_assign(to_code_assign(instr.code)); - break; - case ASSERT: - accept_assert(to_code_assert(instr.code)); - break; - case FUNCTION_CALL: - accept_function_call(to_code_function_call(instr.code)); - break; - case RETURN: - accept_return(to_code_return(instr.code)); - break; - case SKIP: // fall through - case DECL: // fall through - case GOTO: // fall through - case END_FUNCTION: // fall through - break; - default: - throw "Unexpected instruction type in modular_code_analysist::visit()"; - break; - } -} - -void -modular_code_analysist::print(std::ostream& out) const -{ - // Values - for (value_mapt::const_iterator it = value_map.begin(); - it != value_map.end(); - ++it) - { - out << "Values for \"" << it->first << "\" <--" << std::endl; - - const valuest& values = it->second; - for (valuest::const_iterator it2 = values.begin(); - it2 != values.end(); - ++it2) - { - out << " \"" << *it2 << "\"" << std::endl; - } - } - // Rules - for (rulest::const_iterator it = rules.begin(); - it != rules.end(); - ++it) - { - out << "Rules for \"" << it->first << "\" <--" << std::endl; - - const variablest& variables = it->second; - for (variablest::const_iterator it2 = variables.begin(); - it2 != variables.end(); - ++it2) - { - out << " \"" << *it2 << "\"" << std::endl; - } - } -} - -void -modular_code_analysist::serialize(std::ostream& out) const -{ - // Values - out << value_map.size() << std::endl; - for (value_mapt::const_iterator it = value_map.begin(); - it != value_map.end(); - ++it) - { - const valuest& values = it->second; - - out << it->first << std::endl; - out << values.size() << std::endl; - - for (valuest::const_iterator it2 = values.begin(); - it2 != values.end(); - ++it2) - { - out << *it2 << std::endl; - } - } - // Rules - out << rules.size() << std::endl; - for (rulest::const_iterator it = rules.begin(); - it != rules.end(); - ++it) - { - const variablest& variables = it->second; - out << it->first << std::endl; - out << it->second.size() << std::endl; - - for (variablest::const_iterator it2 = variables.begin(); - it2 != variables.end(); - ++it2) - { - out << *it2 << std::endl; - } - } - // Visible variables - out << visible_variables.size() << std::endl; - for (variablest::const_iterator it = visible_variables.begin(); - it != visible_variables.end(); - ++it) - { - out << *it << std::endl; - } -} - -void -modular_code_analysist::deserialize(std::istream& in) -{ - // Values - int values_map_size; - in >> values_map_size; - if (!in.good()) return; - - for (int i = 0; i < values_map_size; ++i) - { - int values_size; - std::string var_str; - in >> var_str; - in >> values_size; - variablet var(var_str); - if (!in.good()) return; - - for (int j = 0; j < values_size; ++j) - { - std::string val_str; - in >> val_str; - valuet val(val_str); - - add_value(var, val); - } - } - // Rules - int rules_size; - in >> rules_size; - if (!in.good()) return; - - for (int i = 0; i < rules_size; ++i) - { - int vars_size; - std::string var_str; - in >> var_str; - in >> vars_size; - variablet var(var_str); - if (!in.good()) return; - - for (int j = 0; j < vars_size; ++j) - { - std::string var2_str; - in >> var2_str; - variablet var2(var2_str); - - add_rule(var, var2); - } - } - // Visible variables - int visible_size; - in >> visible_size; - if (!in.good()) return; - - for (int i = 0; i < visible_size; ++i) - { - std::string var_str; - in >> var_str; - variablet var(var_str); - - set_visible(var); - } -} - diff --git a/src/deltacheck/old/modular_code_analysis.h b/src/deltacheck/old/modular_code_analysis.h deleted file mode 100644 index 71a1ac028..000000000 --- a/src/deltacheck/old/modular_code_analysis.h +++ /dev/null @@ -1,67 +0,0 @@ -/*******************************************************************\ - -Module: The ancestor of modular (i.e., per C file) fixpoint analysis -of goto-programs. Based on modular_analysist, this class adds visitor-like -processing of goto-programs. - -The class supports serialization and deserialization to/from a text file. -Multiple deserialization results in merged rules, which is useful to merge -results of the analysis of different individual modules. After such merging, -fixpoint computation should be run to obtain the result of the overall analysis. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_CODE_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_CODE_ANALYSIS_H - -#include - -#include - -#include "modular_analysis.h" - -class modular_code_analysist : public modular_analysist { -public: - modular_code_analysist(); - virtual ~modular_code_analysist(); - - virtual const char* get_default_suffix() const = 0; - virtual const char* get_analysis_id() const = 0; - - // Instruction visitor functions - void visit(const goto_programt::instructiont& instr); - virtual void enter_function( - const irep_idt& function_id) - { - current_function = function_id; - } - virtual void exit_function() - { - current_function.clear(); - } - - void set_symbol_table(const symbol_tablet& _symbol_table) - { - symbol_table = &_symbol_table; - } - - virtual void print(std::ostream& out) const; - virtual void serialize(std::ostream& out) const; - virtual void deserialize(std::istream& in); - -protected: - virtual void accept_assume(const code_assumet& instruction) {}; - virtual void accept_assign(const code_assignt& instruction) {}; - virtual void accept_assert(const code_assertt& instruction) {}; - virtual void accept_function_call(const code_function_callt& instruction) {}; - virtual void accept_return(const code_returnt& instruction) {}; - - irep_idt current_function; - const symbol_tablet* symbol_table; -}; - -#endif - diff --git a/src/deltacheck/old/modular_fptr_analysis.cpp b/src/deltacheck/old/modular_fptr_analysis.cpp deleted file mode 100644 index bab3d6338..000000000 --- a/src/deltacheck/old/modular_fptr_analysis.cpp +++ /dev/null @@ -1,275 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of function pointers. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include -#include - -#include - -#include "modular_fptr_analysis.h" -#include "modular_code_analysis.h" - -modular_fptr_analysist::modular_fptr_analysist() : any_variable("__ANY__") -{ - set_visible(any_variable); -} - -modular_fptr_analysist::~modular_fptr_analysist() -{ -} - -void -modular_fptr_analysist::accept_function_call( - const code_function_callt& instruction) -{ - irep_idt function_var = current_function; - set_visible(function_var); - const exprt& target = instruction.function(); - - if (target.id() == ID_symbol) { - // Direct function call, we just mark the call graph edge - const symbol_exprt& symbol = to_symbol_expr(target); - - std::cout << " - direct call to \"" << - symbol.get_identifier() << - "\"" << std::endl; - - irep_idt function_value = symbol.get_identifier(); - - add_value(function_var, function_value); - } - else if (target.id() == ID_dereference) - { - // Indirect call, we find out what is actually dereferenced and add - // the dependency on the corresponding "type [x field]" - std::cout << " - indirect call: " << target.to_string() << - std::endl; - - variablet var; - const dereference_exprt& dereference = to_dereference_expr(target); - - if (try_compute_variable(dereference.pointer(), var)) - { - add_rule(function_var, var); - } - - // FIXME: Handle also parameters here -> hard to do due to yet unknown - // aliasing. - } - else - { - // Unknown function call operand - assert(false); - } -} - -void -modular_fptr_analysist::accept_assign(const code_assignt& instruction) -{ - std::cout << " - rhs: " << instruction.rhs().to_string() << std::endl; - - variablet lhs_var; - if (!try_compute_variable(instruction.lhs(), lhs_var)) - return; - - process_assignment(lhs_var, instruction.rhs()); -} - -void -modular_fptr_analysist::accept_return(const code_returnt& instruction) -{ - std::cout << " - return value: " << instruction.return_value().to_string() << - std::endl; - variablet lhs_var = id2string(current_function) + ".return_value"; - - process_assignment(lhs_var, instruction.return_value()); -} - -void -modular_fptr_analysist::process_assignment(const variablet& lhs_var, - const exprt& rhs) -{ - valuet rhs_val; - variablet rhs_var; - - if (try_compute_constant_value(rhs, rhs_val)) - { - add_value(lhs_var, rhs_val); - } - else - { - if (try_compute_variable(rhs, rhs_var)) - { - add_rule(lhs_var, rhs_var); - } - } -} - -bool -modular_fptr_analysist::try_compute_constant_value(const exprt& expr, - valuet& value) -{ - if (expr.id() == ID_typecast) - { - return try_compute_constant_value(expr.op0(), value); - } - else if (expr.id() == ID_address_of) - { - const address_of_exprt& address_of = to_address_of_expr(expr); - - if (expr.type().id() == ID_pointer && - expr.type().subtype().id() == ID_code && - address_of.object().id() == ID_symbol) - { - // A constant function pointer (i.e., an address of a function) - value = to_symbol_expr(address_of.object()).get_identifier(); - return true; - } - } - return false; -} - -bool -modular_fptr_analysist::try_compute_symbol_variable(const exprt& expr, - variablet& variable) -{ - assert(symbol_table); - - if(expr.id() == ID_symbol) - { - // Directly a symbol - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_lvalue) - { - variable = id; - - if (is_symbol_visible(symbol)) - set_visible(variable); - - return true; - } - } - else if (expr.id() == ID_member) - { - // A field of a structure - const member_exprt& member = to_member_expr(expr); - - if(member.struct_op().id() == ID_symbol) - { - irep_idt id = to_symbol_expr(member.struct_op()).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_lvalue) - { - variable = id2string(id) + "." + id2string(member.get_component_name()); - - if (is_symbol_visible(symbol)) - set_visible(variable); - - return true; - } - } - } - else if (expr.id() == ID_typecast) - { - return try_compute_symbol_variable(expr.op0(), variable); - } - return false; -} - -bool -modular_fptr_analysist::try_compute_field_access_variable(const exprt& expr, - variablet& variable) -{ - if (expr.id() == ID_member) - { - const member_exprt& member = to_member_expr(expr); - const typet& struct_type = member.struct_op().type(); - assert(struct_type.id() == ID_symbol); - - variable = irep_idt( - id2string(to_symbol_type(struct_type).get_identifier()) + "." + - id2string(member.get_component_name())); - - set_visible(variable); - return true; - } - return false; -} - -bool -modular_fptr_analysist::try_compute_type_variable(const exprt& expr, - variablet& variable) -{ - const typet& type = expr.type(); - assert(type.id() == ID_pointer); - - if (type.id() == ID_pointer && type.subtype().id() == ID_code) - { - variable = type2name(to_pointer_type(type)); - set_visible(variable); - return true; - } - return false; -} - -bool -modular_fptr_analysist::try_compute_variable(const exprt& expr, - variablet& variable) -{ - assert(symbol_table); - - // Ignore any non-pointer expression - if (expr.type().id() != ID_pointer) - { - std::cout << "Ignoring type: " << type2name(expr.type()) << std::endl; - return false; - } - - variablet prev_var = any_variable; - variable = prev_var; - - if (try_compute_type_variable(expr, variable)) - { - add_rule(prev_var, variable); - prev_var = variable; - } - if (try_compute_field_access_variable(expr, variable)) { - add_rule(prev_var, variable); - prev_var = variable; - } - if (try_compute_symbol_variable(expr, variable)) { - add_rule(prev_var, variable); - prev_var = variable; - } - return true; -} - -bool -modular_fptr_analysist::is_symbol_visible(const symbolt& symbol) -{ - return symbol.is_static_lifetime && !symbol.is_file_local; -} - -bool -modular_fptr_analysist::get_callees(const irep_idt& id, valuest& functions) -{ - functions.clear(); - - value_mapt::const_iterator it = - value_map.find(id); - - if (it == value_map.end()) - return false; - - functions.insert(it->second.begin(), it->second.end()); - - return functions.size() != 0; -} diff --git a/src/deltacheck/old/modular_fptr_analysis.h b/src/deltacheck/old/modular_fptr_analysis.h deleted file mode 100644 index 47c5b7da2..000000000 --- a/src/deltacheck/old/modular_fptr_analysis.h +++ /dev/null @@ -1,63 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of function pointers. - -The aliasing data is assigned to the following entities with decreasing -priority: -- symbol (variable, variable.field, function) -- fields of structures (i.e., shared among structures of the same type) -- types (i.e., specific type of function pointer) -- wildcard (matches anything unknown) - -FIXME: The following is not addressed at all: -- function parameters, return value -- non-scalar assignment: structures, arrays - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_FPTR_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_FPTR_ANALYSIS_H - -#include "modular_code_analysis.h" - -class modular_fptr_analysist : public modular_code_analysist { -public: - modular_fptr_analysist(); - virtual ~modular_fptr_analysist(); - - virtual const char* get_default_suffix() const - { - return ".dc_fp"; - } - virtual const char* get_analysis_id() const - { - return "Function pointer analysis"; - } - - // Queries for the call graph - bool get_callees(const irep_idt& function, valuest& functions); - -protected: - virtual void accept_assign(const code_assignt& instruction); - virtual void accept_function_call(const code_function_callt& instruction); - virtual void accept_return(const code_returnt& instruction); - -private: - irep_idt any_variable; - - void process_assignment(const variablet& lhs_var, const exprt& rhs); - - bool try_compute_symbol_variable(const exprt& expr, variablet& variable); - bool try_compute_field_access_variable(const exprt& expr, variablet& variable); - bool try_compute_type_variable(const exprt& expr, variablet& variable); - - bool try_compute_constant_value(const exprt& expr, valuet& value); - bool try_compute_variable(const exprt& expr, variablet& variable); - - bool is_symbol_visible(const symbolt& symbol); -}; - -#endif - diff --git a/src/deltacheck/old/modular_globals_analysis.cpp b/src/deltacheck/old/modular_globals_analysis.cpp deleted file mode 100644 index a1f0f492f..000000000 --- a/src/deltacheck/old/modular_globals_analysis.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of globals. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#include -#include -#include -#include - -#include - -#include "modular_globals_analysis.h" - -modular_globals_analysist::modular_globals_analysist() -{ -} - -modular_globals_analysist::~modular_globals_analysist() -{ -} - -bool -modular_globals_analysist::get_aliased_globals(const irep_idt& id, - valuest& globals) -{ - assert(symbol_table); - globals.clear(); - - const typet& type = symbol_table->lookup(id).type; - irep_idt type_id = type2name(type); - - value_mapt::const_iterator it = - value_map.find(type_id); - - if (it == value_map.end()) - return false; - - globals.insert(it->second.begin(), it->second.end()); - - return globals.size() != 0; -} - -void -modular_globals_analysist::accept_assign(const code_assignt& instruction) -{ - process_global_dereferences_rec(instruction.rhs()); -} - -void -modular_globals_analysist::accept_function_call( - const code_function_callt& instruction) -{ - const code_function_callt::argumentst& args = instruction.arguments(); - - for(code_function_callt::argumentst::const_iterator it = args.begin(); - it != args.end(); - ++it) - { - process_global_dereferences_rec(*it); - } -} - -void -modular_globals_analysist::accept_return(const code_returnt& instruction) -{ - process_global_dereferences_rec(instruction.return_value()); -} - -bool -modular_globals_analysist::try_compute_value(const exprt& expr, valuet& value) -{ - assert(symbol_table); - if (expr.id() == ID_symbol) - { - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_static_lifetime && symbol.is_lvalue) - { - value = id; - return true; - } - } - return false; -} - -bool -modular_globals_analysist::try_compute_variable( - const exprt& expr, variablet& variable) -{ - assert(symbol_table); - if (expr.id() == ID_symbol) - { - irep_idt id = to_symbol_expr(expr).get_identifier(); - const symbolt& symbol = symbol_table->lookup(id); - - if(symbol.is_static_lifetime && symbol.is_lvalue) - { - variable = type2name(symbol.type); - set_visible(variable); - return true; - } - } - return false; -} - -void -modular_globals_analysist::process_global_dereferences_rec(const exprt& expr) -{ - if (expr.id() == ID_address_of) - { - // Dereference, check if it is a global one - const address_of_exprt& address_of = to_address_of_expr(expr); - valuet value; - variablet variable; - - if (try_compute_value(address_of.object(), value) && - try_compute_variable(address_of.object(), variable)) - { - add_value(variable, value); - return; - } - } - - // Process everything else recursively - forall_operands(it, expr) - { - process_global_dereferences_rec(*it); - } -} diff --git a/src/deltacheck/old/modular_globals_analysis.h b/src/deltacheck/old/modular_globals_analysis.h deleted file mode 100644 index b1f4b3df7..000000000 --- a/src/deltacheck/old/modular_globals_analysis.h +++ /dev/null @@ -1,47 +0,0 @@ -/*******************************************************************\ - -Module: Modular (i.e., per C file) analysis of globals. - -Simple analysis of aliasing of global variables. Any dereferenced global is -marked as possibly aliasing with pointers to the same type. - -Author: Ondrej Sery, ondrej.sery@d3s.mff.cuni.cz - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_MODULAR_GLOBALS_ANALYSIS_H -#define CPROVER_DELTACHECK_MODULAR_GLOBALS_ANALYSIS_H - -#include "modular_code_analysis.h" - -class modular_globals_analysist : public modular_code_analysist { -public: - modular_globals_analysist(); - virtual ~modular_globals_analysist(); - - virtual const char* get_default_suffix() const - { - return ".dc_gl"; - } - virtual const char* get_analysis_id() const - { - return "Globals analysis"; - } - - // Queries for the aliases of globals, symbol_table has to be set when calling - bool get_aliased_globals(const irep_idt& id, valuest& globals); - -protected: - virtual void accept_assign(const code_assignt& instruction); - virtual void accept_function_call(const code_function_callt& instruction); - virtual void accept_return(const code_returnt& instruction); - -private: - bool try_compute_value(const exprt& expr, valuet& value); - bool try_compute_variable(const exprt& expr, variablet& variable); - - void process_global_dereferences_rec(const exprt& expr); -}; - -#endif - diff --git a/src/deltacheck/old/predicates.cpp b/src/deltacheck/old/predicates.cpp deleted file mode 100644 index f07e0f0af..000000000 --- a/src/deltacheck/old/predicates.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicates.h" - -/*******************************************************************\ - -Function: predicatest::make_list - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string predicatest::make_list() const -{ - std::string result; - - for(predicate_vectort::const_iterator - p_it=predicate_vector.begin(); - p_it!=predicate_vector.end(); - p_it++) - { - if(p_it!=predicate_vector.begin()) result+=", "; - result+=from_expr(ns, "", p_it->expr); - } - - return result; -} - -/*******************************************************************\ - -Function: predicatest::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatest::add(Cudd &mgr, const exprt &src) -{ - if(predicate_map.find(src)!=predicate_map.end()) - return; - - unsigned index=size(); - - predicate_vector.push_back(predicatet()); - predicate_vector.back().expr=src; - - // this will cause the ordering of the variable and - // it's next-state version to be interleaved - predicate_vector.back().var=mgr.bddVar(index*2); - predicate_vector.back().next_var=mgr.bddVar(index*2+1); - predicate_vector.back().string=from_expr(ns, "", src); - - predicate_map[src]=index; -} diff --git a/src/deltacheck/old/predicates.h b/src/deltacheck/old/predicates.h deleted file mode 100644 index 11b8c43ed..000000000 --- a/src/deltacheck/old/predicates.h +++ /dev/null @@ -1,58 +0,0 @@ -/*******************************************************************\ - -Module: Predicates - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PREDICATES_H -#define CPROVER_DELTACHECK_PREDICATES_H - -#include -#include - -#include - -struct predicatet -{ - irep_idt id; - exprt expr; - - // the BDD variable for the predicate and the next-state version - BDD var, next_var; - - std::string string; -}; - -class predicatest -{ -public: - explicit predicatest(const namespacet &_ns):ns(_ns) - { - } - - typedef std::vector predicate_vectort; - typedef std::map predicate_mapt; - predicate_vectort predicate_vector; - predicate_mapt predicate_map; - - inline unsigned size() const - { - return (unsigned)predicate_vector.size(); - } - - void add(Cudd &mgr, const exprt &src); - - std::string make_list() const; - - const predicatet &operator[] (std::size_t i) const - { - return predicate_vector[i]; - } - -protected: - const namespacet &ns; -}; - -#endif diff --git a/src/deltacheck/old/reporting.cpp b/src/deltacheck/old/reporting.cpp deleted file mode 100644 index cc593a709..000000000 --- a/src/deltacheck/old/reporting.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/*******************************************************************\ - -Module: Collation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "reporting.h" -#include "get_goto_program.h" -#include "call_graph.h" - -/*******************************************************************\ - -Function: get_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_functions(const xmlt &xml, std::set &dest) -{ - xmlt::elementst::const_iterator functions=xml.find("functions"); - - if(functions!=xml.elements.end()) - { - for(xmlt::elementst::const_iterator - f_it=functions->elements.begin(); - f_it!=functions->elements.end(); - f_it++) - { - dest.insert(f_it->get_attribute("id")); - } - } -} - -/*******************************************************************\ - -Function: collate - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void collate( - const std::vector &files, - const optionst &options, - message_handlert &message_handler) -{ - -} - -/*******************************************************************\ - -Function: call_graph_dot - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void call_graph_dot( - const std::list &files, - const std::string &dest_file, - message_handlert &message_handler) -{ - messaget message(message_handler); - call_grapht call_graph; - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - { - xmlt xml; - if(parse_xml(*it+".summary", message_handler, xml)) - message.warning("failed to read summary of "+*it); - - summary_to_call_graph(xml, call_graph); - } - - { - message.status("Writing call graph"); - std::ofstream out(dest_file.c_str()); - if(!out) - throw "failed to write call graph DOT"; - call_graph.output_dot(out); - return; - } -} - -/*******************************************************************\ - -Function: reporting_cmdline - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_cmdline( - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - messaget message(message_handler); - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - show_claims(ns, ui_message_handlert::PLAIN, goto_functions); -} - -/*******************************************************************\ - -Function: reporting_cmdline - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_cmdline( - const std::list &files, - const optionst &options, - message_handlert &message_handler) -{ - if(options.get_option("call-graph-dot")!="") - { - call_graph_dot(files, options.get_option("call-graph-dot"), - message_handler); - return; - } - - // report status of claims on a per-file basis - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - reporting_cmdline(*it, options, message_handler); -} - -/*******************************************************************\ - -Function: reporting_html - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_html( - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - #if 0 - messaget message(message_handler); - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - namespacet ns(symbol_table); - - show_claims(ns, ui_message_handlert::PLAIN, goto_functions); - #endif -} - -/*******************************************************************\ - -Function: reporting_html - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reporting_html( - const std::list &files, - const optionst &options, - message_handlert &message_handler) -{ - if(options.get_option("call-graph-dot")!="") - call_graph_dot(files, options.get_option("call-graph-dot"), - message_handler); - - // report status of claims on a per-file basis - - for(std::list::const_iterator - it=files.begin(); - it!=files.end(); - it++) - reporting_html(*it, options, message_handler); -} - diff --git a/src/deltacheck/old/reporting.h b/src/deltacheck/old/reporting.h deleted file mode 100644 index f9a67f355..000000000 --- a/src/deltacheck/old/reporting.h +++ /dev/null @@ -1,28 +0,0 @@ -/*******************************************************************\ - -Module: Collation - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_REPORTING_H -#define CPROVER_DELTACHECK_REPORTING_H - -#include -#include - -#include -#include - -void reporting_html( - const std::list &files, - const optionst &, - message_handlert &); - -void reporting_cmdline( - const std::list &files, - const optionst &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/statement_transformer.cpp b/src/deltacheck/old/statement_transformer.cpp deleted file mode 100644 index c15c2ef02..000000000 --- a/src/deltacheck/old/statement_transformer.cpp +++ /dev/null @@ -1,201 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include - -#include "canonicalize.h" -#include "statement_transformer.h" - -/*******************************************************************\ - -Function: statement_transformert::assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#include - -BDD statement_transformert::assign( - const BDD &from, - const code_assignt &assignment) -{ - BDD result=from; - - std::cout << "Statement: " << from_expr(ns, "", assignment.lhs()) - << " = " << from_expr(ns, "", assignment.rhs()) << std::endl; - - // Assign all predicates based on WP. - // This is cheap cartesian abstraction. - for(unsigned p=0; psecond].var; - if(negation) result=!result; - return result; - } - - // give up, produce non-determinism - return aux_variable(); -} - -/*******************************************************************\ - -Function: statement_transformert::remove_aux - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -BDD statement_transformert::remove_aux(const BDD &src) -{ - if(aux_variable_start==aux_variable_end) return src; - - // build cube made of auxiliary variables - BDD cube=mgr.bddOne(); - - for(unsigned i=aux_variable_start; i - -#include - -#include -#include -#include - -#include "predicates.h" - -class statement_transformert -{ -public: - statement_transformert( - const predicatest &_predicates, - Cudd &_mgr, - const namespacet &_ns): - predicates(_predicates), - mgr(_mgr), - ns(_ns), - aux_variable_start(0), - aux_variable_end(0) - { - } - - BDD assign( - const BDD &from, const code_assignt &assignment); - - inline BDD guard(const BDD &from, const exprt &g) - { - return remove_aux(from & abstract(g)); - } - - inline BDD guard_not(const BDD &from, const exprt &g) - { - return remove_aux(from & !abstract(g)); - } - -protected: - const predicatest &predicates; - Cudd &mgr; - const namespacet &ns; - - BDD abstract(const exprt &g); - BDD remove_aux(const BDD &src); - - // record any auxiliary variables - unsigned aux_variable_start, aux_variable_end; - - BDD aux_variable(); -}; diff --git a/src/deltacheck/old/summarization.cpp b/src/deltacheck/old/summarization.cpp deleted file mode 100644 index e0e904cca..000000000 --- a/src/deltacheck/old/summarization.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "get_goto_program.h" -#include "xml_conversion.h" -#include "summarization.h" -#include "dependencies.h" -#include "function_transformer.h" - -//#include "cgraph_builder.h" -//#include "modular_fptr_analysis.h" -//#include "modular_globals_analysis.h" - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls_rec( - const namespacet &ns, - const goto_functionst &goto_functions, - const exprt &function, - std::set &called_functions) -{ - if(function.id()==ID_symbol) - { - irep_idt id=to_symbol_expr(function).get_identifier(); - const symbolt &symbol=ns.lookup(id); - if(!symbol.is_file_local) - called_functions.insert(id); - } - else if(function.id()==ID_dereference) - { - } - else if(function.id()==ID_if) - { - } -} - -/*******************************************************************\ - -Function: summarize_function_calls - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function_calls( - const namespacet &ns, - const goto_functionst &goto_functions, - const goto_functionst::goto_functiont &goto_function, - std::ostream &out) -{ - std::set called_functions; - - forall_goto_program_instructions(it, goto_function.body) - { - if(it->is_function_call()) - { - const exprt &function=to_code_function_call(it->code).function(); - - summarize_function_calls_rec( - ns, goto_functions, function, called_functions); - } - } - - for(std::set::const_iterator - it=called_functions.begin(); - it!=called_functions.end(); - it++) - { - out << " "; - out << "" << std::endl; - } -} - -/*******************************************************************\ - -Function: summarize_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarize_function( - const namespacet &ns, - const goto_functionst &goto_functions, - const symbolt &symbol, - const goto_functionst::goto_functiont &goto_function, - message_handlert &message_handler, - std::ostream &out) -{ - out << "" << std::endl; - - if(symbol.location.is_not_nil() && - symbol.location.get_file()!="") - out << " " << xml(symbol.location); - - summarize_function_calls(ns, goto_functions, goto_function, out); - - function_transformer(ns, goto_functions, goto_function, message_handler, out); - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: dump_exported_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_exported_functions( - const namespacet &ns, - const goto_functionst &goto_functions, - message_handlert &message_handler, - std::ostream &out) -{ - out << "" << std::endl; - - // do this for each function - forall_goto_functions(f_it, goto_functions) - { - if(!f_it->second.body_available) - continue; - - if(has_prefix(id2string(f_it->first), CPROVER_PREFIX)) - continue; - - const symbolt &symbol=ns.lookup(f_it->first); - - if(symbol.is_file_local) - continue; - - messaget message(message_handler); - message.status("Summarizing "+id2string(f_it->first)); - - summarize_function( - ns, goto_functions, symbol, f_it->second, message_handler, out); - } - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: dump_state_variables - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void dump_state_variables( - const symbol_tablet &symbol_table, - std::ostream &out) -{ - out << "" << std::endl; - - forall_symbols(s_it, symbol_table.symbols) - { - const symbolt &symbol=s_it->second; - - if(has_prefix(id2string(symbol.name), CPROVER_PREFIX)) - continue; - - if(symbol.type.id()==ID_code || - symbol.is_type) - continue; - - if(symbol.is_file_local) - continue; - - out << "" << std::endl; - - if(symbol.location.is_not_nil() && symbol.location.get_file()!="") - out << xml(symbol.location); - - out << "" << std::endl; - } - - out << "" << std::endl; - out << std::endl; -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const symbol_tablet &symbol_table, - const goto_functionst &goto_functions, - const optionst &options, - message_handlert &message_handler, - std::ostream &out) -{ - // first collect non-static function symbols that - // have a body - - namespacet ns(symbol_table); - - dump_exported_functions(ns, goto_functions, message_handler, out); - - dump_state_variables(symbol_table, out); - - #if 0 - cgraph_buildert cg_builder; - modular_fptr_analysist fptr_analysis; - modular_globals_analysist globals_analysis; - - cg_builder.add_analysis(&fptr_analysis); - cg_builder.add_analysis(&globals_analysis); - - cg_builder.analyze_module(symbol_table, goto_functions); - cg_builder.serialize(file_name); - #endif -} - -/*******************************************************************\ - -Function: summarization - - Inputs: - - Outputs: - - Purpose: Phase I: produce a summary for a given file - -\*******************************************************************/ - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &options, - message_handlert &message_handler) -{ - // first check dependencies - if(!options.get_bool_option("force") && - dependencies(function_file_map, file_name, message_handler)==FRESH) - return; - - // get the goto program - symbol_tablet symbol_table; - goto_functionst goto_functions; - - get_goto_program(file_name, options, symbol_table, goto_functions, message_handler); - - //goto_functions.output(ns, std::cout); - - std::string summary_file_name=file_name+".summary"; - std::ofstream summary_file(summary_file_name.c_str(), - std::ios::binary|std::ios::trunc|std::ios::out); - - if(!summary_file) - throw std::string("failed to write summary file"); - - summary_file << "" << std::endl; - - ::summarization( - function_file_map, - symbol_table, - goto_functions, - options, - message_handler, - summary_file); - - summary_file << "" << std::endl; - - messaget message(message_handler); - message.status("Summary written as "+summary_file_name); -} - diff --git a/src/deltacheck/old/summarization.h b/src/deltacheck/old/summarization.h deleted file mode 100644 index 6737d5ad1..000000000 --- a/src/deltacheck/old/summarization.h +++ /dev/null @@ -1,24 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SUMMARIZATION_H -#define CPROVER_DELTACHECK_SUMMARIZATION_H - -#include -#include -#include - -#include "function_file_map.h" - -void summarization( - const function_file_mapt &function_file_map, - const std::string &file_name, - const optionst &, - message_handlert &); - -#endif diff --git a/src/deltacheck/old/xml_conversion.cpp b/src/deltacheck/old/xml_conversion.cpp deleted file mode 100644 index 50e976ff6..000000000 --- a/src/deltacheck/old/xml_conversion.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "xml_conversion.h" - -/*******************************************************************\ - -Function: xml - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#if 0 -xmlt xml(const locationt &location) -{ - xmlt xml_location; - xml_location.name="location"; - - if(location.get_file()!="") - xml_location.set_attribute("file", id2string(location.get_file())); - - if(location.get_line()!="") - xml_location.set_attribute("line", id2string(location.get_line())); - - return xml_location; -} -#endif diff --git a/src/deltacheck/old/xml_conversion.h b/src/deltacheck/old/xml_conversion.h deleted file mode 100644 index f23800f03..000000000 --- a/src/deltacheck/old/xml_conversion.h +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include - -//xmlt xml(const locationt &location); diff --git a/src/deltacheck/properties.cpp b/src/deltacheck/properties.cpp deleted file mode 100644 index e4aa9fddd..000000000 --- a/src/deltacheck/properties.cpp +++ /dev/null @@ -1,361 +0,0 @@ -/*******************************************************************\ - -Module: Property Management - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../html/html_escape.h" -#include "properties.h" - -/*******************************************************************\ - -Function: report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_properties( - const propertiest &properties, - std::ostream &out) -{ - // produce clickable table of properties - - out << "\n"; - - out << "" - << "\n" - << "\n" - << "\n" - << "\n\n"; - - unsigned count; - - count=0; - - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++, count++) - { - const source_locationt &source_location=p_it->loc->source_location; - - out << "\n"; - - out << " \n"; // ✗ - else if(p_it->status.is_true()) - out << "" - "\n"; // ✓ - else - out << "?" - "\n"; - - out << " \n"; - - out << " \n"; - - out << " \n"; - - out << " \n"; - - out << "\n\n"; - } - - out << "
StatusLocationProperty
"; - - if(p_it->status.is_false()) - out << "" - " "; - out << html_escape(source_location.get_file()); - out << ""; - out << html_escape(source_location.get_line()); - out << ""; - out << html_escape(source_location.get_property_class()); - out << ""; - out << html_escape(source_location.get_comment()); - out << "
\n\n"; -} - -/*******************************************************************\ - -Function: report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_properties( - const propertiest &properties, - messaget &message) -{ - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++) - { - message.status() - << "[" << p_it->loc->source_location.get_property_id() << "] " - << p_it->loc->source_location.get_comment() << ": "; - if(p_it->status.is_true()) - message.status() << "OK"; - else if(p_it->status.is_false()) - message.status() << "FAILED"; - else - message.status() << "UNKNOWN"; - message.status() << messaget::eom; - } -} - -/*******************************************************************\ - -Function: get_tracked_expr - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_tracked_expr( - const exprt &src, - std::list &dest) -{ - forall_operands(it, src) - get_tracked_expr(*it, dest); - - if(src.id()==ID_symbol) - dest.push_back(src); -} - -/*******************************************************************\ - -Function: get_tracked_expr_lhs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void get_tracked_expr_lhs( - const exprt &src, - std::list &dest_lhs, - std::list &dest_rhs) -{ - if(src.id()==ID_symbol) - dest_lhs.push_back(src); - else if(src.id()==ID_member) - { - get_tracked_expr_lhs(to_member_expr(src).struct_op(), dest_lhs, dest_rhs); - } - else if(src.id()==ID_index) - { - get_tracked_expr_lhs(to_index_expr(src).array(), dest_lhs, dest_rhs); - get_tracked_expr(to_index_expr(src).index(), dest_rhs); - } - else - get_tracked_expr(src, dest_rhs); -} - -/*******************************************************************\ - -Function: report_countermodel - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodel( - const propertyt &property, - const namespacet &ns, - const local_SSAt &SSA, - unsigned count, - std::ostream &out) -{ - for(propertyt::value_mapt::const_iterator - v_it=property.value_map.begin(); - v_it!=property.value_map.end(); - v_it++) - { - out << "// " << from_expr(ns, "", v_it->first) - << " -> " - << from_expr(ns, "", v_it->second) - << "\n"; - } - - std::map var_count; - - forall_goto_program_instructions(i_it, SSA.goto_function.body) - { - std::list tracked_expr_rhs, tracked_expr_lhs; - - if(i_it->is_assert() || i_it->is_assume() || i_it->is_goto()) - { - get_tracked_expr(i_it->guard, tracked_expr_rhs); - } - else if(i_it->is_assign()) - { - const code_assignt &code_assign=to_code_assign(i_it->code); - get_tracked_expr_lhs(code_assign.lhs(), tracked_expr_lhs, tracked_expr_rhs); - get_tracked_expr(code_assign.rhs(), tracked_expr_rhs); - } - - // lhs - for(std::list::const_iterator - e_it=tracked_expr_lhs.begin(); - e_it!=tracked_expr_lhs.end(); - e_it++) - { - exprt renamed_lhs=SSA.read_lhs(*e_it, i_it); - - const propertyt::value_mapt::const_iterator v_it_lhs= - property.value_map.find(renamed_lhs); - - if(v_it_lhs!=property.value_map.end()) - { - std::string var=from_expr(ns, "", *e_it); - std::string var_loc=var+"@"+id2string(i_it->source_location.get_line()); - out << "ce" << count << "['" << var_loc - << "." << ++var_count[var_loc] - << "']='" - << from_expr(ns, "", v_it_lhs->second) - << "';\n"; - } - } - - // rhs - for(std::list::const_iterator - e_it=tracked_expr_rhs.begin(); - e_it!=tracked_expr_rhs.end(); - e_it++) - { - exprt renamed_rhs=SSA.read_rhs(*e_it, i_it); - - const propertyt::value_mapt::const_iterator v_it_rhs= - property.value_map.find(renamed_rhs); - - if(v_it_rhs==property.value_map.end()) - continue; - - std::string var=from_expr(ns, "", *e_it); - std::string var_loc=var+"@"+id2string(i_it->source_location.get_line()); - out << "ce" << count << "['" << var_loc - << "." << ++var_count[var_loc] - << "']='" - << from_expr(ns, "", v_it_rhs->second) - << "';\n"; - } - } -} - -/*******************************************************************\ - -Function: report_countermodels - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodels( - const local_SSAt &SSA_old, - const local_SSAt &SSA_new, - const propertiest &properties, - std::ostream &out) -{ - out << "\n\n"; -} - -/*******************************************************************\ - -Function: report_countermodels - - Inputs: - - Outputs: - - Purpose: this produces counterexample values as JavaScript - -\*******************************************************************/ - -void report_countermodels( - const local_SSAt &SSA, - const propertiest &properties, - std::ostream &out) -{ - out << "\n\n"; -} - diff --git a/src/deltacheck/properties.h b/src/deltacheck/properties.h deleted file mode 100644 index adda09751..000000000 --- a/src/deltacheck/properties.h +++ /dev/null @@ -1,52 +0,0 @@ -/*******************************************************************\ - -Module: Property Management - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_PROPERTIES_H -#define DELTACHECK_PROPERTIES_H - -#include -#include - -#include "../ssa/local_ssa.h" - -class propertyt -{ -public: - goto_programt::const_targett loc; - tvt status; - - // given in SSA form - exprt guard, condition; - - // in case of failed properties: countermodel - typedef std::map value_mapt; - value_mapt value_map; -}; - -typedef std::list propertiest; - -void report_properties( - const propertiest &, - std::ostream &); - -void report_properties( - const propertiest &, - messaget &); - -void report_countermodels( - const local_SSAt &, - const propertiest &, - std::ostream &); - -void report_countermodels( - const local_SSAt &SSA_old, - const local_SSAt &SSA_new, - const propertiest &, - std::ostream &); - -#endif diff --git a/src/deltacheck/rename.cpp b/src/deltacheck/rename.cpp deleted file mode 100644 index b82821a13..000000000 --- a/src/deltacheck/rename.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/*******************************************************************\ - -Module: Symbol Renaming - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "rename.h" - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(symbol_tablet &symbol_table) -{ - for(symbol_tablet::symbolst::iterator - s_it=symbol_table.symbols.begin(); - s_it!=symbol_table.symbols.end(); - s_it++) - { - operator()(s_it->second); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(symbolt &symbol) -{ - operator()(symbol.value); - operator()(symbol.type); -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(exprt &expr) -{ - Forall_operands(it, expr) - operator()(*it); - - operator()(expr.type()); - - if(expr.id()==ID_symbol) - { - const irep_idt old_name=expr.get(ID_identifier); - const irep_idt new_name=id2string(old_name)+suffix; - expr.set(ID_identifier, new_name); - } - - if(expr.find(ID_C_c_sizeof_type).is_not_nil()) - { - irept &c_sizeof_type=expr.add(ID_C_c_sizeof_type); - - if(c_sizeof_type.is_not_nil()) - operator()(static_cast(c_sizeof_type)); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(typet &type) -{ - if(type.has_subtype()) - operator()(type.subtype()); - - Forall_subtypes(it, type) - operator()(*it); - - if(type.id()==ID_struct || - type.id()==ID_union) - { - struct_union_typet &struct_union_type=to_struct_union_type(type); - struct_union_typet::componentst &components=struct_union_type.components(); - - for(struct_union_typet::componentst::iterator - it=components.begin(); - it!=components.end(); - it++) - operator()(*it); - } - else if(type.id()==ID_code) - { - code_typet &code_type=to_code_type(type); - operator()(code_type.return_type()); - - code_typet::parameterst ¶meters=code_type.parameters(); - - for(code_typet::parameterst::iterator - it=parameters.begin(); - it!=parameters.end(); - it++) - { - operator()(*it); - } - } - else if(type.id()==ID_symbol) - { - const irep_idt old_name=type.get(ID_identifier); - const irep_idt new_name=id2string(old_name)+"$old"; - type.set(ID_identifier, new_name); - } - else if(type.id()==ID_array) - { - // do the size -- the subtype is already done - operator()(to_array_type(type).size()); - } -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(goto_functionst &goto_functions) -{ - for(goto_functionst::function_mapt::iterator - f_it=goto_functions.function_map.begin(); - f_it!=goto_functions.function_map.end(); - f_it++) - operator()(f_it->second); -} - -/*******************************************************************\ - -Function: renamet::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void renamet::operator()(goto_functionst::goto_functiont &goto_function) -{ - operator()(goto_function.type); - - Forall_goto_program_instructions(i_it, goto_function.body) - { - operator()(i_it->code); - operator()(i_it->guard); - } -} diff --git a/src/deltacheck/rename.h b/src/deltacheck/rename.h deleted file mode 100644 index 9745210cc..000000000 --- a/src/deltacheck/rename.h +++ /dev/null @@ -1,34 +0,0 @@ -/*******************************************************************\ - -Module: Symbol Renaming - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_RENAME_H -#define CPROVER_DELTACHECK_RENAME_H - -#include -#include - -class renamet -{ -public: - renamet():suffix("$old") - { - } - - void operator()(symbol_tablet &); - void operator()(symbolt &); - void operator()(exprt &); - void operator()(typet &); - void operator()(goto_functionst &); - void operator()(goto_functionst::goto_functiont &); - - std::string suffix; - -protected: -}; - -#endif diff --git a/src/deltacheck/report_source_code.h b/src/deltacheck/report_source_code.h deleted file mode 100644 index 7987fb4c5..000000000 --- a/src/deltacheck/report_source_code.h +++ /dev/null @@ -1,40 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_REPORT_SOURCE_CODE_H -#define CPROVER_REPORT_SOURCE_CODE_H - -#include - -#include -#include - -#include "properties.h" - -void report_source_code( - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - const propertiest &properties, - std::ostream &, - message_handlert &); - -void report_source_code( - const std::string &path_prefix_old, - const source_locationt &location_old, - const goto_programt &goto_program_old, - const std::string &description_old, - const std::string &path_prefix, - const source_locationt &location, - const goto_programt &goto_program, - const std::string &description, - const propertiest &properties, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/show.cpp b/src/deltacheck/show.cpp deleted file mode 100644 index 02a2b2e4f..000000000 --- a/src/deltacheck/show.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/*******************************************************************\ - -Module: Showing various debugging information - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include - -#include "../ssa/ssa_domain.h" -#include "../ssa/guard_map.h" -#include "../ssa/local_ssa.h" -#include "../functions/index.h" -#include "ssa_fixed_point.h" - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - ssa_objectst ssa_objects(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects); - ssa_ait ssa_analysis(assignments); - ssa_analysis(goto_function, ns); - ssa_analysis.output(ns, goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_defs(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - guard_mapt guard_map(goto_function.body); - guard_map.output(goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_guards(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - local_SSA.output(out); -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_ssa(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_point( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - ssa_fixed_pointt ssa_fixed_point(local_SSA, ns); - ssa_fixed_point.output(out); -} - -/*******************************************************************\ - -Function: show_fixed_points - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_points( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - const namespacet ns(model.symbol_table); - const std::set &functions=file_it->second; - - // now do all functions from model - for(std::set::const_iterator - fkt_it=functions.begin(); - fkt_it!=functions.end(); - fkt_it++) - { - const irep_idt &id=*fkt_it; - - const goto_functionst::function_mapt::const_iterator m_it= - model.goto_functions.function_map.find(id); - - assert(m_it!=model.goto_functions.function_map.end()); - - const goto_functionst::goto_functiont *index_fkt= - &m_it->second; - - out << ">>>> Function " << id << " in " << file_it->first - << std::endl; - - show_fixed_point(*index_fkt, ns, out); - - out << std::endl; - } - } - -} - -/*******************************************************************\ - -Function: show_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_properties( - const indext &index, - const optionst &options, - std::ostream &out, - message_handlert &message_handler) -{ - for(indext::file_to_functiont::const_iterator - file_it=index.file_to_function.begin(); - file_it!=index.file_to_function.end(); - file_it++) - { - // read the file - goto_modelt model; - read_goto_binary(index.full_path(file_it->first), model, message_handler); - - // add the properties - goto_check(options, model); - model.goto_functions.update(); - label_properties(model.goto_functions); - - show_properties(model, ui_message_handlert::PLAIN); - } - -} diff --git a/src/deltacheck/show.h b/src/deltacheck/show.h deleted file mode 100644 index 88617dfaf..000000000 --- a/src/deltacheck/show.h +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: SSA Showing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SHOW_H -#define CPROVER_SHOW_H - -#include -#include - -class message_handlert; -class indext; -class optionst; - -void show_ssa( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_defs( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_guards( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_fixed_points( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -void show_properties( - const indext &index, - const optionst &options, - std::ostream &, - message_handlert &); - -#endif diff --git a/src/deltacheck/source_diff.cpp b/src/deltacheck/source_diff.cpp deleted file mode 100644 index 7cda65b44..000000000 --- a/src/deltacheck/source_diff.cpp +++ /dev/null @@ -1,243 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Diffing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#include -#include -#include - -#ifdef DEBUG -#include -#endif - -#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__CYGWIN__) || defined(__MACH__) -#include -#endif - -#include - -#include "get_source.h" - -/*******************************************************************\ - -Function: diff_action - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -class diff_actiont -{ -public: - char action; - unsigned old_from, old_to, old_size; - unsigned new_from, new_to, new_size; - diff_actiont(const std::string &); - - void output(std::ostream &out) - { - if(action!=0) - out << "Action: " << action - << " Old: " << old_from << "-" << old_to << " (" << old_size << ")" - << " New: " << new_from << "-" << new_to << " (" << new_size << ")" - << "\n"; - } -}; - -diff_actiont::diff_actiont(const std::string &src) -{ - old_from=old_to=new_from=new_to=old_size=new_size=0; - action=0; - - // e.g. 4,5c4 - if(src.empty() || !isdigit(src[0])) return; - - std::string old_from_str, old_to_str, new_from_str, new_to_str; - - unsigned i; - - for(i=0; iold_to || new_from>new_to) action=0; -} - -/*******************************************************************\ - -Function: process_diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void process_diff( - std::list &lines_old, - std::list &lines_new, - const std::list &diff) -{ - // constant-time access to the list of lines members - std::vector::iterator> - l_it_old, l_it_new; - - l_it_old.reserve(lines_old.size()); - l_it_new.reserve(lines_new.size()); - - for(std::list::iterator it=lines_old.begin(); - it!=lines_old.end(); it++) - l_it_old.push_back(it); - - for(std::list::iterator it=lines_new.begin(); - it!=lines_new.end(); it++) - l_it_new.push_back(it); - - // now process the diff - for(std::list::const_iterator - d_it=diff.begin(); - d_it!=diff.end(); - d_it++) - { - diff_actiont da(*d_it); - - #ifdef DEBUG - da.output(std::cout); - #endif - - switch(da.action) - { - case 'c': // change - if(da.new_size>da.old_size) // enlarge - { - for(unsigned i=da.old_size; ida.new_size) // shrink - { - for(unsigned i=da.new_size; i &lines1, - std::list &lines2) -{ - std::string tmp1_name=get_temporary_file("delta_diff1", "txt"); - std::string tmp2_name=get_temporary_file("delta_diff2", "txt"); - std::string tmp3_name=get_temporary_file("delta_diff3", "txt"); - - { - std::ofstream out1(tmp1_name.c_str()); - std::ofstream out2(tmp2_name.c_str()); - - for(std::list::const_iterator l_it=lines1.begin(); - l_it!=lines1.end(); l_it++) - out1 << l_it->line << "\n"; - - for(std::list::const_iterator l_it=lines2.begin(); - l_it!=lines2.end(); l_it++) - out2 << l_it->line << "\n"; - } - - std::string cmdline="diff \""+tmp1_name+"\""+ - " \""+tmp2_name+"\""+ - "> \""+tmp3_name+"\""; - - int result=system(cmdline.c_str()); - - // open output - if(result>=0) - { - std::ifstream in(tmp3_name.c_str()); - std::string line; - std::list diff; - while(std::getline(in, line)) diff.push_back(line); - process_diff(lines1, lines2, diff); - } - - // clean up - unlink(tmp1_name.c_str()); - unlink(tmp2_name.c_str()); - unlink(tmp3_name.c_str()); -} - diff --git a/src/deltacheck/source_diff.h b/src/deltacheck/source_diff.h deleted file mode 100644 index 40eff5ad7..000000000 --- a/src/deltacheck/source_diff.h +++ /dev/null @@ -1,13 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Diffing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "get_source.h" - -void diff_it( - std::list &lines1, - std::list &lines2); diff --git a/src/deltacheck/ssa_fixed_point.cpp b/src/deltacheck/ssa_fixed_point.cpp deleted file mode 100644 index 672d22c0e..000000000 --- a/src/deltacheck/ssa_fixed_point.cpp +++ /dev/null @@ -1,411 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include -#include - -#include "ssa_fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: ssa_fixed_pointt::tie_inputs_together - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::tie_inputs_together(std::list &dest) -{ - // the following are inputs: - // 1) the parameters of the functions - // 2) any global objects mentioned - // 3) the guard at the entry point - - if(SSA_old.goto_function.body.empty() || - SSA_new.goto_function.body.empty()) - return; - - // 1) function parameters - - const code_typet::parameterst &p_new= - SSA_new.goto_function.type.parameters(); - - const code_typet::parameterst &p_old= - SSA_old.goto_function.type.parameters(); - - for(unsigned p=0; pis_backwards_goto()); - - //locationt to=from->get_target(); - - // Record the objects modified by the loop to get - // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, from); - symbol_exprt out=SSA.read_rhs(*o_it, from); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - - ssa_objectt guard=SSA.guard_symbol(); - fixed_point.pre_state_vars.push_back(SSA.name(guard, local_SSAt::LOOP_BACK, from)); - fixed_point.post_state_vars.push_back(SSA.name(guard, local_SSAt::OUT, from)); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::do_backwards_edges - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::do_backwards_edges() -{ - // old program, if applicable - if(use_old) - { - forall_goto_program_instructions(i_it, SSA_old.goto_function.body) - { - if(i_it->is_backwards_goto()) - do_backwards_edge(SSA_old, i_it); - } - } - - // new program - forall_goto_program_instructions(i_it, SSA_new.goto_function.body) - { - if(i_it->is_backwards_goto()) - do_backwards_edge(SSA_new, i_it); - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::compute_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::compute_fixed_point() -{ - do_backwards_edges(); - setup_properties(); - - // set up transition relation - - // new function - fixed_point.transition_relation << SSA_new; - - if(use_old) - { - // old function, if applicable - fixed_point.transition_relation << SSA_old; - - // tie inputs together, if applicable - tie_inputs_together(fixed_point.transition_relation); - } - - // compute the fixed-point - fixed_point(); - - // we check the properties once we have the fixed point - check_properties(); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::check_properties() -{ - for(propertiest::iterator - p_it=properties.begin(); p_it!=properties.end(); p_it++) - { - #if 0 - solvert solver(ns); - #else - satcheckt satcheck; - bv_pointerst solver(ns, satcheck); - //solver.set_message_handler(get_message_handler()); - #endif - - // feed transition relation into solver - solver << fixed_point.transition_relation; - - // feed in fixed-point - solver << fixed_point.state_predicate; - - #ifdef DEBUG - std::cout << "GUARD: " << from_expr(ns, "", p_it->guard) << "\n"; - std::cout << "CHECKING: " << from_expr(ns, "", p_it->condition) << "\n"; - #endif - - // feed in the assertion - solver.set_to_true(p_it->guard); - solver.set_to_false(p_it->condition); - - // now solve - decision_proceduret::resultt result=solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - tvt status; - - if(result==decision_proceduret::D_UNSATISFIABLE) - status=tvt(true); - else if(result==decision_proceduret::D_SATISFIABLE) - { - status=tvt(false); - generate_countermodel(*p_it, solver); - } - else - status=tvt::unknown(); - - p_it->status=status; - - #ifdef DEBUG - std::cout << "RESULT: " << status << "\n"; - std::cout << "\n"; - #endif - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::countermodel_expr - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::countermodel_expr( - const exprt &src, - std::set &dest) -{ - forall_operands(it, src) - countermodel_expr(*it, dest); - - if(src.id()==ID_symbol) - dest.insert(src); -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::generate_countermodel - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::generate_countermodel( - propertyt &property, - const decision_proceduret &solver) -{ - // collect all expressions from SSA that seem interesting - - std::set expressions; - - for(local_SSAt::nodest::const_iterator - n_it=SSA_new.nodes.begin(); - n_it!=SSA_new.nodes.end(); - n_it++) - { - const local_SSAt::nodet &node=n_it->second; - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=node.equalities.begin(); - e_it!=node.equalities.end(); - e_it++) - { - countermodel_expr(e_it->lhs(), expressions); - countermodel_expr(e_it->rhs(), expressions); - } - - for(local_SSAt::nodet::constraintst::const_iterator - e_it=node.constraints.begin(); - e_it!=node.constraints.end(); - e_it++) - { - countermodel_expr(*e_it, expressions); - } - - if(node.assertion.is_not_nil()) - countermodel_expr(node.assertion, expressions); - } - - if(use_old) - { - for(local_SSAt::nodest::const_iterator - n_it=SSA_old.nodes.begin(); - n_it!=SSA_old.nodes.end(); - n_it++) - { - const local_SSAt::nodet &node=n_it->second; - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=node.equalities.begin(); - e_it!=node.equalities.end(); - e_it++) - { - countermodel_expr(e_it->lhs(), expressions); - countermodel_expr(e_it->rhs(), expressions); - } - - for(local_SSAt::nodet::constraintst::const_iterator - e_it=node.constraints.begin(); - e_it!=node.constraints.end(); - e_it++) - { - countermodel_expr(*e_it, expressions); - } - - if(node.assertion.is_not_nil()) - countermodel_expr(node.assertion, expressions); - } - } - - // now collect from property - countermodel_expr(property.guard, expressions); - countermodel_expr(property.condition, expressions); - - // get values for those expressions - for(std::set::const_iterator - e_it=expressions.begin(); - e_it!=expressions.end(); - e_it++) - { - exprt value=solver.get(*e_it); - if(value.is_not_nil()) - property.value_map[*e_it]=value; - } -} - -/*******************************************************************\ - -Function: ssa_fixed_pointt::setup_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_pointt::setup_properties() -{ - forall_goto_program_instructions(i_it, SSA_new.goto_function.body) - { - if(i_it->is_assert()) - { - properties.push_back(propertyt()); - properties.back().loc=i_it; - properties.back().condition=SSA_new.read_rhs(i_it->guard, i_it); - properties.back().guard=SSA_new.guard_symbol(i_it); - } - } -} diff --git a/src/deltacheck/ssa_fixed_point.h b/src/deltacheck/ssa_fixed_point.h deleted file mode 100644 index 11f42c5ab..000000000 --- a/src/deltacheck/ssa_fixed_point.h +++ /dev/null @@ -1,88 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SSA_DATA_FLOW_H -#define DELTACHECK_SSA_DATA_FLOW_H - -#include - -#include "../ssa/local_ssa.h" -#include "properties.h" -#include "../solver/fixed_point.h" - -class ssa_fixed_pointt -{ -public: - typedef local_SSAt::locationt locationt; - - explicit ssa_fixed_pointt( - const local_SSAt &_SSA_old, - const local_SSAt &_SSA_new, - const namespacet &_ns): - SSA_old(_SSA_old), - SSA_new(_SSA_new), - ns(_ns), - use_old(true), - fixed_point(_ns) - { - compute_fixed_point(); - } - - explicit ssa_fixed_pointt( - const local_SSAt &_SSA, - const namespacet &_ns): - SSA_old(_SSA), - SSA_new(_SSA), - ns(_ns), - use_old(false), - fixed_point(_ns) - { - compute_fixed_point(); - } - - inline void output(std::ostream &out) const - { - fixed_point.output(out); - } - -protected: - const local_SSAt &SSA_old; - const local_SSAt &SSA_new; - const namespacet &ns; - bool use_old; - -public: - propertiest properties; - fixed_pointt fixed_point; - -protected: - // fixed-point computation - void tie_inputs_together(std::list &dest); - void compute_fixed_point(); - bool iteration(); - void initialize_invariant(); - - void do_backwards_edge( - const local_SSAt &SSA, locationt loc); - - void do_backwards_edges(); - - // properties - void setup_properties(); - void check_properties(); - - void countermodel_expr( - const exprt &src, - std::set &dest); - - void generate_countermodel( - propertyt &property, - const decision_proceduret &solver); -}; - -#endif diff --git a/src/deltacheck/statistics.cpp b/src/deltacheck/statistics.cpp deleted file mode 100644 index 9ac5feb4a..000000000 --- a/src/deltacheck/statistics.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/*******************************************************************\ - -Module: Statistics - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "../html/html_escape.h" -#include "statistics.h" - -/*******************************************************************\ - -Function: statisticst::clear - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::clear() -{ - time_map.clear(); - number_map.clear(); -} - -/*******************************************************************\ - -Function: statisticst::start - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::start(const std::string &what) -{ - timet &t=time_map[what]; - assert(!t.running); - t.start=current_time(); - t.running=true; -} - -/*******************************************************************\ - -Function: statisticst::stop - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::stop(const std::string &what) -{ - timet &t=time_map[what]; - assert(t.running); - time_periodt this_period=current_time()-t.start; - t.last=this_period; - t.total+=this_period; - t.running=false; -} - -/*******************************************************************\ - -Function: statisticst::html_report_total - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::html_report_total(std::ostream &out) const -{ - out << "

\n"; - - out << "\n"; - - for(number_mapt::const_iterator - it=number_map.begin(); it!=number_map.end(); it++) - { - out << "\n"; - } - - for(time_mapt::const_iterator - it=time_map.begin(); it!=time_map.end(); it++) - { - out << "\n"; - } - - out << "
"; - out << html_escape(it->first) << ":" - << it->second - << "
"; - out << html_escape(it->first) << ":" - << it->second.total - << "s
\n"; - - out << "

\n"; -} - -/*******************************************************************\ - -Function: statisticst::html_report_last - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void statisticst::html_report_last(std::ostream &out) const -{ - out << "

\n"; - - for(time_mapt::const_iterator - it=time_map.begin(); it!=time_map.end(); it++) - { - if(it!=time_map.begin()) out << " "; - out << html_escape(it->first) << ": " - << it->second.last - << "s"; - } - - out << "\n

\n"; -} - diff --git a/src/deltacheck/statistics.h b/src/deltacheck/statistics.h deleted file mode 100644 index 4a61ebec9..000000000 --- a/src/deltacheck/statistics.h +++ /dev/null @@ -1,49 +0,0 @@ -/*******************************************************************\ - -Module: Statistics - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_STATISTICS_H -#define DELTACHECK_STATISTICS_H - -#include -#include -#include - -#include - -class statisticst -{ -public: - inline statisticst() - { - clear(); - } - - struct timet - { - timet():running(false), total(0) { } - bool running; - time_periodt total, last; - absolute_timet start; - }; - - typedef std::map number_mapt; - number_mapt number_map; - - typedef std::map time_mapt; - time_mapt time_map; - - void html_report_total(std::ostream &) const; - void html_report_last(std::ostream &) const; - - void clear(); - - void start(const std::string &what); - void stop(const std::string &what); -}; - -#endif diff --git a/src/deltacheck/summarize_function_pointers.cpp b/src/deltacheck/summarize_function_pointers.cpp deleted file mode 100644 index 6c010e1f0..000000000 --- a/src/deltacheck/summarize_function_pointers.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "summarize_function_pointers.h" - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_programt::instructiont &instruction, - function_pointerst &function_pointers) -{ - if(instruction.is_assign()) - { - - } -} - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_programt &goto_program, - function_pointerst &function_pointers) -{ - forall_goto_program_instructions(i_it, goto_program) - { - summarize_function_pointers(context, *i_it, function_pointers); - } -} - -/*******************************************************************\ - -Function: summarize_function_pointers - - Inputs: - - Outputs: - - Purpose: field-sensitive value-set analysis for function pointers - -\*******************************************************************/ - -void summarize_function_pointers( - const contextt &context, - const goto_functionst &goto_functions, - function_pointerst &function_pointers) -{ - forall_goto_functions(f_it, goto_functions) - { - summarize_function_pointers(context, f_it->second.body, function_pointers); - } -} - diff --git a/src/deltacheck/summarize_function_pointers.h b/src/deltacheck/summarize_function_pointers.h deleted file mode 100644 index 177d9d0b1..000000000 --- a/src/deltacheck/summarize_function_pointers.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Summarization of Function Pointers - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SUMMARIZE_FUNCTION_POINTERS_H -#define CPROVER_DELTACHECK_SUMMARIZE_FUNCTION_POINTERS_H - -#include -#include - -#include - -class function_pointerst -{ -public: - // a map from struct.field to a set of function identifiers - - typedef std::set id_sett; - - typedef hash_map_cont field_mapt; - field_mapt field_map; - - void print(std::ostream &); -}; - -void summarize_function_pointers( - const contextt &, - const goto_functionst &, - function_pointerst &dest); - -#endif diff --git a/src/deltacheck/version.h b/src/deltacheck/version.h deleted file mode 100644 index 805317409..000000000 --- a/src/deltacheck/version.h +++ /dev/null @@ -1 +0,0 @@ -#define DELTACHECK_VERSION "0.4.1" diff --git a/src/deltagit/deltagit_config.cpp b/src/deltagit/deltagit_config.cpp deleted file mode 100644 index 482c32351..000000000 --- a/src/deltagit/deltagit_config.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include - -#include "deltagit_config.h" - -/*******************************************************************\ - -Function: deltagit_configt::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltagit_configt::read() -{ - xmlt src; - - console_message_handlert message_handler; - - if(parse_xml("config.xml", message_handler, src)) - return; - - if(src.name!="deltagit_config") - throw std::string("unexpected XML for deltagit config"); - - description=src.get_element("description"); -} - diff --git a/src/deltagit/deltagit_config.h b/src/deltagit/deltagit_config.h deleted file mode 100644 index 8b6de7398..000000000 --- a/src/deltagit/deltagit_config.h +++ /dev/null @@ -1,29 +0,0 @@ -/*******************************************************************\ - -Module: Deltagit configuration - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_CONFIG_H -#define DELTAGIT_CONFIG_H - -#include - -class deltagit_configt -{ -public: - deltagit_configt() - { - read(); - } - - std::string description; - - void read(); - -protected: -}; - -#endif diff --git a/src/deltagit/deltagit_parse_options.cpp b/src/deltagit/deltagit_parse_options.cpp deleted file mode 100644 index bbd8f4e1d..000000000 --- a/src/deltagit/deltagit_parse_options.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include - -#include "../deltacheck/version.h" - -#include "show_jobs.h" -#include "do_job.h" -#include "init.h" -#include "reset.h" -#include "reanalyse.h" -#include "deltagit_parse_options.h" -#include "revisions_report.h" - -/*******************************************************************\ - -Function: deltagit_parse_optionst::deltagit_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -deltagit_parse_optionst::deltagit_parse_optionst( - int argc, const char **argv): - parse_options_baset(DELTACHECK_OPTIONS, argc, argv) -{ -} - -/*******************************************************************\ - -Function: deltagit_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int deltagit_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - try - { - if(cmdline.args.size()==0) - { - usage_error(); - return 10; - } - - const std::string command=cmdline.args[0]; - - if(command=="jobs") - { - show_jobs(std::cout); - } - else if(command=="init") - { - if(cmdline.args.size()==1) - { - init(0); - } - else if(cmdline.args.size()==2) - { - init(atoi(cmdline.args[1].c_str())); - } - else - { - usage_error(); - return 10; - } - } - else if(command=="do") - { - if(cmdline.args.size()==2) - { - do_job(cmdline.args[1]); - } - else if(cmdline.args.size()==1) - { - do_job(); - } - else - { - usage_error(); - return 10; - } - } - else if(command=="reset") - { - if(cmdline.args.size()==2) - reset(cmdline.args[1]); - else if(cmdline.args.size()==1) - reset(); - else - { - usage_error(); - return 10; - } - } - else if(command=="reanalyse") - { - if(cmdline.args.size()==2) - reanalyse(cmdline.args[1]); - else if(cmdline.args.size()==1) - reanalyse(); - else - { - usage_error(); - return 10; - } - } - else if(command=="report") - { - bool partial_html=cmdline.isset("partial-html"); - unsigned max_revs=0; - std::string rel_path; - if(cmdline.isset("max-revs")) - max_revs=unsafe_string2unsigned(cmdline.get_value("max-revs")); - if(partial_html) - rel_path=cmdline.get_value("partial-html"); - revisions_report(partial_html, rel_path, max_revs); - } - else - { - usage_error(); - return 10; - } - } - - catch(const std::string &e) - { - std::cerr << e << std::endl; - return 13; - } - - catch(std::bad_alloc) - { - std::cerr << "Out of memory" << std::endl; - return 14; - } - - return 0; -} - -/*******************************************************************\ - -Function: deltagit_parse_optionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void deltagit_parse_optionst::help() -{ - std::cout << - "\n" - "* * DELTAGIT " DELTACHECK_VERSION " - Copyright (C) 2012-2013 * *\n" - "* * Daniel Kroening * *\n" - "* * Oxford University, Computer Science Department * *\n" - "* * kroening@kroening.com * *\n" - "\n" - "Usage: Purpose:\n" - "\n" - " deltagit [-?] [-h] [--help] show help\n" - " deltagit init set up the jobs\n" - " deltagit jobs list the jobs\n" - " deltagit do do given job\n" - " deltagit do do a job that needs work\n" - " deltagit reset clear failure bit on all jobs\n" - " deltagit reanalyse redo analysis\n" - " deltagit report generate top-level report\n" - "\n" - "Reporting options:\n" - " --partial-html generate a partial HTML file \"include.html\"\n" - " --max-revs report on the last revisions\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - "\n"; -} diff --git a/src/deltagit/deltagit_parse_options.h b/src/deltagit/deltagit_parse_options.h deleted file mode 100644 index 357f563ab..000000000 --- a/src/deltagit/deltagit_parse_options.h +++ /dev/null @@ -1,30 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PARSE_OPTIONS_H -#define CPROVER_DELTACHECK_PARSE_OPTIONS_H - -#include - -#define DELTACHECK_OPTIONS \ - "(verbosity):(version)(description):" \ - "(max-revs):(partial-html):" - -class deltagit_parse_optionst:public parse_options_baset -{ -public: - virtual int doit(); - virtual void help(); - - deltagit_parse_optionst( - int argc, const char **argv); - -protected: -}; - -#endif diff --git a/src/deltagit/do_job.cpp b/src/deltagit/do_job.cpp deleted file mode 100644 index 714cfc462..000000000 --- a/src/deltagit/do_job.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/*******************************************************************\ - -Module: Do a jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include - -#include "job_status.h" -#include "do_job.h" - -/*******************************************************************\ - -Function: check_out - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void check_out(job_statust &job_status) -{ - const std::string working_dir=job_status.get_wd(); - - // check if we already have it - if(access((working_dir+"/.git/HEAD").c_str(), R_OK)==0) - { - std::cout << "git repository for " << job_status.id - << " already present\n"; - job_status.next_stage(); - job_status.write(); - return; - } - - std::cout << "Checking out " << job_status.id << "\n"; - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command; - - // Do a shared clone -- this uses very little disc space. - // Will overwrite checkout log. - command="git clone --no-checkout --shared source-repo "+ - working_dir+ - " > jobs/"+job_status.id+".checkout.log 2>&1"; - - int result1=system(command.c_str()); - if(result1!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - return; - } - - // Now do checkout; this will eat disc space. - command="(cd "+working_dir+"; "+ - "git checkout --detach "+job_status.commit+ - ") >> jobs/"+job_status.id+".checkout.log 2>&1"; - - int result2=system(command.c_str()); - - if(result2!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - return; - } - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void build(job_statust &job_status) -{ - std::cout << "Building " << job_status.id << "\n"; - - const std::string working_dir=job_status.get_wd(); - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command; - - // Now run build script in working directory. - command="(cd "+working_dir+"; ../../build"+ - ") >> jobs/"+job_status.id+".build.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Build has failed\n"; - return; - } - - std::cout << "Build successful\n"; - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: analyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void analyse( - job_statust &job_status, - const std::list &jobs) -{ - // get the job before this one - - std::string previous=""; - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - if(j_it->id==job_status.id) break; - previous=j_it->id; - } - - if(previous!="") - { - std::cout << "Differential analysis between " - << previous << " and " << job_status.id - << "\n"; - - // is it built already? - job_statust old_version(previous); - - if(old_version.stage!=job_statust::ANALYSE && - old_version.stage!=job_statust::DONE) - { - std::cout << "Job " << previous << " is not built yet\n"; - return; - } - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command= - "./analyse \""+previous+"\" \""+job_status.id+"\"" - " > jobs/"+job_status.id+".analysis.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Analysis has failed\n"; - return; - } - - job_status.next_stage(); - job_status.write(); - std::cout << "Analysis completed\n"; - } - else - { - std::cout << "One-version analysis for " << job_status.id - << "\n"; - - job_status.status=job_statust::RUNNING; - job_status.set_hostname(); - job_status.write(); - - std::string command= - "./analyse-one \""+job_status.id+"\"" - " > jobs/"+job_status.id+".analysis.log 2>&1"; - - int result=system(command.c_str()); - - if(result!=0) - { - job_status.status=job_statust::FAILURE; - job_status.write(); - std::cout << "Analysis has failed\n"; - return; - } - - job_status.next_stage(); - job_status.write(); - std::cout << "Analysis completed\n"; - } -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job(job_statust &job_status, - const std::list &jobs) -{ - if(job_status.status==job_statust::FAILURE) - { - std::cout << "Job " << job_status.id << " has failed, " - "consider resetting it.\n"; - } - else if(job_status.status==job_statust::RUNNING) - { - std::cout << "Job " << job_status.id - << " is already running.\n"; - } - else if(job_status.status==job_statust::COMPLETED) - { - std::cout << "Job " << job_status.id - << " is already completed.\n"; - } - else - { - switch(job_status.stage) - { - case job_statust::INIT: return; // done by deltagit init - case job_statust::CHECK_OUT: check_out(job_status); break; - case job_statust::BUILD: build(job_status); break; - case job_statust::ANALYSE: analyse(job_status, jobs); break; - case job_statust::DONE: break; - } - } - -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job(const std::string &id) -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // get current job status - job_statust job_status(id); - do_job(job_status, jobs); -} - -/*******************************************************************\ - -Function: do_job - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void do_job() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // Do a job that needs work, - // starting from the end of the log. - for(std::list::reverse_iterator - j_it=jobs.rbegin(); - j_it!=jobs.rend(); - j_it++) - { - if(j_it->stage!=job_statust::DONE && - j_it->stage!=job_statust::INIT && - j_it->status==job_statust::WAITING) - { - std::cout << "Doing job " << j_it->id << std::endl; - do_job(*j_it, jobs); - return; - } - } -} - diff --git a/src/deltagit/do_job.h b/src/deltagit/do_job.h deleted file mode 100644 index e0df19a50..000000000 --- a/src/deltagit/do_job.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Do a jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_DO_JOB_H -#define CPROVER_DELTAREPO_DO_JOB_H - -#include - -void do_job(const std::string &id); -void do_job(); - -#endif diff --git a/src/deltagit/git_branch.cpp b/src/deltagit/git_branch.cpp deleted file mode 100644 index 42e915ea5..000000000 --- a/src/deltagit/git_branch.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/*******************************************************************\ - -Module: Get git branch -a as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "git_branch.h" - -/*******************************************************************\ - -Function: git_brancht::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void git_brancht::read() -{ - temporary_filet tmpfile("deltagit", "log"); - - // get the git branches by running "git branch -a" - std::string command; - command="cd source-repo; git branch --list -a -v --no-abbrev > "+tmpfile(); - system(command.c_str()); - - // read it from temporary file - - std::ifstream in(tmpfile().c_str()); - if(!in) return; - - std::string line; - - - while(std::getline(in, line)) - { - if(has_prefix(line, "* ") || has_prefix(line, " ")) - { - const std::size_t branch_end=line.find(' ', 2); - if(branch_end==std::string::npos) continue; - - const std::size_t commit_pos=line.find_first_not_of(' ', branch_end); - if(commit_pos==std::string::npos) continue; - - std::size_t commit_end=line.find(' ', commit_pos); - if(commit_end==std::string::npos) commit_end=line.size(); - - entryt entry; - entry.name=line.substr(2, branch_end-1); - entry.commit=line.substr(commit_pos, commit_pos-commit_end); - entries.push_back(entry); - } - } -} diff --git a/src/deltagit/git_branch.h b/src/deltagit/git_branch.h deleted file mode 100644 index 23f1f513b..000000000 --- a/src/deltagit/git_branch.h +++ /dev/null @@ -1,37 +0,0 @@ -/*******************************************************************\ - -Module: Get git branch -a as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_GIT_BRANCH_H -#define DELTAGIT_GIT_BRANCH_H - -#include -#include - -class git_brancht -{ -public: - class entryt - { - public: - std::string name; - std::string commit; - }; - - typedef std::list entriest; - entriest entries; - - git_brancht() - { - read(); - } - -protected: - void read(); -}; - -#endif diff --git a/src/deltagit/git_log.cpp b/src/deltagit/git_log.cpp deleted file mode 100644 index 6f9816a65..000000000 --- a/src/deltagit/git_log.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/*******************************************************************\ - -Module: Get git log as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include -#include - -#include "git_log.h" - -/*******************************************************************\ - -Function: git_logt::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void git_logt::read(unsigned max_commits) -{ - temporary_filet tmpfile("deltagit", "log"); - - // get the git log by running "git log" - std::string command; - command="cd source-repo; git log --name-only"; - if(max_commits!=0) command+=" --max-count="+i2string(max_commits); - command+=" > "+tmpfile(); - system(command.c_str()); - - // read it from temporary file - - std::ifstream in(tmpfile().c_str()); - if(!in) return; - - std::string line; - - entryt entry; - - while(std::getline(in, line)) - { - if(has_prefix(line, "commit ")) - { - if(entry.commit!="") - { - entries.push_back(entry); - entry=entryt(); // clear it - } - - entry.commit=line.substr(7, std::string::npos); - } - else if(has_prefix(line, "Author: ")) - { - entry.author=line.substr(8, std::string::npos); - } - else if(has_prefix(line, "Date: ")) - { - entry.date=line.substr(8, std::string::npos); - } - else if(has_prefix(line, " git-svn-id: ")) - { - std::size_t pos1=line.rfind('@'); - std::size_t pos2=line.rfind(' '); - if(pos1!=std::string::npos && pos2!=std::string::npos) - entry.git_svn_id=line.substr(pos1+1, pos2-pos1-1); - } - else if(!line.empty() && line[0]!=' ') - { - // shall be file name - entry.files.push_back(line); - } - } - - // last one - if(entry.commit!="") - entries.push_back(entry); -} diff --git a/src/deltagit/git_log.h b/src/deltagit/git_log.h deleted file mode 100644 index 27b307707..000000000 --- a/src/deltagit/git_log.h +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Get git log as data structure - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_GIT_LOG_H -#define DELTAGIT_GIT_LOG_H - -#include -#include - -class git_logt -{ -public: - class entryt - { - public: - std::string commit; - std::string author; - std::string date; - std::string git_svn_id; - std::list files; - }; - - typedef std::list entriest; - entriest entries; - - // Read at most max_commits many entries; - // 0 means no limit. - explicit git_logt(unsigned max_commits=0) - { - read(max_commits); - } - -protected: - void read(unsigned max_commits); -}; - -#endif diff --git a/src/deltagit/init.cpp b/src/deltagit/init.cpp deleted file mode 100644 index ff5c8b9aa..000000000 --- a/src/deltagit/init.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*******************************************************************\ - -Module: Initialize Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#include - -#ifdef _WIN32 -#include -#endif - -#include -#include - -#include "job_status.h" -#include "init.h" -#include "git_log.h" - -/*******************************************************************\ - -Function: init - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void init(job_statust &job_status) -{ - std::string command; - temporary_filet tempfile("deltagit", "txt"); - - // do a git show to learn more about the job - command="cd source-repo; git show "+job_status.commit+ - " --numstat"+ - " > "+tempfile(); - - int result1=system(command.c_str()); - if(result1!=0) - { - job_status.status=job_statust::FAILURE; - // don't write, commit might be bogus - return; - } - - // parse the file - std::ifstream in(tempfile().c_str()); - if(!in) return; - - std::string line; - - job_status.added=0; - job_status.deleted=0; - job_status.message=""; - - while(std::getline(in, line)) - { - if(has_prefix(line, " git-svn-id: ")) - { - } - else if(has_prefix(line, " ")) - { - // commit message - job_status.message+=line.substr(4, std::string::npos)+"\n"; - } - else if(has_prefix(line, "Author: ")) - { - job_status.author=line.substr(8, std::string::npos); - } - else if(has_prefix(line, "Date: ")) - { - job_status.date=line.substr(8, std::string::npos); - } - else if(!line.empty() && isdigit(line[0])) - { - // \t\t - const std::size_t end_added=line.find('\t', 0); - if(end_added==std::string::npos) continue; - const std::size_t end_deleted=line.find('\t', end_added+1); - if(end_deleted==std::string::npos) continue; - - job_status.added+=atol(line.substr(0, end_added).c_str()); - job_status.deleted+=atol(line.substr(end_added+1, end_deleted-end_added-1).c_str()); - } - } - - // strip trailing \n from commit message - std::string &message=job_status.message; - while(!message.empty() && message[message.size()-1]=='\n') - message.resize(message.size()-1); - - job_status.next_stage(); - job_status.write(); -} - -/*******************************************************************\ - -Function: get_extension - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_extension(const std::string &s) -{ - std::size_t pos=s.rfind('.'); - if(pos==std::string::npos) return ""; - return s.substr(pos+1, std::string::npos); -} - -/*******************************************************************\ - -Function: get_file - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_file(const std::string &s) -{ - std::size_t pos=s.rfind('/'); - if(pos==std::string::npos) return s; - return s.substr(pos+1, std::string::npos); -} - -/*******************************************************************\ - -Function: init - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void init(unsigned max_commits) -{ - // get jobs from git log - std::list jobs; - - std::cout << "Getting git log\n"; - - // get the git log - git_logt git_log(max_commits); - - // rummage through it, looking for 'interesting' commits - // we reverse, to start with older commits - for(git_logt::entriest::const_reverse_iterator - l_it=git_log.entries.rbegin(); - l_it!=git_log.entries.rend(); - l_it++) - { - bool found=false; - - for(std::list::const_iterator - f_it=l_it->files.begin(); - f_it!=l_it->files.end(); - f_it++) - { - std::string file=get_file(*f_it); - std::string ext=get_extension(file); - - if(ext=="c" || ext=="C" || - ext=="cpp" || ext=="c++" || - ext=="h" || ext=="hpp") - { - found=true; - break; - } - } - - if(found) - { - std::string id; - - if(l_it->git_svn_id!="") - id="r"+l_it->git_svn_id; - else - id=l_it->commit; - - job_statust job_status(id); - job_status.commit=l_it->commit; - - jobs.push_back(job_status); - } - } - - // Make sure we have a 'jobs' directory - #ifdef _WIN32 - mkdir("jobs"); - #else - mkdir("jobs", 0777); - #endif - - unsigned total=0; - - // Do jobs that need to be initialized, - // starting from the end. - for(std::list::reverse_iterator - j_it=jobs.rbegin(); - j_it!=jobs.rend(); - j_it++) - { - if(j_it->stage==job_statust::INIT && - j_it->status!=job_statust::FAILURE) - { - std::cout << "Setting up job " << j_it->id << "\n"; - std::cout << std::flush; - init(*j_it); - total++; - } - } - - std::cout << "Added " << total << " jobs\n"; -} - diff --git a/src/deltagit/init.h b/src/deltagit/init.h deleted file mode 100644 index 5787e4af6..000000000 --- a/src/deltagit/init.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Initialize Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_INIT_H -#define CPROVER_DELTAGIT_INIT_H - -void init(unsigned max_commits); - -#endif diff --git a/src/deltagit/job_status.cpp b/src/deltagit/job_status.cpp deleted file mode 100644 index e7a06e18a..000000000 --- a/src/deltagit/job_status.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include - -#ifdef _WIN32 -#else -#include -#include -#endif - -#include - -#include -#include -#include - -#include - -#include "git_log.h" -#include "job_status.h" - -/*******************************************************************\ - -Function: job_statust::set_hostname - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::set_hostname() -{ - #ifdef _WIN32 - #else - char s[1000]; - if(gethostname(s, 1000)==0) - { - hostname=std::string(s); - } - #endif -} - -/*******************************************************************\ - -Function: job_statust::next_stage - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::next_stage() -{ - assert(stage!=DONE); - stage=(staget)((int)stage+1); - status=stage==DONE?COMPLETED:WAITING; -} - -/*******************************************************************\ - -Function: job_statust::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::read() -{ - xmlt src; - - console_message_handlert message_handler; - - if(parse_xml("jobs/"+id+".status", message_handler, src)) - { - // assume it's new - clear(); - return; - } - - if(src.name!="deltagit_jobstatus") - throw std::string("unexpected XML for job status"); - - const std::string stage_string=src.get_attribute("stage"); - - if(stage_string=="init") - stage=INIT; - else if(stage_string=="check out") - stage=CHECK_OUT; - else if(stage_string=="build") - stage=BUILD; - else if(stage_string=="analyse") - stage=ANALYSE; - else if(stage_string=="done") - stage=DONE; - else - throw std::string("unexpected stage"); - - const std::string status_string=src.get_attribute("status"); - - if(status_string=="waiting") - status=WAITING; - else if(status_string=="running") - status=RUNNING; - else if(status_string=="failure") - status=FAILURE; - else if(status_string=="completed") - status=COMPLETED; - else - throw std::string("unexpected status"); - - added=atol(src.get_attribute("added").c_str()); - deleted=atol(src.get_attribute("deleted").c_str()); - message=src.get_element("message"); - author=src.get_attribute("author"); - date=src.get_attribute("date"); - commit=src.get_attribute("commit"); - - hostname=src.get_attribute("hostname"); -} - -/*******************************************************************\ - -Function: as_string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string as_string(job_statust::staget stage) -{ - switch(stage) - { - case job_statust::INIT: return "init"; - case job_statust::CHECK_OUT: return "check out"; - case job_statust::BUILD: return "build"; - case job_statust::ANALYSE: return "analyse"; - case job_statust::DONE: return "done"; - default: return ""; - } -} - -/*******************************************************************\ - -Function: as_string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string as_string(job_statust::statust status) -{ - switch(status) - { - case job_statust::WAITING: return "waiting"; - case job_statust::RUNNING: return "running"; - case job_statust::FAILURE: return "failure"; - case job_statust::COMPLETED: return "completed"; - default: return ""; - } -} - -/*******************************************************************\ - -Function: job_statust::write - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void job_statust::write() -{ - xmlt xml; - xml.name="deltagit_jobstatus"; - - xml.set_attribute("id", id); - xml.set_attribute("status", as_string(status)); - xml.set_attribute("stage", as_string(stage)); - xml.set_attribute("commit", commit); - xml.set_attribute("added", added); - xml.set_attribute("deleted", deleted); - xml.set_attribute("author", author); - xml.set_attribute("date", date); - xml.set_attribute("hostname", hostname); - xml.new_element("message").data=message; - - std::ofstream out(("jobs/"+id+".status").c_str()); - out << xml; -} - -/*******************************************************************\ - -Function: get_jobs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -class job_ordering -{ -public: - bool operator()(const job_statust &j1, const job_statust &j2) - { - // SVN revisions rNNNN? - const std::string &s1=j1.id; - const std::string &s2=j2.id; - - if(s1.size()>=2 && s2.size()>=2 && - s1[0]=='r' && s2[0]=='r') - return atol(s1.substr(1, std::string::npos).c_str())< - atol(s2.substr(1, std::string::npos).c_str()); - else - { - #ifdef _WIN32 - throw "todo: win32 doesn't have strptime"; - #else - // use date - struct tm tm1, tm2; - // Tue Feb 4 12:29:13 2014 +0000 - strptime(j1.date.c_str(), "%a %b %d %T %Y %z", &tm1); - strptime(j2.date.c_str(), "%a %b %d %T %Y %z", &tm2); - - time_t t1=mktime(&tm1); - time_t t2=mktime(&tm2); - - // secondary criterion - if(t1==t2) return s1 &jobs) -{ - // sort into set - std::set job_set; - - DIR *dir=opendir("jobs"); - if(dir==NULL) return; - - std::string suffix=".status"; - - struct dirent *ent; - while((ent=readdir(dir))!=NULL) - { - std::string name=ent->d_name; - if(has_suffix(name, suffix)) - { - std::string id=name.substr(0, name.size()-suffix.size()); - job_statust job_status(id); - job_set.insert(job_status); - } - } - - closedir(dir); - - // dump the set into list - for(std::set::const_iterator - s_it=job_set.begin(); - s_it!=job_set.end(); - s_it++) - jobs.push_back(*s_it); -} diff --git a/src/deltagit/job_status.h b/src/deltagit/job_status.h deleted file mode 100644 index 38f0ac1a9..000000000 --- a/src/deltagit/job_status.h +++ /dev/null @@ -1,72 +0,0 @@ -/*******************************************************************\ - -Module: Job Status - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTAGIT_JOB_STATUS_H -#define DELTAGIT_JOB_STATUS_H - -#include -#include - -class job_statust -{ -public: - explicit job_statust(const std::string &_id):id(_id) - { - read(); - } - - // unique identifier - std::string id; - - // stuff about the commit - std::string commit; - std::string message; - std::string author; - std::string date; - - unsigned added, deleted; - - // analysis status - enum statust { WAITING, RUNNING, FAILURE, COMPLETED }; - enum staget { INIT, CHECK_OUT, BUILD, ANALYSE, DONE }; - statust status; - staget stage; - - std::string hostname; - - void read(); - void write(); - - void clear() - { - commit=""; - status=WAITING; - stage=INIT; - added=deleted=0; - } - - void next_stage(); - - std::string get_wd() const - { - return "jobs/"+id+".wd"; - } - - void set_hostname(); - -protected: -}; - -std::string as_string(job_statust::statust); -std::string as_string(job_statust::staget); - -typedef std::list jobst; - -void get_jobs(jobst &); - -#endif diff --git a/src/deltagit/log_scale.cpp b/src/deltagit/log_scale.cpp deleted file mode 100644 index 3777babfc..000000000 --- a/src/deltagit/log_scale.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "log_scale.h" - -const char log_scale[]= - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAAAsCAIAAACPL/3lAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAjlJREFUeNrslsGVozAMhuXpwCnBlGBKUEogJUAJ4ZxcTAlxCaEEKAFKgBLsEjQHzfJYm7AJE/awOzrk2X6S/wTp448gItg/PuabsiyPxyOvrbVCCCFEmqbee+99mqZ8Yq0FgL7vkyQRQiRJ0vc9ABRFwQlFUYQ6REREwzDwFhGJyDknpTTGOOeUUsYYY4xSyjlnjJFSOucQMcsyIsqyDBGbpgGAruu6rgOApmn45svlQkRfMhx5nrMM1/Dh+XxGREQ0xvA34Fumu+73OwAYY7iWiLTWnDzJfCw+ynEclVK8llLyCS+mLQBwznQSlDzszRRKKb4IALz3UkqllPeetwCgtZ7EpoR5yVMyWmspZVVV3vu6rrXWiGit9d5ba6WU0wkA1HWNiFrrtm37X4GICyMQ9IaIbrdbMBT8C6SU3JKu6/gpaa2HYeByLpk3huM3mT1ibQT2xTOOl4ANEh72Zh4bgA0SHuIZx6vAzhO29OYZYF/GcwOw3vsgYYvMH4HN8zxIeGoENgAbJPy3eMbzdjgc2rblbWCXsZ8+25ugT1zCnYjtMvDTF/BcfDWwTGyXgZ++pzeLPM79dHtvVoCdtL+F5yKwgV0GfrpxBILexHYZ++kPnotR17UQgtcreH5LJkmS0+k0d3StNWNUluX2EYiD38q83gXPmNZd8Ixp3QXPOFbwfKeMMabveyHEOI78Jr1er1+fP3iuRpqmVVXtKMP/oUN7frtMnudENHndvg/tL/Xm35WZ8PwcAHt/WQW5PfKNAAAAAElFTkSuQmCC"; diff --git a/src/deltagit/log_scale.h b/src/deltagit/log_scale.h deleted file mode 100644 index d9b389181..000000000 --- a/src/deltagit/log_scale.h +++ /dev/null @@ -1 +0,0 @@ -extern const char log_scale[]; diff --git a/src/deltagit/log_scale.png b/src/deltagit/log_scale.png deleted file mode 100644 index 08a271e100372a891b61e54001eb4e4429bf65e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3342 zcmV+p4e|1cP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0006xNklI^@}YlK%whZaIVYtw#yIDcQpOl#j8e)uXN-|jI_D5E z%Q7O$vJ4TMrU8Ji>$G{*2Z;9I7);A_$g_vhhgAyE}1um<2RW-Vr>NW#WRkVfDIBJ_GTU%fDIBJ;u*)Q zz|Ir4^yV30U&eC-u-^M1VT;Tkfz9(gNO)=lHUJwW{I__<^L98K+P39wV3)C - -#include "job_status.h" -#include "reanalyse.h" - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse(job_statust &job_status) -{ - if(job_status.stage==job_statust::DONE) - { - std::cout << "Reanalysing job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.stage=job_statust::ANALYSE; - job_status.write(); - } - else if(job_status.stage==job_statust::ANALYSE && - job_status.status==job_statust::FAILURE) - { - std::cout << "Resetting job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.stage=job_statust::ANALYSE; - job_status.write(); - } -} - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // reanalyse jobs that need to be - for(std::list::iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - reanalyse(*j_it); - } -} - -/*******************************************************************\ - -Function: reanalyse - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reanalyse(const std::string &job) -{ - job_statust job_status(job); - reanalyse(job_status); -} - diff --git a/src/deltagit/reanalyse.h b/src/deltagit/reanalyse.h deleted file mode 100644 index ddfb3f59f..000000000 --- a/src/deltagit/reanalyse.h +++ /dev/null @@ -1,15 +0,0 @@ -/*******************************************************************\ - -Module: Reanalyse Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_REANALYSE_H -#define CPROVER_DELTAGIT_REANALYSE_H - -void reanalyse(); -void reanalyse(const std::string &job); - -#endif diff --git a/src/deltagit/reset.cpp b/src/deltagit/reset.cpp deleted file mode 100644 index 76ef93b06..000000000 --- a/src/deltagit/reset.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Reset Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "job_status.h" -#include "reset.h" - -/*******************************************************************\ - -Function: reset - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reset() -{ - // get job list - std::list jobs; - get_jobs(jobs); - - // reset jobs that need to be - for(std::list::iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - if(j_it->status==job_statust::FAILURE) - { - std::cout << "Resetting job " << j_it->id << "\n"; - j_it->status=job_statust::WAITING; - j_it->write(); - } - } -} - -/*******************************************************************\ - -Function: reset - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void reset(const std::string &job) -{ - job_statust job_status(job); - - if(job_status.status==job_statust::FAILURE) - { - std::cout << "Resetting job " << job_status.id << "\n"; - job_status.status=job_statust::WAITING; - job_status.write(); - } -} - diff --git a/src/deltagit/reset.h b/src/deltagit/reset.h deleted file mode 100644 index 1e48308d9..000000000 --- a/src/deltagit/reset.h +++ /dev/null @@ -1,15 +0,0 @@ -/*******************************************************************\ - -Module: Reset Jobs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_RESET_H -#define CPROVER_DELTAGIT_RESET_H - -void reset(); -void reset(const std::string &job); - -#endif diff --git a/src/deltagit/revisions_report.h b/src/deltagit/revisions_report.h deleted file mode 100644 index fe5bfff78..000000000 --- a/src/deltagit/revisions_report.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Show the overview for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAGIT_REVISIONS_REPORT_H -#define CPROVER_DELTAGIT_REVISIONS_REPORT_H - -void revisions_report( - bool partial_html, - const std::string &rel_path, - unsigned max_revs); - -#endif diff --git a/src/deltagit/revisions_report_header.html b/src/deltagit/revisions_report_header.html deleted file mode 100644 index 6e5ba3e5b..000000000 --- a/src/deltagit/revisions_report_header.html +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - diff --git a/src/deltagit/shell_escape.cpp b/src/deltagit/shell_escape.cpp deleted file mode 100644 index 96991a113..000000000 --- a/src/deltagit/shell_escape.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "shell_escape.h" - -/*******************************************************************\ - -Function: shell_escape - - Inputs: - - Outputs: - - Purpose: escape characters for bash - -\*******************************************************************/ - -std::string shell_escape(const std::string &src) -{ - bool clean=true; - - for(std::string::const_iterator - it=src.begin(); it!=src.end(); it++) - { - // positive list of safe characters - if(isalnum(*it) || *it=='_' || *it=='/' || - *it=='.' || *it==':') - { - } - else - clean=false; - } - - if(clean) - return src; - - std::string result; - result.reserve(src.size()+2); - - result+='\''; - - for(std::string::const_iterator - it=src.begin(); it!=src.end(); it++) - { - if(*it=='\'') - result+="'\\''"; // quote, backslash, quote - else - result+=*it; - } - - result+='\''; - - return result; -} diff --git a/src/deltagit/shell_escape.h b/src/deltagit/shell_escape.h deleted file mode 100644 index d27c6a5ad..000000000 --- a/src/deltagit/shell_escape.h +++ /dev/null @@ -1,13 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -std::string shell_escape(const std::string &); - - diff --git a/src/deltagit/show_jobs.cpp b/src/deltagit/show_jobs.cpp deleted file mode 100644 index 4956442c7..000000000 --- a/src/deltagit/show_jobs.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/*******************************************************************\ - -Module: Show the jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include "show_jobs.h" -#include "job_status.h" - -/*******************************************************************\ - -Function: show_jobs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_jobs(std::ostream &out) -{ - std::list jobs; - - get_jobs(jobs); - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++) - { - out << j_it->id; - - out << " " << as_string(j_it->stage) - << " " << as_string(j_it->status); - - if(j_it->hostname!="") - out << " on " << j_it->hostname; - - out << "\n"; - } -} diff --git a/src/deltagit/show_jobs.h b/src/deltagit/show_jobs.h deleted file mode 100644 index 749acc2b3..000000000 --- a/src/deltagit/show_jobs.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Show the jobs for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_SHOW_JOBS_H -#define CPROVER_DELTAREPO_SHOW_JOBS_H - -#include - -void show_jobs(std::ostream &); - -#endif diff --git a/src/deltagit/update.cpp b/src/deltagit/update.cpp deleted file mode 100644 index 28385e698..000000000 --- a/src/deltagit/update.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/*******************************************************************\ - -Module: Update a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "update.h" -#include "deltarepo_config.h" - -/*******************************************************************\ - -Function: update - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void update() -{ - deltarepo_configt config; - - switch(config.kind) - { - case NONE: assert(false); break; - - case GIT: - { - } - break; - - case SVN: - { - - } - break; - } -} diff --git a/src/deltagit/update.h b/src/deltagit/update.h deleted file mode 100644 index 4dee5e1a6..000000000 --- a/src/deltagit/update.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Update a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTAREPO_UPDATE_H -#define CPROVER_DELTAREPO_UPDATE_H - -void update(); - -#endif diff --git a/src/dist-bin b/src/dist-bin deleted file mode 100755 index 76c005560..000000000 --- a/src/dist-bin +++ /dev/null @@ -1,9 +0,0 @@ -file=`date "+%Y-%m-%d"` - -strip deltacheck/deltacheck - -rm -f binaries/*.gz - -gzip -9 < deltacheck/deltacheck > binaries/deltacheck-$file-linux32.gz - -scp binaries/*.gz kroening@dkr-srv.cs.ox.ac.uk:/srv/www/cprover.org/tmp/binaries/ diff --git a/src/domains/Makefile b/src/domains/Makefile index f6107d0dc..b3436a465 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -1,10 +1,6 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ - tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ - ssa_analyzer.cpp disjunctive_analyzer.cpp util.cpp incremental_solver.cpp \ - strategy_solver_base.cpp \ +SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ + ssa_analyzer.cpp util.cpp incremental_solver.cpp \ + strategy_solver_base.cpp strategy_solver_equality.cpp \ linrank_domain.cpp lexlinrank_domain.cpp\ ranking_solver_enumeration.cpp lexlinrank_solver_enumeration.cpp \ strategy_solver_enumeration.cpp strategy_solver_binsearch.cpp \ @@ -12,19 +8,24 @@ SRC = predicate.cpp fixed_point.cpp ssa_fixed_point.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ strategy_solver_predabs.cpp \ - simplify_transformer.cpp simplify_bounds.cpp + disjunctive_analyzer.cpp \ + #solver_enumeration.cpp +include ../config.inc include $(CBMC)/src/config.inc include $(CBMC)/src/common +CBMC ?= ../.. -CP_CXXFLAGS += $(SUMMARIZERFLAGS) +CP_CXXFLAGS += $(TWOLSFLAGS) -INCLUDES= -I $(CBMC)/src +INCLUDES= -I $(CBMC)/src -I .. -CLEANFILES = +CLEANFILES = domains$(LIBEXT) -all: $(OBJ) +all: domains$(LIBEXT) ############################################################################### +domains$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/domains/domain.cpp b/src/domains/domain.cpp index bda121290..a693a8180 100644 --- a/src/domains/domain.cpp +++ b/src/domains/domain.cpp @@ -1,36 +1,67 @@ +/*******************************************************************\ + +Module: Abstract domain base class + +Author: Peter Schrammel + +\*******************************************************************/ + #include "domain.h" +/*******************************************************************\ + +Function: domaint::merge_kinds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ domaint::kindt domaint::merge_kinds(kindt k1, kindt k2) { - return (k1==OUT || - k2==OUT ? (k1==LOOP || - k2==LOOP ? OUTL : OUT) : - (k1==LOOP || k2==LOOP ? LOOP : IN)); + return + (k1==OUT || k2==OUT ? (k1==LOOP || k2==LOOP ? OUTL : OUT) : + (k1==LOOP || k2==LOOP ? LOOP : IN)); } -void domaint::output_var_specs(std::ostream &out, const var_specst &var_specs, - const namespacet &ns) -{ - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) +/*******************************************************************\ + +Function: domaint::output_var_specs + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void domaint::output_var_specs( + std::ostream &out, + const var_specst &var_specs, + const namespacet &ns) +{ + for(const auto &v : var_specs) + { + switch(v.kind) { - switch(v->kind) - { - case LOOP: - out << "(LOOP) [ " << from_expr(ns,"",v->pre_guard) << " | "; - out << from_expr(ns,"",v->post_guard) << " ]: "; - break; - case IN: - out << "(IN) "; - out << from_expr(ns,"",v->pre_guard) << ": "; - break; - case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",v->post_guard) << ": "; - break; - default: assert(false); - } - out << from_expr(ns,"",v->var) << std::endl; + case LOOP: + out << "(LOOP) [ " << from_expr(ns, "", v.pre_guard) << " | "; + out << from_expr(ns, "", v.post_guard) << " ]: "; + break; + case IN: + out << "(IN) "; + out << from_expr(ns, "", v.pre_guard) << ": "; + break; + case OUT: case OUTL: + out << "(OUT) "; + out << from_expr(ns, "", v.post_guard) << ": "; + break; + default: assert(false); } + out << from_expr(ns, "", v.var) << std::endl; + } } diff --git a/src/domains/domain.h b/src/domains/domain.h index 4c8669aff..59487f28a 100644 --- a/src/domains/domain.h +++ b/src/domains/domain.h @@ -1,5 +1,13 @@ -#ifndef CPROVER_DOMAIN_H -#define CPROVER_DOMAIN_H +/*******************************************************************\ + +Module: Abstract domain base class + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_DOMAIN_H +#define CPROVER_2LS_DOMAINS_DOMAIN_H #include #include @@ -13,14 +21,19 @@ class domaint { public: - domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domain_number(_domain_number), + domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domain_number(_domain_number), renaming_map(_renaming_map), ns(_ns) - {} - virtual ~domaint() {} + { + } + + virtual ~domaint() + { + } typedef exprt vart; typedef std::vector var_listt; @@ -28,61 +41,98 @@ class domaint typedef enum {LOOP, IN, OUT, OUTL} kindt; - typedef exprt guardt; + typedef exprt guardt; - typedef struct { + typedef struct + { guardt pre_guard; guardt post_guard; vart var; - exprt aux_expr; //some auxiliary per-variable constraint + exprt aux_expr; // some auxiliary per-variable constraint kindt kind; } var_spect; - typedef std::vector var_specst; + typedef std::vector var_specst; - class valuet { - public: - virtual ~valuet() {} + class valuet + { + public: + typedef enum {TOP, BOTTOM, OTHER} basic_valuet; + valuet():basic_value(OTHER) {} + virtual ~valuet() {} + + basic_valuet basic_value; }; - virtual void initialize(valuet &value) { assert(false); } + virtual void initialize(valuet &value) { value.basic_value=valuet::BOTTOM; } - //returns true as long as further refinements are possible + // returns true as long as further refinements are possible virtual void reset_refinements() { } virtual bool refine() { return false; } - virtual void join(valuet &value1, const valuet &value2) { assert(false); } + virtual void join(valuet &value1, const valuet &value2) + { + bool other_bottom= + value1.basic_value==valuet::OTHER && + value2.basic_value==valuet::BOTTOM; + if(value1.basic_value==value2.basic_value || + value1.basic_value==valuet::TOP || + other_bottom) + return; + value1.basic_value=value2.basic_value; + } + + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const + { + assert(false); + } - virtual void output_value(std::ostream &out, const valuet &value, - const namespacet &ns) const { assert(false); } - virtual void output_domain(std::ostream &out, - const namespacet &ns) const { assert(false); } + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const + { + assert(false); + } - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result) - { assert(false); } //(not useful to make value const (e.g. union-find)) + // (not useful to make value const (e.g. union-find)) + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) + { + if(value.basic_value==valuet::BOTTOM) + result=false_exprt(); + else + result=true_exprt(); + } virtual bool is_spec_empty() const { assert(false); } - + static kindt merge_kinds(kindt k1, kindt k2); - static void output_var_specs(std::ostream &out, const var_specst &var_specs, - const namespacet &ns); + static void output_var_specs( + std::ostream &out, + const var_specst &var_specs, + const namespacet &ns); - protected: - unsigned domain_number; //serves as id for variables names +protected: + unsigned domain_number; // serves as id for variables names replace_mapt &renaming_map; const namespacet &ns; - - inline void rename(exprt &expr) - { - replace_expr(renaming_map, expr); + + inline void rename(exprt &expr) + { + replace_expr(renaming_map, expr); } + inline void rename(exprt::operandst &operands) { for(unsigned i=0; i template_generatorst; - template_generatorst template_generators; - -protected: - const local_SSAt &SSA; - incremental_solvert &solver; -}; - -#endif diff --git a/src/domains/domain_refinement_variables.cpp b/src/domains/domain_refinement_variables.cpp deleted file mode 100644 index f8e313ea4..000000000 --- a/src/domains/domain_refinement_variables.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Domain Refinement: Choice of Variables - -Author: Peter Schrammel - -\*******************************************************************/ - -bool domain_refinementt::operator()() -{ - //TODO: analyze counterexample - for(template_generatorst::iterator it = template_generators.begin(); - it != template_generators.end(); ++it) - { - //TODO: select refinement to be performed - - //TODO: update template and domain - //it->add_template(); - } -} diff --git a/src/domains/equality_domain.cpp b/src/domains/equality_domain.cpp index ae5b9aa27..8d77115b3 100644 --- a/src/domains/equality_domain.cpp +++ b/src/domains/equality_domain.cpp @@ -1,13 +1,23 @@ -#include "equality_domain.h" -#include "util.h" +/*******************************************************************\ +Module: Equalities/Disequalities domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#include +#endif #include #include #include #include -#include + +#include "equality_domain.h" +#include "util.h" /*******************************************************************\ @@ -23,7 +33,12 @@ Function: equality_domaint::initialize void equality_domaint::initialize(valuet &value) { - equ_valuet &v = static_cast(value); +#if 0 + if(templ.size()==0) + return domaint::initialize(value); +#endif + + equ_valuet &v=static_cast(value); v.equs.clear(); v.disequs.clear(); } @@ -43,50 +58,103 @@ Function: equality_domaint::get_pre_equ_constraint exprt equality_domaint::get_pre_equ_constraint(unsigned index) { assert(index(value); +#if 0 + if(templ.size()==0) + return domaint::project_on_vars(value, vars, result); +#endif + + equ_valuet &v=static_cast(value); exprt::operandst c; - for(unsigned index = 0; index(value); - equ_valuet v = _v; + const equ_valuet &_v=static_cast(value); + equ_valuet v=_v; - for(unsigned index = 0; index " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " - << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) << "===> " + << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " - << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << "===> " + << std::endl << " "; break; default: assert(false); } - const var_pairt &vv = templ_row.var_pair; - out << from_expr(ns,"",vv.first) << " =!= " - << from_expr(ns,"",vv.second) << std::endl; + const var_pairt &vv=templ_row.var_pair; + out << from_expr(ns, "", vv.first) << "=!= " + << from_expr(ns, "", vv.second) << std::endl; } } /*******************************************************************\ -Function: equality_domaint::make_template +Function: equality_domaint::adapt_types Inputs: @@ -296,119 +378,140 @@ Function: equality_domaint::make_template \*******************************************************************/ -bool adapt_types(exprt &v1, exprt &v2) +bool equality_domaint::adapt_types(exprt &v1, exprt &v2) { - //signed, unsigned integers + // signed, unsigned integers if((v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv) && - (v2.type().id()==ID_signedbv || v2.type().id()==ID_unsignedbv)) + (v2.type().id()==ID_signedbv || v2.type().id()==ID_unsignedbv)) { - unsigned size1 = 0, size2 = 0; - if(v1.type().id()==ID_signedbv) - size1 = to_signedbv_type(v1.type()).get_width(); - if(v1.type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(v1.type()).get_width(); - if(v2.type().id()==ID_signedbv) - size2 = to_signedbv_type(v2.type()).get_width(); - if(v2.type().id()==ID_unsignedbv) - size2 = to_unsignedbv_type(v2.type()).get_width(); + unsigned size1=0, size2=0; + if(v1.type().id()==ID_signedbv) + size1=to_signedbv_type(v1.type()).get_width(); + if(v1.type().id()==ID_unsignedbv) + size1=to_unsignedbv_type(v1.type()).get_width(); + if(v2.type().id()==ID_signedbv) + size2=to_signedbv_type(v2.type()).get_width(); + if(v2.type().id()==ID_unsignedbv) + size2=to_unsignedbv_type(v2.type()).get_width(); if(v1.type().id()==v2.type().id()) - { - if(size1==size2) return true; - - typet new_type = v1.type(); - if(new_type.id()==ID_signedbv) - to_signedbv_type(new_type).set_width(std::max(size1,size2)); - else //if(new_type.id()==ID_unsignedbv) - to_unsignedbv_type(new_type).set_width(std::max(size1,size2)); - - if(size1>size2) v2 = typecast_exprt(v2,new_type); - else v1 = typecast_exprt(v1,new_type); - return true; - } - - //types are different - typet new_type = signedbv_typet(std::max(size1,size2)+1); - v1 = typecast_exprt(v1,new_type); - v2 = typecast_exprt(v2,new_type); + { + if(size1==size2) + return true; + + typet new_type=v1.type(); + if(new_type.id()==ID_signedbv) + to_signedbv_type(new_type).set_width(std::max(size1, size2)); + else // if(new_type.id()==ID_unsignedbv) + to_unsignedbv_type(new_type).set_width(std::max(size1, size2)); + + if(size1>size2) + v2=typecast_exprt(v2, new_type); + else + v1=typecast_exprt(v1, new_type); + return true; + } + + // types are different + typet new_type=signedbv_typet(std::max(size1, size2)+1); + v1=typecast_exprt(v1, new_type); + v2=typecast_exprt(v2, new_type); return true; } - //pointer equality - if(v1.type().id()==ID_pointer && v2.type().id()==ID_pointer) + // pointer equality + if(v1.type().id()==ID_pointer && v2.type().id()==ID_pointer) { - if(to_pointer_type(v1.type()).subtype() == + if(to_pointer_type(v1.type()).subtype()== to_pointer_type(v2.type()).subtype()) return true; return false; } - if(v1.id()==ID_index || v2.id()==ID_index) + if(v1.id()==ID_index || v2.id()==ID_index) { #if 0 std::cout << "v1: " << v1 << std::endl; std::cout << "v2: " << v2 << std::endl; #endif - //TODO: implement - return false; - } - - return false; //types incompatible + // TODO: implement + return false; + } + + return false; // types incompatible } +/*******************************************************************\ + +Function: equality_domaint::make_template + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void equality_domaint::make_template( const var_specst &var_specs, const namespacet &ns) -{ - unsigned size = var_specs.size(); //just an estimate +{ + unsigned size=var_specs.size(); // just an estimate templ.clear(); - templ.reserve(size); + templ.reserve(size); - for(var_specst::const_iterator v1 = var_specs.begin(); + for(var_specst::const_iterator v1=var_specs.begin(); v1!=var_specs.end(); v1++) { - // NULL pointer checks + // NULL pointer checks if(v1->var.type().id()==ID_pointer) { templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.var_pair = var_pairt(v1->var, - null_pointer_exprt(to_pointer_type(v1->var.type()))); - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.aux_expr = v1->aux_expr; - templ_row.kind = v1->kind; + template_rowt &templ_row=templ.back(); + templ_row.var_pair= + var_pairt(v1->var, null_pointer_exprt(to_pointer_type(v1->var.type()))); + templ_row.pre_guard=v1->pre_guard; + templ_row.post_guard=v1->post_guard; + templ_row.aux_expr=v1->aux_expr; + templ_row.kind=v1->kind; } - var_specst::const_iterator v2 = v1; v2++; + var_specst::const_iterator v2=v1; v2++; for(; v2!=var_specs.end(); v2++) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - //if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + +#if 0 + // TODO: must be done in caller (for preconditions, e.g.) + if(k==IN) + continue; +#endif exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - exprt vv1 = v1->var; - exprt vv2 = v2->var; - if(!adapt_types(vv1,vv2)) continue; + exprt vv1=v1->var; + exprt vv2=v2->var; + if(!adapt_types(vv1, vv2)) + continue; templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.var_pair = var_pairt(vv1,vv2); - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.aux_expr = aux_expr; - templ_row.kind = k; + template_rowt &templ_row=templ.back(); + templ_row.var_pair=var_pairt(vv1, vv2); + templ_row.pre_guard=pre_g; + templ_row.post_guard=post_g; + templ_row.aux_expr=aux_expr; + templ_row.kind=k; } } } /*******************************************************************\ -Function: equality_domaint::get_var_pairs +Function: equality_domaint::get_index_set Inputs: @@ -418,7 +521,8 @@ Function: equality_domaint::get_var_pairs \*******************************************************************/ -void equality_domaint::get_index_set(std::set &indices) +void equality_domaint::get_index_set(std::set &indices) { - for(unsigned i=0;i #include #include -#include - #include "domain.h" -class equality_domaint : public domaint +class equality_domaint:public domaint { - public: - typedef std::pair var_pairt; +public: + typedef std::pair var_pairt; typedef std::set var_pairst; typedef std::set index_sett; - equality_domaint(unsigned _domain_number, replace_mapt &_renaming_map, - const var_specst &var_specs, - const namespacet &ns) - : domaint(_domain_number,_renaming_map, ns) +equality_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const var_specst &var_specs, + const namespacet &ns): + domaint(_domain_number, _renaming_map, ns) { - make_template(var_specs,ns); + make_template(var_specs, ns); } - class equ_valuet : public valuet + class equ_valuet:public valuet { - public: - + public: union_find equs; index_sett disequs; }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -52,21 +61,29 @@ class equality_domaint : public domaint void set_equal(unsigned index, equ_valuet &value); void set_disequal(unsigned index, equ_valuet &value); - virtual void output_value(std::ostream &out, const valuet &value, + virtual void output_value( + std::ostream &out, + const valuet &value, const namespacet &ns) const; + virtual void output_domain(std::ostream &out, const namespacet &ns) const; - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); - void get_index_set(index_sett &indices); + void get_index_set(index_sett &indices); const var_pairt &get_var_pair(unsigned index); - protected: +protected: templatet templ; void make_template( const var_specst &var_specs, const namespacet &ns); + + bool adapt_types(exprt &v1, exprt &v2); }; -#endif +#endif // CPROVER_2LS_DOMAINS_EQUALITY_DOMAIN_H diff --git a/src/domains/fixed_point.cpp b/src/domains/fixed_point.cpp deleted file mode 100644 index 6ddb75f60..000000000 --- a/src/domains/fixed_point.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/*******************************************************************\ - -Module: Forward Least Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: fixed_pointt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::operator()() -{ - iteration_number=0; - - // Set up the state predicate, starting with 'false' - // (the empty set). - - state_predicate.state_vars=pre_state_vars; - state_predicate.make_false(); - - bool change; - - do - { - iteration_number++; - - #ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; - #endif - - change=iteration(); - } - while(change); - - #ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - output(std::cout); - #endif -} - -/*******************************************************************\ - -Function: fixed_pointt::iteration - - Inputs: - - Outputs: 'true' if there is a change in the state predicate - - Purpose: - -\*******************************************************************/ - -bool fixed_pointt::iteration() -{ - #if 0 - solvert solver(ns); - - // Feed transition relation into solver. - for(constraintst::const_iterator - it=transition_relation.begin(); - it!=transition_relation.end(); - it++) - solver << *it; - - // Feed current state predicate into solver. - state_predicate.set_to_true(solver); - - #ifdef DEBUG - std::cout << "Entry state:\n"; - output(std::cout); - #endif - - // solve - solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - // now get new post-state - predicatet post_state; - post_state.state_vars=post_state_vars; - - post_state.get(solver); - - #ifdef DEBUG - std::cout << "Post state:\n"; - post_state.output(std::cout); - #endif - - // Now 'OR' with previous state predicate. - // First rename post-state to pre-state. - post_state.rename(pre_state_vars); - - // Form disjunction of previous state predicate and the new one. - return state_predicate.disjunction(post_state); - #endif - - return false; -} - -/*******************************************************************\ - -Function: fixed_pointt::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::output(std::ostream &out) const -{ - state_predicate.output(out); -} diff --git a/src/domains/fixed_point.h b/src/domains/fixed_point.h deleted file mode 100644 index bdc4ec029..000000000 --- a/src/domains/fixed_point.h +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************************************\ - -Module: Forward Greatest Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_FIXED_POINT_H -#define DELTACHECK_FIXED_POINT_H - -#include "predicate.h" - -class fixed_pointt -{ -public: - explicit fixed_pointt(const namespacet &_ns):ns(_ns) - { - } - - typedef std::list constraintst; - constraintst transition_relation; - - predicatet::state_var_listt pre_state_vars, post_state_vars; - - // this is over pre_state_vars - predicatet state_predicate; - - void output(std::ostream &) const; - - unsigned iteration_number; - - void operator()(); - -protected: - const namespacet &ns; - - // fixed-point iteration - void initialize(); - bool iteration(); -}; - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, - const std::list &src) -{ - for(std::list::const_iterator - c_it=src.begin(); c_it!=src.end(); c_it++) - dest << *c_it; - return dest; -} - -#endif diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index c251326c8..f25f23cdf 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -1,13 +1,35 @@ +/*******************************************************************\ + +Module: Incremental Solver Interface + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include -#include #include #include #include "incremental_solver.h" -void incremental_solvert::new_context() +/*******************************************************************\ + +Function: incremental_solvert::new_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::new_context() { #ifdef NON_INCREMENTAL contexts.push_back(constraintst()); @@ -18,9 +40,11 @@ void incremental_solvert::new_context() #else - literalt activation_literal = solver->convert( - symbol_exprt("goto_symex::\\act$"+ - i2string(activation_literal_counter++), bool_typet())); + literalt activation_literal= + solver->convert( + symbol_exprt( + "goto_symex::\\act$"+ + i2string(activation_literal_counter++), bool_typet())); #ifdef DEBUG_OUTPUT debug() << "new context: " << activation_literal<< eom; @@ -30,12 +54,24 @@ void incremental_solvert::new_context() solver->set_assumptions(activation_literals); #if 0 - return !activation_literals.back(); //not to be used anymore + return !activation_literals.back(); // not to be used anymore #endif #endif } -void incremental_solvert::pop_context() +/*******************************************************************\ + +Function: incremental_solvert::pop_context + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::pop_context() { #ifdef NON_INCREMENTAL assert(!contexts.empty()); @@ -49,7 +85,7 @@ void incremental_solvert::pop_context() #else assert(!activation_literals.empty()); - literalt activation_literal = activation_literals.back(); + literalt activation_literal=activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA solver->set_to_false(literal_exprt(activation_literal)); @@ -65,16 +101,28 @@ void incremental_solvert::pop_context() #endif } -void incremental_solvert::make_context_permanent() +/*******************************************************************\ + +Function: incremental_solvert::make_context_permanent + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::make_context_permanent() { #ifdef NON_INCREMENTAL assert(contexts.size()>=2); - contextst::iterator c_it = contexts.end(); c_it--; c_it--; - c_it->insert(c_it->end(),contexts.back().begin(),contexts.back().end()); + contextst::iterator c_it=contexts.end(); c_it--; c_it--; + c_it->insert(c_it->end(), contexts.back().begin(), contexts.back().end()); contexts.pop_back(); #else assert(!activation_literals.empty()); - literalt activation_literal = activation_literals.back(); + literalt activation_literal=activation_literals.back(); activation_literals.pop_back(); #ifndef DEBUG_FORMULA solver->set_to_true(literal_exprt(activation_literal)); @@ -90,48 +138,44 @@ void incremental_solvert::make_context_permanent() #endif } -void incremental_solvert::debug_add_to_formula(const exprt &_expr, - exprt activation) +/*******************************************************************\ + +Function: incremental_solvert::debug_add_to_formula + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void incremental_solvert::debug_add_to_formula(const exprt &expr) { - if(_expr.id()!=ID_and) - { #ifdef NON_INCREMENTAL - // no debug mode for non-incremental yet - assert(0); + // no debug mode for non-incremental yet #else - exprt expr; - if(activation.is_nil()) - expr = _expr; - else - expr = or_exprt(_expr, activation); - literalt l = solver->convert(expr); - if(l.is_false()) - { -#ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": false = " << from_expr(ns,"",expr) <convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); + literalt l=solver->convert(expr); + if(l.is_false()) + { #ifdef DEBUG_OUTPUT - debug() << "literal " << dummy << ", " << !dummy << ": " - << from_expr(ns,"",expr) << eom; + debug() << "literal " << l << ": false=" << from_expr(ns, "", expr) <convert(symbol_exprt("goto_symex::\\dummy", bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); #ifdef DEBUG_OUTPUT - debug() << "literal " << l << ": " << from_expr(ns,"",expr) << eom; -#endif - formula.push_back(l); - formula_expr.push_back(expr); - } + debug() << "literal " << dummy << ", " << !dummy << ": " + << from_expr(ns, "", expr) << eom; #endif } - else + else if(!l.is_true()) { - forall_operands(it, _expr) - debug_add_to_formula(*it, activation); +#ifdef DEBUG_OUTPUT + debug() << "literal " << l << ": " << from_expr(ns, "", expr) << eom; +#endif + formula.push_back(l); } +#endif } diff --git a/src/domains/incremental_solver.h b/src/domains/incremental_solver.h index e30a2c1da..08f5b1ae2 100644 --- a/src/domains/incremental_solver.h +++ b/src/domains/incremental_solver.h @@ -6,8 +6,8 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_INCREMENTAL_SOLVER_H -#define CPROVER_INCREMENTAL_SOLVER_H +#ifndef CPROVER_2LS_DOMAINS_INCREMENTAL_SOLVER_H +#define CPROVER_2LS_DOMAINS_INCREMENTAL_SOLVER_H #include #include @@ -15,41 +15,41 @@ Author: Peter Schrammel #include #include #include -#include #include "domain.h" #include "util.h" -//#define NO_ARITH_REFINEMENT -//#define NON_INCREMENTAL // (experimental) +// #define DISPLAY_FORMULA +// #define NO_ARITH_REFINEMENT +// #define NON_INCREMENTAL // (experimental) -//#define DISPLAY_FORMULA -//#define DEBUG_FORMULA -//#define DEBUG_OUTPUT +// #define DISPLAY_FORMULA +// #define DEBUG_FORMULA +// #define DEBUG_OUTPUT -class incremental_solvert : public messaget +class incremental_solvert:public messaget { - public: typedef std::list constraintst; typedef std::list contextst; explicit incremental_solvert( - const namespacet &_ns, bool _arith_refinement=false) : + const namespacet &_ns, + bool _arith_refinement=false): sat_check(NULL), - solver(NULL), + solver(NULL), ns(_ns), activation_literal_counter(0), domain_number(0), arith_refinement(_arith_refinement), solver_calls(0) - { + { allocate_solvers(_arith_refinement); contexts.push_back(constraintst()); } virtual ~incremental_solvert() - { + { deallocate_solvers(); } @@ -69,62 +69,33 @@ class incremental_solvert : public messaget #ifdef NON_INCREMENTAL deallocate_solvers(); allocate_solvers(arith_refinement); - unsigned context_no = 0; - for(contextst::const_iterator c_it = contexts.begin(); - c_it != contexts.end(); c_it++, context_no++) + unsigned context_no=0; + for(const auto &context : contexts) { #ifdef DISPLAY_FORMULA std::cerr << "context: " << context_no << std::endl; #endif - for(incremental_solvert::constraintst::const_iterator it = c_it->begin(); - it != c_it->end(); it++) + for(const auto &constraint : context) { #ifdef DISPLAY_FORMULA - std::cerr << "actual add_to_solver: " << from_expr(ns,"",*it) << std::endl; + std::cerr << "actual add_to_solver: " + << from_expr(ns, "", constraint) << std::endl; #endif - *solver << *it; + *solver << constraint; } } #else #ifdef DEBUG_FORMULA - bvt whole_formula = formula; - whole_formula.insert(whole_formula.end(),activation_literals.begin(), - activation_literals.end()); + bvt whole_formula=formula; + whole_formula.insert( + whole_formula.end(), + activation_literals.begin(), + activation_literals.end()); solver->set_assumptions(whole_formula); #endif #endif -#if defined(DEBUG_FORMULA) || defined(DISPLAY_FORMULA) - decision_proceduret::resultt result = (*solver)(); -#endif -#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) - if(result==decision_proceduret::D_UNSATISFIABLE) - { - for(unsigned i=0; iis_in_conflict(formula[i])) - std::cout << "is_in_conflict: " - << from_expr(ns, "", formula_expr[i]) << std::endl; - } - } -#endif -#if (defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT)) || defined(DISPLAY_FORMULA) - if(result==decision_proceduret::D_SATISFIABLE) - { - std::set vars; - for(unsigned i=0; i::const_iterator it = vars.begin(); - it != vars.end(); ++it) - { - std::cout << "assignment: " << from_expr(ns, "", *it) << " = " - << from_expr(ns, "", solver->get(*it)) << std::endl; - } - } - return result; -#endif -#if !defined(DEBUG_FORMULA) && !defined(DISPLAY_FORMULA) - return (*solver)(); -#endif + + return (*solver)(); } exprt get(const exprt& expr) { return solver->get(expr); } @@ -135,75 +106,90 @@ class incremental_solvert : public messaget unsigned next_domain_number() { return domain_number++; } - static incremental_solvert *allocate(const namespacet &_ns, - bool arith_refinement=false) - { - return new incremental_solvert(_ns,arith_refinement); + static incremental_solvert *allocate( + const namespacet &_ns, + bool arith_refinement=false) + { + return new incremental_solvert(_ns, arith_refinement); } inline prop_convt & get_solver() { return *solver; } - propt* sat_check; - prop_convt* solver; + propt *sat_check; + prop_convt *solver; const namespacet &ns; void new_context(); void pop_context(); void make_context_permanent(); - //for debugging + // for debugging bvt formula; - exprt::operandst formula_expr; - void debug_add_to_formula(const exprt &expr, exprt activation=nil_exprt()); + void debug_add_to_formula(const exprt &expr); - //context assumption literals + // context assumption literals bvt activation_literals; - //non-incremental solving + // non-incremental solving contextst contexts; protected: unsigned activation_literal_counter; - unsigned domain_number; //ids for each domain instance to make symbols unique + unsigned domain_number; // ids for each domain instance to make symbols unique bool arith_refinement; - //statistics + // statistics unsigned solver_calls; void allocate_solvers(bool arith_refinement) { - sat_check = new satcheckt(); + sat_check=new satcheckt(); #if 0 - sat_check = new satcheck_minisat_no_simplifiert(); + sat_check=new satcheck_minisat_no_simplifiert(); #endif #ifdef NON_INCREMENTAL - solver = new bv_pointerst(ns,*sat_check); + solver=new bv_pointerst(ns, *sat_check); #else - solver = new bv_refinementt(ns,*sat_check); + solver=new bv_refinementt(ns, *sat_check); solver->set_all_frozen(); - ((bv_refinementt *)solver)->do_array_refinement = false; - ((bv_refinementt *)solver)->do_arithmetic_refinement = arith_refinement; + static_cast(solver)->do_array_refinement=false; + static_cast(solver)->do_arithmetic_refinement= + arith_refinement; #endif } void deallocate_solvers() { - if(solver!=NULL) delete solver; - if(sat_check!=NULL) delete sat_check; + if(solver!=NULL) + delete solver; + if(sat_check!=NULL) + delete sat_check; } }; -static inline incremental_solvert & operator << ( +/*******************************************************************\ + +Function: incremental_solvert::operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static inline incremental_solvert &operator<<( incremental_solvert &dest, const exprt &src) { #ifdef DISPLAY_FORMULA if(!dest.activation_literals.empty()) - std::cerr << "add_to_solver(" << !dest.activation_literals.back() << "): " - << from_expr(dest.ns,"",src) << std::endl; + std::cerr << "add_to_solver(" << !dest.activation_literals.back() << "): " + << from_expr(dest.ns, "", src) << std::endl; else - std::cerr << "add_to_solver: " << from_expr(dest.ns,"",src) << std::endl; - dest.formula_expr.push_back(src); + std::cerr << "add_to_solver: " + << from_expr(dest.ns, "", src) << std::endl; #endif #ifdef NON_INCREMENTAL @@ -211,33 +197,44 @@ static inline incremental_solvert & operator << ( #else #ifndef DEBUG_FORMULA if(!dest.activation_literals.empty()) - *dest.solver << or_exprt(src, - literal_exprt(!dest.activation_literals.back())); - else + *dest.solver << + or_exprt(src, literal_exprt(!dest.activation_literals.back())); + else *dest.solver << src; #else if(!dest.activation_literals.empty()) - dest.debug_add_to_formula(src, - literal_exprt(!dest.activation_literals.back())); - else + dest.debug_add_to_formula( + or_exprt(src, literal_exprt(!dest.activation_literals.back()))); + else dest.debug_add_to_formula(src); #endif #endif return dest; } -static inline incremental_solvert& operator << ( +/*******************************************************************\ + +Function: incremental_solvert::operator<< + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static inline incremental_solvert &operator<<( incremental_solvert &dest, const incremental_solvert::constraintst &src) { #ifdef NON_INCREMENTAL - dest.contexts.back().insert(dest.contexts.back().begin() - ,src.begin(),src.end()); + dest.contexts.back().insert( + dest.contexts.back().begin(), src.begin(), src.end()); #else - for(incremental_solvert::constraintst::const_iterator it = src.begin(); - it != src.end(); it++) + for(const auto &constraint : src) { - dest << *it; + dest << constraint; } #endif return dest; diff --git a/src/domains/lexlinrank_domain.cpp b/src/domains/lexlinrank_domain.cpp index 89bd7a8b8..0fc5aa5ea 100644 --- a/src/domains/lexlinrank_domain.cpp +++ b/src/domains/lexlinrank_domain.cpp @@ -1,7 +1,14 @@ -#include "lexlinrank_domain.h" -#include "util.h" +/*******************************************************************\ +Module: Lexicographic linear ranking function domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "lexlinrank_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define EXTEND_TYPES @@ -17,123 +27,192 @@ #define COEFF_C_SIZE 10 #define MAX_REFINEMENT 2 +/*******************************************************************\ + +Function: lexlinrank_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void lexlinrank_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row=MAX_REFINEMENT) return false; + if(refinement_level>=MAX_REFINEMENT) + return false; refinement_level++; return true; } +/*******************************************************************\ + +Function: lexlinrank_domaint::reset_refinements + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void lexlinrank_domaint::reset_refinements() { - refinement_level = 0; + refinement_level=0; } -exprt lexlinrank_domaint::get_not_constraints(const lexlinrank_domaint::templ_valuet &value, - exprt::operandst &cond_exprs, - std::vector &value_exprs) +/*******************************************************************\ + +Function: lexlinrank_domaint::get_not_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt lexlinrank_domaint::get_not_constraints( + const lexlinrank_domaint::templ_valuet &value, + exprt::operandst &cond_exprs, + std::vector &value_exprs) { cond_exprs.resize(value.size()); value_exprs.resize(value.size()); - for(unsigned row = 0; row true) - cond_exprs[row] = false_exprt(); + // !(g=> true) + cond_exprs[row]=false_exprt(); } else if(is_row_value_false(value[row])) { - // !(g => false) - cond_exprs[row] = - and_exprt(templ[row].pre_guard, templ[row].post_guard); + // !(g=> false) + cond_exprs[row]= + and_exprt(templ[row].pre_guard, templ[row].post_guard); } else { exprt::operandst elmts; elmts.reserve(value[row].size()); - for(unsigned elm=0; elm=1); exprt::operandst c; - c.reserve(1 + value[row].size() - (elm+1)); + c.reserve(1+value[row].size()-(elm+1)); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(value[row][elm].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum= + mult_exprt( + value[row][elm].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(value[row][elm].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(value[row][elm].c[0],templ[row].expr[0].second); + exprt sum_pre= + mult_exprt(value[row][elm].c[0], templ[row].expr[0].first); + exprt sum_post= + mult_exprt(value[row][elm].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < value[row][elm].c.size(); ++i) + for(std::size_t i=1; i=1); exprt::operandst c; - c.reserve(1 + symb_values.size() - (elm+1)); + c.reserve(1+symb_values.size()-(elm+1)); - symb_values[elm].c[0] = symbol_exprt(SYMB_COEFF_VAR+std::string("c!")+ - i2string(row)+"$"+i2string(elm)+"$0", - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + symb_values[elm].c[0]=symbol_exprt( + SYMB_COEFF_VAR+std::string("c!")+ + i2string(row)+"$"+i2string(elm)+"$0", + signedbv_typet(COEFF_C_SIZE)); // coefficients are signed integers #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(symb_values[elm].c[0], - minus_exprt(values[0].first, values[0].second)); + exprt sum=mult_exprt( + symb_values[elm].c[0], + minus_exprt(values[0].first, values[0].second)); #else - exprt sum_pre = mult_exprt(symb_values[elm].c[0],values[0].first); - exprt sum_post = mult_exprt(symb_values[elm].c[0],values[0].second); + exprt sum_pre=mult_exprt(symb_values[elm].c[0], values[0].first); + exprt sum_post=mult_exprt(symb_values[elm].c[0], values[0].second); #endif -// symb_values[elm].d = -// symbol_exprt(SYMB_BOUND_VAR+std::string("d!")+i2string(row)+std::string("$")+i2string(elm), -// signedbv_typet(COEFF_D_SIZE)); - - for(unsigned i = 1; i < values.size(); ++i) + for(std::size_t i=1; i(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl; + out << "(RANK) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " ]===> " << std::endl; break; default: assert(false); } - for(unsigned elm=0; elm0) out << " + "; - out << from_expr(ns,"",v[row][elm].c[i]) << " * " - << from_expr(ns,"",templ_row.expr[i].first); + if(i>0) + out << "+"; + out << from_expr(ns, "", v[row][elm].c[i]) << " * " + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } } -void lexlinrank_domaint::output_domain(std::ostream &out, const namespacet &ns) const +/*******************************************************************\ + +Function: lexlinrank_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void lexlinrank_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " + out << "(RANK) (" << from_expr(ns, "", templ_row.pre_guard) + << ") && (" << from_expr(ns, "", templ_row.post_guard) << ")===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; + if(i>0) + out << "+"; out << "c!" << row << "$" << i << " * " - << from_expr(ns,"",templ_row.expr[i].first); + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void lexlinrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) +/*******************************************************************\ + +Function: lexlinrank_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void lexlinrank_domaint::project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) { - //don't do any projection - const templ_valuet &v = static_cast(value); + // don't do any projection + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; // c is the conjunction of all rows c.reserve(templ.size()); - for(unsigned row = 0; row false) + // (g=> false) c.push_back(implies_exprt( and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); } else if(is_row_value_true(v[row])) { - //(g => true) + // (g=> true) c.push_back(implies_exprt( and_exprt(templ[row].pre_guard, templ[row].post_guard), true_exprt())); @@ -434,50 +626,63 @@ void lexlinrank_domaint::project_on_vars(valuet &value, const var_sett &vars, ex { exprt::operandst d; // d is the disjunction of lexicographic elements d.reserve(v[row].size()); - for(unsigned elm=0; elm=1); - exprt::operandst con; // con is the constraints for a single element of the lexicography - con.reserve(1 + v[row].size() - (elm+1)); + // con is the constraints for a single element of the lexicography + exprt::operandst con; + con.reserve(1+v[row].size()-(elm+1)); - exprt sum = mult_exprt(v[row][elm].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + v[row][elm].c[0], + minus_exprt( + templ[row].expr[0].first, + templ[row].expr[0].second)); - for(unsigned i = 1; i < v[row][elm].c.size(); ++i) + for(std::size_t i=1; ikind==LOOP) + if(v.kind==LOOP) { - has_loop = true; + has_loop=true; break; } } - if(!has_loop) return; + if(!has_loop) + return; templ.reserve(templ.size()+1); templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.kind = LOOP; + template_rowt &templ_row=templ.back(); + templ_row.kind=LOOP; exprt::operandst preg; exprt::operandst postg; - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - if(v->kind!=LOOP) continue; - preg.push_back(v->pre_guard); - postg.push_back(v->post_guard); - exprt vpost = v->var; //copy + if(v.kind!=LOOP) + continue; + preg.push_back(v.pre_guard); + postg.push_back(v.post_guard); + exprt vpost=v.var; // copy rename(vpost); - templ_row.expr.push_back(std::pair(v->var,vpost)); + templ_row.expr.push_back(std::pair(v.var, vpost)); } - templ_row.pre_guard = conjunction(preg); - templ_row.post_guard = conjunction(postg); + templ_row.pre_guard=conjunction(preg); + templ_row.post_guard=conjunction(postg); } /*******************************************************************\ @@ -556,14 +762,28 @@ Function: lexlinrank_domaint::is_row_value_false \*******************************************************************/ -bool lexlinrank_domaint::is_row_value_false(const row_valuet & row_value) const +bool lexlinrank_domaint::is_row_value_false( + const row_valuet & row_value) const { assert(row_value.size()>=1); assert(row_value[0].c.size()>=1); return row_value[0].c[0].get(ID_value)==ID_false; } -bool lexlinrank_domaint::is_row_element_value_false(const row_value_elementt & row_value_element) const +/*******************************************************************\ + +Function: lexlinrank_domaint::is_row_element_value_false + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool lexlinrank_domaint::is_row_element_value_false( + const row_value_elementt & row_value_element) const { assert(false); assert(row_value_element.c.size()>=1); @@ -589,7 +809,20 @@ bool lexlinrank_domaint::is_row_value_true(const row_valuet & row_value) const return row_value[0].c[0].get(ID_value)==ID_true; } -bool lexlinrank_domaint::is_row_element_value_true(const row_value_elementt & row_value_element) const +/*******************************************************************\ + +Function: lexlinrank_domaint::is_row_element_value_true + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool lexlinrank_domaint::is_row_element_value_true( + const row_value_elementt & row_value_element) const { assert(false); assert(row_value_element.c.size()>=1); @@ -608,12 +841,14 @@ Function: lexlinrank_domaint::add_element \*******************************************************************/ -void lexlinrank_domaint::add_element(const rowt &row, templ_valuet &value) -{ +void lexlinrank_domaint::add_element( + const rowt &row, + templ_valuet &value) +{ value[row].push_back(row_value_elementt()); for(unsigned i=0; i +#endif #include #include #include #include #include -#include -class lexlinrank_domaint : public domaint +#include "domain.h" + +class lexlinrank_domaint:public domaint { public: typedef unsigned rowt; - typedef std::vector > pre_post_valuest; + typedef std::vector > pre_post_valuest; typedef pre_post_valuest row_exprt; typedef struct { @@ -23,11 +34,11 @@ class lexlinrank_domaint : public domaint typedef std::vector row_valuet; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -37,14 +48,14 @@ class lexlinrank_domaint : public domaint typedef std::vector templatet; - - - lexlinrank_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number,_renaming_map, _ns), + lexlinrank_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns), refinement_level(0) - {} + { + } // initialize value virtual void initialize(valuet &value); @@ -53,34 +64,47 @@ class lexlinrank_domaint : public domaint virtual void reset_refinements(); // value -> constraints - exprt get_not_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs,// identical to before - std::vector &value_exprs); // (x, x') - exprt get_row_symb_constraint(row_valuet &symb_values, // contains vars c and d - const rowt &row, - const pre_post_valuest &values, - exprt &refinement_constraint - ); + exprt get_not_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, // identical to before + std::vector &value_exprs); // (x, x') + exprt get_row_symb_constraint( + row_valuet &symb_values, // contains vars c and d + const rowt &row, + const pre_post_valuest &values, + exprt &refinement_constraint); void add_element(const rowt &row, templ_valuet &value); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); void set_row_value_to_true(const rowt &row, templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; - - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const; + + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size() {return templ.size();} // generating templates - void add_template(const var_specst &var_specs, - const namespacet &ns); + void add_template( + const var_specst &var_specs, + const namespacet &ns); protected: templatet templ; @@ -88,11 +112,10 @@ class lexlinrank_domaint : public domaint bool is_row_value_false(const row_valuet & row_value) const; bool is_row_value_true(const row_valuet & row_value) const; - bool is_row_element_value_false(const row_value_elementt & row_value_element) const; - bool is_row_element_value_true(const row_value_elementt & row_value_element) const; - - + bool is_row_element_value_false( + const row_value_elementt & row_value_element) const; + bool is_row_element_value_true( + const row_value_elementt & row_value_element) const; }; - -#endif +#endif // CPROVER_2LS_DOMAINS_LEXLINRANK_DOMAIN_H diff --git a/src/domains/lexlinrank_solver_enumeration.cpp b/src/domains/lexlinrank_solver_enumeration.cpp index d3490c63b..bd87b6af5 100644 --- a/src/domains/lexlinrank_solver_enumeration.cpp +++ b/src/domains/lexlinrank_solver_enumeration.cpp @@ -1,46 +1,76 @@ +/*******************************************************************\ + +Module: Enumeration-based solver for lexicographic linear ranking + functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include + #include "lexlinrank_solver_enumeration.h" #include "util.h" -//#define DEBUG_OUTER_FORMULA -//#define DEBUG_INNER_FORMULA +// #define DEBUG_OUTER_FORMULA +// #define DEBUG_INNER_FORMULA + +/*******************************************************************\ + +Function: lexlinrank_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ -lexlinrank_solver_enumerationt::progresst lexlinrank_solver_enumerationt::iterate(invariantt &_rank) +bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) { - lexlinrank_domaint::templ_valuet &rank = + lexlinrank_domaint::templ_valuet &rank= static_cast(_rank); - progresst progress = CONVERGED; + bool improved=false; static std::vector number_elements_per_row; number_elements_per_row.resize(rank.size()); - debug() << "(RANK) no rows = " << rank.size() << eom; + debug() << "(RANK) no rows=" << rank.size() << eom; solver.new_context(); - //choose round to even rounding mode for template computations - // not clear what its implications on soundness and termination of the synthesis are - exprt rounding_mode = symbol_exprt(CPROVER_PREFIX "rounding_mode",signedbv_typet(32)); - solver << equal_exprt(rounding_mode,from_integer(mp_integer(0),signedbv_typet(32))); + // choose round to even rounding mode for template computations + // not clear what its implications on soundness and + // termination of the synthesis are + exprt rounding_mode= + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); - //handles on values to retrieve from model + // handles on values to retrieve from model std::vector rank_value_exprs; exprt::operandst rank_cond_exprs; bvt rank_cond_literals; - exprt rank_expr = lexlinrank_domain.get_not_constraints(rank, - rank_cond_exprs, rank_value_exprs); + exprt rank_expr= + lexlinrank_domain.get_not_constraints( + rank, + rank_cond_exprs, + rank_value_exprs); solver << rank_expr; rank_cond_literals.resize(rank_cond_exprs.size()); - for(unsigned i = 0; i < rank_cond_exprs.size(); i++) - { - rank_cond_literals[i] = solver.solver->convert(rank_cond_exprs[i]); + for(std::size_t i=0; iconvert(rank_cond_exprs[i]); } debug() << "Outer solve(): "; @@ -48,193 +78,205 @@ lexlinrank_solver_enumerationt::progresst lexlinrank_solver_enumerationt::iterat #if 0 // check whether the literal is UNSAT to start with satcheck_minisat_no_simplifiert test_satcheck; - bv_pointerst test_solver = bv_pointerst(ns, test_satcheck); + bv_pointerst test_solver=bv_pointerst(ns, test_satcheck); test_solver << rank_expr; - if(test_solver() == decision_proceduret::D_SATISFIABLE) + if(test_solver()==decision_proceduret::D_SATISFIABLE) debug() << "test solver: SAT" << eom; else debug() << "test solver: UNSAT" << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { debug() << "Outer solver: SAT" << eom; - for(unsigned row = 0; row < rank_cond_literals.size(); row++) + for(std::size_t row=0; rowl_get(rank_cond_literals[row]).is_true()) + + if(solver.solver->l_get(rank_cond_literals[row]).is_true()) { - for(lexlinrank_domaint::pre_post_valuest::iterator it = - rank_value_exprs[row].begin(); - it != rank_value_exprs[row].end(); ++it) - { - // model for x_i - exprt value = solver.solver->get(it->first); - debug() << "Row " << row << " Value for " - << from_expr(ns,"",it->first) - << ": " << from_expr(ns,"",value) << eom; - // model for x'_i - exprt post_value = solver.solver->get(it->second); - debug() << "Row " << row << " Value for " - << from_expr(ns,"",it->second) - << ": " << from_expr(ns,"",post_value) << eom; - // record all the values - values.push_back(std::make_pair(value, post_value)); - } - - lexlinrank_domaint::row_valuet symb_values; + for(auto &row_expr : rank_value_exprs[row]) + { + // model for x_i + exprt value=solver.solver->get(row_expr.first); + debug() << "Row " << row << " Value for " + << from_expr(ns, "", row_expr.first) + << ": " << from_expr(ns, "", value) << eom; + // model for x'_i + exprt post_value=solver.solver->get(row_expr.second); + debug() << "Row " << row << " Value for " + << from_expr(ns, "", row_expr.second) + << ": " << from_expr(ns, "", post_value) << eom; + // record all the values + values.push_back(std::make_pair(value, post_value)); + } + + lexlinrank_domaint::row_valuet symb_values; symb_values.resize(rank[row].size()); - //debug() << "elements: " << rank[row].size() << eom; + // debug() << "elements: " << rank[row].size() << eom; - exprt constraint; - exprt refinement_constraint; + exprt constraint; + exprt refinement_constraint; - // generate the new constraint - constraint = lexlinrank_domain.get_row_symb_constraint(symb_values, - row, values, refinement_constraint); + // generate the new constraint + constraint=lexlinrank_domain.get_row_symb_constraint( + symb_values, + row, values, + refinement_constraint); - simplify_expr(constraint, ns); - debug() << "Constraint sent to the inner solver: " << row - << " constraint "; - pretty_print_termination_argument(debug(), ns, constraint); - debug() << eom; + simplify_expr(constraint, ns); + debug() << "Constraint sent to the inner solver: " << row + << " constraint "; + pretty_print_termination_argument(debug(), ns, constraint); + debug() << eom; - *inner_solver << constraint; + *inner_solver << constraint; - //set rounding mode - *inner_solver << equal_exprt(rounding_mode, - from_integer(mp_integer(0),signedbv_typet(32))); + // set rounding mode + *inner_solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); - //refinement - if(!refinement_constraint.is_true()) - { - inner_solver->new_context(); - *inner_solver << refinement_constraint; - } + // refinement + if(!refinement_constraint.is_true()) + { + inner_solver->new_context(); + *inner_solver << refinement_constraint; + } - debug() << "Inner solve()" << eom; - // solve + debug() << "Inner solve()" << eom; + // solve solver_calls++; - bool inner_solver_result = (*inner_solver)(); - if(inner_solver_result == decision_proceduret::D_SATISFIABLE && - number_inner_iterations < max_inner_iterations) - { - number_inner_iterations++; - - debug() << "Inner solver: SAT and the max number of iterations was not reached " << eom; - debug() << "Inner solver: Current number of iterations = " << number_inner_iterations << eom; - debug() << "Inner solver: Current number of components for row " << row << " is " << number_elements_per_row[row]+1 << eom; - - // new_row_values will contain the new values for c and d - lexlinrank_domaint::row_valuet new_row_values; + bool inner_solver_result=(*inner_solver)(); + if(inner_solver_result==decision_proceduret::D_SATISFIABLE && + number_inner_iterations c = symb_values[constraint_no].c; - - // get the model for all c - for(std::vector::iterator it = c.begin(); - it != c.end(); ++it) - { - exprt v = inner_solver->solver->get(*it); - new_row_values[constraint_no].c.push_back(v); - debug() << "Inner Solver: row " << row - << " ==> c value for "; - pretty_print_termination_argument(debug(), ns, *it); - debug() << ": "; - pretty_print_termination_argument(debug(), ns, v); - debug() << eom; - } - } - - progress = CHANGED; - - // update the current template - lexlinrank_domain.set_row_value(row, new_row_values, rank); - - if(!refinement_constraint.is_true()) inner_solver->pop_context(); - } - else - { - if(inner_solver_result == decision_proceduret::D_UNSATISFIABLE) - debug() << "Inner solver: UNSAT" << eom; - else - debug() << "Inner solver: reached max number of iterations" << eom; - - debug() << "Inner solver: number of iterations = " << number_inner_iterations << eom; + for(std::size_t constraint_no=0; + constraint_no c=symb_values[constraint_no].c; + + // get the model for all c + for(auto &e : c) + { + exprt v=inner_solver->solver->get(e); + new_row_values[constraint_no].c.push_back(v); + debug() << "Inner Solver: row " << row + << "==> c value for "; + pretty_print_termination_argument(debug(), ns, e); + debug() << ": "; + pretty_print_termination_argument(debug(), ns, v); + debug() << eom; + } + } + + improved=true; + + // update the current template + lexlinrank_domain.set_row_value(row, new_row_values, rank); + + if(!refinement_constraint.is_true()) + inner_solver->pop_context(); + } + else + { + if(inner_solver_result==decision_proceduret::D_UNSATISFIABLE) + debug() << "Inner solver: UNSAT" << eom; + else + debug() << "Inner solver: reached max number of iterations" << eom; + + debug() << "Inner solver: number of iterations=" + << number_inner_iterations << eom; #ifdef DEBUG_INNER_FORMULA - for(unsigned i=0; iformula.size(); i++) - { - if(inner_solver->solver->is_in_conflict(inner_solver->formula[i])) - debug() << "is_in_conflict: " << inner_solver->formula[i] << eom; - else - debug() << "not_in_conflict: " << inner_solver->formula[i] << eom; - } -#endif - - if(lexlinrank_domain.refine()) - { - debug() << "refining..." << eom; - progress = CHANGED; //refinement possible - - if(!refinement_constraint.is_true()) inner_solver->pop_context(); - } - else - { - if(number_elements_per_row[row] == max_elements-1) - { - debug() << "Reached the max no of lexicographic components and no ranking function was found" << eom; - // no ranking function for the current template - lexlinrank_domain.set_row_value_to_true(row, rank); - lexlinrank_domain.reset_refinements(); - } - else - { - number_elements_per_row[row]++; - debug() << "Inner solver: increasing the number of lexicographic components for row " << row << " to " << number_elements_per_row[row] + 1 << eom; - // reset the inner solver - debug() << "Reset the inner solver " << eom; - delete inner_solver; - inner_solver = incremental_solvert::allocate(ns); + for(std::size_t i=0; iformula.size(); i++) + { + if(inner_solver->solver->is_in_conflict(inner_solver->formula[i])) + debug() << "is_in_conflict: " << inner_solver->formula[i] << eom; + else + debug() << "not_in_conflict: " << inner_solver->formula[i] << eom; + } +#endif + + if(lexlinrank_domain.refine()) + { + debug() << "refining..." << eom; + improved=true; // refinement possible + + if(!refinement_constraint.is_true()) + inner_solver->pop_context(); + } + else + { + if(number_elements_per_row[row]==max_elements-1) + { + debug() << "Reached the max no of lexicographic components " + << "and no ranking function was found" << eom; + // no ranking function for the current template + lexlinrank_domain.set_row_value_to_true(row, rank); + lexlinrank_domain.reset_refinements(); + } + else + { + number_elements_per_row[row]++; + debug() << "Inner solver: increasing the number " + << "of lexicographic components for row " << row + << " to " << number_elements_per_row[row]+1 << eom; + // reset the inner solver + debug() << "Reset the inner solver " << eom; + delete inner_solver; + inner_solver=incremental_solvert::allocate(ns); solver_instances++; - lexlinrank_domain.reset_refinements(); - - lexlinrank_domain.add_element(row, rank); - number_inner_iterations = 0; - debug() << "Inner solver: the number of inner iterations for row " << row << " was reset to " << number_inner_iterations << eom; - progress = CHANGED; - } - } - } + lexlinrank_domain.reset_refinements(); + + lexlinrank_domain.add_element(row, rank); + number_inner_iterations=0; + debug() << "Inner solver: " + << "the number of inner iterations for row " << row + << " was reset to " << number_inner_iterations << eom; + improved=true; + } + } + } } } - } - else + else { debug() << "Outer solver: UNSAT!!" << eom; lexlinrank_domain.reset_refinements(); + #ifdef DEBUG_OUTER_FORMULA - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; + debug() << "is_in_conflict: " << solver.formula[i] << eom; else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + debug() << "not_in_conflict: " << solver.formula[i] << eom; } -#endif - - +#endif } solver.pop_context(); - return progress; + return improved; } diff --git a/src/domains/lexlinrank_solver_enumeration.h b/src/domains/lexlinrank_solver_enumeration.h index 270dd1890..23744aa51 100644 --- a/src/domains/lexlinrank_solver_enumeration.h +++ b/src/domains/lexlinrank_solver_enumeration.h @@ -1,5 +1,14 @@ -#ifndef CPROVER_LEXLINRANK_SOLVER_ENUMERATION_H -#define CPROVER_LEXLINRANK_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Enumeration-based solver for lexicographic linear ranking + functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_LEXLINRANK_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_LEXLINRANK_SOLVER_ENUMERATION_H #include "strategy_solver_base.h" #include "../domains/incremental_solver.h" @@ -8,27 +17,26 @@ #include -class lexlinrank_solver_enumerationt : public strategy_solver_baset +class lexlinrank_solver_enumerationt:public strategy_solver_baset { public: explicit lexlinrank_solver_enumerationt( lexlinrank_domaint &_lexlinrank_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, const namespacet &_ns, unsigned _max_elements, // lexicographic components - unsigned _max_inner_iterations - ) : - strategy_solver_baset(_solver, literalt(), _ns), - lexlinrank_domain(_lexlinrank_domain), + unsigned _max_inner_iterations): + strategy_solver_baset(_solver, literalt(), _ns), + lexlinrank_domain(_lexlinrank_domain), max_elements(_max_elements), max_inner_iterations(_max_inner_iterations), number_inner_iterations(0) { - inner_solver = incremental_solvert::allocate(_ns); + inner_solver=incremental_solvert::allocate(_ns); solver_instances++; } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); protected: lexlinrank_domaint &lexlinrank_domain; @@ -37,8 +45,7 @@ class lexlinrank_solver_enumerationt : public strategy_solver_baset // the "inner" solver const unsigned max_inner_iterations; incremental_solvert *inner_solver; - unsigned number_inner_iterations; - + unsigned number_inner_iterations; }; #endif diff --git a/src/domains/linrank_domain.cpp b/src/domains/linrank_domain.cpp index 043ca50ef..064ce3859 100644 --- a/src/domains/linrank_domain.cpp +++ b/src/domains/linrank_domain.cpp @@ -1,7 +1,14 @@ -#include "linrank_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Linear ranking function domain + +Author: Peter Schrammel +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "linrank_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define EXTEND_TYPES @@ -17,24 +27,61 @@ #define COEFF_C_SIZE 10 #define MAX_REFINEMENT 2 +/*******************************************************************\ + +Function: linrank_domaint::initialize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void linrank_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row=MAX_REFINEMENT) return false; - refinement_level++; +{ + if(refinement_level>=MAX_REFINEMENT) + return false; + refinement_level++; return true; } +/*******************************************************************\ + +Function: linrank_domaint::get_not_constraints + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + exprt linrank_domaint::get_not_constraints( const linrank_domaint::templ_valuet &value, exprt::operandst &cond_exprs, @@ -44,21 +91,23 @@ exprt linrank_domaint::get_not_constraints( cond_exprs.resize(value.size()); value_exprs.resize(value.size()); - for(unsigned row = 0; row true) - cond_exprs[row] = false_exprt(); + // !(g=> true) + cond_exprs[row]=false_exprt(); } else if(is_row_value_false(value[row])) { - // !(g => false) - cond_exprs[row] = - and_exprt(templ[row].pre_guard, templ[row].post_guard); + // !(g=> false) + cond_exprs[row]= + and_exprt(templ[row].pre_guard, templ[row].post_guard); } else { @@ -66,26 +115,28 @@ exprt linrank_domaint::get_not_constraints( assert(value[row].c.size()>=1); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(value[row].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + value[row].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(value[row].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(value[row].c[0],templ[row].expr[0].second); + exprt sum_pre=mult_exprt(value[row].c[0], templ[row].expr[0].first); + exprt sum_post=mult_exprt(value[row].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < value[row].c.size(); ++i) + for(unsigned i=1; i=1); - symb_values.c[0] = symbol_exprt(SYMB_COEFF_VAR+std::string("c!")+ - i2string(row)+"$0", - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + symb_values.c[0]=symbol_exprt( + SYMB_COEFF_VAR+std::string("c!")+i2string(row)+"$0", + signedbv_typet(COEFF_C_SIZE)); // coefficients are signed integers #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(symb_values.c[0], - minus_exprt(values[0].first, - values[0].second)); + exprt sum=mult_exprt( + symb_values.c[0], + minus_exprt(values[0].first, values[0].second)); #else - exprt sum_pre = mult_exprt(symb_values.c[0],values[0].first); - exprt sum_post = mult_exprt(symb_values.c[0],values[0].second); + exprt sum_pre=mult_exprt(symb_values.c[0], values[0].first); + exprt sum_post=mult_exprt(symb_values.c[0], values[0].second); #endif - for(unsigned i = 1; i < symb_values.c.size(); ++i) - { - symb_values.c[i] = symbol_exprt( - SYMB_COEFF_VAR+std::string("c!")+i2string(row)+"$"+i2string(i), - signedbv_typet(COEFF_C_SIZE)); //coefficients are signed integers + for(unsigned i=1; i(value); - for(unsigned row = 0; row(value); + for(unsigned row=0; row " << std::endl << " "; + out << "(RANK) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) + << " ]===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; - out << from_expr(ns,"",v[row].c[i]) << " * " - << from_expr(ns,"",templ_row.expr[i].first); + if(i>0) + out << "+"; + out << from_expr(ns, "", v[row].c[i]) << " * " + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void linrank_domaint::output_domain(std::ostream &out, const namespacet &ns) const +/*******************************************************************\ + +Function: linrank_domaint::output_domain + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void linrank_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " + out << "(RANK) (" << from_expr(ns, "", templ_row.pre_guard) + << ") && (" << from_expr(ns, "", templ_row.post_guard) << ")===> " << std::endl << " "; break; default: assert(false); } - for(unsigned i = 0; i0) out << " + "; + if(i>0) + out << "+"; out << "c!" << row << "$" << i << " * " - << from_expr(ns,"",templ_row.expr[i].first); + << from_expr(ns, "", templ_row.expr[i].first); } out << std::endl; } } -void linrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt &result) +/*******************************************************************\ + +Function: linrank_domaint::project_on_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void linrank_domaint::project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result) { - //don't do any projection - const templ_valuet &v = static_cast(value); + // don't do any projection + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; c.reserve(templ.size()); - for(unsigned row = 0; row true) + // (g=> true) c.push_back(true_exprt()); } else if(is_row_value_false(v[row])) { - // (g => false) - c.push_back(implies_exprt(and_exprt(templ[row].pre_guard, templ[row].post_guard),false_exprt())); + // (g=> false) + c.push_back(implies_exprt( + and_exprt(templ[row].pre_guard, templ[row].post_guard), false_exprt())); } else { @@ -309,26 +466,28 @@ void linrank_domaint::project_on_vars(valuet &value, const var_sett &vars, exprt assert(v[row].c.size()>=1); #ifdef DIFFERENCE_ENCODING - exprt sum = mult_exprt(v[row].c[0], - minus_exprt(templ[row].expr[0].first, - templ[row].expr[0].second)); + exprt sum=mult_exprt( + v[row].c[0], + minus_exprt(templ[row].expr[0].first, templ[row].expr[0].second)); #else - exprt sum_pre = mult_exprt(v[row].c[0],templ[row].expr[0].first); - exprt sum_post = mult_exprt(v[row].c[0],templ[row].expr[0].second); + exprt sum_pre=mult_exprt(v[row].c[0], templ[row].expr[0].first); + exprt sum_post=mult_exprt(v[row].c[0], templ[row].expr[0].second); #endif - for(unsigned i = 1; i < v[row].c.size(); ++i) + for(unsigned i=1; ikind==LOOP) { - has_loop = true; + has_loop=true; break; } } - if(!has_loop) return; + if(!has_loop) + return; templ.reserve(templ.size()+1); templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.kind = LOOP; + template_rowt &templ_row=templ.back(); + templ_row.kind=LOOP; exprt::operandst preg; exprt::operandst postg; - for(var_specst::const_iterator v = var_specs.begin(); + for(var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind!=LOOP) continue; + if(v->kind!=LOOP) + continue; preg.push_back(v->pre_guard); postg.push_back(v->post_guard); - exprt vpost = v->var; //copy + exprt vpost=v->var; // copy rename(vpost); - templ_row.expr.push_back(std::pair(v->var,vpost)); + templ_row.expr.push_back(std::pair(v->var, vpost)); } - templ_row.pre_guard = conjunction(preg); - templ_row.post_guard = conjunction(postg); + templ_row.pre_guard=conjunction(preg); + templ_row.post_guard=conjunction(postg); } /*******************************************************************\ diff --git a/src/domains/linrank_domain.h b/src/domains/linrank_domain.h index 6be96436f..4d1383098 100644 --- a/src/domains/linrank_domain.h +++ b/src/domains/linrank_domain.h @@ -1,31 +1,42 @@ -#ifndef CPROVER_LINRANK_DOMAIN_H -#define CPROVER_LINRANK_DOMAIN_H +/*******************************************************************\ -#include "domain.h" +Module: Linear ranking function domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H +#define CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H + +#ifdef DEBUG +#include +#endif #include #include #include #include #include -#include -class linrank_domaint : public domaint +#include "domain.h" + +class linrank_domaint:public domaint { public: typedef unsigned rowt; - typedef std::vector > pre_post_valuest; + typedef std::vector > pre_post_valuest; typedef pre_post_valuest row_exprt; typedef struct { std::vector c; } row_valuet; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -35,12 +46,14 @@ class linrank_domaint : public domaint typedef std::vector templatet; - linrank_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number, _renaming_map, _ns), - refinement_level(0) - {} + linrank_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns), + refinement_level(0) + { + } // initialize value virtual void initialize(valuet &value); @@ -48,33 +61,45 @@ class linrank_domaint : public domaint virtual bool refine(); // value -> constraints - exprt get_not_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs,// identical to before - std::vector &value_exprs); // (x, x') - exprt get_row_symb_constraint(row_valuet &symb_values, // contains vars c - const rowt &row, - const pre_post_valuest &values, - exprt &refinement_constraint - ); - + exprt get_not_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, // identical to before + std::vector &value_exprs); // (x, x') + exprt get_row_symb_constraint( + row_valuet &symb_values, // contains vars c + const rowt &row, + const pre_post_valuest &values, + exprt &refinement_constraint); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); void set_row_value_to_true(const rowt &row, templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; - - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); - - unsigned template_size() {return templ.size();} + virtual void output_value( + std::ostream &out, + const valuet &value, + const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, + const namespacet &ns) const; + + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); + + unsigned template_size() { return templ.size(); } // generating templates - void add_template(const var_specst &var_specs, - const namespacet &ns); + void add_template( + const var_specst &var_specs, + const namespacet &ns); protected: templatet templ; @@ -82,9 +107,6 @@ class linrank_domaint : public domaint bool is_row_value_false(const row_valuet & row_value) const; bool is_row_value_true(const row_valuet & row_value) const; - - }; - -#endif +#endif // CPROVER_2LS_DOMAINS_LINRANK_DOMAIN_H diff --git a/src/domains/predabs_domain.cpp b/src/domains/predabs_domain.cpp index ea898d5c0..ec05e41ac 100644 --- a/src/domains/predabs_domain.cpp +++ b/src/domains/predabs_domain.cpp @@ -1,7 +1,14 @@ -#include "predabs_domain.h" -#include "util.h" +/*******************************************************************\ + +Module: Predicate abstraction domain + +Author: Peter Schrammel + +\*******************************************************************/ +#ifdef DEBUG #include +#endif #include #include @@ -9,6 +16,9 @@ #include #include +#include "predabs_domain.h" +#include "util.h" + #define SYMB_COEFF_VAR "symb_coeff#" #define COMPLEXITY_COUNTER_PREFIX "__CPROVER_CPLX_CNT_" @@ -26,51 +36,80 @@ Function: predabs_domaint::initialize void predabs_domaint::initialize(valuet &value) { - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row (row_value => row_expr) + Purpose: pre_guard==> (row_value=> row_expr) \*******************************************************************/ -exprt predabs_domaint::get_row_constraint(const rowt &row, +exprt predabs_domaint::get_row_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_pre_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_pre_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_pre_constraint(row,value[row]); + return get_row_pre_constraint(row, value[row]); } /*******************************************************************\ @@ -81,27 +120,44 @@ Function: predabs_domaint::get_row_post_constraint Outputs: - Purpose: post_guard => (row_value => row_expr) + Purpose: post_guard=> (row_value=> row_expr) \*******************************************************************/ -exprt predabs_domaint::get_row_post_constraint(const rowt &row, +exprt predabs_domaint::get_row_post_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_value=> row_expr) + +\*******************************************************************/ + +exprt predabs_domaint::get_row_post_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_post_constraint(row,value[row]); + return get_row_post_constraint(row, value[row]); } /*******************************************************************\ @@ -112,19 +168,19 @@ Function: predabs_domaint::to_pre_constraints Outputs: - Purpose: /\_all_rows ( pre_guard ==> (row_value => row_expr) ) + Purpose: /\_all_rows ( pre_guard==> (row_value=> row_expr) ) \*******************************************************************/ exprt predabs_domaint::to_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_value => row_expr) ) + Purpose: for all rows !(post_guard==> (row_value=> row_expr) ) to be connected disjunctively \*******************************************************************/ -void predabs_domaint::make_not_post_constraints(const templ_valuet &value, +void predabs_domaint::make_not_post_constraints( + const templ_valuet &value, exprt::operandst &cond_exprs) { assert(value.size()==templ.size()); cond_exprs.resize(templ.size()); - exprt::operandst c; - for(unsigned row = 0; row(value); + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); + find_symbols(templ_row.expr, symbols); - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) + bool pure=true; + for(const auto &symbol : symbols) { - if(vars.find(*it)==vars.end()) + if(vars.find(symbol)==vars.end()) { - pure = false; + pure=false; break; } } - if(!pure) continue; + if(!pure) + continue; - const row_valuet &row_v = v[row]; + const row_valuet &row_v=v[row]; if(templ_row.kind==LOOP) { - c.push_back(implies_exprt(templ_row.pre_guard, - implies_exprt(row_v,templ_row.expr))); + c.push_back(implies_exprt( + templ_row.pre_guard, + implies_exprt(row_v, templ_row.expr))); } else { - c.push_back(implies_exprt(row_v,templ_row.expr)); + c.push_back(implies_exprt(row_v, templ_row.expr)); } } - result = conjunction(c); + result=conjunction(c); } /*******************************************************************\ @@ -239,11 +301,13 @@ Function: predabs_domaint::set_row_value \*******************************************************************/ void predabs_domaint::set_row_value( - const rowt &row, const predabs_domaint::row_valuet &row_value, templ_valuet &value) + const rowt &row, + const predabs_domaint::row_valuet &row_value, + templ_valuet &value) { assert(row(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; case IN: out << "(IN) "; break; case OUT: case OUTL: out << "(OUT) "; break; default: assert(false); } - out << "( " << from_expr(ns,"",v[row]) << " ==> " << - from_expr(ns,"",templ_row.expr) << " )" << std::endl; + out << "( " << from_expr(ns, "", v[row]) << "==> " << + from_expr(ns, "", templ_row.expr) << " )" << std::endl; } } @@ -293,30 +360,34 @@ Function: predabs_domaint::output_domain \*******************************************************************/ -void predabs_domaint::output_domain(std::ostream &out, const namespacet &ns) const +void predabs_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) + << "===> " << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) + << "===> " << std::endl << " "; break; default: assert(false); } - out << "( " << - from_expr(ns,"",templ_row.expr) << ")" << std::endl; + out << "( " << from_expr(ns, "", templ_row.expr) << ")" << std::endl; } } @@ -339,7 +410,7 @@ unsigned predabs_domaint::template_size() /*******************************************************************\ -Function: add_template_row +Function: predabs_domaint::add_template_row Inputs: @@ -358,19 +429,19 @@ predabs_domaint::template_rowt &predabs_domaint::add_template_row( ) { templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = expr; - //extend_expr_types(templ_row.expr); - templ_row.pre_guard = pre_guard; - templ_row.post_guard = post_guard; - templ_row.aux_expr = aux_expr; - templ_row.kind = kind; + template_rowt &templ_row=templ.back(); + templ_row.expr=expr; + // extend_expr_types(templ_row.expr); + templ_row.pre_guard=pre_guard; + templ_row.post_guard=post_guard; + templ_row.aux_expr=aux_expr; + templ_row.kind=kind; return templ_row; } /*******************************************************************\ -Function: equality_domaint::get_var_pairs +Function: predabs_domaint::get_row_set Inputs: @@ -380,7 +451,8 @@ Function: equality_domaint::get_var_pairs \*******************************************************************/ -void predabs_domaint::get_row_set(std::set &rows) +void predabs_domaint::get_row_set(std::set &rows) { - for(unsigned i=0;i #include #include #include -#include -class predabs_domaint : public domaint +#include "domain.h" + +class predabs_domaint:public domaint { public: typedef unsigned rowt; - typedef exprt row_exprt; //predicate - typedef constant_exprt row_valuet; //true/false + typedef exprt row_exprt; // predicate + typedef constant_exprt row_valuet; // true/false typedef std::set row_sett; - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -31,11 +40,13 @@ class predabs_domaint : public domaint typedef std::vector templatet; - predabs_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet _ns) : - domaint(_domain_number, _renaming_map, _ns) - {} + predabs_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet _ns): + domaint(_domain_number, _renaming_map, _ns) + { + } // initialize value virtual void initialize(valuet &value); @@ -48,19 +59,29 @@ class predabs_domaint : public domaint exprt get_row_post_constraint(const rowt &row, const templ_valuet &value); exprt to_pre_constraints(const templ_valuet &value); - void make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs); + void make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; + virtual void output_value( + std::ostream &out, + const valuet &value, const + namespacet &ns) const; virtual void output_domain(std::ostream &out, const namespacet &ns) const; - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + // projection + virtual void project_on_vars( + valuet &value, + const var_sett &vars, + exprt &result); unsigned template_size(); @@ -70,14 +91,12 @@ class predabs_domaint : public domaint const exprt& pre_guard, const exprt& post_guard, const exprt& aux_expr, - kindt kind - ); + kindt kind); - void get_row_set(row_sett &rows); + void get_row_set(row_sett &rows); protected: templatet templ; - }; #endif diff --git a/src/domains/predicate.cpp b/src/domains/predicate.cpp deleted file mode 100644 index 3c772b4c5..000000000 --- a/src/domains/predicate.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Abstract State - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicate.h" - -/*******************************************************************\ - -Function: predicatet::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatet::make_false() -{ - uuf.resize(state_vars.size()); - - for(unsigned v1=0; v1 &dest) const -{ - for(unsigned v=0; v eq_count; - - for(unsigned v=0; v::const_iterator - e_it=eq_count.begin(); e_it!=eq_count.end(); e_it++) - { - if(e_it->second>=2) - { - for(unsigned v=0; vfirst==uuf.find(v)) - out << "Equal: " << from_expr(state_vars[v]) << "\n"; - out << "\n"; - } - } - - // print intervals - #if 0 - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); i_it!=integer_intervals.end(); i_it++) - { - if(i_it->lower_is_set || i_it->upper_is_set) - { - if(i_it->lower_is_set) - out << from_expr(i_it->lower) << " <= "; - - out << from_expr(vars[i_it-intervals.begin()]); - - if(i_it->upper_is_set) - out << " <= " << from_expr(i_it->lower); - - out << "\n"; - } - } - #endif -} - -/*******************************************************************\ - -Function: predicatet::disjunction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool predicatet::disjunction(const predicatet &other) -{ - bool change=false; - - // - // do equalities - // - - assert(other.state_vars.size()==state_vars.size()); - - unsigned_union_find new_uuf; - new_uuf.resize(uuf.size()); - - // can be done better than quadratic - for(unsigned v1=0; v1 -#include -#include -#include -#include - -#include - -struct predicatet -{ -public: - predicatet() - { - } - - // support set of the predicate - typedef std::vector state_var_listt; - state_var_listt state_vars; - - void output(std::ostream &) const; - void make_false(); - - // returns 'true' iff predicate is weakened - bool disjunction(const predicatet &); - - // rename supporting set of variables - void rename(const state_var_listt &new_state_vars); - - // read the predicate from a solver state - //void get(const solvert &); - - // push the predicate to a solver as constraint - void set_to_true(decision_proceduret &) const; - - bool is_bottom() const - { - return false; - } - - bool is_top() const - { - for(unsigned i=0; i &) const; - -protected: - // for now, we can track: - // * equalities between variables - // * intervals - - unsigned_union_find uuf; - - typedef expanding_vector integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - integer_intervalst integer_intervals; - ieee_float_intervalst ieee_float_intervals; -}; - -static inline std::ostream & operator << ( - std::ostream &out, const predicatet &predicate) -{ - predicate.output(out); - return out; -} - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, const predicatet &predicate) -{ - predicate.set_to_true(dest); - return dest; -} - -#endif diff --git a/src/domains/ranking_solver_enumeration.cpp b/src/domains/ranking_solver_enumeration.cpp index a73fa7501..9d196369b 100644 --- a/src/domains/ranking_solver_enumeration.cpp +++ b/src/domains/ranking_solver_enumeration.cpp @@ -1,149 +1,172 @@ +/*******************************************************************\ + +Module: Enumeration-based solver for linear ranking functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include + #include "ranking_solver_enumeration.h" #include "util.h" -#include -ranking_solver_enumerationt::progresst ranking_solver_enumerationt::iterate(invariantt &_rank) +/*******************************************************************\ + +Function: ranking_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ranking_solver_enumerationt::iterate(invariantt &_rank) { - linrank_domaint::templ_valuet &rank = + linrank_domaint::templ_valuet &rank= static_cast(_rank); - progresst progress = CONVERGED; + bool improved=false; - //context for "outer" solver + // context for "outer" solver solver.new_context(); - //choose round to even rounding mode for template computations - // not clear what its implications on soundness and termination of the synthesis are - exprt rounding_mode = symbol_exprt(CPROVER_PREFIX "rounding_mode",signedbv_typet(32)); - solver << equal_exprt(rounding_mode,from_integer(mp_integer(0),signedbv_typet(32))); - - //handles on values to retrieve from model + // choose round to even rounding mode for template computations + // not clear what its implications on soundness and + // termination of the synthesis are + exprt rounding_mode= + symbol_exprt(CPROVER_PREFIX "rounding_mode", signedbv_typet(32)); + solver << + equal_exprt( + rounding_mode, + from_integer(mp_integer(0), signedbv_typet(32))); + + // handles on values to retrieve from model std::vector rank_value_exprs; exprt::operandst rank_cond_exprs; bvt rank_cond_literals; - exprt rank_expr = + exprt rank_expr= linrank_domain.get_not_constraints(rank, rank_cond_exprs, rank_value_exprs); solver << rank_expr; rank_cond_literals.resize(rank_cond_exprs.size()); - for(unsigned i = 0; i < rank_cond_literals.size(); i++) - { - rank_cond_literals[i] = solver.solver->convert(rank_cond_exprs[i]); + for(std::size_t i=0; iconvert(rank_cond_exprs[i]); } debug() << "solve(): "; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { debug() << "SAT" << eom; - - for(unsigned row = 0; row < rank_cond_literals.size(); row++) + + for(std::size_t row=0; rowl_get(rank_cond_literals[row]).is_true()) + + if(solver.solver->l_get(rank_cond_literals[row]).is_true()) { - for(linrank_domaint::pre_post_valuest::iterator it = - rank_value_exprs[row].begin(); - it != rank_value_exprs[row].end(); ++it) - { - // model for x_i - exprt value = solver.solver->get(it->first); - debug() << "(RANK) Row " << row << " Value for " - << from_expr(ns,"",it->first) - << ": " << from_expr(ns,"",value) << eom; - // model for x'_i - exprt post_value = solver.solver->get(it->second); - debug() << "(RANK) Row " << row << " Value for " - << from_expr(ns,"",it->second) - << ": " << from_expr(ns,"",post_value) << eom; - // record all the values - values.push_back(std::make_pair(value, post_value)); - } - - linrank_domaint::row_valuet symb_values; - exprt constraint; - exprt refinement_constraint; - - // generate the new constraint - constraint = - linrank_domain.get_row_symb_constraint(symb_values, row, - values,refinement_constraint); - simplify_expr(constraint, ns); - debug() << "Inner Solver: " << row << " constraint " - << from_expr(ns,"", constraint) << eom; - - inner_solver << equal_exprt(rounding_mode, - from_integer(mp_integer(0),signedbv_typet(32))); - inner_solver << constraint; - - //refinement - if(!refinement_constraint.is_true()) - { - inner_solver.new_context(); - inner_solver << refinement_constraint; - } - - //solve - debug() << "inner solve()" << eom; - solver_calls++; - if(inner_solver() == decision_proceduret::D_SATISFIABLE && - //TODO: not sure where number_inner_iterations is used - number_inner_iterations < max_inner_iterations) - { - - debug() << "inner solver: SAT" << eom; - - std::vector c = symb_values.c; - - // new_row_values will contain the new values for c - linrank_domaint::row_valuet new_row_values; - - // get the model for all c - for(std::vector::iterator it = c.begin(); it != c.end(); ++it) - { - exprt v = inner_solver.solver->get(*it); - new_row_values.c.push_back(v); - debug() << "Inner Solver: " << row << " c value for " - << from_expr(ns,"", *it) << ": " - << from_expr(ns,"", v) << eom; - } - exprt rmv = inner_solver.solver->get(rounding_mode); + for(const auto &rve : rank_value_exprs[row]) + { + // model for x_i + exprt value=solver.solver->get(rve.first); + debug() << "(RANK) Row " << row << " Value for " + << from_expr(ns, "", rve.first) + << ": " << from_expr(ns, "", value) << eom; + // model for x'_i + exprt post_value=solver.solver->get(rve.second); + debug() << "(RANK) Row " << row << " Value for " + << from_expr(ns, "", rve.second) + << ": " << from_expr(ns, "", post_value) << eom; + // record all the values + values.push_back(std::make_pair(value, post_value)); + } + + linrank_domaint::row_valuet symb_values; + exprt constraint; + exprt refinement_constraint; + + // generate the new constraint + constraint=linrank_domain.get_row_symb_constraint( + symb_values, row, values, refinement_constraint); + simplify_expr(constraint, ns); + debug() << "Inner Solver: " << row << " constraint " + << from_expr(ns, "", constraint) << eom; + + inner_solver << equal_exprt( + rounding_mode, from_integer(mp_integer(0), signedbv_typet(32))); + inner_solver << constraint; + + // refinement + if(!refinement_constraint.is_true()) + { + inner_solver.new_context(); + inner_solver << refinement_constraint; + } + + // solve + debug() << "inner solve()" << eom; + solver_calls++; + if(inner_solver()==decision_proceduret::D_SATISFIABLE && + number_inner_iterations c=symb_values.c; + + // new_row_values will contain the new values for c + linrank_domaint::row_valuet new_row_values; + + // get the model for all c + for(const auto &e : c) + { + exprt v=inner_solver.solver->get(e); + new_row_values.c.push_back(v); + debug() << "Inner Solver: " << row << " c value for " + << from_expr(ns, "", e) << ": " + << from_expr(ns, "", v) << eom; + } + exprt rmv=inner_solver.solver->get(rounding_mode); debug() << "Rounding mode: " << from_expr(ns, "", rmv) << eom; - // update the current template - linrank_domain.set_row_value(row, new_row_values, rank); - - progress = CHANGED; - } - else - { - debug() << "inner solver: UNSAT" << eom; - - if(linrank_domain.refine()) - { - debug() << "refining..." << eom; - progress = CHANGED; //refinement possible - } - else - { + // update the current template + linrank_domain.set_row_value(row, new_row_values, rank); + + improved=true; + } + else + { + debug() << "inner solver: UNSAT" << eom; + + if(linrank_domain.refine()) + { + debug() << "refining..." << eom; + improved=true; // refinement possible + } + else + { // no ranking function for the current template - linrank_domain.set_row_value_to_true(row, rank); - linrank_domain.reset_refinements(); - } - } + linrank_domain.set_row_value_to_true(row, rank); + linrank_domain.reset_refinements(); + } + } - if(!refinement_constraint.is_true()) inner_solver.pop_context(); + if(!refinement_constraint.is_true()) + inner_solver.pop_context(); } } - } - else + else { debug() << "UNSAT" << eom; linrank_domain.reset_refinements(); @@ -151,5 +174,5 @@ ranking_solver_enumerationt::progresst ranking_solver_enumerationt::iterate(inva solver.pop_context(); - return progress; + return improved; } diff --git a/src/domains/ranking_solver_enumeration.h b/src/domains/ranking_solver_enumeration.h index 8c07e0429..2daaccdbc 100644 --- a/src/domains/ranking_solver_enumeration.h +++ b/src/domains/ranking_solver_enumeration.h @@ -1,34 +1,41 @@ -#ifndef CPROVER_RANKING_SOLVER_ENUMERATION_H -#define CPROVER_RANKING_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Enumeration-based solver for linear ranking functions + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_RANKING_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_RANKING_SOLVER_ENUMERATION_H #include #include #include "strategy_solver_base.h" -#include "../domains/incremental_solver.h" +#include "incremental_solver.h" #include "linrank_domain.h" -class ranking_solver_enumerationt : public strategy_solver_baset +class ranking_solver_enumerationt:public strategy_solver_baset { - public: - explicit ranking_solver_enumerationt( +public: + ranking_solver_enumerationt( linrank_domaint &_linrank_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, const namespacet &_ns, - unsigned _max_inner_iterations - ) : - strategy_solver_baset(_solver, literalt(), _ns), + unsigned _max_inner_iterations): + strategy_solver_baset(_solver, literalt(), _ns), linrank_domain(_linrank_domain), max_inner_iterations(_max_inner_iterations), inner_solver(_ns), number_inner_iterations(0) - { - solver_instances++; - } + { + solver_instances++; + } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); - protected: +protected: linrank_domaint &linrank_domain; // the "inner" solver diff --git a/src/domains/simplify_bounds.cpp b/src/domains/simplify_bounds.cpp deleted file mode 100644 index cfa64de81..000000000 --- a/src/domains/simplify_bounds.cpp +++ /dev/null @@ -1,454 +0,0 @@ -/*******************************************************************\ - -Module: Bounds simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include "simplify_bounds.h" -#include "simplify_bounds_class.h" -#include - -#define DEBUGX - -#ifdef DEBUGX -#include -#include -#endif - -/*******************************************************************\ - -Function: simplify_boundst::get_min_bound - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::get_min_bound(const exprt &expr, mp_integer &value) -{ -#ifdef DEBUGX - std::cout << "get_min_bound: " << from_expr(ns, "", expr) - << "\n"; -#endif - - if(!is_bitvector_type(expr.type())) - return false; - - if(expr.id()==ID_symbol) - { - value=get_smallest(expr.type()); - return true; - } - else if(expr.id()==ID_typecast) - { - return get_min_bound(to_typecast_expr(expr).op(), value); - } - else if(expr.id()==ID_unary_minus) - { - bool result=get_max_bound(to_unary_minus_expr(expr).op(), value); - value=-value; - return result; - } - else if(expr.id()==ID_plus) - { - mp_integer vr, vl; - bool rr=get_min_bound(expr.op0(), vr); - bool rl=get_min_bound(expr.op1(), vl); - if(rr && rl) - { - value=vr+vl; - return true; - } - } - else if(expr.id()==ID_minus) - { - mp_integer vr, vl; - bool rr=get_min_bound(expr.op0(), vr); - bool rl=get_max_bound(expr.op1(), vl); - if(rr && rl) - { - value=vr-vl; - return true; - } - } - - return false; -} - -/*******************************************************************\ - -Function: simplify_boundst::get_max_bound - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::get_max_bound(const exprt &expr, mp_integer &value) -{ -#ifdef DEBUGX - std::cout << "get_max_bound: " << from_expr(ns, "", expr) - << "\n"; -#endif - - if(!is_bitvector_type(expr.type())) - return false; - - if(expr.id()==ID_symbol) - { - value=get_largest(expr.type()); - return true; - } - else if(expr.id()==ID_typecast) - { - return get_max_bound(to_typecast_expr(expr).op(), value); - } - else if(expr.id()==ID_unary_minus) - { - bool result=get_min_bound(to_unary_minus_expr(expr).op(), value); - value=-value; - return result; - } - else if(expr.id()==ID_plus) - { - mp_integer vr, vl; - bool rr=get_max_bound(expr.op0(), vr); - bool rl=get_max_bound(expr.op1(), vl); - if(rr && rl) - { - value=vr+vl; - return true; - } - } - else if(expr.id()==ID_minus) - { - mp_integer vr, vl; - bool rr=get_max_bound(expr.op0(), vr); - bool rl=get_min_bound(expr.op1(), vl); - if(rr && rl) - { - value=vr-vl; - return true; - } - } - - return false; -} - -/*******************************************************************\ - -Function: simplify_boundst::simplify_rec - - Inputs: - - Outputs: returns true if expression unchanged; - returns false if changed - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::simplify_rec(exprt &expr) -{ -#ifdef DEBUGX - exprt old(expr); -#endif - - bool result=true; - if(expr.id()==ID_le) - { - binary_relation_exprt &e = to_binary_relation_expr(expr); - if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && - e.lhs().id()!=ID_symbol) - { - mp_integer rhs_value, lhs_max, lhs_min; - to_integer(e.rhs(), rhs_value); - if(get_max_bound(e.lhs(), lhs_max)) - { - if(lhs_max<=rhs_value) - { - expr=true_exprt(); - result=false; - } - else - result=clean_up_typecast(expr,rhs_value); - } - else if(get_min_bound(e.lhs(), lhs_min)) - { - if(lhs_min>rhs_value) - { - expr=false_exprt(); - result=false; - } - } - } - } - else if(expr.id()==ID_ge) - { - binary_relation_exprt &e = to_binary_relation_expr(expr); - if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && - e.lhs().id()!=ID_symbol) - { - mp_integer rhs_value, lhs_max, lhs_min; - to_integer(e.rhs(), rhs_value); - if(get_max_bound(e.lhs(), lhs_max)) - { - if(lhs_max=rhs_value) - { - expr=true_exprt(); - result=false; - } - } - } - } - else if(expr.id()==ID_lt) - { - binary_relation_exprt &e = to_binary_relation_expr(expr); - if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && - e.lhs().id()!=ID_symbol) - { - mp_integer rhs_value, lhs_max, lhs_min; - to_integer(e.rhs(), rhs_value); - if(get_max_bound(e.lhs(), lhs_max)) - { - if(lhs_max=rhs_value) - { - expr=false_exprt(); - result=false; - } - } - } - } - else if(expr.id()==ID_gt) - { - binary_relation_exprt &e = to_binary_relation_expr(expr); - if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && - e.lhs().id()!=ID_symbol) - { - mp_integer rhs_value, lhs_max, lhs_min; - to_integer(e.rhs(), rhs_value); - if(get_max_bound(e.lhs(), lhs_max)) - { - if(lhs_max<=rhs_value) - { - expr=false_exprt(); - result=false; - } - } - else if(get_min_bound(e.lhs(), lhs_min)) - { - if(lhs_min>rhs_value) - { - expr=true_exprt(); - result=false; - } - } - } - } - else - { - Forall_operands(it, expr) - if(!simplify_rec(*it)) - result=false; - } -#ifdef DEBUGX - std::cout << "===== " << from_expr(ns, "", old) - << "\n ---> " << from_expr(ns, "", expr) - << "\n"; -#endif - - return result; -} - -/*******************************************************************\ - -Function: simplify_boundst::clean_up_typecast - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::clean_up_typecast(exprt &expr, - const mp_integer &rhs_value) -{ - if(expr.id()==ID_le && expr.op0().id()==ID_unary_minus && - expr.op0().op0().id()==ID_typecast) - { - const typet &inner_type=expr.op0().op0().op0().type(); - if(-rhs_value>get_smallest(inner_type)) - { - expr=binary_relation_exprt(expr.op0().op0().op0(), ID_ge, - from_integer(-rhs_value,inner_type)); - return true; - } - } - return false; -} - -/*******************************************************************\ - -Function: simplify_boundst::clean_up_implications - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::clean_up_implications(exprt &expr) -{ - bool result=true; - if(expr.id()==ID_and) - { - Forall_operands(it, expr) - if(!clean_up_implications(*it)) - result=false; - exprt::operandst::iterator it=expr.operands().begin(); - while(it!=expr.operands().end()) - { - if(it->is_true()) - it=expr.operands().erase(it); - else - ++it; - } - if(expr.operands().empty()) - expr=true_exprt(); - else if(expr.operands().size()==1) - expr=expr.op0(); - } - else if(expr.id()==ID_implies) - { - if(expr.op1().is_true()) - { - expr=true_exprt(); - result=false; - } - } - return result; -} - -/*******************************************************************\ - -Function: simplify_boundst::clean_up_implications - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::regroup_implications(exprt &expr) -{ - std::map implication_map; - exprt::operandst r; - if(expr.id()==ID_and) - { - Forall_operands(it, expr) - if(it->id()==ID_implies) - implication_map[it->op0()].push_back(it->op1()); - else - r.push_back(*it); - } - else - return true; - for(auto &i : implication_map) - r.push_back(implies_exprt(i.first, conjunction(i.second))); - expr=conjunction(r); - return false; -} - -/*******************************************************************\ - -Function: simplify_boundst::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_boundst::operator()(exprt &expr) -{ - bool result=simplify_rec(expr); - if(!clean_up_implications(expr)) - result=false; - if(!regroup_implications(expr)) - result=false; - return result; -} - -/*******************************************************************\ - -Function: simplify - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_bounds(exprt &expr, - const namespacet &ns) -{ - return simplify_boundst(ns)(expr); -} - -/*******************************************************************\ - -Function: simplify_bounds - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt simplify_bounds(const exprt &src, - const namespacet &ns) -{ - exprt tmp=src; - simplify_boundst simplify_bounds(ns); - simplify_bounds(tmp); - return tmp; -} - diff --git a/src/domains/simplify_bounds.h b/src/domains/simplify_bounds.h deleted file mode 100644 index 6799b76a2..000000000 --- a/src/domains/simplify_bounds.h +++ /dev/null @@ -1,32 +0,0 @@ -/*******************************************************************\ - -Module: Bounds simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SIMPLIFY_BOUNDS_H -#define CPROVER_SIMPLIFY_BOUNDS_H - -#include - -class exprt; -class namespacet; - -// -// simplify bounds -// -// true: did nothing -// false: simplified something -// - -bool simplify_bounds( - exprt &expr, - const namespacet &ns); - -exprt simplify_bounds( - const exprt &src, - const namespacet &ns); - -#endif diff --git a/src/domains/simplify_bounds_class.h b/src/domains/simplify_bounds_class.h deleted file mode 100644 index 3510f42ed..000000000 --- a/src/domains/simplify_bounds_class.h +++ /dev/null @@ -1,66 +0,0 @@ -/*******************************************************************\ - -Module: Bounds Simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SIMPLIFY_BOUNDS_CLASS_H -#define CPROVER_SIMPLIFY_BOUNDS_CLASS_H - -#include -#include - -class exprt; -class namespacet; - -class simplify_boundst -{ -public: - explicit simplify_boundst(const namespacet &_ns): - ns(_ns) - { - } - - virtual ~simplify_boundst() - { - } - - // These below all return 'true' if the simplification wasn't applicable. - // If false is returned, the expression has changed. - - virtual bool operator()(exprt &expr); - - inline static bool is_bitvector_type(const typet &type) - { - return type.id()==ID_unsignedbv || - type.id()==ID_signedbv; - } - inline static mp_integer get_largest(const typet &type) - { - if(type.id()==ID_signedbv) return to_signedbv_type(type).largest(); - else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).largest(); - assert(false); - } - inline static mp_integer get_smallest(const typet &type) - { - if(type.id()==ID_signedbv) return to_signedbv_type(type).smallest(); - else if(type.id()==ID_unsignedbv) return to_unsignedbv_type(type).smallest(); - assert(false); - } - -protected: - const namespacet &ns; - - bool simplify_rec(exprt &expr); - bool get_min_bound(const exprt &expr, mp_integer &value); - bool get_max_bound(const exprt &expr, mp_integer &value); - - bool clean_up_implications(exprt &expr); - bool regroup_implications(exprt &expr); - bool clean_up_typecast(exprt &expr, const mp_integer &value); - -}; - -#endif diff --git a/src/domains/simplify_transformer.cpp b/src/domains/simplify_transformer.cpp deleted file mode 100644 index 87f43673d..000000000 --- a/src/domains/simplify_transformer.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*******************************************************************\ - -Module: Transformer simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include "simplify_transformer.h" -#include "simplify_transformer_class.h" -#include - -#define DEBUGX - -#ifdef DEBUGX -#include -#include -#endif - -/*******************************************************************\ - -Function: simplify_transformert::collect_node - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void simplify_transformert::collect_node(const exprt &expr, - replace_mapt &substitutions, - bool frozen_only, - bool make_copy) -{ - if(expr.id()==ID_equal) - { - const equal_exprt &e = to_equal_expr(expr); - - bool rhs_is_constant=e.rhs().id()==ID_constant; - bool rhs_is_symbol=e.rhs().id()==ID_symbol || - e.rhs().id()==ID_nondet_symbol; - bool rhs_is_frozen=rhs_is_symbol && - frozen_symbols.find(e.rhs().get(ID_identifier))!=frozen_symbols.end(); - bool lhs_is_constant=e.lhs().id()==ID_constant; - bool lhs_is_symbol=e.lhs().id()==ID_symbol || - e.lhs().id()==ID_nondet_symbol; - bool lhs_is_frozen=lhs_is_symbol && - frozen_symbols.find(e.lhs().get(ID_identifier))!=frozen_symbols.end(); - - exprt lhs, rhs; - lhs.make_nil(); - rhs.make_nil(); - //stupid matching - if((rhs_is_frozen || rhs_is_constant || !frozen_only) && - lhs_is_symbol && !lhs_is_frozen) - { - lhs=e.lhs(); - rhs=e.rhs(); - } - if((lhs_is_frozen || lhs_is_constant || !frozen_only) && - rhs_is_symbol && !rhs_is_frozen) - { - rhs=e.lhs(); - lhs=e.rhs(); - } - if(rhs.is_not_nil() && lhs.is_not_nil()) - { - if(make_copy) //make lazy copy - { - replace_mapt _subst = substitutions; - substitutions = _subst; - } - substitutions[lhs]=rhs; - } - } - -#ifdef DEBUGX - std::cout << "COLLECT: " << from_expr(ns, "", expr) << std::endl; - for(const auto &it : substitutions) - std::cout << from_expr(ns, "", it.first) - << "---> " << from_expr(ns, "", it.second) - << "\n"; -#endif -} - -/*******************************************************************\ - -Function: simplify_transformert::simplify_node - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_transformert::simplify_node(exprt &expr, - const replace_mapt &substitutions) -{ - return replace_expr(substitutions, expr); -} - -/*******************************************************************\ - -Function: simplify_transformert::simplify_rec - - Inputs: - - Outputs: returns true if expression unchanged; - returns false if changed - - Purpose: - -\*******************************************************************/ - -bool simplify_transformert::simplify_rec(exprt &expr, - replace_mapt &substitutions) -{ -#ifdef DEBUGX - exprt old(expr); -#endif - - bool result=true; - if(expr.id()==ID_and) - { - //first propagate from frozen symbols - bool res=false; - do - { - Forall_operands(it, expr) - collect_node(*it, substitutions, true, false); - - Forall_operands(it, expr) - if(!simplify_rec(*it, substitutions)) - result=false; - - res=simplify_node(expr, substitutions); - if(!res) result=false; - } - while(!res); - - //simplify remaining equalities - Forall_operands(it, expr) - collect_node(*it, substitutions, false, false); - - res=false; - do - { - res=simplify_node(expr, substitutions); - if(!res) result=false; - } - while(!res); - } - -#if 0 //for later extension to disjunctions - //TODO: handle negation, too - else if(expr.id()==ID_or || expr.id()==ID_implies) - { - Forall_operands(it, expr) - { - collect_node(*it, substitutions, true); - if(!simplify_rec(*it, substitutions)) - result=false; - - bool res=false; - do - { - res=simplify_node(*it, substitutions); - if(!res) result=false; - } - while(!res); - } - } -#endif - -#ifdef DEBUGX - std::cout << "===== " << from_expr(ns, "", old) - << "\n ---> " << from_expr(ns, "", expr) - << "\n"; -#endif - - return result; -} - -/*******************************************************************\ - -Function: simplify_transformert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify_transformert::operator()(exprt &expr) -{ - replace_mapt substitutions; - return simplify_rec(expr, substitutions); -} - -/*******************************************************************\ - -Function: simplify - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool simplify(exprt &expr, - const std::set &frozen_vars, - const namespacet &ns) -{ - return simplify_transformert(ns, frozen_vars)(expr); -} - -/*******************************************************************\ - -Function: simplify_transformer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt simplify_transformer(const exprt &src, - const std::set &frozen_vars, - const namespacet &ns) -{ -#ifdef DEBUGX - std::cout << "FROZEN:"; - for(const auto &it : frozen_vars) - std::cout << " " << it; - std::cout << "\n"; -#endif - - exprt tmp=src; - simplify_transformert(ns, frozen_vars)(tmp); - return tmp; -} - diff --git a/src/domains/simplify_transformer.h b/src/domains/simplify_transformer.h deleted file mode 100644 index 32a815c8c..000000000 --- a/src/domains/simplify_transformer.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Transformer simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SIMPLIFY_TRANSFORMER_H -#define CPROVER_SIMPLIFY_TRANSFORMER_H - -#include -#include - -class exprt; -class namespacet; - -// -// simplify transformers by best-effort intermediate variable elimination -// -// true: did nothing -// false: simplified something -// - -bool simplify( - exprt &expr, - const std::set &frozen_vars, //do not eliminate these - const namespacet &ns); - -exprt simplify_transformer( - const exprt &src, - const std::set &frozen_vars, //do not eliminate these - const namespacet &ns); - -#endif diff --git a/src/domains/simplify_transformer_class.h b/src/domains/simplify_transformer_class.h deleted file mode 100644 index ccae118b4..000000000 --- a/src/domains/simplify_transformer_class.h +++ /dev/null @@ -1,59 +0,0 @@ -/*******************************************************************\ - -Module: Transformer Simplification - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H -#define CPROVER_SIMPLIFY_TRANSFORMER_CLASS_H - -#include -#include - -class exprt; -class namespacet; - -class simplify_transformert -{ -public: - explicit simplify_transformert(const namespacet &_ns, - const std::set &_frozen_symbols): - ns(_ns), - frozen_symbols(_frozen_symbols) - { - } - - virtual ~simplify_transformert() - { - } - - // These below all return 'true' if the simplification wasn't applicable. - // If false is returned, the expression has changed. - - // main recursion - bool simplify_rec(exprt &expr, replace_mapt &substitutions); - - virtual bool operator()(exprt &expr); - - inline static bool is_bitvector_type(const typet &type) - { - return type.id()==ID_unsignedbv || - type.id()==ID_signedbv || - type.id()==ID_bv; - } - -protected: - const namespacet &ns; - const std::set &frozen_symbols; - - void collect_node( - const exprt &expr, replace_mapt &substitutions, - bool frozen_only, - bool make_copy); - bool simplify_node(exprt &expr, const replace_mapt &substitutions); - -}; - -#endif diff --git a/src/domains/solver_enumeration.cpp b/src/domains/solver_enumeration.cpp deleted file mode 100644 index 5f5ac9431..000000000 --- a/src/domains/solver_enumeration.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include - -#include -#include "solver_enumeration.h" - -bool solver_enumerationt::iterate(invariantt &_inv) -{ - tpolyhedra_domaint::templ_valuet &inv = - static_cast(_inv); - - bool improved = false; - - literalt activation_literal = new_context(); - - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; - -#ifndef DEBUG_FORMULA - solver << or_exprt(inv_expr, literal_exprt(activation_literal)); -#else - debug() << "literal " << activation_literal << eom; - literalt l = solver.convert(or_exprt(inv_expr, literal_exprt(activation_literal))); - if(!l.is_constant()) - { - debug() << "literal " << l << ": " << from_expr(ns,"",or_exprt(inv_expr, literal_exprt(activation_literal))) <first) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " << - from_expr(ns, "", solver.get(it->second)) << eom; - } - - #endif - - tpolyhedra_domaint::templ_valuet new_value; - tpolyhedra_domain.initialize(new_value); - for(unsigned row=0;row - - -#include "strategy_solver_base.h" -#include "strategy_solver_enumeration.h" -//#include "solver_enumeration.h" -#include "strategy_solver_binsearch.h" -#include "strategy_solver_binsearch2.h" -#include "strategy_solver_binsearch3.h" -#include "strategy_solver_equality.h" -#include "linrank_domain.h" -#include "lexlinrank_domain.h" -#include "ranking_solver_enumeration.h" -#include "lexlinrank_solver_enumeration.h" -#include "template_generator_ranking.h" -#include "strategy_solver_predabs.h" -#include "ssa_analyzer.h" - -#include -#include - -#include -#include -#include -#include - -#define BINSEARCH_SOLVER strategy_solver_binsearcht(*static_cast(domain), solver, assertions_check, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch2t(*static_cast(domain), solver, assertions_check, SSA.ns) -//#define BINSEARCH_SOLVER strategy_solver_binsearch3t(*static_cast(domain), solver, assertions_check, SSA, SSA.ns) - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: ssa_analyzert::operator() - - Inputs: - - Outputs: true if the computation was not aborted due to - assertion_checks that did not pass - Purpose: - -\*******************************************************************/ - -bool ssa_analyzert::operator()(incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator, - bool check_assertions) -{ - if(SSA.goto_function.body.instructions.empty()) - return true; - - solver << SSA; - SSA.mark_nodes(); - - solver.new_context(); - solver << SSA.get_enabling_exprs(); - - // add precondition (or conjunction of asssertion in backward analysis) - solver << precondition; - - domain = template_generator.domain(); - - // get assertions if check_assertions is requested - literalt assertions_check = const_literal(false); - bvt assertion_literals; - if(check_assertions) - { - exprt::operandst ll; - for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - assert(n_it->assertions.size()<=1); - for(local_SSAt::nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) - { - literalt l = solver.solver->convert(*a_it); - assertion_literals.push_back(!l); - ll.push_back(literal_exprt(!l)); - nonpassed_assertions.push_back(n_it); - } - } - assertions_check = solver.solver->convert(disjunction(ll)); - } - - // get strategy solver from options - strategy_solver_baset *strategy_solver; - if(template_generator.options.get_bool_option("equalities")) - { - strategy_solver = new strategy_solver_equalityt( - *static_cast(domain), solver, - assertions_check, SSA.ns); - result = new equality_domaint::equ_valuet(); - } - else - { - if(template_generator.options.get_bool_option("enum-solver")) - { - result = new tpolyhedra_domaint::templ_valuet(); - strategy_solver = new strategy_solver_enumerationt( - *static_cast(domain), solver, - assertions_check, SSA.ns); - } - else if(template_generator.options.get_bool_option("predabs-solver")) - { - result = new predabs_domaint::templ_valuet(); - strategy_solver = new strategy_solver_predabst( - *static_cast(domain), solver, - assertions_check, SSA.ns); - } - else if(template_generator.options.get_bool_option("predabs-solver")) - { - result = new predabs_domaint::templ_valuet(); - strategy_solver = new strategy_solver_predabst( - *static_cast(domain), solver, - assertions_check, SSA.ns); - } - else if(template_generator.options.get_bool_option("binsearch-solver")) - { - result = new tpolyhedra_domaint::templ_valuet(); - strategy_solver = new BINSEARCH_SOLVER; - } - else assert(false); - } - - strategy_solver->set_message_handler(get_message_handler()); - - unsigned iteration_number=0; - - // initialize domain - domain->initialize(*result); - - strategy_solver_baset::progresst status; - - do - { - iteration_number++; - -#ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; -#endif - - status = strategy_solver->iterate(*result); - -#ifdef DEBUG - if(status == strategy_solver_baset::CHANGED) - { - std::cout << "Value after " << iteration_number - << " iteration(s):\n"; - domain->output_value(std::cout,*result,SSA.ns); - } -#endif - } - while(status == strategy_solver_baset::CHANGED); - -#ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - domain->output_value(std::cout,*result,SSA.ns); -#endif - - //get status of assertions - if(!assertions_check.is_false() && - status == strategy_solver_baset::FAILED) - { - nonpassed_assertionst::iterator it = nonpassed_assertions.begin(); - for(unsigned i=0;il_get(assertion_literals[i]).is_true()) - nonpassed_assertions.erase(it++); - else ++it; - } - } - else nonpassed_assertions.clear(); - - solver.pop_context(); - - //statistics - solver_calls += strategy_solver->get_number_of_solver_calls(); - solver_instances += strategy_solver->get_number_of_solver_instances(); - solver_instances += strategy_solver->get_number_of_solver_instances(); - - delete strategy_solver; - - return (status == strategy_solver_baset::CONVERGED); -} - -/*******************************************************************\ - -Function: ssa_analyzert::get_result - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) -{ - domain->project_on_vars(*result,vars,_result); -} +/*******************************************************************\ + +Module: SSA Analyzer + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "strategy_solver_base.h" +#include "strategy_solver_enumeration.h" +#include "strategy_solver_binsearch.h" +#include "strategy_solver_binsearch2.h" +#include "strategy_solver_binsearch3.h" +#include "strategy_solver_equality.h" +#include "linrank_domain.h" +#include "lexlinrank_domain.h" +#include "ranking_solver_enumeration.h" +#include "lexlinrank_solver_enumeration.h" +#include "template_generator_ranking.h" +#include "strategy_solver_predabs.h" +#include "ssa_analyzer.h" + +#define BINSEARCH_SOLVER strategy_solver_binsearcht(\ + *static_cast(domain), solver, assertions_check, SSA.ns) +#if 0 +#define BINSEARCH_SOLVER strategy_solver_binsearch2t(\ + *static_cast(domain), solver, SSA.ns) +#define BINSEARCH_SOLVER strategy_solver_binsearch3t(\ + *static_cast(domain), solver, SSA, SSA.ns) +#endif + +/*******************************************************************\ + +Function: ssa_analyzert::operator() + + Inputs: + + Outputs: true if the computation was not aborted due to + assertion_checks that did not pass + Purpose: + +\*******************************************************************/ + +bool ssa_analyzert::operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions) +{ + if(SSA.goto_function.body.instructions.empty()) + return true; + + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + solver << SSA.get_enabling_exprs(); + + // add precondition (or conjunction of asssertion in backward analysis) + solver << precondition; + + domain=template_generator.domain(); + + // get assertions if check_assertions is requested + literalt assertions_check=const_literal(false); + bvt assertion_literals; + + if(check_assertions) + { + exprt::operandst ll; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + assert(n_it->assertions.size()<=1); + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + { + literalt l = solver.solver->convert(*a_it); + assertion_literals.push_back(!l); + ll.push_back(literal_exprt(!l)); + nonpassed_assertions.push_back(n_it); + } + } + assertions_check = solver.solver->convert(disjunction(ll)); + } + + // get strategy solver from options + strategy_solver_baset *strategy_solver; + + if(template_generator.options.get_bool_option("compute-ranking-functions")) + { + if(template_generator.options.get_bool_option( + "monolithic-ranking-function")) + { + strategy_solver=new ranking_solver_enumerationt( + *static_cast(domain), solver, SSA.ns, + template_generator.options.get_unsigned_int_option( + "max-inner-ranking-iterations")); + result=new linrank_domaint::templ_valuet(); + } + else + { + strategy_solver=new lexlinrank_solver_enumerationt( + *static_cast(domain), solver, SSA.ns, + template_generator.options.get_unsigned_int_option( + "lexicographic-ranking-function"), + template_generator.options.get_unsigned_int_option( + "max-inner-ranking-iterations")); + result=new lexlinrank_domaint::templ_valuet(); + } + } + + else if(template_generator.options.get_bool_option("equalities")) + { + strategy_solver=new strategy_solver_equalityt( + *static_cast(domain), solver, assertions_check, SSA.ns); + result=new equality_domaint::equ_valuet(); + } + else + { + if(template_generator.options.get_bool_option("enum-solver")) + { + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new strategy_solver_enumerationt( + *static_cast(domain), solver, assertions_check, SSA.ns); + } + else if(template_generator.options.get_bool_option("predabs-solver")) + { + result=new predabs_domaint::templ_valuet(); + strategy_solver=new strategy_solver_predabst( + *static_cast(domain), solver, SSA.ns); + } + else if(template_generator.options.get_bool_option("binsearch-solver")) + { + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new BINSEARCH_SOLVER; + } + else + assert(false); + } + + strategy_solver->set_message_handler(get_message_handler()); + + // initialize inv + domain->initialize(*result); + + // iterate + while(strategy_solver->iterate(*result)) {} +/* + //get status of assertions + if(!assertions_check.is_false() && + status==strategy_solver_baset::FAILED) + { + nonpassed_assertionst::iterator it=nonpassed_assertions.begin(); + for(unsigned i=0;il_get(assertion_literals[i]).is_true()) + nonpassed_assertions.erase(it++); + else + ++it; + } + } + else + nonpassed_assertions.clear(); +*/ + solver.pop_context(); + + //statistics + solver_calls += strategy_solver->get_number_of_solver_calls(); + solver_instances += strategy_solver->get_number_of_solver_instances(); + + delete strategy_solver; + + //return (status == strategy_solver_baset::CONVERGED); +} + +/*******************************************************************\ + +Function: ssa_analyzert::get_result + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) +{ + domain->project_on_vars(*result, vars, _result); +} diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index 3e45aedc7..1c9050ff7 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -1,69 +1,70 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SSA_ANALYZER_H -#define DELTACHECK_SSA_ANALYZER_H - -#include - -#include "../ssa/local_ssa.h" -#include "strategy_solver_base.h" -#include "template_generator_base.h" - -class ssa_analyzert : public messaget -{ -public: - typedef strategy_solver_baset::constraintst constraintst; - typedef strategy_solver_baset::var_listt var_listt; - - explicit ssa_analyzert() - : - result(NULL), - solver_instances(0), - solver_calls(0) - { - } - - ~ssa_analyzert() - { - if(result!=NULL) delete result; - } - - // returns true if the computation was not aborted due to - // assertion_checks that did not pass - bool operator()(incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator, - bool check_assertions=false); - - //retrieve the result if operator() returned true - void get_result(exprt &result, const domaint::var_sett &vars); - - //retrieve the non-passed assertions if operator() returned false - typedef std::list - nonpassed_assertionst; - nonpassed_assertionst get_nonpassed_assertions() - { return nonpassed_assertions; } - - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } - -protected: - domaint *domain; //template generator is responsable for the domain object - domaint::valuet *result; - nonpassed_assertionst nonpassed_assertions; - - //statistics - unsigned solver_instances; - unsigned solver_calls; -}; - - -#endif - +/*******************************************************************\ + +Module: SSA Analyzer + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SSA_ANALYZER_H +#define CPROVER_2LS_DOMAINS_SSA_ANALYZER_H + +#include + +#include + +#include "strategy_solver_base.h" +#include "template_generator_base.h" + +class ssa_analyzert:public messaget +{ +public: + typedef strategy_solver_baset::constraintst constraintst; + typedef strategy_solver_baset::var_listt var_listt; + + ssa_analyzert(): + result(NULL), + solver_instances(0), + solver_calls(0) + { + } + + ~ssa_analyzert() + { + if(result!=NULL) + delete result; + } + + // returns true if the computation was not aborted due to + // assertion_checks that did not pass + bool operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions=false); + + //retrieve the result if operator() returned true + void get_result(exprt &result, const domaint::var_sett &vars); + + //retrieve the non-passed assertions if operator() returned false + typedef std::list + nonpassed_assertionst; + nonpassed_assertionst get_nonpassed_assertions() + { return nonpassed_assertions; } + + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + +protected: + domaint *domain; // template generator is responsable for the domain object + domaint::valuet *result; + nonpassed_assertionst nonpassed_assertions; + + // statistics + unsigned solver_instances; + unsigned solver_calls; +}; + +#endif + diff --git a/src/domains/ssa_fixed_point.cpp b/src/domains/ssa_fixed_point.cpp deleted file mode 100644 index 8e592b7d5..000000000 --- a/src/domains/ssa_fixed_point.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" -#include "ssa_fixed_point.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: ssa_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_fixed_point(local_SSAt &SSA) -{ - if(SSA.goto_function.body.instructions.empty()) - return; - - fixed_pointt fixed_point(SSA.ns); - - // get all backwards edges - - forall_goto_program_instructions(i_it, SSA.goto_function.body) - { - if(i_it->is_backwards_goto()) - { - // Record the objects modified by the loop to get - // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, i_it); - symbol_exprt out=SSA.read_rhs(*o_it, i_it); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - - { - ssa_objectt guard=SSA.guard_symbol(); - symbol_exprt in=SSA.name(guard, local_SSAt::LOOP_BACK, i_it); - symbol_exprt out=SSA.name(guard, local_SSAt::OUT, i_it); - - fixed_point.pre_state_vars.push_back(in); - fixed_point.post_state_vars.push_back(out); - } - } - } - - // transition relation - fixed_point.transition_relation << SSA; - - // kick off fixed-point computation - fixed_point(); - - // Add fixed-point as constraints back into SSA. - // We simply use the last CFG node. It would be prettier to put - // these close to the loops. - assert(SSA.nodes.begin()!=SSA.nodes.end()); - fixed_point.state_predicate.get_constraints(SSA.nodes.back().constraints); -} diff --git a/src/domains/ssa_fixed_point.h b/src/domains/ssa_fixed_point.h deleted file mode 100644 index ac197e75a..000000000 --- a/src/domains/ssa_fixed_point.h +++ /dev/null @@ -1,16 +0,0 @@ -/*******************************************************************\ - -Module: Data Flow Analysis - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SSA_FIXED_POINT_H -#define DELTACHECK_SSA_FIXED_POINT_H - -#include "../ssa/local_ssa.h" - -void ssa_fixed_point(local_SSAt &); - -#endif diff --git a/src/domains/strategy_solver_base.cpp b/src/domains/strategy_solver_base.cpp index 4b6ce48ea..edf2329ba 100644 --- a/src/domains/strategy_solver_base.cpp +++ b/src/domains/strategy_solver_base.cpp @@ -1,3 +1,10 @@ +/*******************************************************************\ + +Module: Strategy iteration solver base class + +Author: Peter Schrammel + +\*******************************************************************/ #include "strategy_solver_base.h" diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index 7a071d569..11361519f 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -1,18 +1,20 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BASE_H -#define CPROVER_STRATEGY_SOLVER_BASE_H +/*******************************************************************\ -#include -#include +Module: Strategy iteration solver base class -#include +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BASE_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BASE_H #include "domain.h" +#include "incremental_solver.h" #include "util.h" -#include "../domains/incremental_solver.h" -class strategy_solver_baset : public messaget +class strategy_solver_baset:public messaget { - public: typedef std::list constraintst; typedef std::vector var_listt; @@ -20,32 +22,32 @@ class strategy_solver_baset : public messaget typedef enum {CHANGED, CONVERGED, FAILED} progresst; explicit strategy_solver_baset( - incremental_solvert &_solver, + incremental_solvert &_solver, literalt _assertion_check, - const namespacet &_ns) : - solver(_solver), + const namespacet &_ns): + solver(_solver), assertion_check(_assertion_check), ns(_ns), solver_instances(0), solver_calls(0) {} - virtual progresst iterate(invariantt &inv) { assert(false); } + virtual bool iterate(invariantt &inv) { assert(false); } - unsigned get_number_of_solver_calls() { return solver_calls; } - unsigned get_number_of_solver_instances() { return solver_instances; } + inline unsigned get_number_of_solver_calls() { return solver_calls; } + inline unsigned get_number_of_solver_instances() { return solver_instances; } - protected: + protected: incremental_solvert &solver; literalt assertion_check; const namespacet &ns; - //handles on values to retrieve from model + // handles on values to retrieve from model bvt strategy_cond_literals; exprt::operandst strategy_value_exprs; - //statistics for additional solvers + // statistics for additional solvers unsigned solver_instances; unsigned solver_calls; }; diff --git a/src/domains/strategy_solver_binsearch.cpp b/src/domains/strategy_solver_binsearch.cpp index b230ecac9..97ec242df 100644 --- a/src/domains/strategy_solver_binsearch.cpp +++ b/src/domains/strategy_solver_binsearch.cpp @@ -1,44 +1,65 @@ +/*******************************************************************\ + +Module: Simplified strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include "strategy_solver_binsearch.h" #include "util.h" -strategy_solver_baset::progresst -strategy_solver_binsearcht::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_binsearcht::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_binsearcht::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - progresst progress = CONVERGED; + bool improved=false; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + // solver.set_frozen(strategy_cond_literals[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } #if 0 debug() << eom; @@ -51,41 +72,47 @@ strategy_solver_binsearcht::iterate(invariantt &_inv) debug() << "solve(): "; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + if(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - + #if 0 - for(unsigned i=0; i improve_rows; improve_rows.insert(row); - tpolyhedra_domaint::row_valuet upper = + tpolyhedra_domaint::row_valuet upper= tpolyhedra_domain.get_max_row_value(row); - tpolyhedra_domaint::row_valuet lower = + tpolyhedra_domaint::row_valuet lower= simplify_const(solver.get(strategy_value_exprs[row])); - solver.pop_context(); //improvement check - - solver.new_context(); //symbolic value system + solver.pop_context(); // improvement check - exprt pre_inv_expr = - tpolyhedra_domain.to_symb_pre_constraints(inv,improve_rows); + solver.new_context(); // symbolic value system + + exprt pre_inv_expr= + tpolyhedra_domain.to_symb_pre_constraints(inv, improve_rows); solver << pre_inv_expr; - exprt post_inv_expr = tpolyhedra_domain.get_row_symb_post_constraint(row); + exprt post_inv_expr=tpolyhedra_domain.get_row_symb_post_constraint(row); solver << post_inv_expr; #if 0 debug() << "symbolic value system: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",pre_inv_expr) << eom; - debug() << "post-inv: " << from_expr(ns,"",post_inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns, "", post_inv_expr) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; // row_symb_value >= middle - exprt c = tpolyhedra_domain.get_row_symb_value_constraint(row,middle,true); + exprt c= + tpolyhedra_domain.get_row_symb_value_constraint(row, middle, true); #if 0 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif solver.new_context(); // binary search iteration @@ -147,106 +176,85 @@ strategy_solver_binsearcht::iterate(invariantt &_inv) solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - + #if 0 - for(unsigned i=0; ifirst) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " - << from_expr(ns, "", solver.get(it->second)) << eom; - } +#if 0 + for(const auto &rm : renaming_map) + { + debug() << "replace_map (1st): " + << from_expr(ns, "", rm.first) << " " + << from_expr(ns, "", solver.get(rm.first)) << eom; + debug() << "replace_map (2nd): " + << from_expr(ns, "", rm.second) << " " + << from_expr(ns, "", solver.get(rm.second)) << eom; + } #endif - - lower = simplify_const( - solver.get(tpolyhedra_domain.get_row_symb_value(row))); + + lower=simplify_const( + solver.get(tpolyhedra_domain.get_row_symb_value(row))); } - else + else { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif #if 0 - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; - else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + if(solver.solver->is_in_conflict(solver.formula[i])) + debug() << "is_in_conflict: " << solver.formula[i] << eom; + else + debug() << "not_in_conflict: " << solver.formula[i] << eom; } #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; - upper = middle; + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; + upper=middle; } solver.pop_context(); // binary search iteration } - - debug() << "update value: " << from_expr(ns,"",lower) << eom; - solver.pop_context(); //symbolic value system + debug() << "update value: " << from_expr(ns, "", lower) << eom; + + solver.pop_context(); // symbolic value system - tpolyhedra_domain.set_row_value(row,lower,inv); - progress = CHANGED; + tpolyhedra_domain.set_row_value(row, lower, inv); + improved=true; } - else + else { #if 0 debug() << "UNSAT" << eom; #endif #ifdef DEBUG_FORMULA - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; + debug() << "is_in_conflict: " << solver.formula[i] << eom; else - debug() << "not_in_conflict: " << solver.formula[i] << eom; + debug() << "not_in_conflict: " << solver.formula[i] << eom; } #endif -#ifdef DEBUG_FORMULA - for(unsigned i=0; iis_in_conflict(solver.formula[i])) - debug() << "is_in_conflict: " << solver.formula[i] << eom; - else - debug() << "not_in_conflict: " << solver.formula[i] << eom; - } -#endif - - if(tpolyhedra_domain.refine()) - { - debug() << "refining..." << eom; - progress = CHANGED; //refinement possible - } - else - { - tpolyhedra_domain.reset_refinements(); - solver.pop_context(); //improvement check - } + solver.pop_context(); // improvement check } - - return progress; + return improved; } diff --git a/src/domains/strategy_solver_binsearch.h b/src/domains/strategy_solver_binsearch.h index 13521f708..367bc9cc8 100644 --- a/src/domains/strategy_solver_binsearch.h +++ b/src/domains/strategy_solver_binsearch.h @@ -1,25 +1,34 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH_H +/*******************************************************************\ + +Module: Simplified strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearcht : public strategy_solver_baset +class strategy_solver_binsearcht:public strategy_solver_baset { - public: - explicit strategy_solver_binsearcht( +public: + strategy_solver_binsearcht( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, literalt _assertion_check, - const namespacet &_ns) : - strategy_solver_baset(_solver, _assertion_check, _ns), - tpolyhedra_domain(_tpolyhedra_domain) {} + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), + tpolyhedra_domain(_tpolyhedra_domain) + { + } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); - protected: +protected: tpolyhedra_domaint &tpolyhedra_domain; - }; #endif diff --git a/src/domains/strategy_solver_binsearch2.cpp b/src/domains/strategy_solver_binsearch2.cpp index e6d90c18f..864a7a6b3 100644 --- a/src/domains/strategy_solver_binsearch2.cpp +++ b/src/domains/strategy_solver_binsearch2.cpp @@ -1,4 +1,16 @@ +/*******************************************************************\ + +Module: Strategy iteration solver by binary search + with optimisation of the parameter sum + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include @@ -6,47 +18,57 @@ #include "strategy_solver_binsearch2.h" #include "util.h" - #define SUM_BOUND_VAR "sum_bound#" +/*******************************************************************\ + +Function: strategy_solver_binsearch2t::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + //TODO: implement assertion check -strategy_solver_baset::progresst -strategy_solver_binsearch2t::iterate(invariantt &_inv) +bool strategy_solver_binsearch2t::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - progresst progress = CONVERGED; + bool improved=false; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + // solver.set_frozen(strategy_cond_literals[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } debug() << eom; @@ -56,165 +78,170 @@ strategy_solver_binsearch2t::iterate(invariantt &_inv) debug() << "solve(): "; #endif - std::map symb_values; - std::map lower_values; + std::map symb_values; + std::map lower_values; exprt::operandst blocking_constraint; std::set improve_rows; - bool improved_from_neginf = false; - while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + bool improved_from_neginf=false; + while(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - progress = CHANGED; + improved=true; - unsigned row=0; - for(;row=1); - std::map::iterator - it = symb_values.begin(); - exprt _lower = lower_values[it->first]; + std::map::iterator + it=symb_values.begin(); + exprt _lower=lower_values[it->first]; #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); - exprt _upper = + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); + exprt _upper= tpolyhedra_domain.get_max_row_value(it->first); - exprt sum = it->second; - for(it++; it != symb_values.end(); it++) + exprt sum=it->second; + for(++it; it!=symb_values.end(); ++it) { - sum = plus_exprt(sum,it->second); - _upper = plus_exprt(_upper,tpolyhedra_domain.get_max_row_value(it->first)); - _lower = plus_exprt(_lower,lower_values[it->first]); + sum=plus_exprt(sum, it->second); + _upper=plus_exprt(_upper, tpolyhedra_domain.get_max_row_value(it->first)); + _lower=plus_exprt(_lower, lower_values[it->first]); #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); } - - //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) return progress; - solver.new_context(); //symbolic value system + // do not solve system if we have just reached a new loop + // (the system will be very large!) + if(improved_from_neginf) + return improved; + + solver.new_context(); // symbolic value system solver << pre_inv_expr; solver << post_inv_expr; extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); - tpolyhedra_domaint::row_valuet upper = simplify_const(_upper); - //from_integer(mp_integer(512),_upper.type()); - tpolyhedra_domaint::row_valuet lower = simplify_const(_lower); + tpolyhedra_domaint::row_valuet upper=simplify_const(_upper); + // from_integer(mp_integer(512), _upper.type()); + tpolyhedra_domaint::row_valuet lower=simplify_const(_lower); assert(sum.type()==upper.type()); assert(sum.type()==lower.type()); - symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << equal_exprt(sum_bound,sum); + symbol_exprt sum_bound( + SUM_BOUND_VAR+i2string(sum_bound_counter++), + sum.type()); + solver << equal_exprt(sum_bound, sum); #if 0 - debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; + debug() << from_expr(ns, "", equal_exprt(sum_bound, sum)) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; - // row_symb_value >= middle - assert(sum_bound.type()==middle.type()); - exprt c = binary_relation_exprt(sum_bound,ID_ge,middle); + // row_symb_value >= middle + assert(sum_bound.type()==middle.type()); + exprt c=binary_relation_exprt(sum_bound, ID_ge, middle); #if 0 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif - solver.new_context(); // binary search iteration + solver.new_context(); // binary search iteration #if 0 - debug() << "constraint: " << from_expr(ns, "", c) << eom; + debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << c; + solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - - lower = middle; - for(std::map::iterator - it = symb_values.begin(); it != symb_values.end(); it++) - { + lower=middle; + + for(const auto &sv : symb_values) + { #if 1 - debug() << "update row " << it->first << " " - << from_expr(ns,"",it->second) << ": "; + debug() << "update row " << sv.first << " " + << from_expr(ns, "", sv.second) << ": "; #endif - constant_exprt lower_row = - simplify_const(solver.get(it->second)); + constant_exprt lower_row= + simplify_const(solver.get(sv.second)); #if 1 - debug() << from_expr(ns,"",lower_row) << eom; + debug() << from_expr(ns, "", lower_row) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_row,inv); - } - } - else - { + tpolyhedra_domain.set_row_value(sv.first, lower_row, inv); + } + } + else + { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; - upper = middle; - } - solver.pop_context(); // binary search iteration - } + upper=middle; + } + solver.pop_context(); // binary search iteration + } - solver.pop_context(); //symbolic value system + solver.pop_context(); // symbolic value system - return progress; + return improved; } diff --git a/src/domains/strategy_solver_binsearch2.h b/src/domains/strategy_solver_binsearch2.h index 94d6d06e4..7eb07e33c 100644 --- a/src/domains/strategy_solver_binsearch2.h +++ b/src/domains/strategy_solver_binsearch2.h @@ -1,10 +1,19 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH2_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH2_H +/*******************************************************************\ + +Module: Strategy iteration solver by binary search + with optimisation of the parameter sum + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH2_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH2_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearch2t : public strategy_solver_baset +class strategy_solver_binsearch2t:public strategy_solver_baset { public: explicit strategy_solver_binsearch2t( @@ -14,14 +23,15 @@ class strategy_solver_binsearch2t : public strategy_solver_baset const namespacet &_ns) : strategy_solver_baset( _solver, _assertion_check, _ns), tpolyhedra_domain(_tpolyhedra_domain), - sum_bound_counter(0) {} + sum_bound_counter(0) + { + } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; unsigned sum_bound_counter; - }; diff --git a/src/domains/strategy_solver_binsearch3.cpp b/src/domains/strategy_solver_binsearch3.cpp index f010932ea..42eefebf1 100644 --- a/src/domains/strategy_solver_binsearch3.cpp +++ b/src/domains/strategy_solver_binsearch3.cpp @@ -1,4 +1,15 @@ +/*******************************************************************\ + +Module: Full strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif + #include #include @@ -6,47 +17,56 @@ #include "strategy_solver_binsearch3.h" #include "util.h" - #define SUM_BOUND_VAR "sum_bound#" +/*******************************************************************\ + +Function: strategy_solver_binsearch3t::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + //TODO: implement assertion check -strategy_solver_baset::progresst -strategy_solver_binsearch3t::iterate(invariantt &_inv) +bool strategy_solver_binsearch3t::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - progresst progress = CONVERGED; + bool improved=false; - solver.new_context(); //for improvement check + solver.new_context(); // for improvement check - exprt inv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt inv_expr=tpolyhedra_domain.to_pre_constraints(inv); #if 0 debug() << "improvement check: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", inv_expr) << eom; #endif solver << inv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - + #if 0 debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]); + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } debug() << eom; @@ -57,201 +77,173 @@ strategy_solver_binsearch3t::iterate(invariantt &_inv) #endif std::set improve_rows; - std::map symb_values; - std::map lower_values; + std::map symb_values; + std::map lower_values; exprt::operandst blocking_constraint; - bool improved_from_neginf = false; - while(solver() == decision_proceduret::D_SATISFIABLE) //improvement check - { + bool improved_from_neginf=false; + while(solver()==decision_proceduret::D_SATISFIABLE) // improvement check + { #if 0 debug() << "SAT" << eom; #endif - progress = CHANGED; + improved=true; - unsigned row=0; - for(;row=1); - std::map::iterator - it = symb_values.begin(); - exprt _lower = lower_values[it->first]; + std::map::iterator + it=symb_values.begin(); + exprt _lower=lower_values[it->first]; #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); - exprt _upper = + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); + exprt _upper= tpolyhedra_domain.get_max_row_value(it->first); - exprt sum = it->second; - for(it++; it != symb_values.end(); it++) + exprt sum=it->second; + for(++it; it!=symb_values.end(); ++it) { - sum = plus_exprt(sum,it->second); - _upper = plus_exprt(_upper,tpolyhedra_domain.get_max_row_value(it->first)); - _lower = plus_exprt(_lower,lower_values[it->first]); + sum=plus_exprt(sum, it->second); + _upper=plus_exprt(_upper, tpolyhedra_domain.get_max_row_value(it->first)); + _lower=plus_exprt(_lower, lower_values[it->first]); #if 1 - debug() << "update row " << it->first << ": " - << from_expr(ns,"",lower_values[it->first]) << eom; + debug() << "update row " << it->first << ": " + << from_expr(ns, "", lower_values[it->first]) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_values[it->first],inv); + tpolyhedra_domain.set_row_value(it->first, lower_values[it->first], inv); } - - //do not solve system if we have just reached a new loop (the system will be very large!) - if(improved_from_neginf) return progress; - solver.new_context(); //symbolic value system + // do not solve system if we have just reached a new loop + // (the system will be very large!) + if(improved_from_neginf) + return improved; + + solver.new_context(); // symbolic value system solver << pre_inv_expr; solver << post_inv_expr; #if 1 debug() << "symbolic value system: " << eom; - debug() << "pre-inv: " << from_expr(ns,"",pre_inv_expr) << eom; - debug() << "post-inv: " << from_expr(ns,"",post_inv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", pre_inv_expr) << eom; + debug() << "post-inv: " << from_expr(ns, "", post_inv_expr) << eom; #endif -/* - //add renamed SSA for rows 1..n-1 - SSA.unmark_nodes(); - for(unsigned i=1; i program; - program << SSA; - for(std::list::iterator it = program.begin(); - it != program.end(); it++) - { -// tpolyhedra_domain.rename_for_row(*it,i); -#if 1 - debug() << "ssa " << i << ": " << from_expr(ns,"",*it) << eom; -#endif - } - solver << SSA; - } - SSA.mark_nodes(); -*/ extend_expr_types(sum); extend_expr_types(_upper); extend_expr_types(_lower); - tpolyhedra_domaint::row_valuet upper = simplify_const(_upper); - tpolyhedra_domaint::row_valuet lower = simplify_const(_lower); + tpolyhedra_domaint::row_valuet upper=simplify_const(_upper); + tpolyhedra_domaint::row_valuet lower=simplify_const(_lower); assert(sum.type()==upper.type()); assert(sum.type()==lower.type()); - symbol_exprt sum_bound(SUM_BOUND_VAR+i2string(sum_bound_counter++),sum.type()); - solver << equal_exprt(sum_bound,sum); + symbol_exprt sum_bound( + SUM_BOUND_VAR+i2string(sum_bound_counter++), + sum.type()); + solver << equal_exprt(sum_bound, sum); #if 1 - debug() << from_expr(ns,"",equal_exprt(sum_bound,sum)) << eom; + debug() << from_expr(ns, "", equal_exprt(sum_bound, sum)) << eom; #endif - while(tpolyhedra_domain.less_than(lower,upper)) + while(tpolyhedra_domain.less_than(lower, upper)) { - tpolyhedra_domaint::row_valuet middle = - tpolyhedra_domain.between(lower,upper); - if(!tpolyhedra_domain.less_than(lower,middle)) middle = upper; + tpolyhedra_domaint::row_valuet middle= + tpolyhedra_domain.between(lower, upper); + if(!tpolyhedra_domain.less_than(lower, middle)) + middle=upper; - // row_symb_value >= middle - assert(sum_bound.type()==middle.type()); - exprt c = binary_relation_exprt(sum_bound,ID_ge,middle); + // row_symb_value >= middle + assert(sum_bound.type()==middle.type()); + exprt c=binary_relation_exprt(sum_bound, ID_ge, middle); #if 1 - debug() << "upper: " << from_expr(ns,"",upper) << eom; - debug() << "middle: " << from_expr(ns,"",middle) << eom; - debug() << "lower: " << from_expr(ns,"",lower) << eom; + debug() << "upper: " << from_expr(ns, "", upper) << eom; + debug() << "middle: " << from_expr(ns, "", middle) << eom; + debug() << "lower: " << from_expr(ns, "", lower) << eom; #endif - solver.new_context(); // binary search iteration + solver.new_context(); // binary search iteration #if 1 - debug() << "constraint: " << from_expr(ns, "", c) << eom; + debug() << "constraint: " << from_expr(ns, "", c) << eom; #endif - solver << c; + solver << c; - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 - debug() << "SAT" << eom; + debug() << "SAT" << eom; #endif - - lower = middle; - for(std::map::iterator - it = symb_values.begin(); it != symb_values.end(); it++) - { + lower=middle; + + for(const auto &sv : symb_values) + { #if 1 - debug() << "update row " << it->first << " " - << from_expr(ns,"",it->second) << ": "; + debug() << "update row " << sv.first << " " + << from_expr(ns, "", sv.second) << ": "; #endif - constant_exprt lower_row = - simplify_const(solver.get(it->second)); + constant_exprt lower_row= + simplify_const(solver.get(sv.second)); #if 1 - debug() << from_expr(ns,"",lower_row) << eom; + debug() << from_expr(ns, "", lower_row) << eom; #endif - tpolyhedra_domain.set_row_value(it->first,lower_row,inv); - } - } - else - { + tpolyhedra_domain.set_row_value(sv.first, lower_row, inv); + } + } + else + { #if 0 - debug() << "UNSAT" << eom; + debug() << "UNSAT" << eom; #endif - if(!tpolyhedra_domain.less_than(middle,upper)) middle = lower; - - upper = middle; - } - solver.pop_context(); // binary search iteration - } + if(!tpolyhedra_domain.less_than(middle, upper)) + middle=lower; - solver.pop_context(); //symbolic value system + upper=middle; + } + solver.pop_context(); // binary search iteration + } + solver.pop_context(); // symbolic value system - return progress; + return improved; } diff --git a/src/domains/strategy_solver_binsearch3.h b/src/domains/strategy_solver_binsearch3.h index cc233c0ac..5af5dcd92 100644 --- a/src/domains/strategy_solver_binsearch3.h +++ b/src/domains/strategy_solver_binsearch3.h @@ -1,16 +1,26 @@ -#ifndef CPROVER_STRATEGY_SOLVER_BINSEARCH3_H -#define CPROVER_STRATEGY_SOLVER_BINSEARCH3_H +/*******************************************************************\ + +Module: Full strategy iteration solver by binary search + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H + +#include #include "../ssa/local_ssa.h" #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_binsearch3t : public strategy_solver_baset +class strategy_solver_binsearch3t:public strategy_solver_baset { public: explicit strategy_solver_binsearch3t( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, literalt _assertion_check, local_SSAt& _SSA, const namespacet &_ns) : @@ -19,16 +29,12 @@ class strategy_solver_binsearch3t : public strategy_solver_baset tpolyhedra_domain(_tpolyhedra_domain), sum_bound_counter(0) {} - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); protected: local_SSAt &SSA; tpolyhedra_domaint &tpolyhedra_domain; unsigned sum_bound_counter; -// std::set improve_rows; -// std::map symb_values; -// std::map lower_values; - }; -#endif +#endif // CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_BINSEARCH3_H diff --git a/src/domains/strategy_solver_enumeration.cpp b/src/domains/strategy_solver_enumeration.cpp index 710724651..be2ac3c4d 100644 --- a/src/domains/strategy_solver_enumeration.cpp +++ b/src/domains/strategy_solver_enumeration.cpp @@ -1,4 +1,14 @@ +/*******************************************************************\ + +Module: Synthesis by enumeration of counterexamples + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include @@ -6,43 +16,53 @@ #include "strategy_solver_enumeration.h" #include "util.h" -strategy_solver_baset::progresst -strategy_solver_enumerationt::iterate(invariantt &_inv) +/*******************************************************************\ + +Function: strategy_solver_enumerationt::iterate + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_enumerationt::iterate(invariantt &_inv) { - tpolyhedra_domaint::templ_valuet &inv = + tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - progresst progress = CONVERGED; + bool improved=false; solver.new_context(); - exprt preinv_expr = tpolyhedra_domain.to_pre_constraints(inv); + exprt preinv_expr=tpolyhedra_domain.to_pre_constraints(inv); #ifdef DEBUG_OUTPUT - debug() << "pre-inv: " << from_expr(ns,"",preinv_expr) << eom; + debug() << "pre-inv: " << from_expr(ns, "", preinv_expr) << eom; #endif solver << preinv_expr; exprt::operandst strategy_cond_exprs; - tpolyhedra_domain.make_not_post_constraints(inv, - strategy_cond_exprs, strategy_value_exprs); - + tpolyhedra_domain.make_not_post_constraints( + inv, strategy_cond_exprs, strategy_value_exprs); + strategy_cond_literals.resize(strategy_cond_exprs.size()); - - exprt postinv_expr = disjunction(strategy_cond_exprs); + + exprt postinv_expr=disjunction(strategy_cond_exprs); #ifdef DEBUG_OUTPUT debug() << "post-inv: "; #endif - for(unsigned i = 0; i0 ? " || " : "") << from_expr(ns,"",strategy_cond_exprs[i]) ; + debug() << (i>0 ? " || " : "") << from_expr(ns, "", strategy_cond_exprs[i]); #endif - strategy_cond_literals[i] = solver.convert(strategy_cond_exprs[i]); - //solver.set_frozen(strategy_cond_literals[i]); - strategy_cond_exprs[i] = literal_exprt(strategy_cond_literals[i]); + strategy_cond_literals[i]=solver.convert(strategy_cond_exprs[i]); + strategy_cond_exprs[i]=literal_exprt(strategy_cond_literals[i]); } #ifdef DEBUG_OUTPUT debug() << eom; @@ -55,102 +75,97 @@ strategy_solver_enumerationt::iterate(invariantt &_inv) debug() << "solve(): "; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #ifdef DEBUG_OUTPUT debug() << "SAT" << eom; #endif - + #ifdef DEBUG_OUTPUT - for(unsigned i=0; i vars; - find_symbols(preinv_expr,vars); + find_symbols(preinv_expr, vars); - for(std::set::const_iterator - it=vars.begin(); - it!=vars.end(); - ++it) + for(const auto &var : vars) { - debug() << "var: " << from_expr(ns, "", *it) << " = " << - from_expr(ns, "", solver.get(*it)) << eom; + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; } } - for(unsigned i=0; i vars; - find_symbols(strategy_value_exprs[i],vars); + find_symbols(strategy_value_exprs[i], vars); - for(std::set::const_iterator - it=vars.begin(); - it!=vars.end(); - ++it) + for(const auto &var : vars) { - debug() << "var: " << from_expr(ns, "", *it) << " = " << - from_expr(ns, "", solver.get(*it)) << eom; + debug() << "var: " << from_expr(ns, "", var) << "=" + << from_expr(ns, "", solver.get(var)) << eom; } } #endif - - for(unsigned row=0;rowis_in_conflict(solver.formula[i])) debug() << "is_in_conflict: " << solver.formula[i] << eom; else debug() << "not_in_conflict: " << solver.formula[i] << eom; - } -#endif + } +#endif } solver.pop_context(); - return progress; + return improved; } diff --git a/src/domains/strategy_solver_enumeration.h b/src/domains/strategy_solver_enumeration.h index 206a6b027..edfb75a9e 100644 --- a/src/domains/strategy_solver_enumeration.h +++ b/src/domains/strategy_solver_enumeration.h @@ -1,25 +1,34 @@ -#ifndef CPROVER_STRATEGY_SOLVER_ENUMERATION_H -#define CPROVER_STRATEGY_SOLVER_ENUMERATION_H +/*******************************************************************\ + +Module: Synthesis by enumeration of counterexamples + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_ENUMERATION_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_ENUMERATION_H #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" -class strategy_solver_enumerationt : public strategy_solver_baset +class strategy_solver_enumerationt:public strategy_solver_baset { - public: - explicit strategy_solver_enumerationt( +public: + strategy_solver_enumerationt( tpolyhedra_domaint &_tpolyhedra_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, literalt _assertion_check, - const namespacet &_ns) : + const namespacet &_ns): strategy_solver_baset(_solver, _assertion_check, _ns), - tpolyhedra_domain(_tpolyhedra_domain) {} + tpolyhedra_domain(_tpolyhedra_domain) + { + } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); - protected: +protected: tpolyhedra_domaint &tpolyhedra_domain; - }; #endif diff --git a/src/domains/strategy_solver_equality.cpp b/src/domains/strategy_solver_equality.cpp index a9869e437..b14446aa4 100644 --- a/src/domains/strategy_solver_equality.cpp +++ b/src/domains/strategy_solver_equality.cpp @@ -1,27 +1,46 @@ +/*******************************************************************\ + +Module: Solver for equalities/disequalities domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include #include "strategy_solver_equality.h" -//Comment: assertion check is not possible because this is a gfp solver +/*******************************************************************\ + +Function: strategy_solver_equalityt::iterate -strategy_solver_baset::progresst -strategy_solver_equalityt::iterate(invariantt &_inv) + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool strategy_solver_equalityt::iterate(invariantt &_inv) { - equality_domaint::equ_valuet &inv = + equality_domaint::equ_valuet &inv= static_cast(_inv); - worklistt::iterator e_it = todo_equs.begin(); - if(e_it!=todo_equs.end()) //check equalities + worklistt::iterator e_it=todo_equs.begin(); + if(e_it!=todo_equs.end()) // check equalities { solver.new_context(); - exprt pre_expr = equality_domain.get_pre_equ_constraint(*e_it); + exprt pre_expr=equality_domain.get_pre_equ_constraint(*e_it); solver << pre_expr; - - exprt post_expr = equality_domain.get_post_not_equ_constraint(*e_it); - literalt cond_literal = solver.convert(post_expr); + + exprt post_expr=equality_domain.get_post_not_equ_constraint(*e_it); + literalt cond_literal=solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -31,8 +50,8 @@ strategy_solver_equalityt::iterate(invariantt &_inv) debug() << "Post: " << from_expr(ns, "", post_expr) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 debug() << "SAT" << eom; #endif @@ -40,53 +59,40 @@ strategy_solver_equalityt::iterate(invariantt &_inv) solver.pop_context(); } - else //equality holds + else // equality holds { #if 0 debug() << "UNSAT" << eom; #endif - - equality_domain.set_equal(*e_it,inv); + + equality_domain.set_equal(*e_it, inv); solver.pop_context(); - solver << pre_expr; //make permanent + solver << pre_expr; // make permanent - //due to transitivity, we would like to recheck equalities that did not hold - todo_equs.insert(todo_disequs.begin(),todo_disequs.end()); + // due to transitivity, we have to recheck equalities + // that did not hold + todo_equs.insert(todo_disequs.begin(), todo_disequs.end()); todo_disequs.clear(); } - todo_equs.erase(e_it); - - //check status of remaining equalities - /* worklistt rm_equs; - for(e_it = todo_equs.begin(); e_it!=todo_equs.end(); e_it++) - { - equality_domaint::var_pairt vv = equality_domain.get_var_pair(*e_it); - if(solver.get(vv.first)!=solver.get(vv.second)) - rm_equs.insert(*e_it); - } - for(e_it = rm_equs.begin(); e_it!=rm_equs.end(); e_it++) - { - todo_disequs.insert(*e_it); - todo_equs.erase(*e_it); - } */ } - else //check disequalities + else // check disequalities { - e_it = todo_disequs.begin(); - if(e_it==todo_disequs.end()) return CONVERGED; //done + e_it=todo_disequs.begin(); + if(e_it==todo_disequs.end()) + return false; // done solver.new_context(); - exprt pre_expr = equality_domain.get_pre_disequ_constraint(*e_it); + exprt pre_expr=equality_domain.get_pre_disequ_constraint(*e_it); solver << pre_expr; - - exprt post_expr = equality_domain.get_post_not_disequ_constraint(*e_it); - literalt cond_literal = solver.convert(post_expr); + + exprt post_expr=equality_domain.get_post_not_disequ_constraint(*e_it); + literalt cond_literal=solver.convert(post_expr); solver << literal_exprt(cond_literal); @@ -96,19 +102,19 @@ strategy_solver_equalityt::iterate(invariantt &_inv) debug() << "Post: " << from_expr(ns, "", post_expr) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { + if(solver()==decision_proceduret::D_SATISFIABLE) + { #if 0 debug() << "SAT" << eom; -#endif +#endif } - else //equality holds + else // equality holds { #if 0 debug() << "UNSAT" << eom; -#endif - equality_domain.set_disequal(*e_it,inv); - solver << pre_expr; //make permanent +#endif + equality_domain.set_disequal(*e_it, inv); + solver << pre_expr; // make permanent } solver.pop_context(); diff --git a/src/domains/strategy_solver_equality.h b/src/domains/strategy_solver_equality.h index fadd18b1d..2dbf7f6a0 100644 --- a/src/domains/strategy_solver_equality.h +++ b/src/domains/strategy_solver_equality.h @@ -1,24 +1,32 @@ -#ifndef CPROVER_STRATEGY_SOLVER_EQUALITY_H -#define CPROVER_STRATEGY_SOLVER_EQUALITY_H +/*******************************************************************\ + +Module: Solver for equalities/disequalities domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_EQUALITY_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_EQUALITY_H #include "strategy_solver_base.h" #include "equality_domain.h" -class strategy_solver_equalityt : public strategy_solver_baset +class strategy_solver_equalityt:public strategy_solver_baset { - public: - explicit strategy_solver_equalityt( +public: + strategy_solver_equalityt( equality_domaint &_equality_domain, - incremental_solvert &_solver, + incremental_solvert &_solver, literalt _assertion_check, - const namespacet &_ns) : - strategy_solver_baset(_solver, _assertion_check, _ns), + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), equality_domain(_equality_domain) { equality_domain.get_index_set(todo_equs); } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); protected: equality_domaint &equality_domain; diff --git a/src/domains/strategy_solver_predabs.cpp b/src/domains/strategy_solver_predabs.cpp index f49e090d0..c3f5fe2ca 100644 --- a/src/domains/strategy_solver_predabs.cpp +++ b/src/domains/strategy_solver_predabs.cpp @@ -1,81 +1,103 @@ +/*******************************************************************\ + +Module: Solver for predicate abstraction domain + +Author: Peter Schrammel, Cristina David + +\*******************************************************************/ + +#ifdef DEBUG #include +#endif #include + #include "strategy_solver_predabs.h" -//Comment: assertion check is not possible because this is a gfp solver +/*******************************************************************\ + +Function: strategy_solver_predabst::iterate + + Inputs: + + Outputs: + + Purpose: -strategy_solver_baset::progresst -strategy_solver_predabst::iterate(invariantt &_inv) +\*******************************************************************/ + +bool strategy_solver_predabst::iterate(invariantt &_inv) { - predabs_domaint::templ_valuet &inv = + predabs_domaint::templ_valuet &inv= static_cast(_inv); - worklistt::iterator e_it = todo_preds.begin(); - if(e_it != todo_preds.end()) //check positive preds - { - solver.new_context(); - exprt preinv_expr = predabs_domain.get_row_pre_constraint(*e_it, true_exprt()); + worklistt::iterator e_it=todo_preds.begin(); + if(e_it!=todo_preds.end()) // check positive preds + { + solver.new_context(); + exprt preinv_expr= + predabs_domain.get_row_pre_constraint(*e_it, true_exprt()); #ifdef DEBUG_OUTPUT - debug() << "pre-pred: " << from_expr(ns,"",preinv_expr) << eom; + debug() << "pre-pred: " << from_expr(ns, "", preinv_expr) << eom; #endif - solver << preinv_expr; - - exprt strategy_cond_expr; - strategy_cond_expr = predabs_domain.get_row_post_constraint(*e_it, true_exprt()); + solver << preinv_expr; + + exprt strategy_cond_expr; + strategy_cond_expr= + predabs_domain.get_row_post_constraint(*e_it, true_exprt()); - literalt cond_literal = solver.convert(not_exprt(strategy_cond_expr)); - solver << literal_exprt(cond_literal); + literalt cond_literal=solver.convert(not_exprt(strategy_cond_expr)); + solver << literal_exprt(cond_literal); #ifdef DEBUG_OUTPUT - debug() << "post-pred: " << from_expr(ns,"",not_exprt(strategy_cond_expr)) << eom; + debug() << "post-pred: " + << from_expr(ns, "", not_exprt(strategy_cond_expr)) << eom; #endif - if(solver() == decision_proceduret::D_SATISFIABLE) - { - debug() << "SAT" << eom; - -#if 0 - for(replace_mapt::const_iterator - it=predabs_domain.renaming_map.begin(); - it!=predabs_domain.renaming_map.end(); - ++it) - { - debug() << "replace_map (1st): " << - from_expr(ns, "", it->first) << " " << - from_expr(ns, "", solver.get(it->first)) << eom; - debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) << " " - << from_expr(ns, "", solver.get(it->second)) << eom; - } + if(solver()==decision_proceduret::D_SATISFIABLE) + { + debug() << "SAT" << eom; + +#if 0 + for(replace_mapt::const_iterator + it=predabs_domain.renaming_map.begin(); + it!=predabs_domain.renaming_map.end(); + ++it) + { + debug() << "replace_map (1st): " << + from_expr(ns, "", it->first) << " " << + from_expr(ns, "", solver.get(it->first)) << eom; + debug() << "replace_map (2nd): " << from_expr(ns, "", it->second) + << " " << from_expr(ns, "", solver.get(it->second)) << eom; + } #endif - todo_notpreds.insert(*e_it); - - solver.pop_context(); + todo_notpreds.insert(*e_it); - } - else - { - - debug() << "UNSAT" << eom; + solver.pop_context(); + } + else + { + debug() << "UNSAT" << eom; - predabs_domain.set_row_value(*e_it, true_exprt(), inv); + predabs_domain.set_row_value(*e_it, true_exprt(), inv); - solver.pop_context(); + solver.pop_context(); - solver << preinv_expr; //make permanent + solver << preinv_expr; // make permanent - //due to transitivity, we would like to recheck predicates that did not hold - todo_preds.insert(todo_notpreds.begin(),todo_notpreds.end()); - todo_notpreds.clear(); - } + // due to transitivity, we would like to + // recheck predicates that did not hold + todo_preds.insert(todo_notpreds.begin(), todo_notpreds.end()); + todo_notpreds.clear(); + } - todo_preds.erase(e_it); + todo_preds.erase(e_it); - return CHANGED; - } + return true; + } - return CONVERGED; + return false; } diff --git a/src/domains/strategy_solver_predabs.h b/src/domains/strategy_solver_predabs.h index e31b547f8..04c8134b3 100644 --- a/src/domains/strategy_solver_predabs.h +++ b/src/domains/strategy_solver_predabs.h @@ -1,24 +1,31 @@ -#ifndef CPROVER_STRATEGY_SOLVER_PREDABS_H -#define CPROVER_STRATEGY_SOLVER_PREDABS_H +/*******************************************************************\ + +Module: Solver for predicate abstraction domain + +Author: Peter Schrammel, Cristina David + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_PREDABS_H +#define CPROVER_2LS_DOMAINS_STRATEGY_SOLVER_PREDABS_H #include "strategy_solver_base.h" #include "predabs_domain.h" -class strategy_solver_predabst : public strategy_solver_baset +class strategy_solver_predabst:public strategy_solver_baset { public: explicit strategy_solver_predabst( predabs_domaint &_predabs_domain, - incremental_solvert &_solver, - literalt _assertion_check, - const namespacet &_ns) : - strategy_solver_baset(_solver, _assertion_check, _ns), + incremental_solvert &_solver, + const namespacet &_ns): + strategy_solver_baset(_solver, literalt(), _ns), predabs_domain(_predabs_domain) { predabs_domain.get_row_set(todo_preds); } - virtual progresst iterate(invariantt &inv); + virtual bool iterate(invariantt &inv); protected: predabs_domaint &predabs_domain; @@ -26,8 +33,6 @@ class strategy_solver_predabst : public strategy_solver_baset typedef std::set worklistt; worklistt todo_preds; worklistt todo_notpreds; - - }; #endif diff --git a/src/domains/template_domain.cpp b/src/domains/template_domain.cpp deleted file mode 100644 index 4b010ccaf..000000000 --- a/src/domains/template_domain.cpp +++ /dev/null @@ -1,1300 +0,0 @@ -#include "template_domain.h" - -#include - -#include -#include -#include -#include - -#define SYMB_BOUND_VAR "symb_bound#" - -/*******************************************************************\ - -Function: template_domaint::initialize - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::initialize(valuet &value) -{ - templ_valuet &v = static_cast(value); - v.resize(templ.size()); - for(unsigned row = 0; row=vlower); - if(vlower+1==vupper) return from_integer(vlower,lower.type()); //floor - return from_integer((vupper+vlower)/2,lower.type()); - } - if(lower.type().id()==ID_floatbv && upper.type().id()==ID_floatbv) - { - ieee_floatt vlower(to_constant_expr(lower)); - ieee_floatt vupper(to_constant_expr(upper)); - if(vlower.get_sign()==vupper.get_sign()) - { - mp_integer plower = vlower.pack(); //compute "median" float number - mp_integer pupper = vupper.pack(); - //assert(pupper>=plower); - ieee_floatt res; - res.unpack((plower+pupper)/2); //...by computing integer mean - return res.to_expr(); - } - ieee_floatt res; - res.make_zero(); - return res.to_expr(); - } - assert(false); //types do not match or are not supported -} - -/*******************************************************************\ - -Function: template_domaint::leq - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::less_than(const row_valuet &v1, const row_valuet &v2) -{ - if(v1.type()==v2.type() && - (v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv)) - { - mp_integer vv1, vv2; - to_integer(v1, vv1); - to_integer(v2, vv2); - return vv1 row_expr <= row_value - -\*******************************************************************/ - -exprt template_domaint::get_row_constraint(const rowt &row, - const row_valuet &row_value) -{ - assert(row (row_expr <= row_value) ) - -\*******************************************************************/ - -exprt template_domaint::to_pre_constraints(const templ_valuet &value) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= row_value)) - to be connected disjunctively - -\*******************************************************************/ - -void template_domaint::make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, - exprt::operandst &value_exprs) -{ - assert(value.size()==templ.size()); - cond_exprs.resize(templ.size()); - value_exprs.resize(templ.size()); - - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_value) - -\*******************************************************************/ - -exprt template_domaint::get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value) -{ - assert(row= row_symb_value) (!!!) - -\*******************************************************************/ - -exprt template_domaint::get_row_symb_post_constraint(const rowt &row) -{ - assert(row (row_expr <= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_pre_constraints(const templ_valuet &value) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows) -{ - assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr >= symb_row_value) - -\*******************************************************************/ - -exprt template_domaint::to_symb_post_constraints() -{ - exprt::operandst c; - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - c.reserve(templ.size()); - for(unsigned row = 0; row(value); - assert(v.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); - - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) - { - if(vars.find(*it)==vars.end()) - { - pure = false; - break; - } - } - if(!pure) continue; - - const row_valuet &row_v = v[row]; - if(is_row_value_neginf(row_v)) c.push_back(false_exprt()); - else if(is_row_value_inf(row_v)) c.push_back(true_exprt()); - else c.push_back(binary_relation_exprt(templ_row.expr,ID_le,row_v)); - } - result = conjunction(c); -} - -/*******************************************************************\ - -Function: template_domaint::set_row_value - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::set_row_value( - const rowt &row, const template_domaint::row_valuet &row_value, templ_valuet &value) -{ - assert(row(value); - for(unsigned row = 0; row " << std::endl << " "; - break; - case IN: out << "(IN) "; break; - case OUT: case OUTL: out << "(OUT) "; break; - default: assert(false); - } - out << "( " << from_expr(ns,"",templ_row.expr) << " <= "; - if(is_row_value_neginf(v[row])) out << "-oo"; - else if(is_row_value_inf(v[row])) out << "oo"; - else out << from_expr(ns,"",v[row]); - out << " )" << std::endl; - } -} - -/*******************************************************************\ - -Function: template_domaint::output_domain - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::output_domain(std::ostream &out, const namespacet &ns) const -{ - for(unsigned row = 0; row " << std::endl << " "; - break; - case IN: - out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; - break; - case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; - break; - default: assert(false); - } - out << "( " << - from_expr(ns,"",templ_row.expr) << " <= CONST )" << std::endl; - } -} - -/*******************************************************************\ - -Function: template_domaint::template_size - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned template_domaint::template_size() -{ - return templ.size(); -} - -/*******************************************************************\ - -Function: template_domaint::is_row_value_neginf - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::is_row_value_neginf(const row_valuet & row_value) const -{ - return row_value.get(ID_value)==ID_false; -} - -/*******************************************************************\ - -Function: template_domaint::is_row_value_inf - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool template_domaint::is_row_value_inf(const row_valuet & row_value) const -{ - return row_value.get(ID_value)==ID_true; -} - -/*******************************************************************\ - -Function: extend_expr_types - - Inputs: - - Outputs: - - Purpose: increases bitvector sizes such that there are no overflows - -\*******************************************************************/ - -void extend_expr_types(exprt &expr) -{ -// std::cerr << "expr: " << expr << std::endl; - if(expr.id()==ID_typecast) assert(false); - if(expr.id()==ID_constant) return; - if(expr.id()==ID_symbol) return; - if(expr.id()==ID_index) return; - if(expr.id()==ID_unary_minus) - { - extend_expr_types(expr.op0()); - typet new_type = expr.op0().type(); - if(new_type.id()==ID_signedbv) - { - signedbv_typet &new_typebv = to_signedbv_type(new_type); - new_typebv.set_width(new_typebv.get_width()+1); - } - else if(new_type.id()==ID_unsignedbv) - { - unsignedbv_typet &old_type = to_unsignedbv_type(new_type); - new_type = signedbv_typet(old_type.get_width()+1); - } - expr = unary_minus_exprt(typecast_exprt(expr.op0(),new_type),new_type); - return; - } - if(expr.id()==ID_plus || expr.id()==ID_minus) - { - extend_expr_types(expr.op0()); -// std::cerr << "op0: " << expr.op0() << std::endl; - extend_expr_types(expr.op1()); -// std::cerr << "op1: " << expr.op1() << std::endl; - unsigned size0 = 0, size1 = 0; - if(expr.op0().type().id()==ID_signedbv) - size0 = to_signedbv_type(expr.op0().type()).get_width(); - if(expr.op0().type().id()==ID_unsignedbv) - size0 = to_unsignedbv_type(expr.op0().type()).get_width(); - if(expr.op1().type().id()==ID_signedbv) - size1 = to_signedbv_type(expr.op1().type()).get_width(); - if(expr.op1().type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(expr.op1().type()).get_width(); - assert(size0>0); assert(size1>0); //TODO: implement floats - typet new_type = expr.op0().type(); - if(expr.op0().type().id()==expr.op1().type().id()) - { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(std::max(size0,size1)+1); - else if(new_type.id()==ID_unsignedbv) - { - if(expr.id()==ID_minus) - new_type = signedbv_typet(std::max(size0,size1)+1); - else - new_type = unsignedbv_typet(std::max(size0,size1)+1); - } - else assert(false); - } - else - { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(size0<=size1 ? size1+2 : size0+1); - else if(new_type.id()==ID_unsignedbv) - new_type = signedbv_typet(size1<=size0 ? size0+2 : size1+1); - else assert(false); - } - if(expr.id()==ID_plus) - expr = plus_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - else if(expr.id()==ID_minus) - expr = minus_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - else assert(false); - return; - } - //TODO: implement mult - if(expr.id()==ID_mult) - { - extend_expr_types(expr.op0()); - extend_expr_types(expr.op1()); - unsigned size0 = 0, size1 = 0; - if(expr.op0().type().id()==ID_signedbv) - size0 = to_signedbv_type(expr.op0().type()).get_width(); - if(expr.op0().type().id()==ID_unsignedbv) - size0 = to_unsignedbv_type(expr.op0().type()).get_width(); - if(expr.op1().type().id()==ID_signedbv) - size1 = to_signedbv_type(expr.op1().type()).get_width(); - if(expr.op1().type().id()==ID_unsignedbv) - size1 = to_unsignedbv_type(expr.op1().type()).get_width(); - assert(size0>0); assert(size1>0); //TODO: implement floats - typet new_type = signedbv_typet(size0+size1+1); - expr = mult_exprt(typecast_exprt(expr.op0(),new_type),typecast_exprt(expr.op1(),new_type)); - return; - } - std::cerr << "expr: " << expr << std::endl; - assert(false); -} - -/*******************************************************************\ - -Function: make_interval_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_interval_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size(); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) - { - if(v->kind==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v->var; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v->var,v->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v->pre_guard; - templ_row.post_guard = v->post_guard; - templ_row.kind = v->kind; - } - } -} - -/*******************************************************************\ - -Function: make_zone_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_zone_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size()+var_specs.size()*(var_specs.size()-1); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) - { - if(v1->kind!=IN) //TODO: must be done in caller (for preconditions, e.g.) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) - { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); - - // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // x2 - x1 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - } - } -} - -/*******************************************************************\ - -Function: make_octagon_template - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void template_domaint::add_octagon_template(templatet &templ, - const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size()+2*var_specs.size()*(var_specs.size()-1); - templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) - { - if(v1->kind!=IN) //TODO: must be done in caller (for preconditions, e.g.) - { - // x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - templ_row.expr = v1->var; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - - // -x - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - unary_minus_exprt um_expr(v1->var,v1->var.type()); - extend_expr_types(um_expr); - templ_row.expr = um_expr; - templ_row.pre_guard = v1->pre_guard; - templ_row.post_guard = v1->post_guard; - templ_row.kind = v1->kind; - } - } - - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) - { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; //TODO: must be done in caller (for preconditions, e.g.) - - exprt pre_g = and_exprt(v1->pre_guard,v2->pre_guard); - exprt post_g = and_exprt(v1->post_guard,v2->post_guard); - simplify(pre_g,ns); - simplify(post_g,ns); - - // x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v1->var,v2->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // -x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt m_expr(v2->var,v1->var); - extend_expr_types(m_expr); - templ_row.expr = m_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // -x1 - x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - minus_exprt p_expr(unary_minus_exprt(v1->var,v1->var.type()),v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - - // x1 + x2 - { - templ.push_back(template_rowt()); - template_rowt &templ_row = templ.back(); - plus_exprt p_expr(v1->var,v2->var); - extend_expr_types(p_expr); - templ_row.expr = p_expr; - templ_row.pre_guard = pre_g; - templ_row.post_guard = post_g; - templ_row.kind = k; - } - } - } - -} - -/*******************************************************************\ - -Function: simplify_const - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -mp_integer simplify_const_int(const exprt &expr) -{ - if(expr.id()==ID_constant) - { - mp_integer v; - to_integer(expr, v); - return v; - } - if(expr.id()==ID_typecast) - { - const exprt &op0 = expr.op0(); - assert(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv); - return simplify_const_int(op0); - } - if(expr.id()==ID_unary_minus) return -simplify_const_int(expr.op0()); - if(expr.id()==ID_plus) return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); - if(expr.id()==ID_minus) return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); - if(expr.id()==ID_mult) return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); - if(expr.id()==ID_symbol) - { - std::cout << "substituting default value for " << expr << std::endl; - return 0; //default value if not substituted in expr - } - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_signedbv || array_type.id()==ID_unsignedbv) - { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow - assert(index<(index_expr.array().operands().size())); - return simplify_const_int(index_expr.array().operands()[index]); - } - assert(false); //not implemented - } - assert(false); //not implemented -} - -ieee_floatt simplify_const_float(const exprt &expr) -{ - if(expr.id()==ID_constant) - { - ieee_floatt v(to_constant_expr(expr)); - return v; - } - if(expr.id()==ID_typecast) - { - const exprt &op0 = expr.op0(); - if(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv) - { - ieee_floatt v; - v.from_integer(simplify_const_int(op0)); - return v; - } - assert(false); - } - if(expr.id()==ID_unary_minus) - { - ieee_floatt v = simplify_const_float(expr.op0()); - v.set_sign(!v.get_sign()); - return v; - } - if(expr.id()==ID_plus) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 += v2; - return v1; - } - if(expr.id()==ID_minus) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 -= v2; - return v1; - } - if(expr.id()==ID_mult) - { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 *= v2; - return v1; - } - if(expr.id()==ID_symbol) //default value if not substituted in expr - { - ieee_floatt v; - v.make_zero(); - - std::cout << "substituting default value for " << expr << std::endl; - - return v; - } - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_float) - { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow - assert(index<(index_expr.array().operands().size())); - return simplify_const_float(index_expr.array().operands()[index]); - } - assert(false); //not implemented - } - assert(false); //not implemented -} - -constant_exprt simplify_const(const exprt &expr) -{ - if(expr.id()==ID_constant) return to_constant_expr(expr); - if(expr.id()==ID_index) - { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); - if(array_type.id()==ID_signedbv) - { - mp_integer res = simplify_const_int(index_expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(array_type.id()==ID_unsignedbv) - { - mp_integer res = simplify_const_int(index_expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(array_type.id()==ID_float) - return to_constant_expr(simplify_const_float(index_expr).to_expr()); - assert(false); //not implemented - } - // if(expr.id()==ID_typecast) return to_constant_expr(expr.op0()); - if(expr.type().id()==ID_signedbv) - { - mp_integer res = simplify_const_int(expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(expr.type().id()==ID_unsignedbv) - { - mp_integer res = simplify_const_int(expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); - assert(res>=type.smallest()); - assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); - } - if(expr.type().id()==ID_floatbv) - { - return to_constant_expr(simplify_const_float(expr).to_expr()); - } - assert(false); //type not supported -} diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index a59afcda7..e95c83fcc 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -6,17 +6,17 @@ Author: Peter Schrammel \*******************************************************************/ -#include "template_generator_base.h" -#include "equality_domain.h" -#include "tpolyhedra_domain.h" -#include "predabs_domain.h" - #include #include #include #include #include +#include "template_generator_base.h" +#include "equality_domain.h" +#include "tpolyhedra_domain.h" +#include "predabs_domain.h" + #ifdef DEBUG #include #endif @@ -33,29 +33,31 @@ Function: template_generator_baset::get_pre_post_guards \*******************************************************************/ -void template_generator_baset::get_pre_post_guards(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard) +void template_generator_baset::get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, + exprt &post_guard) { #if 0 - std::cout << "post-location: " - << n_it->location->location_number << std::endl; - assert(n_it->loophead != SSA.nodes.end()); - std::cout << "pre-location: " - << n_it->loophead->location->location_number << std::endl; + std::cout << "post-location: " + << n_it->location->location_number << std::endl; + assert(n_it->loophead!=SSA.nodes.end()); + std::cout << "pre-location: " + << n_it->loophead->location->location_number << std::endl; #endif - exprt lhguard = SSA.guard_symbol(n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); - exprt lsguard = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); - pre_guard = and_exprt(lhguard,lsguard); - - exprt pguard = SSA.guard_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); - exprt pcond = SSA.cond_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); - post_guard = and_exprt(pguard,pcond); + exprt lhguard=SSA.guard_symbol(n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), *n_it, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), *n_it, true); + pre_guard=and_exprt(lhguard, lsguard); + + exprt pguard=SSA.guard_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), *n_it, false); + exprt pcond=SSA.cond_symbol(n_it->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), *n_it, false); + post_guard=and_exprt(pguard, pcond); } /*******************************************************************\ @@ -70,20 +72,21 @@ Function: template_generator_baset::get_pre_var \*******************************************************************/ -void template_generator_baset::get_pre_var(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - symbol_exprt &pre_var) +void template_generator_baset::get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var) { - pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); + pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); - symbol_exprt post_var = SSA.read_rhs(*o_it, n_it->location); - ssa_local_unwinder.unwinder_rename(post_var,*n_it,false); - post_renaming_map[pre_var] = post_var; + symbol_exprt post_var=SSA.read_rhs(*o_it, n_it->location); + ssa_local_unwinder.unwinder_rename(post_var, *n_it, false); + post_renaming_map[pre_var]=post_var; rename_aux_post(post_var); - aux_renaming_map[pre_var]=post_var; + aux_renaming_map[pre_var]=post_var; } /*******************************************************************\ @@ -94,36 +97,35 @@ Function: template_generator_baset::get_init_expr Outputs: - Purpose: supposes that loop head PHIs are of the form - xphi = gls?xlb:x0 + Purpose: supposes that loop head PHIs are of the form + xphi=gls?xlb:x0 \*******************************************************************/ -void template_generator_baset::get_init_expr(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - exprt &init_expr) +void template_generator_baset::get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr) { - symbol_exprt phi_var = SSA.name(*o_it, local_SSAt::PHI, - n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(phi_var,*n_it->loophead,true); - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->loophead->equalities.begin(); - e_it != n_it->loophead->equalities.end(); e_it++) + symbol_exprt phi_var= + SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); + ssa_local_unwinder.unwinder_rename(phi_var, *n_it->loophead, true); + for(const auto &e : n_it->loophead->equalities) { - if (e_it->rhs().id() == ID_if && - to_symbol_expr(e_it->lhs()).get_identifier()==phi_var.get_identifier()) + if(e.rhs().id()==ID_if && + to_symbol_expr(e.lhs()).get_identifier()==phi_var.get_identifier()) { - const if_exprt &if_expr = to_if_expr(e_it->rhs()); - init_expr = if_expr.false_case(); - //should already be renamed for inner loops + const if_exprt &if_expr=to_if_expr(e.rhs()); + init_expr=if_expr.false_case(); + // should already be renamed for inner loops break; } } - symbol_exprt pre_var = SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - ssa_local_unwinder.unwinder_rename(pre_var,*n_it,true); - init_renaming_map[pre_var]=init_expr; + symbol_exprt pre_var=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); + ssa_local_unwinder.unwinder_rename(pre_var, *n_it, true); + init_renaming_map[pre_var]=init_expr; } /*******************************************************************\ @@ -138,47 +140,50 @@ Function: template_generator_baset::collect_variables_loop \*******************************************************************/ -void template_generator_baset::collect_variables_loop(const local_SSAt &SSA,bool forward) +void template_generator_baset::collect_variables_loop( + const local_SSAt &SSA, + bool forward) { // used for renaming map var_listt pre_state_vars, post_state_vars; // add loop variables - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop { exprt pre_guard, post_guard; - get_pre_post_guards(SSA,n_it,pre_guard, post_guard); + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); - const ssa_domaint::phi_nodest &phi_nodes = + const ssa_domaint::phi_nodest &phi_nodes= SSA.ssa_analysis[n_it->loophead->location].phi_nodes; - + // Record the objects modified by the loop to get // 'primed' (post-state) and 'unprimed' (pre-state) variables. for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); + o_it=SSA.ssa_objects.objects.begin(); o_it!=SSA.ssa_objects.objects.end(); o_it++) { ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + phi_nodes.find(o_it->get_identifier()); - if(p_it==phi_nodes.end()) continue; // object not modified in this loop + if(p_it==phi_nodes.end()) // object not modified in this loop + continue; symbol_exprt pre_var; - get_pre_var(SSA,o_it,n_it,pre_var); + get_pre_var(SSA, o_it, n_it, pre_var); exprt init_expr; - get_init_expr(SSA,o_it,n_it,init_expr); - add_var(pre_var,pre_guard,post_guard,domaint::LOOP,var_specs); - - #ifdef DEBUG - std::cout << "Adding " << from_expr(ns, "", in) << " " << - from_expr(ns, "", out) << std::endl; - #endif - } - } + get_init_expr(SSA, o_it, n_it, init_expr); + add_var(pre_var, pre_guard, post_guard, domaint::LOOP, var_specs); + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " << + from_expr(ns, "", out) << std::endl; +#endif + } + } } } @@ -197,10 +202,9 @@ Function: template_generator_baset::all_vars domaint::var_sett template_generator_baset::all_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - vars.insert(v->var); + vars.insert(v.var); } return vars; } @@ -221,19 +225,18 @@ void template_generator_baset::filter_template_domain() { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - const domaint::vart &s = v->var; + const domaint::vart &s=v.var; #ifdef DEBUG std::cout << "var: " << s << std::endl; #endif if((s.type().id()==ID_unsignedbv || s.type().id()==ID_signedbv || - s.type().id()==ID_floatbv /*|| s.type().id()==ID_c_enum_tag*/)) + s.type().id()==ID_floatbv /*|| s.type().id()==ID_c_enum_tag*/)) { - var_specs.push_back(*v); + var_specs.push_back(v); } } } @@ -254,16 +257,15 @@ void template_generator_baset::filter_equality_domain() { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - var_specs.push_back(*v); + var_specs.push_back(v); } } /*******************************************************************\ -Function: template_generator_baset::add_vars +Function: template_generator_baset::add_var Inputs: @@ -273,97 +275,123 @@ Function: template_generator_baset::add_vars \*******************************************************************/ -void template_generator_baset::add_var(const domaint::vart &var, - const domaint::guardt &pre_guard, - domaint::guardt post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +void template_generator_baset::add_var( + const domaint::vart &var, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - exprt aux_expr = true_exprt(); + exprt aux_expr=true_exprt(); if(std_invariants && pre_guard.id()==ID_and) { - exprt init_guard = and_exprt(pre_guard.op0(),not_exprt(pre_guard.op1())); - exprt post_var = post_renaming_map[var]; - exprt aux_var = aux_renaming_map[var]; - aux_expr = and_exprt( + exprt init_guard=and_exprt(pre_guard.op0(), not_exprt(pre_guard.op1())); + exprt post_var=post_renaming_map[var]; + exprt aux_var=aux_renaming_map[var]; + aux_expr=and_exprt( implies_exprt(and_exprt(post_guard, not_exprt(init_guard)), - equal_exprt(aux_var,post_var)), - implies_exprt(init_guard,equal_exprt(aux_var,init_renaming_map[var]))); - post_guard = or_exprt(post_guard,init_guard); + equal_exprt(aux_var, post_var)), + implies_exprt(init_guard, equal_exprt(aux_var, init_renaming_map[var]))); + post_guard=or_exprt(post_guard, init_guard); } if(var.type().id()!=ID_array) { var_specs.push_back(domaint::var_spect()); - domaint::var_spect &var_spec = var_specs.back(); - var_spec.pre_guard = pre_guard; - var_spec.post_guard = post_guard; - var_spec.aux_expr = aux_expr; - var_spec.kind = kind; - var_spec.var = var; + domaint::var_spect &var_spec=var_specs.back(); + var_spec.pre_guard=pre_guard; + var_spec.post_guard=post_guard; + var_spec.aux_expr=aux_expr; + var_spec.kind=kind; + var_spec.var=var; } - //arrays + // arrays if(var.type().id()==ID_array && options.get_bool_option("arrays")) { - const array_typet &array_type = to_array_type(var.type()); + const array_typet &array_type=to_array_type(var.type()); mp_integer size; to_integer(array_type.size(), size); - for(mp_integer i=0; i &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) -{ - for(std::set::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); -} +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ -void template_generator_baset::add_vars(const local_SSAt::var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +void template_generator_baset::add_vars( + const local_SSAt::var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(local_SSAt::var_listt::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } -void template_generator_baset::add_vars(const local_SSAt::var_sett &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::add_vars( + const local_SSAt::var_sett &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(local_SSAt::var_sett::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } -void template_generator_baset::add_vars(const var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs) +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void template_generator_baset::add_vars( + const var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) { - for(var_listt::const_iterator it = vars_to_add.begin(); - it != vars_to_add.end(); it++) - add_var(*it,pre_guard,post_guard,kind,var_specs); + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); } /*******************************************************************\ @@ -380,13 +408,249 @@ Function: template_generator_baset::handle_special_functions void template_generator_baset::handle_special_functions(const local_SSAt &SSA) { - const irep_idt &function_id = SSA.goto_function.body.instructions.front().function; + const irep_idt &function_id= + SSA.goto_function.body.instructions.front().function; + if(id2string(function_id)=="__CPROVER_initialize") + { + options.set_option("intervals", true); + options.set_option("enum-solver", true); + } +} + +/*******************************************************************\ + +Function: template_generator_baset::replace_post + + Inputs: + + Outputs: + + Purpose: rename custom template to correct SSA identifiers + +\*******************************************************************/ + +bool template_generator_baset::replace_post( + replace_mapt replace_map, + exprt &expr) +{ + bool replaced=false; + if(expr.id()==ID_function_application) + { + const function_application_exprt &f=to_function_application_expr(expr); + if(f.function().get(ID_identifier)==TEMPLATE_NEWVAR) + { + assert(f.arguments().size()==1); + if(f.arguments()[0].id()==ID_typecast) + expr=replace_map[f.arguments()[0].op0()]; + else + expr=replace_map[f.arguments()[0]]; + return true; + } + } + for(unsigned i=0; iloophead->location].phi_nodes; + + for(const auto &object : SSA.ssa_objects.objects) { - options.set_option("intervals",true); - options.set_option("enum-solver",true); + ssa_domaint::phi_nodest::const_iterator p_it= + phi_nodes.find(object.get_identifier()); + + if(p_it!=phi_nodes.end()) // modified in loop + { + // rename to pre + replace_map[object.get_expr()]= + SSA.name(object, local_SSAt::LOOP_BACK, n_it->location); + + // rename to post + replace_post_map[object.get_expr()]= + SSA.read_rhs(object, n_it->location); + // TODO: unwinding + } + else // not modified in loop + { + // rename to id valid at loop head + replace_map[object.get_expr()]= + SSA.read_rhs(object, n_it->loophead->location); + // TODO: unwinding + } } + + bool contains_newvar=replace_post(replace_post_map, expr); + replace_expr(replace_map, expr); + return contains_newvar; +} + +/*******************************************************************\ + +Function: template_generator_baset::instantiate_custom_templates + + Inputs: + + Outputs: + + Purpose: [experimental] + +\*******************************************************************/ + +bool template_generator_baset::instantiate_custom_templates( + const local_SSAt &SSA) +{ + // TODO: the code below cannot work for unwound SSA + // we deactivate it for now + return false; + + // used for renaming map + var_listt pre_state_vars, post_state_vars; + + bool found_poly=false, found_predabs=false; + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) // we've found a loop + { + exprt pre_guard, post_guard, aux_expr; + get_pre_post_guards(SSA, n_it, pre_guard, post_guard); + aux_expr=true_exprt(); // TODO: change to "standard" invariant semantics + bool add_post_vars=false; + + // search for templates in the loop + for(local_SSAt::nodest::const_iterator nn_it=n_it->loophead; + nn_it!=n_it; nn_it++) + { + if(nn_it->templates.empty()) + continue; +#if 1 + // TODO: there is an unwinder-related bug + if(nn_it->templates.size()>1000) + continue; +#endif + for(local_SSAt::nodet::templatest::const_iterator t_it= + nn_it->templates.begin(); + t_it!=nn_it->templates.end(); t_it++) + { + debug() << "Template expression: " + << from_expr(SSA.ns, "", *t_it) << eom; + + // check whether it is a template polyhedra or a pred abs + std::set symbols; + find_symbols(*t_it, symbols); + + bool predabs=true; + for(std::set::iterator it=symbols.begin(); + it!=symbols.end(); it++) + { + std::size_t found_param= + id2string(it->get_identifier()).find(TEMPLATE_PARAM_PREFIX); + if(found_param!=std::string::npos) + { + predabs=false; + break; + } + } + + // template polyhedra + if(!predabs && t_it->id()==ID_le) + { + debug() << "Custom template polyhedron found" << eom; + if(!found_poly) // create domain + { + domain_ptr=new tpolyhedra_domaint( + domain_number, + post_renaming_map, + SSA.ns); // TODO: aux_renaming_map + found_poly=true; + } + + exprt expr=t_it->op0(); + bool contains_new_var=build_custom_expr(SSA, n_it, expr); + if(contains_new_var) + add_post_vars=true; + + static_cast(domain_ptr)->add_template_row( + expr, pre_guard, + contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + } + // pred abs domain + else if(predabs) + { + options.set_option("predabs-solver", true); + + debug() << "Custom predicate template found" << eom; + if(!found_predabs) // create domain + { + domain_ptr=new predabs_domaint( + domain_number, + post_renaming_map, SSA.ns); // TODO: aux_renaming_map + found_predabs=true; + } + + exprt expr=*t_it; + bool contains_new_var=build_custom_expr(SSA, n_it, expr); + if(contains_new_var) + add_post_vars=true; + + static_cast(domain_ptr)->add_template_row( + expr, pre_guard, + contains_new_var ? and_exprt(pre_guard, post_guard) : post_guard, + aux_expr, + contains_new_var ? domaint::OUT : domaint::LOOP); + } + else // neither pred abs, nor polyhedra + { + warning() << "ignoring unsupported template " + << from_expr(SSA.ns, "", *t_it) << eom; + } + } + if(add_post_vars) // for result retrieval via all_vars() only + { + domaint::var_specst new_var_specs(var_specs); + var_specs.clear(); + for(domaint::var_specst::const_iterator v=new_var_specs.begin(); + v!=new_var_specs.end(); v++) + { + var_specs.push_back(*v); + if(v->kind==domaint::LOOP) + { + var_specs.push_back(*v); + var_specs.back().kind=domaint::OUTL; + replace_expr(aux_renaming_map, var_specs.back().var); + } + } + } + } + } + } + + return (found_poly || found_predabs); } /*******************************************************************\ @@ -401,6 +665,7 @@ Function: template_generator_baset::build_custom_expr \*******************************************************************/ +/* bool template_generator_baset::replace_post(replace_mapt replace_map, exprt &expr) { bool replaced = false; @@ -466,6 +731,7 @@ bool template_generator_baset::build_custom_expr(const local_SSAt &SSA, replace_expr(replace_map,expr); return contains_newvar; } +*/ /*******************************************************************\ @@ -475,17 +741,14 @@ Function: template_generator_baset::instantiate_custom_templates Outputs: - Purpose: [experimental] + Purpose: \*******************************************************************/ +/* bool template_generator_baset::instantiate_custom_templates( const local_SSAt &SSA) { - //TODO: the code below cannot work for unwound SSA - // we deactivate it for now - return false; - // used for renaming map var_listt pre_state_vars, post_state_vars; @@ -537,8 +800,7 @@ bool template_generator_baset::instantiate_custom_templates( if(!found_poly) //create domain { domain_ptr = new tpolyhedra_domaint(domain_number, - post_renaming_map, - SSA.ns); //TODO: aux_renaming_map + post_renaming_map); //TODO: aux_renaming_map found_poly = true; } exprt expr = t_it->op0(); @@ -559,7 +821,7 @@ bool template_generator_baset::instantiate_custom_templates( if(!found_predabs) //create domain { domain_ptr = new predabs_domaint(domain_number, - post_renaming_map, SSA.ns); //TODO: aux_renaming_map + post_renaming_map); //TODO: aux_renaming_map found_predabs = true; } exprt expr = *t_it; @@ -598,6 +860,7 @@ bool template_generator_baset::instantiate_custom_templates( return (found_poly || found_predabs); } +*/ /*******************************************************************\ @@ -611,52 +874,53 @@ Function: template_generator_baset::instantiate_standard_domains \*******************************************************************/ -void template_generator_baset::instantiate_standard_domains(const local_SSAt &SSA) +void template_generator_baset::instantiate_standard_domains( + const local_SSAt &SSA) { - replace_mapt &renaming_map = + replace_mapt &renaming_map= std_invariants ? aux_renaming_map : post_renaming_map; - - //get domain from command line options + + // get domain from command line options if(options.get_bool_option("equalities")) { filter_equality_domain(); - domain_ptr = new equality_domaint(domain_number, - renaming_map, var_specs, SSA.ns); + domain_ptr= + new equality_domaint(domain_number, renaming_map, var_specs, SSA.ns); } else if(options.get_bool_option("intervals")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_interval_template( var_specs, SSA.ns); } else if(options.get_bool_option("zones")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_interval_template( - var_specs, SSA.ns); static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); + static_cast(domain_ptr)->add_interval_template( + var_specs, SSA.ns); } else if(options.get_bool_option("octagons")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); - static_cast(domain_ptr)->add_interval_template( + static_cast(domain_ptr)->add_sum_template( var_specs, SSA.ns); static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); - static_cast(domain_ptr)->add_sum_template( + static_cast(domain_ptr)->add_interval_template( var_specs, SSA.ns); } else if(options.get_bool_option("qzones")) { - domain_ptr = new tpolyhedra_domaint(domain_number, - renaming_map, SSA.ns); + domain_ptr= + new tpolyhedra_domaint(domain_number, renaming_map, SSA.ns); filter_template_domain(); static_cast(domain_ptr)->add_difference_template( var_specs, SSA.ns); diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index 2e423fe91..d745f1dbf 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -6,122 +6,130 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_BASE_H -#define DELTACHECK_TEMPLATE_GENERATOR_BASE_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" +#include +#include #include "strategy_solver_base.h" -#define SHOW_TEMPLATE_VARIABLES -#define SHOW_TEMPLATE +// #define SHOW_TEMPLATE_VARIABLES +// #define SHOW_TEMPLATE -class template_generator_baset : public messaget +class template_generator_baset:public messaget { public: typedef strategy_solver_baset::var_listt var_listt; - explicit template_generator_baset(optionst &_options, ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - options(_options), ssa_db(_ssa_db), - ssa_local_unwinder(_ssa_local_unwinder) + explicit template_generator_baset( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + options(_options), ssa_db(_ssa_db), + ssa_local_unwinder(_ssa_local_unwinder) { - std_invariants = options.get_bool_option("std-invariants"); - } + std_invariants=options.get_bool_option("std-invariants"); + } - virtual ~template_generator_baset() + virtual ~template_generator_baset() { - if(domain_ptr!=NULL) delete domain_ptr; + if(domain_ptr!=NULL) + delete domain_ptr; } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true) - { - domain_number = _domain_number; + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true) + { + domain_number=_domain_number; assert(false); } virtual domaint::var_sett all_vars(); + bool empty() { assert(domain_ptr!=NULL); return domain_ptr->is_spec_empty(); } - domaint *domain() { assert(domain_ptr!=NULL); return domain_ptr; } + inline domaint *domain() { assert(domain_ptr!=NULL); return domain_ptr; } domaint::var_specst var_specs; replace_mapt post_renaming_map; replace_mapt init_renaming_map; replace_mapt aux_renaming_map; - unsigned domain_number; //serves as id for variables names + unsigned domain_number; // serves as id for variables names optionst options; // copy: we may override options protected: const ssa_dbt &ssa_db; const ssa_local_unwindert &ssa_local_unwinder; - domaint* domain_ptr; - bool std_invariants; //include value at loop entry + domaint *domain_ptr; + bool std_invariants; // include value at loop entry - virtual void collect_variables_loop(const local_SSAt &SSA, - bool forward); + virtual void collect_variables_loop( + const local_SSAt &SSA, + bool forward); void filter_template_domain(); void filter_equality_domain(); - void add_var(const domaint::vart &var_to_add, - const domaint::guardt &pre_guard, - domaint::guardt post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const std::set &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const local_SSAt::var_listt &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - void add_vars(const local_SSAt::var_sett &vars_to_add, - const domaint::guardt &pre_guard, - const domaint::guardt &post_guard, - const domaint::kindt &kind, - domaint::var_specst &var_specs); - - void get_pre_post_guards(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard); - void get_pre_var(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - symbol_exprt &pre_var); - - void get_init_expr(const local_SSAt &SSA, - local_SSAt::objectst::const_iterator o_it, - local_SSAt::nodest::const_iterator n_it, - exprt &init_expr); + void add_var( + const domaint::vart &var_to_add, + const domaint::guardt &pre_guard, + domaint::guardt post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const local_SSAt::var_listt &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + void add_vars( + const local_SSAt::var_sett &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); + + void get_pre_post_guards( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &pre_guard, exprt &post_guard); + void get_pre_var( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + symbol_exprt &pre_var); + + void get_init_expr( + const local_SSAt &SSA, + local_SSAt::objectst::const_iterator o_it, + local_SSAt::nodest::const_iterator n_it, + exprt &init_expr); bool replace_post(replace_mapt replace_map, exprt &expr); - bool build_custom_expr(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - exprt &expr); + bool build_custom_expr( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + exprt &expr); virtual void handle_special_functions(const local_SSAt &SSA); void instantiate_standard_domains(const local_SSAt &SSA); bool instantiate_custom_templates(const local_SSAt &SSA); - void rename_aux_post(symbol_exprt &expr) - { + void rename_aux_post(symbol_exprt &expr) + { expr.set_identifier(id2string(expr.get_identifier())+"'"); } }; - -#endif +#endif // CROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_BASE_H diff --git a/src/domains/template_generator_callingcontext.cpp b/src/domains/template_generator_callingcontext.cpp index bf38e3e6f..310ea4303 100644 --- a/src/domains/template_generator_callingcontext.cpp +++ b/src/domains/template_generator_callingcontext.cpp @@ -6,12 +6,13 @@ Author: Peter Schrammel \*******************************************************************/ +#include + +#include + #include "template_generator_callingcontext.h" #include "equality_domain.h" #include "tpolyhedra_domain.h" -#include "../ssa/ssa_inliner.h" - -#include /*******************************************************************\ @@ -25,27 +26,28 @@ Function: template_generator_callingcontextt::operator() \*******************************************************************/ -void template_generator_callingcontextt::operator()(unsigned _domain_number, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward) +void template_generator_callingcontextt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! - collect_variables_loop(SSA,forward); - collect_variables_callingcontext(SSA,n_it,f_it,forward); + collect_variables_loop(SSA, forward); + collect_variables_callingcontext(SSA, n_it, f_it, forward); - //get domain from command line options - instantiate_standard_domains(SSA); + // get domain from command line options + instantiate_standard_domains(SSA); #if 1 debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -61,53 +63,58 @@ Function: template_generator_callingcontextt::collect_variables_callingcontext \*******************************************************************/ void template_generator_callingcontextt::collect_variables_callingcontext( - const local_SSAt &SSA, + const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, bool forward) { - exprt guard = SSA.guard_symbol(n_it->location); + exprt guard=SSA.guard_symbol(n_it->location); - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - const local_SSAt &fSSA = ssa_db.get(fname); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const local_SSAt &fSSA=ssa_db.get(fname); - //getting globals at call site + // getting globals at call site local_SSAt::var_sett cs_globals_in, globals_in; if(forward) { - SSA.get_globals(n_it->location,cs_globals_in,true,false); - //filter out return values - globals_in = fSSA.globals_in; + SSA.get_globals(n_it->location, cs_globals_in, true, false); + // filter out return values + globals_in=fSSA.globals_in; } else { - SSA.get_globals(n_it->location,cs_globals_in,false,true,fname); - //with return values for function call - globals_in = fSSA.globals_out; + SSA.get_globals(n_it->location, cs_globals_in, false, true, fname); + // with return values for function call + globals_in=fSSA.globals_out; } - for(local_SSAt::var_sett::iterator v_it = cs_globals_in.begin(); - v_it != cs_globals_in.end(); v_it++) + for(local_SSAt::var_sett::iterator v_it=cs_globals_in.begin(); + v_it!=cs_globals_in.end(); v_it++) { symbol_exprt dummy; - if(ssa_inlinert::find_corresponding_symbol(*v_it,globals_in,dummy)) - add_var(*v_it,guard,guard, - domaint::OUT, //the same for both forward and backward - var_specs); + if(ssa_inlinert::find_corresponding_symbol(*v_it, globals_in, dummy)) + add_var( + *v_it, + guard, + guard, + domaint::OUT, // the same for both forward and backward + var_specs); } - if(!forward) return; //TODO: actually, the context should contain both, arguments and return values + // TODO: actually, the context should contain both, + // arguments and return values + if(!forward) + return; - //add function arguments - for(exprt::operandst::const_iterator a_it = f_it->arguments().begin(); - a_it != f_it->arguments().end(); a_it++) + // add function arguments + for(exprt::operandst::const_iterator a_it=f_it->arguments().begin(); + a_it!=f_it->arguments().end(); a_it++) { std::set args; - find_symbols(*a_it,args); - add_vars(args,guard,guard,domaint::OUT,var_specs); + find_symbols(*a_it, args); + add_vars(args, guard, guard, domaint::OUT, var_specs); } - } /*******************************************************************\ @@ -125,10 +132,10 @@ Function: template_generator_callingcontextt::callingcontext_vars domaint::var_sett template_generator_callingcontextt::callingcontext_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + for(const auto &v : var_specs) { - if(v->kind==domaint::OUT) vars.insert(v->var); + if(v.kind==domaint::OUT) + vars.insert(v.var); } return vars; } diff --git a/src/domains/template_generator_callingcontext.h b/src/domains/template_generator_callingcontext.h index 946a63ab6..a86d35db8 100644 --- a/src/domains/template_generator_callingcontext.h +++ b/src/domains/template_generator_callingcontext.h @@ -6,36 +6,37 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_CALLINGCONTEXT_H -#define DELTACHECK_TEMPLATE_GENERATOR_CALLINGCONTEXT_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_CALLINGCONTEXT_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_CALLINGCONTEXT_H #include "template_generator_base.h" -class template_generator_callingcontextt : public template_generator_baset +class template_generator_callingcontextt:public template_generator_baset { public: - explicit template_generator_callingcontextt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_callingcontextt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } + } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward=true); + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward=true); virtual domaint::var_sett callingcontext_vars(); protected: - - virtual void collect_variables_callingcontext(const local_SSAt &SSA, + virtual void collect_variables_callingcontext( + const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, bool forward); }; - #endif diff --git a/src/domains/template_generator_ranking.cpp b/src/domains/template_generator_ranking.cpp index 0d1b331f8..fab04e251 100644 --- a/src/domains/template_generator_ranking.cpp +++ b/src/domains/template_generator_ranking.cpp @@ -7,7 +7,6 @@ Author: Peter Schrammel \*******************************************************************/ #include "template_generator_ranking.h" - #include "linrank_domain.h" #include "lexlinrank_domain.h" @@ -32,30 +31,32 @@ Function: template_generator_rankingt::operator() \*******************************************************************/ -void template_generator_rankingt::operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward) +void template_generator_rankingt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! if(options.get_bool_option("monolithic-ranking-function")) { - domain_ptr = new linrank_domaint(domain_number,post_renaming_map, SSA.ns); + domain_ptr=new linrank_domaint(domain_number, post_renaming_map, SSA.ns); } else { - domain_ptr = new lexlinrank_domaint(domain_number,post_renaming_map, SSA.ns); + domain_ptr=new lexlinrank_domaint(domain_number, post_renaming_map, SSA.ns); } - collect_variables_ranking(SSA,forward); + collect_variables_ranking(SSA, forward); - options.set_option("compute-ranking-functions",true); + options.set_option("compute-ranking-functions", true); #if 1 debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -70,61 +71,63 @@ Function: template_generator_rankingt::collect_variables_ranking \*******************************************************************/ -void template_generator_rankingt::collect_variables_ranking(const local_SSAt &SSA,bool forward) +void template_generator_rankingt::collect_variables_ranking( + const local_SSAt &SSA, + bool forward) { // used for renaming map var_listt pre_state_vars, post_state_vars; // add loop variables - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(const auto &node : SSA.nodes) { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop + // we've found a loop + if(node.loophead!=SSA.nodes.end()) { domaint::var_specst new_var_specs; - exprt lhguard = SSA.guard_symbol(n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard),*n_it,true); - exprt lsguard = SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard),*n_it,true); - exprt pre_guard = lhguard; //and_exprt(lhguard,lsguard); - - exprt pguard = SSA.guard_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard),*n_it,false); - exprt pcond = SSA.cond_symbol(n_it->location); - ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond),*n_it,false); - exprt post_guard = and_exprt(pguard,pcond); - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[n_it->loophead->location].phi_nodes; - + exprt lhguard=SSA.guard_symbol(node.loophead->location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lhguard), node, true); + exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(lsguard), node, true); + exprt pre_guard=lhguard; + + exprt pguard=SSA.guard_symbol(node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pguard), node, false); + exprt pcond=SSA.cond_symbol(node.location); + ssa_local_unwinder.unwinder_rename(to_symbol_expr(pcond), node, false); + exprt post_guard=and_exprt(pguard, pcond); + + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[node.loophead->location].phi_nodes; + // Record the objects modified by the loop to get // 'primed' (post-state) and 'unprimed' (pre-state) variables. - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) + for(const auto &object : SSA.ssa_objects.objects) { ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); + phi_nodes.find(object.get_identifier()); + + // object not modified in this loop + if(p_it==phi_nodes.end()) + continue; - if(p_it==phi_nodes.end()) continue; // object not modified in this loop + symbol_exprt in= + SSA.name(object, local_SSAt::PHI, node.loophead->location); + ssa_local_unwinder.unwinder_rename(in, node, true); + symbol_exprt out=SSA.read_rhs(object, node.location); + ssa_local_unwinder.unwinder_rename(out, node, false); -// symbol_exprt in=SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - symbol_exprt in=SSA.name(*o_it, local_SSAt::PHI, n_it->loophead->location); - ssa_local_unwinder.unwinder_rename(in,*n_it,true); - symbol_exprt out=SSA.read_rhs(*o_it, n_it->location); - ssa_local_unwinder.unwinder_rename(out,*n_it,false); + add_var(in, pre_guard, post_guard, domaint::LOOP, new_var_specs); - add_var(in,pre_guard,post_guard,domaint::LOOP,new_var_specs); - // building map for renaming from pre into post-state - post_renaming_map[in] = out; - - #ifdef DEBUG - std::cout << "Adding " << from_expr(ns, "", in) << " " << - from_expr(ns, "", out) << std::endl; - #endif + post_renaming_map[in]=out; + +#ifdef DEBUG + std::cout << "Adding " << from_expr(ns, "", in) << " " + << from_expr(ns, "", out) << std::endl; +#endif } filter_ranking_domain(new_var_specs); @@ -137,8 +140,9 @@ void template_generator_rankingt::collect_variables_ranking(const local_SSAt &SS new_var_specs, SSA.ns); #endif - var_specs.insert(var_specs.end(),new_var_specs.begin(),new_var_specs.end()); - } + var_specs.insert( + var_specs.end(), new_var_specs.begin(), new_var_specs.end()); + } } } @@ -154,24 +158,27 @@ Function: template_generator_rankingt::filter_ranking_domain \*******************************************************************/ -void template_generator_rankingt::filter_ranking_domain(domaint::var_specst &var_specs) +void template_generator_rankingt::filter_ranking_domain( + domaint::var_specst &var_specs) { domaint::var_specst new_var_specs(var_specs); var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) + for(const auto &v : new_var_specs) { - const domaint::vart &s = v->var; - if(s.type().id()==ID_unsignedbv || s.type().id()==ID_signedbv || + const domaint::vart &s=v.var; + if(s.type().id()==ID_unsignedbv || + s.type().id()==ID_signedbv || s.type().id()==ID_floatbv) { - var_specs.push_back(*v); + var_specs.push_back(v); } + #if 0 if(s.type().id()==ID_pointer) { - domaint::var_spect new_varspec = *v; - new_varspec.var = typecast_exprt(v->var,to_pointer_type(v->var.type()).subtype()); + domaint::var_spect new_varspec=v; + new_varspec.var= + typecast_exprt(v.var, to_pointer_type(v.var.type()).subtype()); var_specs.push_back(new_varspec); } #endif diff --git a/src/domains/template_generator_ranking.h b/src/domains/template_generator_ranking.h index cec4d5d6b..037c97358 100644 --- a/src/domains/template_generator_ranking.h +++ b/src/domains/template_generator_ranking.h @@ -2,38 +2,37 @@ Module: Template Generator for Ranking Functions -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_RANKING_H -#define DELTACHECK_TEMPLATE_GENERATOR_RANKING_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H #include "template_generator_base.h" -class template_generator_rankingt : public template_generator_baset +class template_generator_rankingt:public template_generator_baset { public: - - explicit template_generator_rankingt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_rankingt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } - - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true); + } -protected: + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true); - void collect_variables_ranking(const local_SSAt &SSA, - bool forward); +protected: + void collect_variables_ranking( + const local_SSAt &SSA, + bool forward); void filter_ranking_domain(domaint::var_specst &var_specs); - }; - -#endif +#endif // CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_RANKING_H diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index 3fe2b1a41..becceba5f 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -33,18 +33,20 @@ Function: template_generator_summaryt::operator() \*******************************************************************/ -void template_generator_summaryt::operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward) +void template_generator_summaryt::operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward) { - domain_number = _domain_number; + domain_number=_domain_number; handle_special_functions(SSA); // we have to call that to prevent trouble! - collect_variables_loop(SSA,forward); + collect_variables_loop(SSA, forward); // do not compute summary for entry-point - if(SSA.goto_function.body.instructions.front().function != ID__start || + if(SSA.goto_function.body.instructions.front().function!=ID__start || options.get_bool_option("preconditions")) - collect_variables_inout(SSA,forward); + collect_variables_inout(SSA, forward); // either use standard templates or user-supplied ones if(!instantiate_custom_templates(SSA)) @@ -52,12 +54,12 @@ void template_generator_summaryt::operator()(unsigned _domain_number, #ifdef SHOW_TEMPLATE_VARIABLES debug() << "Template variables: " << eom; - domaint::output_var_specs(debug(),var_specs,SSA.ns); debug() << eom; -#endif + domaint::output_var_specs(debug(), var_specs, SSA.ns); debug() << eom; +#endif #ifdef SHOW_TEMPLATE debug() << "Template: " << eom; domain_ptr->output_domain(debug(), SSA.ns); debug() << eom; -#endif +#endif } /*******************************************************************\ @@ -72,30 +74,35 @@ Function: template_generator_summaryt::collect_variables_inout \*******************************************************************/ -void template_generator_summaryt::collect_variables_inout(const local_SSAt &SSA,bool forward) +void template_generator_summaryt::collect_variables_inout( + const local_SSAt &SSA, + bool forward) { // add params and globals_in - exprt first_guard = SSA.guard_symbol(SSA.goto_function.body.instructions.begin()); - add_vars(SSA.params,first_guard,first_guard, - forward ? domaint::IN : domaint::OUT, - var_specs); - add_vars(SSA.globals_in,first_guard,first_guard, - forward ? domaint::IN : domaint::OUT, - var_specs); + exprt first_guard= + SSA.guard_symbol(SSA.goto_function.body.instructions.begin()); + add_vars( + SSA.params, + first_guard, + first_guard, + forward ? domaint::IN : domaint::OUT, + var_specs); + add_vars( + SSA.globals_in, + first_guard, + first_guard, + forward ? domaint::IN : domaint::OUT, + var_specs); // add globals_out (includes return values) - exprt last_guard = + exprt last_guard= SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - add_vars(SSA.globals_out,last_guard,last_guard, - forward ? domaint::OUT : domaint::IN, - var_specs); - - // add nondets for backwards analysis - if(!forward) - { - add_vars(SSA.nondets,first_guard,first_guard, - domaint::OUT, var_specs); - } + add_vars( + SSA.globals_out, + last_guard, + last_guard, + forward ? domaint::OUT : domaint::IN, + var_specs); } /*******************************************************************\ @@ -113,10 +120,11 @@ Function: template_generator_summaryt::inout_vars domaint::var_sett template_generator_summaryt::inout_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::IN || v->kind==domaint::OUT) vars.insert(v->var); + if(v->kind==domaint::IN || v->kind==domaint::OUT) + vars.insert(v->var); } return vars; } @@ -136,10 +144,11 @@ Function: template_generator_summaryt::out_vars domaint::var_sett template_generator_summaryt::out_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::OUT) vars.insert(v->var); + if(v->kind==domaint::OUT) + vars.insert(v->var); } return vars; } @@ -159,10 +168,10 @@ Function: template_generator_summaryt::loop_vars domaint::var_sett template_generator_summaryt::loop_vars() { domaint::var_sett vars; - for(domaint::var_specst::const_iterator v = var_specs.begin(); + for(domaint::var_specst::const_iterator v=var_specs.begin(); v!=var_specs.end(); v++) { - if(v->kind==domaint::LOOP || v->kind==domaint::IN) + if(v->kind==domaint::LOOP || v->kind==domaint::IN) vars.insert(v->var); } return vars; diff --git a/src/domains/template_generator_summary.h b/src/domains/template_generator_summary.h index bb3a22567..45aa4422e 100644 --- a/src/domains/template_generator_summary.h +++ b/src/domains/template_generator_summary.h @@ -2,40 +2,37 @@ Module: Template Generator for Summaries -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ -#ifndef DELTACHECK_TEMPLATE_GENERATOR_SUMMARY_H -#define DELTACHECK_TEMPLATE_GENERATOR_SUMMARY_H +#ifndef CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_SUMMARY_H +#define CPROVER_2LS_DOMAINS_TEMPLATE_GENERATOR_SUMMARY_H #include "template_generator_base.h" -class template_generator_summaryt : public template_generator_baset +class template_generator_summaryt:public template_generator_baset { public: - - explicit template_generator_summaryt(optionst &_options, - ssa_dbt &_ssa_db, - ssa_local_unwindert &_ssa_local_unwinder) - : - template_generator_baset(_options,_ssa_db,_ssa_local_unwinder) + explicit template_generator_summaryt( + optionst &_options, + ssa_dbt &_ssa_db, + ssa_local_unwindert &_ssa_local_unwinder): + template_generator_baset(_options, _ssa_db, _ssa_local_unwinder) { - } + } - virtual void operator()(unsigned _domain_number, - const local_SSAt &SSA, bool forward=true); + virtual void operator()( + unsigned _domain_number, + const local_SSAt &SSA, + bool forward=true); virtual domaint::var_sett inout_vars(); virtual domaint::var_sett loop_vars(); virtual domaint::var_sett out_vars(); -protected: - - virtual void collect_variables_inout(const local_SSAt &SSA, - bool forward); - +protected: + virtual void collect_variables_inout(const local_SSAt &SSA, bool forward); }; - #endif diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 263c464e1..161c9eeb8 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -1,14 +1,22 @@ -#include "tpolyhedra_domain.h" -#include "util.h" +/*******************************************************************\ +Module: Template polyhedra domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG #include +#include +#endif #include #include #include -#include -#include "simplify_bounds.h" +#include "tpolyhedra_domain.h" +#include "util.h" #define SYMB_BOUND_VAR "symb_bound#" @@ -29,15 +37,18 @@ Function: tpolyhedra_domaint::initialize void tpolyhedra_domaint::initialize(valuet &value) { #if 0 - if(templ.size()==0) return domaint::initialize(value); + if(templ.size()==0) + return domaint::initialize(value); #endif - templ_valuet &v = static_cast(value); + templ_valuet &v=static_cast(value); v.resize(templ.size()); - for(unsigned row = 0; row(value1); - const templ_valuet &v2 = static_cast(value2); +#if 0 + if(templ.size()==0) + return domaint::join(value1, value2); +#endif + + templ_valuet &v1=static_cast(value1); + const templ_valuet &v2=static_cast(value2); assert(v1.size()==templ.size()); assert(v1.size()==v2.size()); - for(unsigned row = 0; row=vlower); - if(vlower+1==vupper) return from_integer(vlower,lower.type()); //floor + if(vlower+1==vupper) + return from_integer(vlower, lower.type()); // floor #ifdef ENABLE_HEURISTICS - //heuristics + // heuristics if(type.id()==ID_unsignedbv) { - mp_integer vlargest = to_unsignedbv_type(type).largest(); + mp_integer vlargest=to_unsignedbv_type(type).largest(); if(vlower==mp_integer(0) && vupper==vlargest) - return from_integer(mp_integer(1),type); + return from_integer(mp_integer(1), type); if(vlower==mp_integer(1) && vupper==vlargest) - return from_integer(mp_integer(vupper-1),type); + return from_integer(mp_integer(vupper-1), type); if(vlower==mp_integer(1) && vupper==vlargest-1) - return from_integer(mp_integer(2),type); + return from_integer(mp_integer(2), type); if(vlower=plower); + mp_integer plower=vlower.pack(); // compute "median" float number + mp_integer pupper=vupper.pack(); +#if 0 + assert(pupper>=plower); +#endif ieee_floatt res(to_floatbv_type(lower.type())); - res.unpack((plower+pupper)/2); //...by computing integer mean + res.unpack((plower+pupper)/2); // ...by computing integer mean return res.to_expr(); } ieee_floatt res(to_floatbv_type(lower.type())); res.make_zero(); return res.to_expr(); } - assert(false); //types do not match or are not supported + assert(false); // types do not match or are not supported } /*******************************************************************\ @@ -176,7 +197,7 @@ Function: tpolyhedra_domaint::less_than bool tpolyhedra_domaint::less_than(const row_valuet &v1, const row_valuet &v2) { - if(v1.type()==v2.type() && + if(v1.type()==v2.type() && (v1.type().id()==ID_signedbv || v1.type().id()==ID_unsignedbv)) { mp_integer vv1, vv2; @@ -190,53 +211,84 @@ bool tpolyhedra_domaint::less_than(const row_valuet &v1, const row_valuet &v2) ieee_floatt vv2(to_constant_expr(v2)); return vv1 row_expr <= row_value + Purpose: pre_guard==> row_expr<=row_value \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_constraint( + const rowt &row, const row_valuet &row_value) { assert(row row_expr<=row_value + +\*******************************************************************/ + +exprt tpolyhedra_domaint::get_row_pre_constraint( + const rowt &row, const row_valuet &row_value) { assert(row row_expr<=row_value + +\*******************************************************************/ -exprt tpolyhedra_domaint::get_row_pre_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_pre_constraint( + const rowt &row, const templ_valuet &value) { assert(value.size()==templ.size()); - return get_row_pre_constraint(row,value[row]); + return get_row_pre_constraint(row, value[row]); } /*******************************************************************\ @@ -247,284 +299,328 @@ Function: tpolyhedra_domaint::get_row_post_constraint Outputs: - Purpose: row_expr <= row_value + Purpose: row_expr<=row_value \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_post_constraint(const rowt &row, +exprt tpolyhedra_domaint::get_row_post_constraint( + const rowt &row, const row_valuet &row_value) { assert(row (row_expr <= row_value) ) + Purpose: /\_all_rows ( pre_guard==> (row_expr<=row_value) ) \*******************************************************************/ exprt tpolyhedra_domaint::to_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= row_value)) - to be connected disjunctively + Purpose: for all rows !(post_guard==> (row_expr<=row_value)) + to be connected disjunctively \*******************************************************************/ -void tpolyhedra_domaint::make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, +void tpolyhedra_domaint::make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, exprt::operandst &value_exprs) { assert(value.size()==templ.size()); cond_exprs.resize(templ.size()); value_exprs.resize(templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_value) + Purpose: pre_guard==> (row_expr<=symb_value) \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value) +exprt tpolyhedra_domaint::get_row_symb_pre_constraint( + const rowt &row, + const row_valuet &row_value) { assert(row loop15 regression - binary_relation_exprt(templ_row.expr,ID_le,get_row_symb_value(row))); + const template_rowt &templ_row=templ[row]; + if(templ_row.kind==OUT || templ_row.kind==OUTL) + return true_exprt(); + return implies_exprt( + templ_row.pre_guard, // REMARK: and_expr==> loop15 regression + binary_relation_exprt(templ_row.expr, ID_le, get_row_symb_value(row))); } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_symb_post_constraint + Function: tpolyhedra_domaint::get_row_symb_post_constraint - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: post_guard && (row_expr >= row_symb_value) (!!!) + Purpose: post_guard && (row_expr >= row_symb_value) (!!!) \*******************************************************************/ exprt tpolyhedra_domaint::get_row_symb_post_constraint(const rowt &row) { assert(row (row_expr <= symb_row_value) + Purpose: pre_guard==> (row_expr<=symb_row_value) \*******************************************************************/ exprt tpolyhedra_domaint::to_symb_pre_constraints(const templ_valuet &value) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr <= symb_row_value) + Purpose: pre_guard==> (row_expr<=symb_row_value) \*******************************************************************/ -exprt tpolyhedra_domaint::to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows) +exprt tpolyhedra_domaint::to_symb_pre_constraints( + const templ_valuet &value, + const std::set &symb_rows) { assert(value.size()==templ.size()); - exprt::operandst c; - for(unsigned row = 0; row (row_expr >= symb_row_value) + Purpose: /\_i post_guard==> (row_expr >= symb_row_value) \*******************************************************************/ exprt tpolyhedra_domaint::to_symb_post_constraints( - const std::set &symb_rows) + const std::set &symb_rows) { - exprt::operandst c; - for(std::set::const_iterator it = symb_rows.begin(); - it != symb_rows.end(); it++) + exprt::operandst c; + for(const auto &row : symb_rows) { - c.push_back(get_row_symb_post_constraint(*it)); + c.push_back(get_row_symb_post_constraint(row)); } - return conjunction(c); + return conjunction(c); } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_symb_value_constraint + Function: tpolyhedra_domaint::get_row_symb_value_constraint - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: row_value_value <= symb_row + Purpose: row_value_value<=symb_row \*******************************************************************/ -exprt tpolyhedra_domaint::get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value, bool geq) +exprt tpolyhedra_domaint::get_row_symb_value_constraint( + const rowt &row, + const row_valuet &row_value, + bool geq) { - if(is_row_value_neginf(row_value)) return false_exprt(); - if(is_row_value_inf(row_value)) return true_exprt(); - exprt c = binary_relation_exprt(get_row_symb_value(row), - geq ? ID_ge : ID_le,row_value); + if(is_row_value_neginf(row_value)) + return false_exprt(); + if(is_row_value_inf(row_value)) + return true_exprt(); + exprt c=binary_relation_exprt( + get_row_symb_value(row), + geq ? ID_ge : ID_le, + row_value); return c; } /*******************************************************************\ -Function: tpolyhedra_domaint::get_row_value + Function: tpolyhedra_domaint::get_row_value - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ tpolyhedra_domaint::row_valuet tpolyhedra_domaint::get_row_value( - const rowt &row, const templ_valuet &value) + const rowt &row, + const templ_valuet &value) { assert(row(value); +#if 0 + if(templ.size()==0) + return domaint::project_on_vars(value, vars, result); +#endif + + const templ_valuet &v=static_cast(value); assert(v.size()==templ.size()); exprt::operandst c; - for(unsigned row = 0; row symbols; - find_symbols(templ_row.expr,symbols); + find_symbols(templ_row.expr, symbols); - bool pure = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) + bool pure=true; + for(const auto &symbol : symbols) { - if(vars.find(*it)==vars.end()) + if(vars.find(symbol)==vars.end()) { - pure = false; + pure=false; break; } } - if(!pure) continue; + if(!pure) + continue; - const row_valuet &row_v = v[row]; + const row_valuet &row_v=v[row]; if(templ_row.kind==LOOP) { - if(is_row_value_neginf(value, row)) - //c.push_back(implies_exprt(templ_row.pre_guard,false_exprt())); - c.push_back(not_exprt(templ_row.pre_guard)); - else if(is_row_value_inf(value, row)) - ; //c.push_back(implies_exprt(templ_row.pre_guard,true_exprt())); - else c.push_back(implies_exprt(templ_row.pre_guard, - binary_relation_exprt(templ_row.expr,ID_le,row_v))); + if(is_row_value_neginf(row_v)) + c.push_back(implies_exprt(templ_row.pre_guard, false_exprt())); + else if(is_row_value_inf(row_v)) + c.push_back(implies_exprt(templ_row.pre_guard, true_exprt())); + else + c.push_back( + implies_exprt( + templ_row.pre_guard, + binary_relation_exprt(templ_row.expr, ID_le, row_v))); } else { if(is_row_value_neginf(value, row)) - c.push_back(false_exprt()); - else if(is_row_value_inf(value, row)) - ; //c.push_back(true_exprt()); - else c.push_back(binary_relation_exprt(templ_row.expr,ID_le,row_v)); + c.push_back(false_exprt()); + else if(is_row_value_inf(row_v)) + c.push_back(true_exprt()); + else + c.push_back(binary_relation_exprt(templ_row.expr, ID_le, row_v)); } } - result = conjunction(c); - simplify_bounds(result, ns); + result=conjunction(c); } /*******************************************************************\ -Function: tpolyhedra_domaint::set_row_value + Function: tpolyhedra_domaint::set_row_value - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ void tpolyhedra_domaint::set_row_value( - const rowt &row, const tpolyhedra_domaint::row_valuet &row_value, templ_valuet &value) + const rowt &row, + const tpolyhedra_domaint::row_valuet &row_value, + templ_valuet &value) { assert(row(value); - for(unsigned row = 0; row(value); + for(std::size_t row=0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) + << " ]===> " << std::endl << " "; break; case IN: out << "(IN) "; break; case OUT: case OUTL: out << "(OUT) "; break; default: assert(false); } - out << "( " << from_expr(ns,"",templ_row.expr) << " <= "; - if(is_row_value_neginf(v[row])) out << "-oo"; - else if(is_row_value_inf(v[row])) out << "oo"; - else out << from_expr(ns,"",v[row]); + out << "( " << from_expr(ns, "", templ_row.expr) << "<="; + if(is_row_value_neginf(v[row])) + out << "-oo"; + else if(is_row_value_inf(v[row])) + out << "oo"; + else + out << from_expr(ns, "", v[row]); out << " )" << std::endl; } } /*******************************************************************\ -Function: tpolyhedra_domaint::output_domain + Function: tpolyhedra_domaint::output_domain - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ -void tpolyhedra_domaint::output_domain(std::ostream &out, const namespacet &ns) const +void tpolyhedra_domaint::output_domain( + std::ostream &out, + const namespacet &ns) const { - for(unsigned row = 0; row " << std::endl << " "; + out << "(LOOP) [ " << from_expr(ns, "", templ_row.pre_guard) << " | "; + out << from_expr(ns, "", templ_row.post_guard) << " | "; + out << from_expr(ns, "", templ_row.aux_expr) << " ]===> " + << std::endl << " "; break; - case IN: + case IN: out << "(IN) "; - out << from_expr(ns,"",templ_row.pre_guard) << " ===> " << std::endl << " "; + out << from_expr(ns, "", templ_row.pre_guard) << "===> " + << std::endl << " "; break; case OUT: case OUTL: - out << "(OUT) "; - out << from_expr(ns,"",templ_row.post_guard) << " ===> " << std::endl << " "; + out << "(OUT) "; + out << from_expr(ns, "", templ_row.post_guard) << "===> " + << std::endl << " "; break; default: assert(false); } - out << "( " << - from_expr(ns,"",templ_row.expr) << " <= CONST )" << std::endl; + out << "( " << + from_expr(ns, "", templ_row.expr) << "<=CONST )" << std::endl; } } /*******************************************************************\ -Function: tpolyhedra_domaint::template_size + Function: tpolyhedra_domaint::template_size - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ @@ -776,17 +893,18 @@ unsigned tpolyhedra_domaint::template_size() /*******************************************************************\ -Function: tpolyhedra_domaint::is_row_value_neginf + Function: tpolyhedra_domaint::is_row_value_neginf - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ + bool tpolyhedra_domaint::is_row_value_neginf( - const row_valuet &row_value) const + const row_valuet & row_value) const { return row_value.get(ID_value)==ID_false; } @@ -800,13 +918,13 @@ bool tpolyhedra_domaint::is_row_value_neginf( /*******************************************************************\ -Function: tpolyhedra_domaint::is_row_value_inf + Function: tpolyhedra_domaint::is_row_value_inf - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: + Purpose: \*******************************************************************/ @@ -816,75 +934,45 @@ bool tpolyhedra_domaint::is_row_value_inf( return row_value.get(ID_value)==ID_true; } -bool tpolyhedra_domaint::is_row_value_inf( - const valuet &value, const rowt &row) const -{ - const templ_valuet &v = static_cast(value); - const row_valuet &row_value = v.at(row); - if(row_value.get(ID_value)==ID_true) - return true; - if(row_value==get_max_row_value(row)) - return true; - const row_exprt &row_expr = templ[row].expr; - if(row_expr.id()==ID_unary_minus && - row_expr.op0().id()==ID_typecast) - { - mp_integer rvalue; - to_integer(row_value, rvalue); - const typet &inner_type = row_expr.op0().op0().type(); - mp_integer smallest; - if(inner_type.id()==ID_unsignedbv) - smallest = to_unsignedbv_type(inner_type).smallest(); - else if(inner_type.id()==ID_signedbv) - smallest = to_signedbv_type(inner_type).smallest(); - else - return false; - if(smallest == rvalue) - return true; - } - - return false; -} - /*******************************************************************\ -Function: tpolyhedra_domaint::rename_for_row + Function: tpolyhedra_domaint::rename_for_row - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: add row suffix to non-symbolic-bound variables in expression - (required for strategy iteration (binsearch3)) + Purpose: add row suffix to non-symbolic-bound variables in expression + (required for strategy iteration (binsearch3)) \*******************************************************************/ - void tpolyhedra_domaint::rename_for_row(exprt &expr, const rowt &row) { - if(row==0) return; //do not rename + if(row==0) + return; // do not rename if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - const std::string &old_id = expr.get_string(ID_identifier); - if(old_id.find(SYMB_BOUND_VAR)==std::string::npos) + const std::string &old_id=expr.get_string(ID_identifier); + if(old_id.find(SYMB_BOUND_VAR)==std::string::npos) { - irep_idt id = old_id + "_" + i2string(row); - expr.set(ID_identifier,id); + irep_idt id=old_id+"_"+i2string(row); + expr.set(ID_identifier, id); } } - for(unsigned i=0; i< expr.operands().size(); i++) - rename_for_row(expr.operands()[i],row); + for(std::size_t i=0; ikind==IN) continue; + if(v.kind==IN) + continue; // x - add_template_row(v->var,v->pre_guard,v->post_guard, - v->aux_expr, v->kind); + add_template_row( + v.var, + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); // -x - add_template_row(unary_minus_exprt(v->var,v->var.type()), - v->pre_guard,v->post_guard,v->aux_expr, v->kind); + add_template_row( + unary_minus_exprt(v.var, v.var.type()), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); } } /*******************************************************************\ -Function: add_difference_template + Function: tpolyhedra_domaint::add_difference_template - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: x+-y<=c + Purpose: x+-y<=c \*******************************************************************/ -void tpolyhedra_domaint::add_difference_template(const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = var_specs.size()*(var_specs.size()-1); +void tpolyhedra_domaint::add_difference_template( + const var_specst &var_specs, + const namespacet &ns) +{ + std::size_t size=var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) + + for(var_specst::const_iterator v1=var_specs.begin(); + v1!=var_specs.end(); ++v1) { - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) + var_specst::const_iterator v2=v1; ++v2; + for(; v2!=var_specs.end(); ++v2) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; - if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + if(k==IN) + continue; + if(k==LOOP && v1->pre_guard!=v2->pre_guard) + continue; // TEST: we need better heuristics exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - // x1 - x2 - add_template_row(minus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); + // x1-x2 + add_template_row( + minus_exprt(v1->var, v2->var), pre_g, post_g, aux_expr, k); - // x2 - x1 - add_template_row(minus_exprt(v2->var,v1->var),pre_g,post_g,aux_expr,k); + // x2-x1 + add_template_row( + minus_exprt(v2->var, v1->var), pre_g, post_g, aux_expr, k); } } } /*******************************************************************\ -Function: add_quadratic_template + Function: tpolyhedra_domaint::add_quadratic_template - Inputs: + Inputs: - Outputs: + Outputs: - Purpose: +-x^2<=c + Purpose: +-x^2<=c \*******************************************************************/ -void tpolyhedra_domaint::add_quadratic_template(const var_specst &var_specs, - const namespacet &ns) -{ - unsigned size = 2*var_specs.size(); +void tpolyhedra_domaint::add_quadratic_template( + const var_specst &var_specs, + const namespacet &ns) +{ + unsigned size=2*var_specs.size(); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v = var_specs.begin(); - v!=var_specs.end(); v++) + + for(const auto v : var_specs) { - if(v->kind==IN) continue; + if(v.kind==IN) + continue; // x - add_template_row(mult_exprt(v->var,v->var), - v->pre_guard,v->post_guard,v->aux_expr,v->kind); + add_template_row( + mult_exprt(v.var, v.var), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); // -x - add_template_row(unary_minus_exprt(mult_exprt(v->var,v->var),v->var.type()), - v->pre_guard,v->post_guard,v->aux_expr,v->kind); - }} + add_template_row( + unary_minus_exprt(mult_exprt(v.var, v.var), v.var.type()), + v.pre_guard, + v.post_guard, + v.aux_expr, + v.kind); + } +} /*******************************************************************\ -Function: add_sum_template +Function: tpolyhedra_domaint::add_sum_template Inputs: @@ -1026,39 +1138,47 @@ Function: add_sum_template \*******************************************************************/ -void tpolyhedra_domaint::add_sum_template(const var_specst &var_specs, - const namespacet &ns) +void tpolyhedra_domaint::add_sum_template( + const var_specst &var_specs, + const namespacet &ns) { - unsigned size = var_specs.size()*(var_specs.size()-1); + unsigned size=var_specs.size()*(var_specs.size()-1); templ.reserve(templ.size()+size); - - for(var_specst::const_iterator v1 = var_specs.begin(); - v1!=var_specs.end(); v1++) + + for(var_specst::const_iterator v1=var_specs.begin(); + v1!=var_specs.end(); ++v1) { - var_specst::const_iterator v2 = v1; v2++; - for(; v2!=var_specs.end(); v2++) + var_specst::const_iterator v2=v1; ++v2; + for(; v2!=var_specs.end(); ++v2) { - kindt k = domaint::merge_kinds(v1->kind,v2->kind); - if(k==IN) continue; - if(k==LOOP && v1->pre_guard!=v2->pre_guard) continue; //TEST: we need better heuristics + kindt k=domaint::merge_kinds(v1->kind, v2->kind); + if(k==IN) + continue; + if(k==LOOP && v1->pre_guard!=v2->pre_guard) + continue; // TEST: we need better heuristics exprt pre_g, post_g, aux_expr; merge_and(pre_g, v1->pre_guard, v2->pre_guard, ns); merge_and(post_g, v1->post_guard, v2->post_guard, ns); merge_and(aux_expr, v1->aux_expr, v2->aux_expr, ns); - // -x1 - x2 - add_template_row(minus_exprt(unary_minus_exprt(v1->var,v1->var.type()),v2->var), - pre_g,post_g,aux_expr,k); - - // x1 + x2 - add_template_row(plus_exprt(v1->var,v2->var),pre_g,post_g,aux_expr,k); + // -x1-x2 + add_template_row( + minus_exprt(unary_minus_exprt(v1->var, v1->var.type()), v2->var), + pre_g, + post_g, + aux_expr, + k); + + // x1+x2 + add_template_row( + plus_exprt(v1->var, v2->var), pre_g, post_g, aux_expr, k); } } - } -/*******************************************************************\ + +/******************************************************************* \ Function: tpolyhedra_domaint::refine @@ -1094,7 +1214,7 @@ bool tpolyhedra_domaint::refine() current_refinement_expr = conjunction(c); return true; } - + if(current_refinement>max_refinements) return false; @@ -1106,7 +1226,7 @@ bool tpolyhedra_domaint::refine() } else if(current_refinement==2) current_refinement_expr = true_exprt(); - + current_refinement++; return true; } diff --git a/src/domains/tpolyhedra_domain.h b/src/domains/tpolyhedra_domain.h index db894a939..c8dbdd318 100644 --- a/src/domains/tpolyhedra_domain.h +++ b/src/domains/tpolyhedra_domain.h @@ -1,25 +1,34 @@ -#ifndef CPROVER_TEMPLATE_DOMAIN_H -#define CPROVER_TEMPLATE_DOMAIN_H +/*******************************************************************\ -#include "domain.h" +Module: Template polyhedra domain + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_TPOLYHEDRA_DOMAIN_H +#define CPROVER_2LS_DOMAINS_TPOLYHEDRA_DOMAIN_H + +#include #include #include #include -#include -class tpolyhedra_domaint : public domaint +#include "domain.h" + +class tpolyhedra_domaint:public domaint { public: typedef unsigned rowt; - typedef exprt row_exprt; + typedef exprt row_exprt; typedef constant_exprt row_valuet; // "bound" - class templ_valuet : public domaint::valuet, public std::vector + class templ_valuet:public domaint::valuet, public std::vector { }; - typedef struct + typedef struct { guardt pre_guard; guardt post_guard; @@ -30,12 +39,14 @@ class tpolyhedra_domaint : public domaint typedef std::vector templatet; - tpolyhedra_domaint(unsigned _domain_number, - replace_mapt &_renaming_map, - const namespacet &_ns) : - domaint(_domain_number,_renaming_map, _ns), - current_refinement(0) - {} + tpolyhedra_domaint( + unsigned _domain_number, + replace_mapt &_renaming_map, + const namespacet &_ns): + domaint(_domain_number, _renaming_map, _ns), + current_refinement(0) + { + } // initialize value virtual void initialize(valuet &value); @@ -54,25 +65,32 @@ class tpolyhedra_domaint : public domaint exprt get_row_post_constraint(const rowt &row, const templ_valuet &value); exprt to_pre_constraints(const templ_valuet &value); - void make_not_post_constraints(const templ_valuet &value, - exprt::operandst &cond_exprs, - exprt::operandst &value_exprs); + void make_not_post_constraints( + const templ_valuet &value, + exprt::operandst &cond_exprs, + exprt::operandst &value_exprs); // value -> symbolic bound constraints (for optimization) exprt to_symb_pre_constraints(const templ_valuet &value); - exprt to_symb_pre_constraints(const templ_valuet &value, - const std::set &symb_rows); + exprt to_symb_pre_constraints( + const templ_valuet &value, + const std::set &symb_rows); exprt to_symb_post_constraints(const std::set &symb_rows); - exprt get_row_symb_value_constraint(const rowt &row, - const row_valuet &row_value, bool geq=false); - exprt get_row_symb_pre_constraint(const rowt &row, - const row_valuet &row_value); + exprt get_row_symb_value_constraint( + const rowt &row, + const row_valuet &row_value, bool geq=false); + exprt get_row_symb_pre_constraint( + const rowt &row, + const row_valuet &row_value); exprt get_row_symb_post_constraint(const rowt &row); // set, get value row_valuet get_row_value(const rowt &row, const templ_valuet &value); - void set_row_value(const rowt &row, const row_valuet &row_value, templ_valuet &value); + void set_row_value( + const rowt &row, + const row_valuet &row_value, + templ_valuet &value); // max, min, comparison row_valuet get_max_row_value(const rowt &row) const; @@ -85,11 +103,14 @@ class tpolyhedra_domaint : public domaint bool is_row_value_neginf(const valuet &value, const rowt & row) const; // printing - virtual void output_value(std::ostream &out, const valuet &value, const namespacet &ns) const; - virtual void output_domain(std::ostream &out, const namespacet &ns) const; + virtual void output_value( + std::ostream &out, const valuet &value, const namespacet &ns) const; + virtual void output_domain( + std::ostream &out, const namespacet &ns) const; - // projection - virtual void project_on_vars(valuet &value, const var_sett &vars, exprt &result); + // projection + virtual void project_on_vars( + valuet &value, const var_sett &vars, exprt &result); unsigned template_size(); virtual bool is_spec_empty() const { return templ.size()==0; } @@ -100,17 +121,16 @@ class tpolyhedra_domaint : public domaint const exprt& pre_guard, const exprt& post_guard, const exprt& aux_expr, - kindt kind - ); - - void add_interval_template(const var_specst &var_specs, - const namespacet &ns); - void add_difference_template(const var_specst &var_specs, - const namespacet &ns); - void add_sum_template(const var_specst &var_specs, - const namespacet &ns); - void add_quadratic_template(const var_specst &var_specs, - const namespacet &ns); + kindt kind); + + void add_interval_template( + const var_specst &var_specs, const namespacet &ns); + void add_difference_template( + const var_specst &var_specs, const namespacet &ns); + void add_sum_template( + const var_specst &var_specs, const namespacet &ns); + void add_quadratic_template( + const var_specst &var_specs, const namespacet &ns); symbol_exprt get_row_symb_value(const rowt &row); diff --git a/src/domains/util.cpp b/src/domains/util.cpp index 1cdf044b4..e49925ee4 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -1,10 +1,18 @@ +/*******************************************************************\ + +Module: Domain utilities + +Author: Peter Schrammel + +\*******************************************************************/ + #include #include "util.h" /*******************************************************************\ -Function: extend_expr_types +Function: get_bitvector_width Inputs: @@ -19,30 +27,43 @@ unsigned get_bitvector_width(const exprt &expr) return to_bitvector_type(expr.type()).get_width(); } +/*******************************************************************\ + +Function: extend_expr_types + + Inputs: + + Outputs: + + Purpose: increases bitvector sizes such that there are no overflows + +\*******************************************************************/ + void extend_expr_types(exprt &expr) { // std::cerr << "expr: " << expr << std::endl; - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - exprt new_expr = expr.op0(); + exprt new_expr=expr.op0(); extend_expr_types(new_expr); - expr = new_expr; + expr=new_expr; return; } - if(expr.id()==ID_constant) return; - if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) return; - if(expr.id()==ID_index) return; + if(expr.id()==ID_constant || + expr.id()==ID_symbol || + expr.id()==ID_index) + return; if(expr.id()==ID_unary_minus) { extend_expr_types(expr.op0()); - if(expr.op0().type().id()==ID_signedbv || + if(expr.op0().type().id()==ID_signedbv || expr.op0().type().id()==ID_unsignedbv) { - typet new_type = signedbv_typet(get_bitvector_width(expr.op0())+1); - expr = unary_minus_exprt(typecast_exprt(expr.op0(),new_type),new_type); + typet new_type=signedbv_typet(get_bitvector_width(expr.op0())+1); + expr=unary_minus_exprt(typecast_exprt(expr.op0(), new_type), new_type); } - //TODO: shall we extend floats? + // TODO: shall we extend floats? return; } if(expr.id()==ID_plus || expr.id()==ID_minus) @@ -51,119 +72,138 @@ void extend_expr_types(exprt &expr) // std::cerr << "op0: " << expr.op0() << std::endl; extend_expr_types(expr.op1()); // std::cerr << "op1: " << expr.op1() << std::endl; - unsigned size0 = get_bitvector_width(expr.op0()); - unsigned size1 = get_bitvector_width(expr.op1()); - assert(size0>0); assert(size1>0); - typet new_type = expr.op0().type(); + unsigned size0=get_bitvector_width(expr.op0()); + unsigned size1=get_bitvector_width(expr.op1()); + assert(size0>0); assert(size1>0); + typet new_type=expr.op0().type(); if(expr.op0().type().id()==expr.op1().type().id()) { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(std::max(size0,size1)+1); - else if(new_type.id()==ID_unsignedbv) + if(new_type.id()==ID_signedbv) + new_type=signedbv_typet(std::max(size0, size1)+1); + else if(new_type.id()==ID_unsignedbv) + { + if(expr.id()==ID_minus) + new_type=signedbv_typet(std::max(size0, size1)+1); + else + new_type=unsignedbv_typet(std::max(size0, size1)+1); + } + else if(new_type.id()==ID_floatbv) { - if(expr.id()==ID_minus) - new_type = signedbv_typet(std::max(size0,size1)+1); - else - new_type = unsignedbv_typet(std::max(size0,size1)+1); + // TODO: shall we extend floats? } - else if(new_type.id()==ID_floatbv) {} //TODO: shall we extend floats? - else assert(false); + else + assert(false); } - else //operands do not have the same type + else // operands do not have the same type { - if(new_type.id()==ID_signedbv) - new_type = signedbv_typet(size0<=size1 ? size1+2 : size0+1); - else if(new_type.id()==ID_unsignedbv) - new_type = signedbv_typet(size1<=size0 ? size0+2 : size1+1); - else assert(false); //TODO: implement floats + if(new_type.id()==ID_signedbv) + new_type=signedbv_typet(size0<=size1 ? size1+2 : size0+1); + else if(new_type.id()==ID_unsignedbv) + new_type=signedbv_typet(size1<=size0 ? size0+2 : size1+1); + else + assert(false); // TODO: implement floats } if(expr.id()==ID_plus) - expr = plus_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + expr=plus_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); else if(expr.id()==ID_minus) - expr = minus_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); - else assert(false); //TODO: implement floats + { + expr=minus_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); + } + else + assert(false); // TODO: implement floats return; } if(expr.id()==ID_mult) { extend_expr_types(expr.op0()); extend_expr_types(expr.op1()); - unsigned size0 = get_bitvector_width(expr.op0()); + unsigned size0=get_bitvector_width(expr.op0()); // std::cerr << "expr1: " << expr.op1() << std::endl; - unsigned size1 = get_bitvector_width(expr.op1()); - - assert(size0>0); assert(size1>0); - if((expr.op0().type().id()==ID_unsignedbv || - expr.op0().type().id()==ID_signedbv) && - (expr.op1().type().id()==ID_unsignedbv || - expr.op1().type().id()==ID_signedbv)) + unsigned size1=get_bitvector_width(expr.op1()); + + assert(size0>0); assert(size1>0); + if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + (expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv)) { - typet new_type = signedbv_typet(size0+size1+1); - expr = mult_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + typet new_type=signedbv_typet(size0+size1+1); + expr=mult_exprt(typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } - else if(expr.op0().type().id()==ID_floatbv && - expr.op1().type().id()==ID_floatbv) + else if(expr.op0().type().id()==ID_floatbv && + expr.op1().type().id()==ID_floatbv) { - // TODO: shall we extend floats? + // TODO: shall we extend floats? } - else if((expr.op0().type().id()==ID_unsignedbv || - expr.op0().type().id()==ID_signedbv) && - expr.op1().type().id()==ID_floatbv) + else if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + expr.op1().type().id()==ID_floatbv) { - typet new_type = expr.op1().type(); // TODO: shall we extend floats? - expr = mult_exprt(typecast_exprt(expr.op0(),new_type),expr.op1()); + typet new_type=expr.op1().type(); // TODO: shall we extend floats? + expr=mult_exprt(typecast_exprt(expr.op0(), new_type), expr.op1()); return; } - else if((expr.op1().type().id()==ID_unsignedbv || - expr.op1().type().id()==ID_signedbv) && - expr.op0().type().id()==ID_floatbv) + else if((expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv) && + expr.op0().type().id()==ID_floatbv) { - typet new_type = expr.op0().type(); // TODO: shall we extend floats? - expr = mult_exprt(expr.op0(),typecast_exprt(expr.op1(),new_type)); + typet new_type=expr.op0().type(); // TODO: shall we extend floats? + expr=mult_exprt(expr.op0(), typecast_exprt(expr.op1(), new_type)); return; } - else assert(false); + else + assert(false); } if(expr.id()==ID_div) { extend_expr_types(expr.op0()); extend_expr_types(expr.op1()); - unsigned size0 = get_bitvector_width(expr.op0()); - unsigned size1 = get_bitvector_width(expr.op1()); - assert(size0>0); assert(size1>0); - if((expr.op0().type().id()==ID_unsignedbv || expr.op0().type().id()==ID_signedbv) && - (expr.op1().type().id()==ID_unsignedbv || expr.op1().type().id()==ID_signedbv)) + unsigned size0=get_bitvector_width(expr.op0()); + unsigned size1=get_bitvector_width(expr.op1()); + assert(size0>0); + assert(size1>0); + if((expr.op0().type().id()==ID_unsignedbv || + expr.op0().type().id()==ID_signedbv) && + (expr.op1().type().id()==ID_unsignedbv || + expr.op1().type().id()==ID_signedbv)) { typet new_type; - if(expr.op0().type().id()==ID_unsignedbv && expr.op0().type().id()==ID_unsignedbv) - new_type = unsignedbv_typet(std::max(size0,size1)); - else if(expr.op0().type().id()==ID_signedbv && expr.op0().type().id()==ID_unsignedbv) - new_type = signedbv_typet(size0>size1 ? size0 : size1+1); - else new_type = signedbv_typet(size0>=size1 ? size0+1 : size1); - - expr = div_exprt(typecast_exprt(expr.op0(),new_type), - typecast_exprt(expr.op1(),new_type)); + if(expr.op0().type().id()==ID_unsignedbv && + expr.op0().type().id()==ID_unsignedbv) + new_type=unsignedbv_typet(std::max(size0, size1)); + else if(expr.op0().type().id()==ID_signedbv && + expr.op0().type().id()==ID_unsignedbv) + new_type=signedbv_typet(size0>size1 ? size0 : size1+1); + else + new_type=signedbv_typet(size0>=size1 ? size0+1 : size1); + + expr=div_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } - else if(expr.op0().type().id()==ID_floatbv || expr.op1().type().id()==ID_floatbv) + else if(expr.op0().type().id()==ID_floatbv || + expr.op1().type().id()==ID_floatbv) { - // TODO: shall we extend floats? + // TODO: shall we extend floats? return; } - else assert(false); + else + assert(false); } std::cerr << "failed expr: " << expr.pretty() << std::endl; assert(false); } - /*******************************************************************\ -Function: simplify_const +Function: simplify_const_int Inputs: @@ -175,69 +215,82 @@ Function: simplify_const mp_integer simplify_const_int(const exprt &expr) { - if(expr.id()==ID_constant) + if(expr.id()==ID_constant) { mp_integer v; to_integer(expr, v); return v; } - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - const exprt &op0 = expr.op0(); + const exprt &op0=expr.op0(); assert(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv); return simplify_const_int(op0); } - if(expr.id()==ID_unary_minus) return -simplify_const_int(expr.op0()); - if(expr.id()==ID_plus) + if(expr.id()==ID_unary_minus) + return -simplify_const_int(expr.op0()); + if(expr.id()==ID_plus) return simplify_const_int(expr.op0())+simplify_const_int(expr.op1()); - if(expr.id()==ID_minus) + if(expr.id()==ID_minus) return simplify_const_int(expr.op0())-simplify_const_int(expr.op1()); - if(expr.id()==ID_mult) - return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); - if(expr.id()==ID_div) + if(expr.id()==ID_mult) + return simplify_const_int(expr.op0())*simplify_const_int(expr.op1()); + if(expr.id()==ID_div) { - mp_integer d = simplify_const_int(expr.op1()); + mp_integer d=simplify_const_int(expr.op1()); assert(d!=0); - return simplify_const_int(expr.op0())/d; + return simplify_const_int(expr.op0())/d; } - if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) + if(expr.id()==ID_symbol) { #if 0 std::cerr << "substituting default value for " << expr << std::endl; #endif - return 0; //default value if not substituted in expr + return 0; // default value if not substituted in expr } - if(expr.id()==ID_index) + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_signedbv || array_type.id()==ID_unsignedbv) { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow + mp_integer mp_index=simplify_const_int(index_expr.index()); + unsigned index=integer2unsigned(mp_index); // TODO: might overflow assert(index<(index_expr.array().operands().size())); return simplify_const_int(index_expr.array().operands()[index]); } - assert(false); //not implemented + assert(false); // not implemented } - assert(false); //not implemented + assert(false); // not implemented } +/*******************************************************************\ + +Function: simplify_const_float + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + ieee_floatt simplify_const_float(const exprt &expr) { - if(expr.id()==ID_constant) + if(expr.id()==ID_constant) { ieee_floatt v(to_constant_expr(expr)); return v; } - if(expr.id()==ID_typecast) + if(expr.id()==ID_typecast) { - const exprt &op0 = expr.op0(); + const exprt &op0=expr.op0(); if(op0.type().id()==ID_signedbv || op0.type().id()==ID_unsignedbv) { ieee_floatt v; v.from_integer(simplify_const_int(op0)); - return v; + return v; } if(op0.type().id()==ID_floatbv) { @@ -245,41 +298,41 @@ ieee_floatt simplify_const_float(const exprt &expr) } assert(false); } - if(expr.id()==ID_unary_minus) + if(expr.id()==ID_unary_minus) { - ieee_floatt v = simplify_const_float(expr.op0()); + ieee_floatt v=simplify_const_float(expr.op0()); v.set_sign(!v.get_sign()); - return v; + return v; } - if(expr.id()==ID_plus) + if(expr.id()==ID_plus) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 += v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1+=v2; + return v1; } if(expr.id()==ID_minus) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 -= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1-=v2; + return v1; } if(expr.id()==ID_mult) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 *= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1*=v2; + return v1; } if(expr.id()==ID_div) { - ieee_floatt v1 = simplify_const_float(expr.op0()); - ieee_floatt v2 = simplify_const_float(expr.op1()); - v1 /= v2; - return v1; + ieee_floatt v1=simplify_const_float(expr.op0()); + ieee_floatt v2=simplify_const_float(expr.op1()); + v1/=v2; + return v1; } - if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) //default value if not substituted in expr + if(expr.id()==ID_symbol) // default value if not substituted in expr { ieee_floatt v; v.make_zero(); @@ -288,138 +341,171 @@ ieee_floatt simplify_const_float(const exprt &expr) std::cerr << "substituting default value for " << expr << std::endl; #endif - return v; + return v; } - if(expr.id()==ID_index) + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_float) { - mp_integer mp_index = simplify_const_int(index_expr.index()); - unsigned index = integer2unsigned(mp_index); //TODO: might overflow + mp_integer mp_index=simplify_const_int(index_expr.index()); + unsigned index=integer2unsigned(mp_index); // TODO: might overflow assert(index<(index_expr.array().operands().size())); return simplify_const_float(index_expr.array().operands()[index]); } - assert(false); //not implemented + assert(false); // not implemented } - assert(false); //not implemented + assert(false); // not implemented } +/*******************************************************************\ + +Function: simplify_const + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + constant_exprt simplify_const(const exprt &expr) { - if(expr.id()==ID_constant) return to_constant_expr(expr); - if(expr.id()==ID_index) +// std::cerr << "const: " << expr << std::endl; + if(expr.id()==ID_constant) + return to_constant_expr(expr); + // TODO: handle "address_of" constants + if(expr.id()==ID_index) { - const index_exprt &index_expr = to_index_expr(expr); - const typet &array_type = to_array_type(index_expr.array().type()).subtype(); + const index_exprt &index_expr=to_index_expr(expr); + const typet &array_type=to_array_type(index_expr.array().type()).subtype(); if(array_type.id()==ID_signedbv) { - mp_integer res = simplify_const_int(index_expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); + mp_integer res=simplify_const_int(index_expr); + const signedbv_typet &type=to_signedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(array_type.id()==ID_unsignedbv) { - mp_integer res = simplify_const_int(index_expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); + mp_integer res=simplify_const_int(index_expr); + const unsignedbv_typet &type=to_unsignedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(array_type.id()==ID_float) return to_constant_expr(simplify_const_float(index_expr).to_expr()); - assert(false); //not implemented + assert(false); // not implemented } // if(expr.id()==ID_typecast) return to_constant_expr(expr.op0()); - if(expr.type().id()==ID_signedbv) + if(expr.type().id()==ID_signedbv) { - mp_integer res = simplify_const_int(expr); - const signedbv_typet &type = to_signedbv_type(expr.type()); + mp_integer res=simplify_const_int(expr); + const signedbv_typet &type=to_signedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(expr.type().id()==ID_unsignedbv) { - mp_integer res = simplify_const_int(expr); - const unsignedbv_typet &type = to_unsignedbv_type(expr.type()); + mp_integer res=simplify_const_int(expr); + const unsignedbv_typet &type=to_unsignedbv_type(expr.type()); assert(res>=type.smallest()); assert(res<=type.largest()); - return to_constant_expr(from_integer(res,expr.type())); + return to_constant_expr(from_integer(res, expr.type())); } if(expr.type().id()==ID_floatbv) { return to_constant_expr(simplify_const_float(expr).to_expr()); } - assert(false); //type not supported + assert(false); // type not supported } +/*******************************************************************\ + +Function: remove_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void remove_typecast(exprt& expr) { - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); ++it) + Forall_operands(it, expr) remove_typecast(*it); - if (expr.id() == ID_typecast) - expr = expr.op0(); + if(expr.id()==ID_typecast) + expr=expr.op0(); } /*******************************************************************\ -Function: pretty_print_termination_argument() +Function: pretty_print_termination_argument Inputs: Outputs: - Purpose: print ranking argument expressions in a more readable format + Purpose: -\******************************************************************/ +\*******************************************************************/ -void pretty_print_termination_argument(std::ostream &out, const namespacet &ns, const exprt &_expr) +void pretty_print_termination_argument( + std::ostream &out, + const namespacet &ns, + const exprt &_expr) { - exprt expr = _expr; + exprt expr=_expr; remove_typecast(expr); if(expr.id()==ID_and) { - // should be of the form /\_i g_i => R_i - for(exprt::operandst::const_iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) + // should be of the form /\_i g_i=> R_i + forall_operands(it, expr) { out << "\n"; - if(it == expr.operands().begin()) out << " "; - else out << "&& "; + if(it==expr.operands().begin()) + out << " "; + else + out << "&& "; if(it->id()==ID_implies) { - out << from_expr(ns,"",it->op0()) << " ==> "; + out << from_expr(ns, "", it->op0()) << "==> "; if(it->op1().id()==ID_gt) - out << from_expr(ns,"",it->op1().op0()); + out << from_expr(ns, "", it->op1().op0()); else if(it->op1().id()==ID_or) // needed for lexicographic ones { - for(exprt::operandst::const_iterator it_lex = it->op1().operands().begin(); - it_lex != it->op1().operands().end(); ++it_lex) + forall_operands(it_lex, it->op1()) { - if(it_lex->id() == ID_and) - { - if(it_lex == it->op1().operands().begin()) out << "("; - else out << "\n " << " " << ","; - out << from_expr(ns,"",it_lex->op0()); - } - else - { - out << "\n " << " " << "," - << from_expr(ns,"",*it_lex); - } + if(it_lex->id()==ID_and) + { + if(it_lex==it->op1().operands().begin()) + out << "("; + else + out << "\n " << " " << ", "; + out << from_expr(ns, "", it_lex->op0()); + } + else + { + out << "\n " << " " << ", " + << from_expr(ns, "", *it_lex); + } } out << ")"; } - else out << from_expr(ns,"",it->op1()); + else + out << from_expr(ns, "", it->op1()); } - else out << from_expr(ns,"",*it); + else + out << from_expr(ns, "", *it); } return; } @@ -429,12 +515,13 @@ void pretty_print_termination_argument(std::ostream &out, const namespacet &ns, { if(expr.op1().id()==ID_gt) { - out << from_expr(ns,"",expr.op0()) << " ==> " << from_expr(ns,"",expr.op1().op0()); - return; + out << from_expr(ns, "", expr.op0()) << "==> " + << from_expr(ns, "", expr.op1().op0()); + return; } } } - out << from_expr(ns,"",expr); + out << from_expr(ns, "", expr); } /*******************************************************************\ @@ -449,12 +536,13 @@ Function: merge_and \*******************************************************************/ -void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, - const namespacet &ns) +void merge_and( + exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns) { - result = expr1; - if(expr1!=expr2) result = and_exprt(expr1,expr2); - simplify(result,ns); + result=expr1; + if(expr1!=expr2) + result=and_exprt(expr1, expr2); + simplify(result, ns); } /*******************************************************************\ @@ -472,8 +560,8 @@ Function: make_zero constant_exprt make_zero(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(0),type); - + return from_integer(mp_integer(0), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -498,8 +586,8 @@ Function: make_one constant_exprt make_one(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(1),type); - + return from_integer(mp_integer(1), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -524,8 +612,8 @@ Function: make_minusone constant_exprt make_minusone(const typet &type) { if(type.id()==ID_unsignedbv || type.id()==ID_signedbv) - return from_integer(mp_integer(-1),type); - + return from_integer(mp_integer(-1), type); + if(type.id()==ID_floatbv) { ieee_floatt cst(to_floatbv_type(type)); @@ -536,3 +624,69 @@ constant_exprt make_minusone(const typet &type) assert(false); } +/*******************************************************************\ + +Function: get_original_name + + Inputs: + + Outputs: + + Purpose: retrieve original variable name from ssa variable + +\*******************************************************************/ + +irep_idt get_original_name( + const symbol_exprt &symbol_expr) +{ + std::string s=id2string(symbol_expr.get_identifier()); + std::size_t pos1=s.find_last_of("#"); + if(pos1==std::string::npos || (s.substr(pos1+1, 12)=="return_value")) + return irep_idt(s); + return s.substr(0, pos1); +} + +/*******************************************************************\ + +Function: clean_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void clean_expr(exprt &expr) +{ + if(expr.id()==ID_symbol) + { + symbol_exprt &symbol_expr=to_symbol_expr(expr); + symbol_expr.set_identifier(get_original_name(symbol_expr)); + } + else if(expr.id()==ID_and) + { + Forall_operands(it, expr) + clean_expr(*it); + } + else if(expr.id()==ID_le) + { + if(expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast && + expr.op1().id()==ID_constant) + { + exprt lhs=expr.op0().op0().op0(); + clean_expr(lhs); + constant_exprt rhs=to_constant_expr(expr.op1()); + mp_integer c; + to_integer(rhs, c); + expr=binary_relation_exprt(lhs, ID_ge, from_integer(-c, lhs.type())); + } + else + { + clean_expr(expr.op0()); + } + } +} + diff --git a/src/domains/util.h b/src/domains/util.h index 5c33fa2be..de0c316ef 100644 --- a/src/domains/util.h +++ b/src/domains/util.h @@ -1,5 +1,13 @@ -#ifndef CPROVER_DOMAIN_UTIL_H -#define CPROVER_DOMAIN_UTIL_H +/*******************************************************************\ + +Module: Domain utilities + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_UTIL_H +#define CPROVER_2LS_DOMAINS_UTIL_H #include #include @@ -12,12 +20,15 @@ void extend_expr_types(exprt &expr); constant_exprt simplify_const(const exprt &expr); ieee_floatt simplify_const_float(const exprt &expr); mp_integer simplify_const_int(const exprt &expr); -void pretty_print_termination_argument(std::ostream &out, - const namespacet &ns, const exprt &expr); -void merge_and(exprt & result, const exprt &expr1, const exprt &expr2, - const namespacet &ns); +void pretty_print_termination_argument( + std::ostream &out, const namespacet &ns, const exprt &expr); +void merge_and( + exprt & result, const exprt &expr1, const exprt &expr2, const namespacet &ns); constant_exprt make_zero(const typet &type); constant_exprt make_one(const typet &type); constant_exprt make_minusone(const typet &type); +irep_idt get_original_name(const symbol_exprt &); +void clean_expr(exprt &expr); + #endif diff --git a/src/functions/call_graph.cpp b/src/functions/call_graph.cpp deleted file mode 100644 index 3e214ff61..000000000 --- a/src/functions/call_graph.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include "call_graph.h" -#include "get_function.h" - -/*******************************************************************\ - -Function: call_grapht::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void call_grapht::build( - const indext &index, - const irep_idt &file, - const goto_modelt &model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=model.goto_functions.function_map.begin(); - new_fkt_it!=model.goto_functions.function_map.end(); - new_fkt_it++) - { - f_idt this_f_id; - this_f_id.file=file; - this_f_id.function_id=new_fkt_it->first; - - datat &data=file_map[file][new_fkt_it->first]; - - const goto_programt &body=new_fkt_it->second.body; - - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - const f_idt called_f_id=get_f_id(index, file, symbol.get_identifier()); - data.calls.insert(called_f_id); - file_map[called_f_id.file][called_f_id.function_id].called_by.insert(this_f_id); - } - } - } -} diff --git a/src/functions/call_graph.h b/src/functions/call_graph.h deleted file mode 100644 index 620898107..000000000 --- a/src/functions/call_graph.h +++ /dev/null @@ -1,66 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CALL_GRAPH_H -#define CPROVER_DELTACHECK_CALL_GRAPH_H - -#include "index.h" - -class call_grapht -{ -public: - // function names are not unique - struct f_idt - { - irep_idt file, function_id; - }; - - friend bool operator<(const f_idt &f1, const f_idt &f2) - { - if(f1.file calls; - std::set called_by; - }; - - // function ID to 'datat' map - typedef std::map function_mapt; - - // file to functions map - typedef std::map file_mapt; - file_mapt file_map; - - inline datat &operator[](const f_idt &f) - { - return file_map[f.file][f.function_id]; - } - - void build( - const indext &index, - const irep_idt &file, - const goto_modelt &); - -protected: - f_idt get_f_id( - const indext &index, - const irep_idt &file, - const irep_idt &function_id) - { - f_idt result; - result.file=index.get_file_for_function(file, function_id); - result.function_id=function_id; - return result; - } -}; - -#endif diff --git a/src/functions/get_function.h b/src/functions/get_function.h deleted file mode 100644 index 325f0f3bd..000000000 --- a/src/functions/get_function.h +++ /dev/null @@ -1,42 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GET_FUNCTION_H -#define CPROVER_GET_FUNCTION_H - -#include -#include - -#include "index.h" - -class get_functiont:public messaget -{ -public: - explicit get_functiont(const indext &_index): - index(_index), ns(goto_model.symbol_table) - { - } - - goto_functionst::goto_functiont * operator()( - const irep_idt &function_id); - - inline irep_idt get_file_name() const - { - return current_file_name; - } - -protected: - const indext &index; - irep_idt current_file_name; - goto_modelt goto_model; - -public: - const namespacet ns; -}; - -#endif diff --git a/src/functions/index.h b/src/functions/index.h deleted file mode 100644 index b27cac36d..000000000 --- a/src/functions/index.h +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Summarization - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_INDEX_H -#define CPROVER_DELTACHECK_INDEX_H - -#include -#include -#include -#include - -#include - -class indext:public messaget -{ -public: - // function names to file - typedef std::map > function_to_filet; - function_to_filet function_to_file; - - // file names to functions - typedef std::map > file_to_functiont; - file_to_functiont file_to_function; - - void read(const std::string &file); - - void write(std::ostream &) const; - - void build(const std::vector &files, - const std::string &description); - - void index_goto_binary(const irep_idt &file); - - std::string description, file_name, path_prefix; - - std::string full_path(const irep_idt &) const; - - inline std::string full_path(const file_to_functiont::const_iterator it) const - { - return full_path(it->first); - } - - irep_idt get_file_for_function( - const irep_idt &preferred_file, - const irep_idt &function_id) const; - -}; - -#endif diff --git a/src/functions/path_util.cpp b/src/functions/path_util.cpp deleted file mode 100644 index ff0f6cb1b..000000000 --- a/src/functions/path_util.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#ifdef _WIN32 -const char pathsep='\\'; -#else -const char pathsep='/'; -#endif - -/*******************************************************************\ - -Function: make_relative_path - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string make_relative_path( - const std::string &directory, - const std::string &src) -{ - // is src already absolute? - - #ifdef _WIN32 - if((src.size()>=2 && src[1]==':') || src[0]=='\\') - return src; - #else - if(src[0]=='/') - return src; - #endif - - // anything given? - if(directory.empty()) return src; - - // otherwise, stitch together - if(directory[directory.size()-1]==pathsep) - return directory+src; - else - return directory+std::string(1, pathsep)+src; -} - -/*******************************************************************\ - -Function: get_directory - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_directory(const std::string &src) -{ - std::size_t last_pos=src.rfind(pathsep); - - if(last_pos==std::string::npos) - return ""; // no directory given - - // cut off file name, but keep pathsep - return std::string(src, 0, last_pos+1); -} - -/*******************************************************************\ - -Function: get_file_name - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_file_name(const std::string &src) -{ - std::size_t last_pos=src.rfind(pathsep); - - if(last_pos==std::string::npos) - return src; // no directory given - - // cut off directory - return std::string(src, last_pos+1, std::string::npos); -} - diff --git a/src/functions/path_util.h b/src/functions/path_util.h deleted file mode 100644 index e55e6c772..000000000 --- a/src/functions/path_util.h +++ /dev/null @@ -1,18 +0,0 @@ -/*******************************************************************\ - -Module: - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -std::string make_relative_path( - const std::string &directory, - const std::string &src); - -std::string get_directory(const std::string &src); - -std::string get_file_name(const std::string &src); - diff --git a/src/functions/summary.cpp b/src/functions/summary.cpp deleted file mode 100644 index 1726a1dd1..000000000 --- a/src/functions/summary.cpp +++ /dev/null @@ -1,10 +0,0 @@ -/*******************************************************************\ - -Module: Function Summary - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "summary.h" - diff --git a/src/functions/summary.h b/src/functions/summary.h deleted file mode 100644 index c948f76fc..000000000 --- a/src/functions/summary.h +++ /dev/null @@ -1,31 +0,0 @@ -/*******************************************************************\ - -Module: Function Summary - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_SUMMARY_H -#define DELTACHECK_SUMMARY_H - -#include "../solver/predicate.h" - -class summaryt -{ -public: - // signature - predicatet::state_var_listt entry_vars, exit_vars; - - // build from fixedpoint - void from_fixedpoint(class ssa_fixed_pointt &); - - // a summary has two parts: - // 1) pre-state (a predicate over entry_vars) - // 2) transformer (a predicate over entry_vars and exit_vars) - - predicatet pre_state; - predicatet transformer; -}; - -#endif diff --git a/src/html/html_escape.cpp b/src/html/html_escape.cpp deleted file mode 100644 index 936a472ad..000000000 --- a/src/html/html_escape.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "html_escape.h" - -/*******************************************************************\ - -Function: html_escape - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string html_escape(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - for(unsigned i=0; i': result+=">"; break; - case '"': result+="""; break; - case '&': result+="&"; break; - - // ' does not seem to be universally supported, - // and Unicode seems to suggest to prefer ’ over ' - case '\'': result+="&8217;"; break; - - default: result+=src[i]; - } - - return result; -} - -/*******************************************************************\ - -Function: html_escape - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string html_escape(const dstring &src) -{ - return html_escape(as_string(src)); -} diff --git a/src/html/html_escape.h b/src/html/html_escape.h deleted file mode 100644 index 1be002cae..000000000 --- a/src/html/html_escape.h +++ /dev/null @@ -1,19 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_HTML_ESCAPE_H -#define DELTACHECK_HTML_ESCAPE_H - -#include - -#include - -std::string html_escape(const std::string &); -std::string html_escape(const dstring &); - -#endif diff --git a/src/html/logo.cpp b/src/html/logo.cpp deleted file mode 100644 index 5955ceedc..000000000 --- a/src/html/logo.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "logo.h" - -const char deltacheck_logo[]= - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPYAAAA4CAYAAADO8AUMAAAACXBIWXMAAASdAAAEnQF8NGuhAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAFKFJREFUeNrsXQt0VEWa/m+/EiTQzWMFBOygnplZHScd1BFFTKNHZMK4RGYBdwdNRx4+dhii43HOnNFNg+weZ8+MCSMMyCjpADo+UJrhiMo6pgO+dmFJWFgZUKEziLwUusmDPPtu1U3ddHXl1u3br9CB+s+pc5Pue6vvrarv//7/r7/qSpDFYsnJ4X7X1d4OQoQI0RYpi8EsUUX9XyZ/y1QRQBciJFuBTQHahIq5u7NzuByJuGiAS2bzfrPFEkJ/d6MSIUf1b1kAXIgQgqcsAjUGrxkB03Prrbcuvuuuu37gdrtjzgsEAnDo0KGj+/bt24/kHclk2mm2Wr9EX3Wg0kkALkSIkEyLLMtFW+oOyM+8GFDK16fPVbOgRgWz9I0I0A21tbWyEamoqMAMvQtdOx4VOyo2VCQ9v1yIECFpklNnmmtHTXtWzr11qVIwuJE4GVDftHDhwrNI4gIan/PEE0+0ILZeh651o3ItKpejkovrEsAWIqTHn80oW7/w1m53uDnq927Y1oAPXgJAEzK9b0Cg3r527VoHEt36GhoaYNrddx97rqrqOWSCv0s+jtA+thAhQjIUPBt2y7gCdHB8+PIe7w3zVrvZ71c+OSNUfPPYq64YM2bEnXfeuev99993xKvT7/fDwkWLdp0Nh1+TJOk4+ug0KidR+QaVc6i0odKNFIUAuJBLXixpAvJMdHCTgiPZMLlwElS+/LHm+f+x/kPHnQUzvcinLtq0aVNcUPt8Pnhw/vz3EEv7EajPYAufFPx3swpqwdpChKTI2AjMdnQoJ6UPOJ//9VoI1HdA44kQ7Kxv7Avuf7kdplzrAJfLpfs7Ho8H1m/Y8AoCdYCw80nC1mdRaYGeiLhiioupLiFCUvCxEagr0CGIfWUtUGO2xqB2F9rggeKrNetY+ebeuKBesGDBeQLqDwigj6HyNQF2EyrtKlNfgqB2iuErJC3Axr4zKvU8QKtyX/GDyvHVbevg2PH/hh9cM6rPOX87EYYde4K6oK6uqVmFQP0XCtQniPmNmVqZt8aAvsRAjS0lVbEWiSGsKDg8hVp6ET6TnHFgEz86oPrQPKHZ+qP6T2H1ay/B/cV/r3nu8nV1XPN7XXX1ZpPZfICA+mtyxOb3eVS6VFBfQlJAOjtEFOtAliKmOJOso5ooOA8q+RdB/y5BpZ56pqTFYhDUWBv6jJyL2RoDG7M1lnPN56CleTdcOdqusDQt2PfGrH37xGif4EDZhj9vPG8eav0cQRgHyI4TUOOL21I0vWt1vnPEUVoNpMHx0Y/K3n7s9M2olAxwIOOB6o4DwABpW1waOedggqm6CIBMi2F8GRXJAKhnkoaOK5itR4+Zp7D14n9b1Pv5+NHjYMHcSnjy99v7XDOl0AnbV/ZYUXhKa1bpT8AyzApWq7W5u1V6qPvbjp2Epc6nwZ8uIAAp57gSKnAbqM/ySXFrnOsjgyzcD6ZZiICjivoc31NdlgPaS7VdAwGvqiRphVrCKFYfubZRoy3UPqHBgM9dehGwtp9RWlLagU3mowN6/jQtaiT8xPGNihmuiizLsPyRZ+CZl08Bnayiyqe+RSC3nIA7ZtwBzZZWsNisYMqxgclmCcttkclth5sOQnqDZHYysOjnwoB5LM51M4lSoEGuAm5LP3W+PECAXUnaCshg9RqwcpykH0oYgJeQZ92rYYG5LyJga7F3UsCO52P7jIKa9a0pVEN3SwecPtwIP5szSfPala/9F6xYtQKaTC1gtloQoK0g2dDRYrZbhtpWQfoj32EN08eIVYLBO5UMphDFOH7i7wnpUZqbCahVpXevQdelkZzrodrXo+MmNVyE7RdMRyUmHbauiBcoY31rLKpvrZILppfLYRh4vV4E7Js1r934zl6YWnwvmG1mVHpAbbaYQDJJIElSUV7hyJ9nIFDmT+HaOmIu0QPLI8Adw7CqRVGTRB01jPIEjk8dEs2dALARqJ2UGZUCWwN0nGqB3y77TQ+1DcmFeT8q0Kxj5//JMHXyVJCsClMjUJswqPEqbHz0koSYbJIwGXwsuCsu4fFUSYG6HFILMO6F2MhwvoBr6oxdbtQE12PrSFcEfjihEEpKoi7TU/OLuKz909mPImAjUJujoKbM3fIsbL8wGcghJohzKc4vF1F9hOMyK9JQ5xaITu0JYKcCbMKMnlTZGrnW0H68GSp/+1xsdGSMA+6Z8l1tFX3YArffdBsLapoNs1EaNZSO9xI1wTPx/EuJ3+kScE0S2HgppSzLiIFkB0amzBQFrUwyzENze8b0Ozs2AY3qSHsX3Df9HzXTRnm+Nl7SOcP9DxqY7tHYZOotG6WGCXq4s4S1+ysjq5Ri1CCkP1LvScSCFEIBm9qeaGqkMwKd4XY4f/QcdHzbCt2tnSBHIhDBAI8QkCOA/1PxbNiy4xt4oPgq2LZzO+1aQ26TCSqe+lfNH8UJKXj+uo9d29wO3zY7Yfzo8bz7dWdxW3qzzMJQk0L6I8BET0/5M1B/XZb3fXYCWwV1V3v7VbbTkZmeyXNg6x/egBP7/wZvr30LlpX9Cka25kHXuXaIdEd6wT1neqkC41e3VceydVsXPPLTRZCfr+0W4USU+6d/T/O7la9/Cg/PfXQgAruGAdGFzBSzUwAL9cNv0c8ayNDv1Am4JsHYCNQTJk6cuPvg7s8cVVVVgDcSxDua4GN5eTl8se8QLJg2D9pPtkCkqxvunXoP+LYegnnTJ8Cftr0Ry9bNJuUaLQmFQlD28INw5PPtSpqpNmtfqWSraUi2+1k0W2HT0UgOdCr50jygBQyYrs403RPbJ8Es6o+CNLWtk2qTVK2odNRjGNh4E8Cje/bs+Q3eCZQnGPDPPr4MOpF5Pvm6OxDA2+CVrS8S3zuWrXnbHOE6mkzNsHr9Glg828X1tX85XzsJjGTDZauwjZevM1DU1TsBUjAgjqToE6ugphsWZ3LVMqUeYlNT1UFXq3FPZ8m9Og1aUXsvcB/Yyf3i+25IsW1LyXVBqk3kOO3BCo4LbWbaVa2nnnyfqLJS+7GCKtXUZ7HBs7zrRu5fUP6wsrcYTzATz5p8D6za+CFMvX4QvPbumyScJsdl62AwCL//4/NgzjFD0/lmcAz6Gux5fTcfxItFzjSPhqF5Q7WqyeYgStCAhVFKBpyDAF8i56k5wj5ILtFFC9TqPbiZ4mKsi1JKKXmIaV1FzHgH+ayBDKps7o8CiK6MCjH9kUjb2glIqkhxUO0Qotqj1EAdfoiuyPMyyt+VRExiL0QzQr1UcZN78gEdOEPFMvSGUcsHXz9SLrxpYtydQl033CJPn/djGV0jO26+QnZMGisPdY1StgXmSWlZqWwZZ5MHXTNYHvz94fJ1P75R2bVU3cGULt+ZVSU/++LvZMTQbEmXGVMEsW8USVe9dJ0VGqCWiQbXkmrq2moD9RcxVoCqyc9S59RrMHY1wyjqeVqDs56qS2t1XC1ovJmln6SC+t3N5LkrGTZ1UozJ6xdW6nXGRAHVvmc5ys4ep44ipl2dOuOSJ5XUPSzh+thIuiWTKWLOscBfw4e5rKuoaGRml86bA4EdtUoSSiQSUczwnDhsveHNDUpWmcliQba/Gb46eQxG5DVyWXtw3o081h6I4qTMX17jlkE0k82ToLLB8+lTSaFNrnLqc7WUUd/7NOIDvSEP5l7dkL07t6ir9h6D2BVhav65j/rMS8DHA4yLnF/HYcwSylqp4sRaXOR36jiBQDfVT/lJuAjlxCLB9azQBDbJw5YlSarFqZwWBLQ/vPpH0PO3MYCvGDwa5M4ukBG4u853QvHkaVzfuvzxx8A0FOeAmwFw2qi5Jxd8246tcM+U73F87QPwyNz5AxnMIQZADmKKNepcU8UMwExKEWVK8wZXncG4Ac1oFyq+oZeXXsaY5h6O8i3XUXR0mzRQyq6IAZ2b9L3eajM6azGUIKh95PddvJhGbOaZSZIx2DDocsbkweO/ekL3F55+8inobmpXouQdp1qVhR6aLY4UxNa//BnMVsTWOGXU0pM2ijNRcLYazlrTkv/94iSMHfNDPbBkuzRQA8bNCbDpPV+mGTJI/Z7bwHPEu98L6XMHDJzj1wn6sYo0kWW45Rp1GPGdsYIflkDAsZoCtRt09gGgp7vw4Yjy9jsMbosJ/nr6Sy5YFZXn8cAVeYi12zvhn4tnc+etly5f2svWkoWwtbK4o+d7nGPOWxyyftuXSiKMKmc/+WrvAAJ2iDIT6Y6XdYpfw8TMlDQSBvZwTEojypQFvTuL+yMQRwGVcGIZWsWl8cwzKYsm3UtKq0k/YWAXQpzNPWK2Rgp9eqxx2C3jQghvDgw+iyMHVq1fowCYB9qnn3waFvx8IZTeX8pl6x31O8FqtymgVtjapOaC9yAbs3bN3F8qC0FYwdsnPfDrWepcebavv3UyYNirMdh9kNhcry/D9xyG5JZW0qxPSzbnGoTi9J2DeiZfEvW6DFo5iYidKF0PUfplRi6yaGo1SVI0lwn5221DOmHpM0uh+qVqLmvjTDL2zZg0W5vziG9tia7cYvPB393xCkwpLNDcg1xlbQTubAe2i2P2ORigDpQsqgLyTPkJmMAlZDCGYWBJPqOsktmNJRPWSoAaV26jbWvi+SG9JrnNAls+eFuJanMdF79fl60V05vD1qpgRubtQY7Bfl9xmVG/5UKKmwPsbPBBjVoceOqETlTxxLnnRg3WHsgbL6bL6sjPwL04jGKAC2xQNzlAQHz04Qoom/9QwnfEsrWJw9aq4D3ItdJMe1j7MJz5+Gi2B85KKI2/pR+1ejoArW7lWwXRiCsO7Ew1YFZW6QSh0iVY4fTXJhYO4E+H9TewPUx7uo20Qx9gn/3kq979wDAA59w9Cz461A1gzdOd/jLK1pIGW6uC9yBfPKdQ8zvsf3925LQ3i0FNL10s1/Htso3NCghwPRSgH4PEUkN90HcboyVpvEc7ZH7qLx1WRyb6uYa4BQ2M4ixKlLEpjSvBbTfNRmfZYOy1Q2HZvz+TMlvr7blI70GuJZUvf+zGr+bNQlDTAy+gwdYBZtBn0zP4KVPbA8nleoc1gOeF9E3VeSGaA5ApaWSA6UmijgbGhE7nnD67U49fr301gY1YGz9kFQ5YbXzvCDww4zuw9cP34JODuwyxtsLWuxJja1VeefsN3U0Ps5S11Q3sgxxN7Y9jul4oKaKsjACktoBjBfSdTvKnwaQtpSygYD8oOdrkXZLC9enu50ZmbOm2r96bQLy3TJxV/vm2L+HqcTb4ReliOBcKwwtr1nAj4L0m9erVcPttU2DYiGEwcvQoGPF3IyFvyJCeeWwp/jbJRYUjuN+99cFnmLULUD3ZMJ9NT0WESMOHOZ3io1jARXzaeFMXS+L460b9xf4KJpVAbBTXRf53Q3JR8grKEgiBfsZeuiwDD/N/PIVnJ32rbrEcoOIoqnKIt/9bEcROj/KkDqJz2Wr7VmmNIy6wz3x81DPtZzVKRPruejS2ZORnd8kQOfmNwsg8cOPo+aZ3d4NlxHVgyh0MUq4VZBu61tSCw+xgZP9z77o/cb/DGzHMnzmxHAzO5yUQ3PBAYtNQRaRRXcQEK4kz8LzkHNrsVVcMhTWCWT6IrsbiiZtzz0HmnC0pgLeAUQ4unXZSd25lwd2QYPvSbauC2h3nnhMNVrk5CthLKRPV/Pdw2rCU3GdQQxnQrO0A/vSZqrx4/exkxpW6NbOHcRnK4priiBHtH+w67I2dU0anmqxgHnk98rV/xx+9y5aDeUg+SGackJILMjqifwyDOq4z19wOL23Z40H3mKr/5tH4H6+9xYsAZnL8FyfpzFrSefmkUwoNsEmjxm+WkEFRDdF1tZshmtzPgoH12co5/rqfea5ShmHw79QzA9IBfVeUFUDfhJryOOZ1mLSHlwFdgLTbTB3mo9vWRQXm8jlsls8ANZ7Zz4Jf616WQmxyimry1lN9VEnGio/qK5ZVWaV+hLC3utFCBfnMy/SzW6OPWSlj7tFDxk3vmJU4wK5Y/lKdd0c93Z94v7Nu6GpvhVMHd8Kq5Y/2YW3M1pOKZoBjrAsG2UfAYMcoyMkbjvRBDsK1OW32kiMvF15/dq4PmePJsPYS0lhGNXyDBmMEIfn3dhUxASveb7KBrGqdgI56P0sNnk+/lqiCAWEQoq8/UhWXD7TXentBP5HDqWHesu2qAi6fuT+1fbUUptarltR795O6azguk5YSDGiYy5UQf8trvecvpdgadPpNBbUToqvCeG3lZRR9vcb5ynvReMBmzS944fV1rq2BbVVdnV1wvqUFhrcPgc1vvhVLeR4P/Of/vA+O4Q4YbLejMgRyLxsEZrMx31rxDcyW0I+m3OVdNPvBuFlmqM5kMrg2MxHWEMSu1GE1fD51TigNQSZ1oHkg+iI6B0RfBujnmH0VGoBgmahGQ4mVQOwCFD8BTZgDlBB1H/R5duYcPxhPRbVTz6o+r4tpf/UYMGCyV1Imfohjnt+r4doEODGIfI5LpG4Iqd433TZ+A1Ya3WZuGnga7eckvxXUCBKq7cW+5dUOnASihGxjxyRExSBvjnRH8tuONsHBT/b35pDjvcyGjx0B1sttYM1FDJ1rA7PNEn2jh3F29AywhR5ChGSdmBI5ubn+mwa5IzJRAul526jLYlZ+4b3MzA4zWcFFZZkZq1oxMxCgCwWohQhJXRKOZpGtiq228Zddm9eWs+bgvgPKKzSv+v7V0GJtRWxtQ2ydo7w1s/dVPfqA9hNQN4ruECIkPWJJ8rrujqOtX5zuOvcLxNQf4V1TmjqbD1hzrVeCxTJYIrujSHwwq76en6SwChEi5EIyNmFtfJ0VO+/XTJiwNSLLlx0OBrFdHrKMyAHLyJxBllxrDpik6wljq0GLoGBmIUKyF9j4gOevBnV1dMxF4LWardb96P8mVE4RVsZbskQy8F5rIUKEZBDY+Fq8WVkeKkOJWd8GPVMGrah0CVALEXJhJKmskUh3t7KFsPovKp0E1OfJsRsVGZ8nRIiQ/hdLitdjUHdgdiYMHlGLYGshQgaYKc6Y5Gw9sgC1ECEXA+33vCJINIQQIUKECBGSKfl/AQYACOr3yNUYh1MAAAAASUVORK5CYII="; diff --git a/src/html/logo.h b/src/html/logo.h deleted file mode 100644 index bd5e0c3c5..000000000 --- a/src/html/logo.h +++ /dev/null @@ -1 +0,0 @@ -extern const char deltacheck_logo[]; diff --git a/src/html/syntax_highlighting.cpp b/src/html/syntax_highlighting.cpp deleted file mode 100644 index 5c6e078d0..000000000 --- a/src/html/syntax_highlighting.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*******************************************************************\ - -Module: Syntax Highlighting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -// may wish to try http://www.gnu.org/software/src-highlite/ - -#include -#include -#include -#include - -#include "../html/html_escape.h" -#include "syntax_highlighting.h" - -/*******************************************************************\ - -Function: is_keyword - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const char *keywords[]= -{ - "auto", "_Bool", "break", "case", "char", "_Complex", "const", "continue", - "default", "do", "double", "else", "enum", "extern", "float", "for", - "goto", "if", "inline", "int", "long", "register", "restrict", "return", - "short", "signed", "sizeof", "static", "struct", "switch", "typedef", - "union", "unsigned", "void", "volatile", "while", "__float128", - "__int128", "__int8", "__int16", "__int32", "__int64", "__ptr32", - "__ptr64", "__complex__", "__complex", "__real__" , "__real", "__imag__" , - "__imag", "offsetof", "__asm", "asm", "__asm__", "bool", "catch", "class", - "constexpr", "delete", "decltype", "explicit", "friend", "mutable", - "namespace", "new", "nullptr", "operator", "private", "protected", - "public", "static_assert", "template", "this", "thread_local", "throw", - "typeid", "typename", "using", "virtual", "wchar_t", "typeof", NULL -}; - -bool is_keyword(const std::string &token) -{ - for(unsigned i=0; keywords[i]!=NULL; i++) - { - if(strcmp(keywords[i], token.c_str())==0) - return true; - } - - return false; -} - -/*******************************************************************\ - - Class: tokenizert - - Purpose: - -\*******************************************************************/ - -const char *tokens[]= -{ "++", "+=", "--", "-=", "&&", "&=", "||", "|=", "/*", - "*/", "//", "%=", "/=", "<<", ">>", "<<=", ">>=", "==", - "!=", "<=", ">=", "::", "->", "##", ".*", "->*", NULL }; - -class tokenizert -{ -public: - explicit tokenizert(const std::string &_buf):buf(_buf) - { - } - - std::string get(); - std::string peek(); - std::string buf; - bool eol() const { return buf.empty(); } - - char get_char() - { - if(buf.empty()) return 0; - char result=buf[0]; - buf.erase(0, 1); - return result; - } - - static inline bool is_identifier_char(char ch) - { - return isalnum(ch) || ch=='_'; - } -}; - -/*******************************************************************\ - -Function: tokenizert::peek - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string tokenizert::peek() -{ - if(buf.empty()) return buf; - - char first=buf[0]; - - unsigned pos=1; - - if(is_identifier_char(first)) - { - // identifier or keyword or number - for(pos=1; pos"; - - if(comment) out << ""; - - std::string token; - - std::map var_count; - - while(!tokenizer.eol()) - { - if(comment) - { - std::string buf; - bool end_of_comment=false; - - while(!end_of_comment) - { - char ch=tokenizer.get_char(); - if(ch==0) break; - buf+=ch; - if(buf.size()>=2 && buf[buf.size()-2]=='*' && buf[buf.size()-1]=='/') - end_of_comment=true; - } - - out << html_escape(buf); - - if(end_of_comment) - { - out << ""; - comment=false; - } - } - else - { - token=tokenizer.get(); - assert(!token.empty()); - - if(isdigit(token[0])) // numeral - { - out << html_escape(token); - } - else if(isalpha(token[0])) - { - if(is_keyword(token)) - out << "" << html_escape(token) << ""; - else - { - if(identifier_tooltip) - out << ""; - else - out << ""; - - out << html_escape(token); - - out << ""; - } - } - else if(token=="/*") - { - comment=true; - out << "" << token; - } - else if(token=="//") - { - out << "" << token; - while(!(token=tokenizer.get()).empty()) - out << html_escape(token); - out << ""; - } - else if(token[0]=='"' || token[0]=='\'') - { - out << "" << html_escape(token) << ""; - } - else - { - // hack to distinguish lhs from rhs without parsing - #if 0 - if(token=="+=" || token=="=" || - token=="-=" || token=="<<=" || - token==">>=" || token=="&=" || - token=="^=" || token=="|=") - lhs=false; - else if(token==";") - lhs=true; - #endif - - out << html_escape(token); - } - } - } - - // close tags - if(comment) out << ""; - if(!strong_class.empty()) out << ""; - - out << "\n"; -} - diff --git a/src/html/syntax_highlighting.h b/src/html/syntax_highlighting.h deleted file mode 100644 index 159abcc28..000000000 --- a/src/html/syntax_highlighting.h +++ /dev/null @@ -1,35 +0,0 @@ -/*******************************************************************\ - -Module: Syntax Highlighting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SYNTAX_HIGHLIGHTING_H -#define CPROVER_SYNTAX_HIGHLIGHTING_H - -#include -#include - -class syntax_highlightingt -{ -public: - explicit syntax_highlightingt(std::ostream &_out): - line_no(0), identifier_tooltip(false), - out(_out), comment(false) { } - - std::string strong_class; - unsigned line_no; - std::string id_suffix; - - bool identifier_tooltip; - - void operator()(const std::string &line); - -protected: - std::ostream &out; - bool comment; -}; - -#endif diff --git a/src/html/to_c_string.perl b/src/html/to_c_string.perl deleted file mode 100755 index 9d1e7e220..000000000 --- a/src/html/to_c_string.perl +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -my @c = ; - -# print "static char* c_str = {\n"; -print "// generated file\n"; -foreach (@c) { - my @s = split(''); - print qq(\t"); - printf("\\x%02x", ord($_)) foreach @s; - print qq("\n); -} -# print "};\n"; - diff --git a/src/solver/Makefile b/src/solver/Makefile index bdbbed955..bd0fe97b7 100644 --- a/src/solver/Makefile +++ b/src/solver/Makefile @@ -1,18 +1,22 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = solver.cpp predicate.cpp fixed_point.cpp +SRC = summarizer_base.cpp summarizer_bw.cpp \ + summarizer_bw_term.cpp summarizer_fw_contexts.cpp \ + summarizer_fw.cpp summarizer_fw_term.cpp \ + summary.cpp summary_db.cpp +include ../config.inc include $(CBMC)/src/config.inc include $(CBMC)/src/common +CBMC ?= ../.. -CP_CXXFLAGS += $(SUMMARIZERFLAGS) +CP_CXXFLAGS += $(TWOLSFLAGS) -INCLUDES= -I $(CBMC)/src +INCLUDES= -I $(CBMC)/src -I .. -CLEANFILES = +CLEANFILES = solver$(LIBEXT) -all: $(OBJ) +all: solver$(LIBEXT) ############################################################################### +solver$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/solver/fixed_point.cpp b/src/solver/fixed_point.cpp deleted file mode 100644 index ca5f09d23..000000000 --- a/src/solver/fixed_point.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/*******************************************************************\ - -Module: Forward Least Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#define DEBUG - -#include "fixed_point.h" -#include "solver.h" - -#ifdef DEBUG -#include -#endif - -/*******************************************************************\ - -Function: fixed_pointt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::operator()() -{ - iteration_number=0; - - // Set up the state predicate, starting with 'false' - // (the empty set). - - state_predicate.state_vars=pre_state_vars; - state_predicate.make_false(); - - bool change; - - do - { - iteration_number++; - - #ifdef DEBUG - std::cout << "\n" - << "******** Forward least fixed-point iteration #" - << iteration_number << "\n"; - #endif - - change=iteration(); - } - while(change); - - #ifdef DEBUG - std::cout << "Fixed-point after " << iteration_number - << " iteration(s)\n"; - output(std::cout); - #endif -} - -/*******************************************************************\ - -Function: fixed_pointt::iteration - - Inputs: - - Outputs: 'true' if there is a change in the state predicate - - Purpose: - -\*******************************************************************/ - -bool fixed_pointt::iteration() -{ - solvert solver(ns); - - // Feed transition relation into solver. - for(constraintst::const_iterator - it=transition_relation.begin(); - it!=transition_relation.end(); - it++) - solver << *it; - - // Feed current state predicate into solver. - state_predicate.set_to_true(solver); - - #ifdef DEBUG - std::cout << "Entry state:\n"; - output(std::cout); - #endif - - // solve - solver.dec_solve(); - - #ifdef DEBUG - std::cout << "=======================\n"; - solver.print_assignment(std::cout); - std::cout << "=======================\n"; - #endif - - // now get new post-state - predicatet post_state; - post_state.state_vars=post_state_vars; - - post_state.get(solver); - - #ifdef DEBUG - std::cout << "Post state:\n"; - post_state.output(std::cout); - #endif - - // Now 'OR' with previous state predicate. - // First rename post-state to pre-state. - post_state.rename(pre_state_vars); - - // Form disjunction of previous state predicate and the new one. - return state_predicate.disjunction(post_state); -} - -/*******************************************************************\ - -Function: fixed_pointt::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void fixed_pointt::output(std::ostream &out) const -{ - state_predicate.output(out); -} diff --git a/src/solver/fixed_point.h b/src/solver/fixed_point.h deleted file mode 100644 index 1c1e213ce..000000000 --- a/src/solver/fixed_point.h +++ /dev/null @@ -1,53 +0,0 @@ -/*******************************************************************\ - -Module: Forward Greatest Fixed-Point - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef DELTACHECK_FIXED_POINT_H -#define DELTACHECK_FIXED_POINT_H - -#include "solver.h" -#include "predicate.h" - -class fixed_pointt -{ -public: - explicit fixed_pointt(const namespacet &_ns):ns(_ns) - { - } - - typedef std::list constraintst; - constraintst transition_relation; - - predicatet::state_var_listt pre_state_vars, post_state_vars; - - predicatet state_predicate; - - void output(std::ostream &) const; - - unsigned iteration_number; - - void operator()(); - -protected: - const namespacet &ns; - - // fixed-point iteration - void initialize(); - bool iteration(); -}; - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, - const std::list &src) -{ - for(std::list::const_iterator - c_it=src.begin(); c_it!=src.end(); c_it++) - dest << *c_it; - return dest; -} - -#endif diff --git a/src/solver/predicate.cpp b/src/solver/predicate.cpp deleted file mode 100644 index c831ca52a..000000000 --- a/src/solver/predicate.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Abstract State - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "predicate.h" - -/*******************************************************************\ - -Function: predicatet::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void predicatet::make_false() -{ - uuf.resize(state_vars.size()); - - for(unsigned v1=0; v1 eq_count; - - for(unsigned v=0; v::const_iterator - e_it=eq_count.begin(); e_it!=eq_count.end(); e_it++) - { - if(e_it->second>=2) - { - for(unsigned v=0; vfirst==uuf.find(v)) - out << "Equal: " << from_expr(state_vars[v]) << "\n"; - out << "\n"; - } - } - - // print intervals - #if 0 - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); i_it!=integer_intervals.end(); i_it++) - { - if(i_it->lower_is_set || i_it->upper_is_set) - { - if(i_it->lower_is_set) - out << from_expr(i_it->lower) << " <= "; - - out << from_expr(vars[i_it-intervals.begin()]); - - if(i_it->upper_is_set) - out << " <= " << from_expr(i_it->lower); - - out << "\n"; - } - } - #endif -} - -/*******************************************************************\ - -Function: predicatet::disjunction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool predicatet::disjunction(const predicatet &other) -{ - bool change=false; - - // - // do equalities - // - - assert(other.state_vars.size()==state_vars.size()); - - unsigned_union_find new_uuf; - new_uuf.resize(uuf.size()); - - // can be done better than quadratic - for(unsigned v1=0; v1 -#include -#include - -#include "solver.h" - -struct predicatet -{ -public: - predicatet() - { - } - - // support set of the predicate - typedef std::vector state_var_listt; - state_var_listt state_vars; - - void output(std::ostream &) const; - void make_false(); - - // returns 'true' iff predicate is weakened - bool disjunction(const predicatet &); - - // rename supporting set of variables - void rename(const state_var_listt &new_state_vars); - - // read the predicate from a solver state - void get(const solvert &); - - // push the predicate to a solver as constraint - void set_to_true(decision_proceduret &) const; - - bool is_bottom() const - { - return false; - } - - bool is_top() const - { - for(unsigned i=0; i integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - integer_intervalst integer_intervals; - ieee_float_intervalst ieee_float_intervals; -}; - -static inline std::ostream & operator << ( - std::ostream &out, const predicatet &predicate) -{ - predicate.output(out); - return out; -} - -static inline decision_proceduret & operator << ( - decision_proceduret &dest, const predicatet &predicate) -{ - predicate.set_to_true(dest); - return dest; -} - -#endif diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp deleted file mode 100644 index c9b579d18..000000000 --- a/src/solver/solver.cpp +++ /dev/null @@ -1,954 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Solver - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include -#include - -#include - -#include -#include -#include -#include - -#include "solver.h" - -/*******************************************************************\ - -Function: solvert::solvert - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -solvert::solvert(const namespacet &_ns):decision_proceduret(_ns) -{ - false_nr=add(false_exprt()); - true_nr=add(true_exprt()); -} - -/*******************************************************************\ - -Function: solvert::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned solvert::add(const exprt &expr) -{ - exprt tmp=expr; - simplify(tmp, ns); - return add_rec(tmp); -} - -/*******************************************************************\ - -Function: solvert::add_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned solvert::add_rec(const exprt &expr) -{ - // we do a mild bit of canonicalization - - if(expr.id()==ID_ge) - { - // rewrite x>=y to y<=x - exprt tmp=expr; - tmp.id(ID_le); - assert(tmp.operands().size()==2); - std::swap(tmp.op0(), tmp.op1()); - return add_rec(tmp); - } - else if(expr.id()==ID_gt) - { - // rewrite x>y to y::number_type a_nr, b_nr; - if(expr_numbering.get_number(tmp_a, a_nr)) return false; - if(expr_numbering.get_number(tmp_b, b_nr)) return false; - return is_equal(a_nr, b_nr); -} - -/*******************************************************************\ - -Function: solvert::add_operands - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void solvert::add_operands(unsigned nr) -{ - // expr_numbering is a vector, and thus not stable. - // We will call add recursively below. - const exprt expr=expr_numbering[nr]; - - const exprt::operandst &expr_op=expr.operands(); - std::vector dest; - - dest.resize(expr_op.size()); - - for(unsigned i=0; i !x==y - set_equal(not_exprt(equal_exprt(expr.op0(), expr.op1())), - expr); - } - else if(expr.id()==ID_equal) - { - add_operands(nr); - equal_list.push_back(nr); - } - else if(expr.id()==ID_address_of) - { - // NOT an uninterpreted function, but rather a constant. - } - else - { - if(expr.has_operands()) // make it uninterpreted - { - add_operands(nr); - uf_map[expr.id()].push_back(nr); - - #ifdef DEBUG - std::cout << "UF " << nr << " added: " << expr.id(); - forall_operands(it, expr) std::cout << " " << add(*it); - std::cout << "\n"; - #endif - } - } -} - -/*******************************************************************\ - -Function: solvert::dec_solve - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -decision_proceduret::resultt solvert::dec_solve() -{ - bool progress; - - do - { - progress=false; - - // rummage through things that are equal to 'true' - // and that we haven't processed yet - - for(unsigned i=0; i !x, !y - { - for(unsigned i=0; i a==true && b==true - { - for(std::vector::const_iterator - o_it=se.op.begin(); o_it!=se.op.end(); o_it++) - implies_equal(*o_it, true_nr, progress); - } - } - - for(solver_expr_listt::const_iterator - not_it=not_list.begin(); - not_it!=not_list.end(); - not_it++) - { - unsigned e_nr=*not_it; - const solver_exprt &se=expr_map[e_nr]; - - if(is_true(se.op[0])) // !true == false - { - implies_equal(false_nr, e_nr, progress); - } - else if(is_false(se.op[0])) // !false == true - { - implies_equal(true_nr, e_nr, progress); - } - - if(is_true(e_nr)) // !true == false - { - implies_equal(false_nr, se.op[0], progress); - } - else if(is_false(e_nr)) // !false == true - { - implies_equal(true_nr, se.op[0], progress); - } - } - - for(solver_expr_listt::const_iterator - equal_it=equal_list.begin(); - equal_it!=equal_list.end(); - equal_it++) - { - unsigned e_nr=*equal_it; - const solver_exprt &se=expr_map[e_nr]; - - unsigned op0=equalities.find(se.op[0]); - unsigned op1=equalities.find(se.op[1]); - - // Is it equal? - if(is_equal(op0, op1)) - implies_equal(true_nr, e_nr, progress); - - // Is there a disequality for this equality? - for(disequalitiest::const_iterator - d_it=disequalities.begin(); - d_it!=disequalities.end(); - d_it++) - { - const std::set &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - if(is_equal(d_it->first, op0) && - is_equal(*diseq_it, op1)) - { - implies_equal(false_nr, e_nr, progress); - } - else if(is_equal(d_it->first, op1) && - is_equal(*diseq_it, op0)) - { - implies_equal(false_nr, e_nr, progress); - } - } - } - } - - for(uf_mapt::const_iterator - uf_map_it=uf_map.begin(); - uf_map_it!=uf_map.end(); - uf_map_it++) - { - const solver_expr_listt &uf_list=uf_map_it->second; - - // boo, quadratic! - for(solver_expr_listt::const_iterator - uf_it1=uf_list.begin(); - uf_it1!=uf_list.end(); - uf_it1++) - { - solver_expr_listt::const_iterator next=uf_it1; - next++; - - for(solver_expr_listt::const_iterator - uf_it2=next; - uf_it2!=uf_list.end(); - uf_it2++) - { - unsigned e_nr1=*uf_it1, e_nr2=*uf_it2; - const solver_exprt &se1=expr_map[e_nr1], - &se2=expr_map[e_nr2]; - - // same number of arguments? - if(se1.op.size()!=se2.op.size()) continue; - - // already equal? - if(is_equal(e_nr1, e_nr2)) continue; - - bool all_equal=true; - - for(unsigned i=0; i &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - if(is_equal(d_it->first, *diseq_it)) - return D_UNSATISFIABLE; - } - } - - for(unsigned i=0; i c+1 <= x - else - --int_val; // x < c ==> x <= c-1 - } - - integer_intervalt new_interval; - - if(lower_upper==LOWER) - new_interval.set_lower(int_val); - else - new_interval.set_upper(int_val); - - integer_intervalt &interval=integer_intervals[add(what)]; - - if(interval.meet(new_interval)) - progress=true; - } - else if(type.id()==ID_floatbv) - { - ieee_floatt float_val(bound); - if(weak_strict!=WEAK) return; - - ieee_float_intervalt new_interval; - - if(lower_upper==LOWER) - new_interval.set_lower(float_val); - else - new_interval.set_upper(float_val); - - ieee_float_intervalt &interval=ieee_float_intervals[add(what)]; - - if(interval.meet(new_interval)) - progress=true; - } -} - -/*******************************************************************\ - -Function: solvert::is_a_constant - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool solvert::is_a_constant(const exprt &expr) const -{ - if(expr.is_constant()) return true; - - if(expr.id()==ID_address_of && - expr.operands().size()==1 && - expr.op0().id()==ID_symbol) - return true; - - return false; -} - -/*******************************************************************\ - -Function: solvert::get - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt solvert::get(const exprt &expr) const -{ - // is it already a constant? - if(expr.is_constant()) - return expr; - - // is it an equality? - if(expr.id()==ID_equal) - { - if(is_equal(to_equal_expr(expr).lhs(), - to_equal_expr(expr).rhs())) - return true_exprt(); - } - else if(expr.id()==ID_not || - expr.id()==ID_and || - expr.id()==ID_or || - expr.id()==ID_implies) - { - exprt tmp=expr; - Forall_operands(it, tmp) - *it=get(*it); // recursive call - return tmp; - } - - exprt tmp=expr; - simplify(tmp, ns); - - numbering::number_type nr; - - if(!expr_numbering.get_number(tmp, nr)) - { - // Equal to some constant? - for(unsigned i=0; i > equality_map; - - for(unsigned i=0; i >::const_iterator - e_it=equality_map.begin(); - e_it!=equality_map.end(); - e_it++) - { - const std::set &eq_set=e_it->second; - - if(eq_set.size()>=2) - { - for(std::set::const_iterator - eq_it=eq_set.begin(); eq_it!=eq_set.end(); eq_it++) - { - out << "Equal: " - << from_expr(ns, "", expr_numbering[*eq_it]) << "\n"; - } - - out << "\n"; - } - } - - // disequalities - - for(disequalitiest::const_iterator - d_it=disequalities.begin(); - d_it!=disequalities.end(); - d_it++) - { - const std::set &diseq_set=d_it->second; - - for(std::set::const_iterator - diseq_it=diseq_set.begin(); diseq_it!=diseq_set.end(); diseq_it++) - { - out << "Disequal: " - << from_expr(ns, "", expr_numbering[d_it->first]) - << " != " - << from_expr(ns, "", expr_numbering[*diseq_it]) - << "\n"; - } - } - - // intervals - - for(integer_intervalst::const_iterator - i_it=integer_intervals.begin(); - i_it!=integer_intervals.end(); i_it++) - { - const integer_intervalt &interval=*i_it; - - if(interval.is_top()) continue; - - out << "Integer interval: "; - - if(interval.lower_set) - out << interval.lower << " <= "; - - out << from_expr(ns, "", expr_numbering[i_it-integer_intervals.begin()]); - - if(interval.upper_set) - out << " <= " << interval.upper; - - if(interval.is_bottom()) out << " (bottom)"; - - out << "\n"; - } - - for(ieee_float_intervalst::const_iterator - i_it=ieee_float_intervals.begin(); - i_it!=ieee_float_intervals.end(); i_it++) - { - const ieee_float_intervalt &interval=*i_it; - - if(interval.is_top()) continue; - - out << "Floating-point interval: "; - - if(interval.lower_set) - out << interval.lower << " <= "; - - out << from_expr(ns, "", expr_numbering[i_it-ieee_float_intervals.begin()]); - - if(interval.upper_set) - out << " <= " << interval.upper; - - if(interval.is_bottom()) out << " (bottom)"; - - out << "\n"; - } - -} - diff --git a/src/solver/solver.h b/src/solver/solver.h deleted file mode 100644 index de961fea6..000000000 --- a/src/solver/solver.h +++ /dev/null @@ -1,182 +0,0 @@ -/*******************************************************************\ - -Module: Delta Checking Solver - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SOLVER_H -#define CPROVER_DELTACHECK_SOLVER_H - -#include - -#include -#include -#include - -#include - -class solvert:public decision_proceduret -{ -public: - // standard solver interface - - explicit solvert(const namespacet &_ns); - - virtual exprt get(const exprt &expr) const; - virtual void print_assignment(std::ostream &out) const; - virtual void set_to(const exprt &expr, bool value); - - virtual resultt dec_solve(); - - virtual std::string decision_procedure_text() const - { - return "DeltaCheck equality+UF solver"; - } - - inline void set_equal(const exprt &a, const exprt &b) - { - set_equal(add(a), add(b)); - } - - bool is_equal(const exprt &a, const exprt &b) const; - - inline void add_expression(const exprt &expr) - { - add(expr); - } - -protected: - // Used to determine whether an expression is suitable - // as value for a model. - bool is_a_constant(const exprt &expr) const; - - void set_to_rec(const exprt &expr, bool value); - - // simplify and add expression, return its handle - unsigned add(const exprt &expr); - - // recursively add a simplified expression, returns its handle - unsigned add_rec(const exprt &expr); - - // make 'a' and 'b' equal - inline void set_equal(unsigned a, unsigned b) - { - equalities.make_union(a, b); - } - - // make 'a' and 'b' equal, and track if - // this wasn't the case before - inline void implies_equal(unsigned a, unsigned b, bool &progress) - { - if(is_equal(a, b)) return; // no progres - equalities.make_union(a, b); - progress=true; // progress! - } - - // add a bound, and track if this is a new bound - enum weak_strictt { WEAK, STRICT }; - enum lower_uppert { LOWER, UPPER }; - - void bound(const constant_exprt &bound, - const exprt &what, - weak_strictt weak_strict, - lower_uppert lower_upper, - bool &progress); - - // a numbering for expressions - numbering expr_numbering; - - // equality logic - unsigned_union_find equalities; - - inline bool is_equal(unsigned a, unsigned b) const - { - return equalities.find(a)==equalities.find(b); - } - - // Disequalities; the smaller index is the key. - // This should really use roots from the equalities. - typedef std::map > disequalitiest; - disequalitiest disequalities; - - inline bool is_disequal(unsigned a, unsigned b) const - { - if(a>b) std::swap(a, b); - disequalitiest::const_iterator it=disequalities.find(a); - if(it==disequalities.end()) return false; - return it->second.find(b)!=it->second.end(); - } - - void implies_disequal(unsigned a, unsigned b, bool &progress) - { - if(a>b) std::swap(a, b); - if((disequalities[a].insert(b)).second) - progress=true; - } - - void set_disequal(unsigned a, unsigned b) - { - if(a>b) std::swap(a, b); - disequalities[a].insert(b); - } - - // further data per expression - struct solver_exprt - { - // the numbers of the operands - std::vector op; - - // the numbers of the expressions that contain this one - std::vector operand_of; - - bool predicate_processed; - - solver_exprt():predicate_processed(false) - { - } - }; - - typedef expanding_vector expr_mapt; - expr_mapt expr_map; - - // lists of expressions with particular IDs - typedef std::vector solver_expr_listt; - solver_expr_listt if_list, or_list, and_list, not_list, equal_list; - - // uninterpreted functions (and predicates), mapping - // expression id -> to the list of expressions of this kind - typedef std::map uf_mapt; - uf_mapt uf_map; - - // builds above solver_exprt for given expression - solver_exprt build_solver_expr(unsigned nr); - - // called to recurse over the operands of a new expression - void add_operands(unsigned nr); - - // called after new expresion with given number has been added - void new_expression(unsigned nr); - - // handy numbers of well-known constants - unsigned false_nr, true_nr; - - inline bool is_true(unsigned a) const - { - return is_equal(a, true_nr); - } - - inline bool is_false(unsigned a) const - { - return is_equal(a, false_nr); - } - - // interval domain - typedef expanding_vector integer_intervalst; - typedef expanding_vector ieee_float_intervalst; - ieee_float_intervalst ieee_float_intervals; - integer_intervalst integer_intervals; -}; - -#endif diff --git a/src/summarizer/summarizer_base.cpp b/src/solver/summarizer_base.cpp similarity index 64% rename from src/summarizer/summarizer_base.cpp rename to src/solver/summarizer_base.cpp index f144b6ce9..7e481013a 100644 --- a/src/summarizer/summarizer_base.cpp +++ b/src/solver/summarizer_base.cpp @@ -17,17 +17,17 @@ Author: Peter Schrammel #include "summarizer_base.h" #include "summary_db.h" -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" +#include +#include +#include -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" +#include +#include /*******************************************************************\ -Function: summarizer_baset::summarize() +Function: summarizer_baset::summarize Inputs: @@ -39,22 +39,23 @@ Function: summarizer_baset::summarize() void summarizer_baset::summarize() { - exprt precondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); + exprt precondition=true_exprt(); // initial calling context + for(functionst::const_iterator it=ssa_db.functions().begin(); it!=ssa_db.functions().end(); it++) { status() << "\nSummarizing function " << it->first << eom; - if(!summary_db.exists(it->first) || - summary_db.get(it->first).mark_recompute) - compute_summary_rec(it->first,precondition,false); - else status() << "Summary for function " << it->first << - " exists already" << eom; + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) + compute_summary_rec(it->first, precondition, false); + else + status() << "Summary for function " << it->first + << " exists already" << eom; } } /*******************************************************************\ -Function: summarizer_baset::summarize() +Function: summarizer_baset::summarize Inputs: @@ -66,22 +67,25 @@ Function: summarizer_baset::summarize() void summarizer_baset::summarize(const function_namet &function_name) { - exprt precondition = true_exprt(); //initial calling context + exprt precondition=true_exprt(); // initial calling context status() << "\nSummarizing function " << function_name << eom; - if(!summary_db.exists(function_name) || - summary_db.get(function_name).mark_recompute) + if(!summary_db.exists(function_name) || + summary_db.get(function_name).mark_recompute) { - compute_summary_rec(function_name,precondition, - options.get_bool_option("context-sensitive")); + compute_summary_rec( + function_name, + precondition, + options.get_bool_option("context-sensitive")); } - else status() << "Summary for function " << function_name << - " exists already" << eom; + else + status() << "Summary for function " << function_name + << " exists already" << eom; } /*******************************************************************\ -Function: summarizer_baset::check_call_reachable() +Function: summarizer_baset::check_call_reachable Inputs: @@ -89,25 +93,25 @@ Function: summarizer_baset::check_call_reachable() Purpose: returns false if function call is not reachable -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_call_reachable( const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt& precondition, bool forward) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); debug() << "Checking reachability of call to " << fname << eom; - bool reachable = false; + bool reachable=false; // reachability check - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -117,29 +121,37 @@ bool summarizer_baset::check_call_reachable( solver << precondition; solver << ssa_inliner.get_summaries(SSA); - symbol_exprt guard = SSA.guard_symbol(n_it->location); - ssa_unwinder.get(function_name).unwinder_rename(guard,*n_it,false); + symbol_exprt guard=SSA.guard_symbol(n_it->location); + ssa_unwinder.get(function_name).unwinder_rename(guard, *n_it, false); solver << guard; #if 0 std::cout << "guard: " << from_expr(SSA.ns, "", guard) << std::endl; - std::cout << "enable: " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) << std::endl; - std::cout << "precondition: " << from_expr(SSA.ns, "", precondition) << std::endl; - std::cout << "summaries: " << from_expr(SSA.ns, "", ssa_inliner.get_summaries(SSA)) << std::endl; + std::cout << "enable: " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << std::endl; + std::cout << "precondition: " << from_expr(SSA.ns, "", precondition) + << std::endl; + std::cout << "summaries: " + << from_expr(SSA.ns, "", ssa_inliner.get_summaries(SSA)) + << std::endl; #endif - if(!forward) + if(!forward) solver << SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); switch(solver()) { - case decision_proceduret::D_SATISFIABLE: { - reachable = true; + case decision_proceduret::D_SATISFIABLE: + { + reachable=true; debug() << "Call is reachable" << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { debug() << "Call is not reachable" << eom; - break; } + break; + } default: assert(false); break; } @@ -150,7 +162,7 @@ bool summarizer_baset::check_call_reachable( /*******************************************************************\ -Function: summarizer_baset::compute_calling_context () +Function: summarizer_baset::compute_calling_context Inputs: @@ -159,23 +171,23 @@ Function: summarizer_baset::compute_calling_context () Purpose: computes callee preconditions from the calling context for a single function call -\******************************************************************/ +\*******************************************************************/ exprt summarizer_baset::compute_calling_context( - const function_namet &function_name, + const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt &precondition, bool forward) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); status() << "Computing calling context for function " << fname << eom; // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -188,40 +200,44 @@ exprt summarizer_baset::compute_calling_context( analyzer.set_message_handler(get_message_handler()); template_generator_callingcontextt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); + options, ssa_db, ssa_unwinder.get(function_name)); template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,n_it,f_it,forward); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, forward); // collect globals at call site - std::map + std::map cs_globals_in; - if(forward) SSA.get_globals(n_it->location,cs_globals_in[f_it]); - else SSA.get_globals(n_it->location,cs_globals_in[f_it],false); + if(forward) + SSA.get_globals(n_it->location, cs_globals_in[f_it]); + else + SSA.get_globals(n_it->location, cs_globals_in[f_it], false); - exprt cond = precondition; - if(!forward) cond = and_exprt(cond, - SSA.guard_symbol(--SSA.goto_function.body.instructions.end())); + exprt cond=precondition; + if(!forward) + cond=and_exprt( + cond, SSA.guard_symbol(--SSA.goto_function.body.instructions.end())); // analyze - analyzer(solver,SSA,cond,template_generator); + analyzer(solver, SSA, cond, template_generator); // set preconditions - local_SSAt &fSSA = ssa_db.get(fname); + local_SSAt &fSSA=ssa_db.get(fname); preconditiont precondition_call; - analyzer.get_result(precondition_call, - template_generator.callingcontext_vars()); - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_in[f_it],fSSA.globals_in, - precondition_call); + analyzer.get_result( + precondition_call, + template_generator.callingcontext_vars()); + ssa_inliner.rename_to_callee( + f_it, fSSA.params, cs_globals_in[f_it], fSSA.globals_in, precondition_call); - debug() << (forward ? "Forward " : "Backward ") << "calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", precondition_call) << eom; + debug() << (forward ? "Forward " : "Backward ") << "calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", precondition_call) << eom; - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); solver.pop_context(); @@ -230,34 +246,28 @@ exprt summarizer_baset::compute_calling_context( /*******************************************************************\ -Function: summarizer_baset::get_assertions() +Function: summarizer_baset::get_assertions Inputs: Outputs: - Purpose: + Purpose: -\******************************************************************/ +\*******************************************************************/ -void summarizer_baset::get_assertions(const local_SSAt &SSA, - exprt::operandst &assertions) +void summarizer_baset::get_assertions( + const local_SSAt &SSA, + exprt::operandst &assertions) { - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator - a_it = n_it->assertions.begin(); - a_it != n_it->assertions.end(); a_it++) - { - assertions.push_back(*a_it); - } - } + for(const auto &node : SSA.nodes) + for(const auto &a : node.assertions) + assertions.push_back(a); } /*******************************************************************\ -Function: summarizer_baset::check_precondition() +Function: summarizer_baset::check_precondition Inputs: @@ -265,77 +275,78 @@ Function: summarizer_baset::check_precondition() Purpose: returns false if the summary needs to be recomputed -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_precondition( const function_namet &function_name, local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, const exprt &precondition, bool context_sensitive) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); status() << "Checking precondition of " << fname << eom; - bool precondition_holds = false; + bool precondition_holds=false; exprt assertion; - if(summary_db.exists(fname)) + if(summary_db.exists(fname)) { - summaryt summary = summary_db.get(fname); - if(summary.mark_recompute) + summaryt summary=summary_db.get(fname); + if(summary.mark_recompute) return false; if(!context_sensitive || - summary.fw_precondition.is_true()) //precondition trivially holds + summary.fw_precondition.is_true()) // precondition trivially holds { - status() << "Precondition trivially holds, replacing by summary." + status() << "Precondition trivially holds, replacing by summary." << eom; summaries_used++; - precondition_holds = true; + precondition_holds=true; } else { - assertion = summary.fw_precondition; + assertion=summary.fw_precondition; - //getting globals at call site - local_SSAt::var_sett cs_globals_in; - SSA.get_globals(n_it->location,cs_globals_in); + // getting globals at call site + local_SSAt::var_sett cs_globals_in; + SSA.get_globals(n_it->location, cs_globals_in); - ssa_inliner.rename_to_caller(f_it,summary.params, - cs_globals_in,summary.globals_in,assertion); + ssa_inliner.rename_to_caller( + f_it, summary.params, cs_globals_in, summary.globals_in, assertion); - debug() << "precondition assertion: " - << from_expr(SSA.ns,"",assertion) << eom; + debug() << "precondition assertion: " + << from_expr(SSA.ns, "", assertion) << eom; - precondition_holds = false; + precondition_holds=false; } } else if(!ssa_db.exists(fname)) { status() << "Function " << fname << " not found" << eom; - precondition_holds = true; + precondition_holds=true; } - else if(fname == function_name) + else if(fname==function_name) { status() << "Havoc recursive function call to " << fname << eom; - precondition_holds = true; + precondition_holds=true; } - else + else { status() << "Function " << fname << " not analyzed yet" << eom; - return false; //function not seen yet + return false; // function not seen yet } - if(precondition_holds) return true; + if(precondition_holds) + return true; assert(!assertion.is_nil()); // precondition check // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -346,23 +357,27 @@ bool summarizer_baset::check_precondition( solver << precondition; solver << ssa_inliner.get_summaries(SSA); - //add precondition + // add precondition solver << not_exprt(assertion); switch(solver()) { - case decision_proceduret::D_SATISFIABLE: { - precondition_holds = false; + case decision_proceduret::D_SATISFIABLE: + { + precondition_holds=false; status() << "Precondition does not hold, need to recompute summary." << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { - precondition_holds = true; + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { + precondition_holds=true; status() << "Precondition holds, replacing by summary." << eom; summaries_used++; - - break; } + + break; + } default: assert(false); break; } @@ -373,7 +388,7 @@ bool summarizer_baset::check_precondition( /*******************************************************************\ -Function: summarizer_baset::check_end_reachable() +Function: summarizer_baset::check_end_reachable Inputs: @@ -381,14 +396,14 @@ Function: summarizer_baset::check_end_reachable() Purpose: returns false if the end of the function is not reachable -\******************************************************************/ +\*******************************************************************/ bool summarizer_baset::check_end_reachable( - const function_namet &function_name, - local_SSAt &SSA, - const exprt &cond) + const function_namet &function_name, + local_SSAt &SSA, + const exprt &cond) { - incremental_solvert &solver = ssa_db.get_solver(function_name); + incremental_solvert &solver=ssa_db.get_solver(function_name); solver.set_message_handler(get_message_handler()); solver << SSA; SSA.mark_nodes(); @@ -398,13 +413,16 @@ bool summarizer_baset::check_end_reachable( solver << ssa_inliner.get_summaries(SSA); solver << cond; + exprt::operandst assertions; + // do not add assertions + // because a failing assertion does not prove termination assertions.push_back( not_exprt(SSA.guard_symbol(--SSA.goto_function.body.instructions.end()))); -// get_assertions(SSA,assertions); //a failing assertion does not prove termination, let's ignore them - solver << not_exprt(conjunction(assertions)); //we want to reach any of them - bool result = (solver()==decision_proceduret::D_SATISFIABLE); + solver << not_exprt(conjunction(assertions)); // we want to reach any of them + + bool result=(solver()==decision_proceduret::D_SATISFIABLE); solver.pop_context(); @@ -425,14 +443,11 @@ Function: summarizer_baset::get_loophead_selects \*******************************************************************/ void summarizer_baset::get_loophead_selects( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, - exprt::operandst &loophead_selects) + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver, + exprt::operandst &loophead_selects) { - //TODO: need to ask ssa_inliner regarding inlined functions - - //TODO: this should be provided by unwindable_local_SSA for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { @@ -504,7 +519,8 @@ void summarizer_baset::get_loop_continues( //TODO: this should be provided by unwindable_local_SSA - ssa_local_unwinder.loop_continuation_conditions(loop_id, loop_continues); + //ssa_local_unwinder.loop_continuation_conditions(loop_id, loop_continues); + ssa_local_unwinder.loop_continuation_conditions(loop_continues); if(loop_continues.size()==0) { //TODO: this should actually be done transparently by the unwinder @@ -567,3 +583,4 @@ bool summarizer_baset::is_fully_unwound( throw "error from decision procedure"; } } + diff --git a/src/solver/summarizer_base.h b/src/solver/summarizer_base.h new file mode 100644 index 000000000..11a6cd0b2 --- /dev/null +++ b/src/solver/summarizer_base.h @@ -0,0 +1,137 @@ +/*******************************************************************\ + +Module: Summarizer Base + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BASE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BASE_H + +#include +#include +#include + +#include +#include +#include +#include + +class summarizer_baset:public messaget +{ +public: + explicit summarizer_baset( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + options(_options), + summary_db(_summary_db), + ssa_db(_ssa_db), + ssa_unwinder(_ssa_unwinder), + ssa_inliner(_ssa_inliner), + solver_instances(0), + solver_calls(0), + summaries_used(0), + termargs_computed(0) + { + } + + typedef summaryt::predicatet preconditiont; + typedef irep_idt function_namet; + typedef local_SSAt function_bodyt; + typedef std::map preconditionst; + typedef ssa_dbt::functionst functionst; + typedef functionst::value_type functiont; + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + + static void get_loop_continues( + const local_SSAt &SSA, + ssa_local_unwindert &ssa_local_unwinder, + exprt::operandst &loop_continues); + + static void get_loop_continues( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues); + + static void get_loophead_selects( + const local_SSAt &SSA, + const ssa_local_unwindert &ssa_local_unwinder, + prop_convt &solver, + exprt::operandst &loophead_selects); + + + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + unsigned get_number_of_summaries_used() { return summaries_used; } + unsigned get_number_of_termargs_computed() { return termargs_computed; } + + protected: + optionst &options; + summary_dbt &summary_db; + ssa_dbt &ssa_db; + ssa_unwindert &ssa_unwinder; + ssa_inlinert &ssa_inliner; + + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) + { + assert(false); + } + + bool check_call_reachable( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt& precondition, + bool forward); + + virtual exprt compute_calling_context( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool forward); + + virtual bool check_precondition( + const function_namet &function_name, + local_SSAt &SSA, + local_SSAt::nodest::const_iterator node, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool context_sensitive); + + void get_assertions( + const local_SSAt &SSA, + exprt::operandst &assertions); + + bool check_end_reachable( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &cond); + + bool is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver); + + + // statistics + unsigned solver_instances; + unsigned solver_calls; + unsigned summaries_used; + unsigned termargs_computed; +}; + + +#endif diff --git a/src/solver/summarizer_bw.cpp b/src/solver/summarizer_bw.cpp new file mode 100644 index 000000000..e52b1df43 --- /dev/null +++ b/src/solver/summarizer_bw.cpp @@ -0,0 +1,541 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "summarizer_bw.h" +#include "summary_db.h" + +/*******************************************************************\ + +Function: summarizer_bwt::summarize + + Inputs: + + Outputs: + + Purpose: analyze only functions reachable in a previous forward analysis + +\*******************************************************************/ + +void summarizer_bwt::summarize() +{ + status() << "\nBackward analysis..." << eom; + + exprt postcondition=true_exprt(); // initial calling context + for(const auto &f : ssa_db.functions()) + { + status() << "\nSummarizing function " << f.first << eom; + if(summary_db.exists(f.first) && + summary_db.get(f.first).bw_precondition.is_nil()) + compute_summary_rec(f.first, postcondition, false); + else + status() << "Skipping function " << f.first << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::summarize + + Inputs: + + Outputs: + + Purpose: summarize from given entry point + +\*******************************************************************/ + +void summarizer_bwt::summarize(const function_namet &function_name) +{ + status() << "\nBackward analysis..." << eom; + + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + if(summary_db.exists(function_name)) + { + compute_summary_rec(function_name, postcondition, true); + } + else + status() << "Skipping function " << function_name << eom; +} + + +/*******************************************************************\ + +Function: summarizer_bwt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + const summaryt &old_summary=summary_db.get(function_name); + + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + old_summary, + postcondition, + context_sensitive, + options.get_bool_option("sufficient")); + + status() << "Analyzing function " << function_name << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.bw_postcondition=postcondition; + + if(!options.get_bool_option("havoc")) + { + do_summary(function_name, SSA, old_summary, summary, context_sensitive); + } + + // store summary in db + summary_db.put(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::do_summary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::do_summary( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive) +{ + bool sufficient=options.get_bool_option("sufficient"); + status() << "Computing preconditions" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + template_generator_summaryt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, false); + + exprt::operandst c; + c.push_back(old_summary.fw_precondition); + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst postcond; + ssa_inliner.get_summaries(SSA, false, postcond, c); // backward summaries + collect_postconditions(function_name, SSA, summary, postcond, sufficient); + if(!sufficient) + { + c.push_back(conjunction(postcond)); + } + else // sufficient + { + c.push_back(not_exprt(conjunction(postcond))); + } + + if(!template_generator.out_vars().empty()) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, conjunction(c), template_generator); + analyzer.get_result( + summary.bw_transformer, template_generator.inout_vars()); + analyzer.get_result(summary.bw_invariant, template_generator.loop_vars()); + analyzer.get_result(summary.bw_precondition, template_generator.out_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } +#if 1 + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + else + { + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << conjunction(c); + exprt result=true_exprt(); + if(solver()==decision_proceduret::D_UNSATISFIABLE) + result=false_exprt(); + solver.pop_context(); + summary.bw_transformer=result; + summary.bw_invariant=result; + summary.bw_precondition=result; + } +#endif + + if(sufficient) + { + summary.bw_transformer=not_exprt(summary.bw_transformer); + summary.bw_invariant=not_exprt(summary.bw_invariant); + summary.bw_precondition=not_exprt(summary.bw_precondition); + } + + if(context_sensitive && !summary.bw_postcondition.is_true()) + { + summary.bw_transformer= + implies_exprt(summary.bw_postcondition, summary.bw_transformer); + summary.bw_invariant= + implies_exprt(summary.bw_postcondition, summary.bw_invariant); + summary.bw_precondition= + implies_exprt(summary.bw_postcondition, summary.bw_precondition); + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bwt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!sufficient && + !check_call_reachable( + function_name, SSA, n_it, f_it, postcondition, false)) + { + continue; + } + + if(!check_postcondition( + function_name, SSA, n_it, f_it, postcondition, context_sensitive)) + { + exprt postcondition_call=true_exprt(); + if(context_sensitive) + postcondition_call= + compute_calling_context2( + function_name, + SSA, + old_summary, + n_it, + f_it, + postcondition, + sufficient); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, postcondition_call, context_sensitive); + summaries_used++; + } + } + } +} + +/*******************************************************************\ + +Function: summarizer_bwt::collect_postconditions + + Inputs: + + Outputs: + + Purpose: collects postconditions where precondition inference starts from + +\*******************************************************************/ + +void summarizer_bwt::collect_postconditions( + const function_namet &function_name, + const local_SSAt &SSA, + const summaryt &summary, + exprt::operandst &postconditions, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); a_it++) + { + postconditions.push_back(*a_it); + } + } + + exprt guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + if(!sufficient) + postconditions.push_back(and_exprt(guard, summary.bw_postcondition)); + else + postconditions.push_back(implies_exprt(guard, summary.bw_postcondition)); +} + +/*******************************************************************\ + +Function: summarizer_bwt::check_postcondition + + Inputs: + + Outputs: + + Purpose: returns false if the summary needs to be recomputed + +\*******************************************************************/ + +bool summarizer_bwt::check_postcondition( + const function_namet &function_name, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &precondition, + bool context_sensitive) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Checking precondition of " << fname << eom; + + bool precondition_holds=false; + exprt assertion; + + if(!summary_db.exists(fname)) + return true; // nothing to do + + summaryt summary=summary_db.get(fname); + + if(summary.bw_precondition.is_nil()) + return false; // there is work to do + + if(!context_sensitive || + summary.fw_precondition.is_true()) // precondition trivially holds + { + status() << "Precondition trivially holds, replacing by summary." + << eom; + summaries_used++; + precondition_holds=true; + } + else + { + assertion=summary.bw_precondition; + + // getting globals at call site + local_SSAt::var_sett cs_globals_in; + SSA.get_globals(n_it->location, cs_globals_in); + + ssa_inliner.rename_to_caller( + f_it, summary.params, cs_globals_in, summary.globals_in, assertion); + + debug() << "precondition assertion: " << + from_expr(SSA.ns, "", assertion) << eom; + + precondition_holds=false; + } + + if(precondition_holds) + return true; + + assert(!assertion.is_nil()); + + // postcondition check + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + solver << SSA; + + solver.new_context(); + solver << SSA.get_enabling_exprs(); + + solver << precondition; + solver << ssa_inliner.get_summaries(SSA); + + // add postcondition + solver << not_exprt(assertion); + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + { + precondition_holds=false; + + status() << "Precondition does not hold, need to recompute summary." << eom; + break; + } + case decision_proceduret::D_UNSATISFIABLE: + { + precondition_holds=true; + + status() << "Precondition holds, replacing by summary." << eom; + summaries_used++; + + break; + } + default: assert(false); break; + } + + return precondition_holds; +} + +/*******************************************************************\ + +Function: summarizer_bwt::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bwt::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + + template_generator_callingcontextt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, false); + + // collect globals at call site + std::map + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + c.push_back(old_summary.fw_precondition); + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst postcond; + ssa_inliner.get_summaries(SSA, false, postcond, c); // backward summaries + old_summary.bw_postcondition=postcondition; // that's a bit awkward + collect_postconditions(function_name, SSA, old_summary, postcond, sufficient); + if(!sufficient) + { + c.push_back(conjunction(postcond)); + } + else // sufficient + { + c.push_back(not_exprt(conjunction(postcond))); + } + + analyzer(solver, SSA, conjunction(c), template_generator); + + // set preconditions + local_SSAt &fSSA=ssa_db.get(fname); + + exprt postcondition_call; + analyzer.get_result( + postcondition_call, + template_generator.callingcontext_vars()); + + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + +#if 1 + // TODO: this should actually be handled by ssa_analyzer + // using a "guard-reachabiliity-only" analysis if template is empty + if(sufficient && + !postcondition_call.is_true()) + { + postcondition_call=not_exprt(postcondition_call); + } +#endif + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + + return postcondition_call; +} diff --git a/src/solver/summarizer_bw.h b/src/solver/summarizer_bw.h new file mode 100644 index 000000000..540f8fedd --- /dev/null +++ b/src/solver/summarizer_bw.h @@ -0,0 +1,86 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_base.h" + +class summarizer_bwt:public summarizer_baset +{ +public: + explicit summarizer_bwt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_baset( + _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive); + + virtual bool check_postcondition( + const function_namet &function_name, + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator node, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool context_sensitive); + + virtual void collect_postconditions( + const function_namet &function_name, + const local_SSAt &SSA, + const summaryt &summary, + exprt::operandst &postconditions, + bool sufficient); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif diff --git a/src/solver/summarizer_bw_term.cpp b/src/solver/summarizer_bw_term.cpp new file mode 100644 index 000000000..3543ab25e --- /dev/null +++ b/src/solver/summarizer_bw_term.cpp @@ -0,0 +1,497 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis with Termination + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "summarizer_bw_term.h" +#include "summarizer_fw_term.h" +#include "summary_db.h" + +#define MAX_PRECONDITION_DISJUNCTS 5 +#define MAX_BOOTSTRAP_ATTEMPTS 20 + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + const summaryt &old_summary=summary_db.get(function_name); + + // recursively compute summaries for function calls + inline_summaries( + function_name, SSA, old_summary, postcondition, context_sensitive, false); + + status() << "Analyzing function " << function_name << eom; + + bool has_loops=false; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) + { + has_loops=true; + break; + } + } + + debug() << "function " << + (has_loops ? "has loops" : "does not have loops") << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.bw_postcondition=postcondition; + + do_nontermination(function_name, SSA, old_summary, summary); + if(!options.get_bool_option("havoc") && + summary.terminates!=NO) + { + if(!has_loops) + { + do_summary(function_name, SSA, old_summary, summary, context_sensitive); + } + else + { + do_summary_term( + function_name, SSA, old_summary, summary, context_sensitive); + } + } + + // store summary in db + summary_db.put(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::do_nontermination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + cond.push_back(old_summary.fw_invariant); + cond.push_back(old_summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + ssa_inliner.get_summaries(SSA, false, cond, cond); // backward summaries + + if(!check_end_reachable(function_name, SSA, conjunction(cond))) + { + status() << "Function never terminates" << eom; + summary.bw_transformer=false_exprt(); + summary.bw_precondition=false_exprt(); + summary.terminates=NO; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::do_summary_term + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_termt::do_summary_term( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive) +{ + status() << "Computing preconditions for termination" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // templates for ranking functions + template_generator_rankingt template_generator1( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator1.set_message_handler(get_message_handler()); + template_generator1(solver.next_domain_number(), SSA, true); + + // templates for backward summary + template_generator_summaryt template_generator2( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator2.set_message_handler(get_message_handler()); + template_generator2(solver.next_domain_number(), SSA, false); + + exprt::operandst bindings; + exprt::operandst postcond; + // backward summaries + ssa_inliner.get_summaries(SSA, false, postcond, bindings); + collect_postconditions(function_name, SSA, summary, postcond, true); + + // prepare solver + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << old_summary.fw_precondition; + solver << old_summary.fw_invariant; + solver << ssa_inliner.get_summaries(SSA); // forward summaries + solver << conjunction(bindings); // bindings for backward summaries + +#if 0 + // compute preconditions individually + // TODO: this should be done more transparently + for(unsigned i=0; iget(*it))); + } + precondition=conjunction(c); + debug() << "bootstrap model for precondition: " + << from_expr(SSA.ns, "", precondition) << eom; + solver.pop_context(); + } + else // whole precondition space covered + { + solver.pop_context(); + break; + } + + termination_argument= + compute_termination_argument( + SSA, precondition, solver, template_generator1); + + if(summarizer_fw_termt::check_termination_argument( + termination_argument)==YES) + { + return true; + } + + solver.new_context(); + checked_candidates.push_back(precondition); + solver << not_exprt(disjunction(checked_candidates)); // next one, please! + } + + return false; +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_termination_argument + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_termt::compute_termination_argument( + local_SSAt &SSA, + const exprt &precondition, + incremental_solvert &solver, + template_generator_rankingt &template_generator) +{ + // compute ranking functions + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, precondition, template_generator); + exprt termination_argument; + analyzer.get_result(termination_argument, template_generator.all_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + termargs_computed++; + + return termination_argument; +} + +/*******************************************************************\ + +Function: summarizer_bw_termt::compute_precondition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_termt::compute_precondition( + local_SSAt &SSA, + summaryt &summary, + const exprt::operandst &postconditions, + incremental_solvert &solver, + template_generator_summaryt &template_generator, + bool context_sensitive) +{ + exprt postcond=not_exprt(conjunction(postconditions)); + + // compute backward summary + exprt bw_transformer, bw_invariant, bw_precondition; + if(!template_generator.out_vars().empty()) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + analyzer(solver, SSA, postcond, template_generator); + analyzer.get_result(bw_transformer, template_generator.inout_vars()); + analyzer.get_result(bw_invariant, template_generator.loop_vars()); + analyzer.get_result(bw_precondition, template_generator.out_vars()); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); + } +#if 1 + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + else + { + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << postcond; + exprt result=true_exprt(); + if(solver()==decision_proceduret::D_UNSATISFIABLE) + result=false_exprt(); + solver.pop_context(); + bw_transformer=result; + bw_invariant=result; + bw_precondition=result; + } +#endif + + bw_transformer=not_exprt(bw_transformer); + bw_invariant=not_exprt(bw_invariant); + bw_precondition=not_exprt(bw_precondition); + + if(context_sensitive && !summary.bw_postcondition.is_true()) + { + bw_transformer=implies_exprt(summary.bw_postcondition, bw_transformer); + bw_invariant=implies_exprt(summary.bw_postcondition, bw_invariant); + bw_precondition=implies_exprt(summary.bw_postcondition, bw_precondition); + } + + // join // TODO: should go into summaryt + if(summary.bw_transformer.is_nil()) + { + summary.bw_transformer=bw_transformer; + summary.bw_invariant=bw_invariant; + summary.bw_precondition=bw_precondition; + } + else + { + summary.bw_transformer=or_exprt(summary.bw_transformer, bw_transformer); + summary.bw_invariant=or_exprt(summary.bw_invariant, bw_invariant); + summary.bw_precondition=or_exprt(summary.bw_precondition, bw_precondition); + } + + return bw_precondition; +} + + diff --git a/src/solver/summarizer_bw_term.h b/src/solver/summarizer_bw_term.h new file mode 100644 index 000000000..190243b03 --- /dev/null +++ b/src/solver/summarizer_bw_term.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis with Termination + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_TERM_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_TERM_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "summarizer_bw.h" + +class summarizer_bw_termt:public summarizer_bwt +{ +public: + explicit summarizer_bw_termt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_bwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &postcondition, + bool context_sensitive); + + void do_summary_term( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + bool context_sensitive); + + void do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary); + + bool bootstrap_preconditions( + local_SSAt &SSA, + summaryt &summary, + incremental_solvert &solver, + template_generator_rankingt &template_generator1, + template_generator_summaryt &template_generator2, + exprt &termination_argument); + + exprt compute_termination_argument( + local_SSAt &SSA, + const exprt &precondition, + incremental_solvert &solver, + template_generator_rankingt &template_generator); + + exprt compute_precondition( + local_SSAt &SSA, + summaryt &summary, + const exprt::operandst &postconditions, + incremental_solvert &solver, + template_generator_summaryt &template_generator, + bool context_sensitive); +}; + +#endif diff --git a/src/solver/summarizer_fw.cpp b/src/solver/summarizer_fw.cpp new file mode 100644 index 000000000..1048a2b0f --- /dev/null +++ b/src/solver/summarizer_fw.cpp @@ -0,0 +1,234 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_fw.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +// #define SHOW_WHOLE_RESULT + +/*******************************************************************\ + +Function: summarizer_fwt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); // TODO: make const + + // recursively compute summaries for function calls + inline_summaries(function_name, SSA, precondition, context_sensitive); + + status() << "Analyzing function " << function_name << eom; + +#if 0 + { + std::ostringstream out; + out << "Function body for " << function_name + << " to be analyzed: " << std::endl; + for(const auto &node : SSA.nodes) + { + if(!node.empty()) + node.output(out, SSA.ns); + } + out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << "\n"; + debug() << out.str() << eom; + } +#endif + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.fw_precondition=precondition; + + if(!options.get_bool_option("havoc")) + { + do_summary(function_name, SSA, summary, true_exprt(), context_sensitive); + } + + +#if 0 + if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary.output(out, SSA.ns); + status() << out.str() << eom; + } +#endif + + // store summary in db + summary_db.put(function_name, summary); + + if(!options.get_bool_option("competition-mode")) + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + status() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_fwt::do_summary + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::do_summary( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary, + exprt cond, + bool context_sensitive) +{ + status() << "Computing summary" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + + template_generator_summaryt template_generator( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, true); + + exprt::operandst conds; + conds.reserve(5); + conds.push_back(cond); + conds.push_back(summary.fw_precondition); + conds.push_back(ssa_inliner.get_summaries(SSA)); + +#ifdef REUSE_INVARIANTS + if(summary_db.exists(function_name)) // reuse existing invariants + { + const exprt &old_inv=summary_db.get(function_name).fw_invariant; + exprt inv=ssa_unwinder.get(function_name).rename_invariant(old_inv); + conds.push_back(inv); + +#if 0 + std::ostringstream out; + out << "(original inv)" << from_expr(SSA.ns, "", old_inv) << "\n"; + debug() << out.str() << eom; + out << "(renamed inv)" << from_expr(SSA.ns, "", inv) << "\n"; + debug() << out.str() << eom; +#endif + } +#endif + + cond=conjunction(conds); + + analyzer(solver, SSA, cond, template_generator); + analyzer.get_result(summary.fw_transformer, template_generator.inout_vars()); + analyzer.get_result(summary.fw_invariant, template_generator.loop_vars()); + +#ifdef SHOW_WHOLE_RESULT + // to see all the custom template values + exprt whole_result; + analyzer.get_result(whole_result, template_generator.all_vars()); + debug() << "whole result: " << from_expr(SSA.ns, "", whole_result) << eom; +#endif + + if(context_sensitive && !summary.fw_precondition.is_true()) + { + summary.fw_transformer= + implies_exprt(summary.fw_precondition, summary.fw_transformer); + summary.fw_invariant= + implies_exprt(summary.fw_precondition, summary.fw_invariant); + } + + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); +} + +/*******************************************************************\ + +Function: summarizer_fwt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fwt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, const exprt &precondition, + bool context_sensitive) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + { + continue; + } + + if(!check_precondition( + function_name, SSA, n_it, f_it, precondition, context_sensitive)) + { + exprt precondition_call=true_exprt(); + if(context_sensitive) + precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, precondition_call, context_sensitive); + summaries_used++; + } + } + } +} + + diff --git a/src/solver/summarizer_fw.h b/src/solver/summarizer_fw.h new file mode 100644 index 000000000..f21255c55 --- /dev/null +++ b/src/solver/summarizer_fw.h @@ -0,0 +1,57 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_FW_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_FW_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_base.h" + +class summarizer_fwt:public summarizer_baset +{ +public: + explicit summarizer_fwt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_baset( + _options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &precondition, + bool context_sensitive); + + void do_summary( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary, + exprt cond, // additional constraints + bool forward); +}; + +#endif diff --git a/src/solver/summarizer_fw_contexts.cpp b/src/solver/summarizer_fw_contexts.cpp new file mode 100644 index 000000000..69c7e3f09 --- /dev/null +++ b/src/solver/summarizer_fw_contexts.cpp @@ -0,0 +1,176 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis with Calling Context output + +Author: Peter Schrammel + +\*******************************************************************/ + +#include // for xml output + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "summarizer_fw_contexts.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +/*******************************************************************\ + +Function: summarizer_fw_contextst::summarize + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::summarize() +{ + exprt precondition=true_exprt(); // initial calling context + for(functionst::const_iterator it=ssa_db.functions().begin(); + it!=ssa_db.functions().end(); it++) + { + if(excluded_functions.find(it->first)!=excluded_functions.end()) + continue; + status() << "\nSummarizing function " << it->first << eom; + if(!summary_db.exists(it->first) || + summary_db.get(it->first).mark_recompute) + compute_summary_rec(it->first, precondition, false); + else + status() << "Summary for function " << it->first + << " exists already" << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_fw_contextst::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_contextst::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, const exprt &precondition, + bool context_sensitive) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + if(!check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + { + continue; + } + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(excluded_functions.find(fname)!=excluded_functions.end()) + { + exprt precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + // output calling context + switch(ui) + { + case ui_message_handlert::PLAIN: + break; + + case ui_message_handlert::XML_UI: + { + xmlt xml_cc("calling-context"); + xml_cc.set_attribute("function", id2string(fname)); + xml_cc.set_attribute( + "goto_location", i2string(n_it->location->location_number)); + + // location + const source_locationt &source_location= + n_it->location->source_location; + xmlt xml_location; + if(source_location.is_not_nil() && source_location.get_file()!="") + xml_location=xml(source_location); + if(xml_location.name!="") + xml_cc.new_element().swap(xml_location); + + // argument ranges + xmlt xml_args("argument-ranges"); + assert(precondition_call.operands().size()%2==0); + for(unsigned i=0; i +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_fw.h" + + +class summarizer_fw_contextst:public summarizer_fwt +{ +public: + explicit summarizer_fw_contextst( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_fwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner), + ui(ui_message_handlert::PLAIN) + { + if(_options.get_bool_option("xml-ui")) + ui=ui_message_handlert::XML_UI; + + optionst::value_listt _excluded_functions= + _options.get_list_option("do-not-analyze-functions"); + excluded_functions.insert( + _excluded_functions.begin(), _excluded_functions.end()); + } + + virtual void summarize(); + + protected: + language_uit::uit ui; // use gui format + std::set excluded_functions; + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const exprt &precondition, + bool context_sensitive); +}; + +#endif diff --git a/src/solver/summarizer_fw_term.cpp b/src/solver/summarizer_fw_term.cpp new file mode 100644 index 000000000..63f96ae92 --- /dev/null +++ b/src/solver/summarizer_fw_term.cpp @@ -0,0 +1,372 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "summarizer_fw_term.h" +#include "summary_db.h" + +/*******************************************************************\ + +Function: summarizer_fw_termt::compute_summary_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive) +{ + if(options.get_bool_option("competition-mode") && + summary_db.exists(ID__start) && + summary_db.get(ID__start).terminates==NO) + { + return; + } + + local_SSAt &SSA=ssa_db.get(function_name); + + // recursively compute summaries for function calls + threevalt calls_terminate=YES; + bool has_function_calls=false; + inline_summaries( + function_name, + SSA, + precondition, + context_sensitive, + calls_terminate, + has_function_calls); + + status() << "Analyzing function " << function_name << eom; + + { + std::ostringstream out; + out << "Function body for " << function_name << + " to be analyzed: " << std::endl; + for(local_SSAt::nodest::iterator n=SSA.nodes.begin(); + n!=SSA.nodes.end(); n++) + { + if(!n->empty()) + n->output(out, SSA.ns); + } + out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) + << "\n"; + debug() << out.str() << eom; + } + + bool has_loops=false; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead!=SSA.nodes.end()) + { + has_loops=true; + break; + } + } + + debug() << "function " + << (has_function_calls ? "has" : "does not have") << " function calls" + << eom; + debug() << "function calls terminate: " + << threeval2string(calls_terminate) << eom; + debug() << "function " + << (has_loops ? "has loops" : "does not have loops") << eom; + + // create summary + summaryt summary; + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.fw_precondition=precondition; + summary.terminates=UNKNOWN; + + // compute summary + if(!options.get_bool_option("havoc")) + { + // We are not allowed to assume the assertions here, + // otherwise we might cut off all terminating executions + // and classify the program as non-terminating. + do_summary(function_name, SSA, summary, true_exprt(), context_sensitive); + } + + // check termination + status() << "Computing termination argument for " << function_name << eom; + // check non-termination if we haven't analyzed this function yet, + // otherwise the termination status is UNKNOWN anyways + if(!summary_db.exists(function_name)) + { + do_nontermination(function_name, SSA, summary); + } + if(summary.terminates==UNKNOWN) + { + bool has_terminating_function_calls= + has_function_calls && calls_terminate==YES; + + if(!has_loops && !has_function_calls) + { + status() << "Function trivially terminates" << eom; + summary.terminates=YES; + } + else if(!has_loops && has_function_calls && calls_terminate==YES) + { + status() << "Function terminates" << eom; + summary.terminates=YES; + } + else if(has_function_calls && calls_terminate!=YES) + { + summary.terminates=calls_terminate; + } + else if(has_loops && + (!has_function_calls || has_terminating_function_calls)) + { + do_termination(function_name, SSA, summary); + } + } + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary.output(out, SSA.ns); + status() << out.str() << eom; + } + + // store summary in db + summary_db.put(function_name, summary); +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::inline_summaries + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, exprt precondition, + bool context_sensitive, + threevalt &calls_terminate, + bool &has_function_calls) +{ + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt::operandst c; + c.push_back(precondition); + get_assertions(SSA, c); // assertions as assumptions + precondition=conjunction(c); + + if(!options.get_bool_option("competition-mode") && + !check_call_reachable( + function_name, SSA, n_it, f_it, precondition, true)) + continue; + + has_function_calls=true; + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(!check_precondition( + function_name, SSA, n_it, f_it, precondition, context_sensitive)) + { + exprt precondition_call=true_exprt(); + if(context_sensitive) + precondition_call=compute_calling_context( + function_name, SSA, n_it, f_it, precondition, true); + + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, precondition_call, context_sensitive); + summaries_used++; + } + + // get information about callee termination + if(summary_db.exists(fname) && summary_db.get(fname).terminates!=YES) + { + // cannot propagate NO + // because call reachability might be over-approximating + calls_terminate=UNKNOWN; + break; + } + } + } +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::do_nontermination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + if(!summary.fw_invariant.is_nil()) + cond.push_back(summary.fw_invariant); + if(!summary.fw_precondition.is_nil()) + cond.push_back(summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + + if(!check_end_reachable(function_name, SSA, conjunction(cond))) + { + status() << "Function never terminates normally" << eom; + + if(summary.fw_precondition.is_true()) + summary.fw_transformer=false_exprt(); + else + summary.fw_transformer= + implies_exprt(summary.fw_precondition, false_exprt()); + + summary.terminates=NO; + } +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::do_termination + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_fw_termt::do_termination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary) +{ + // calling context, invariant, function call summaries + exprt::operandst cond; + if(!summary.fw_invariant.is_nil()) + cond.push_back(summary.fw_invariant); + if(!summary.fw_precondition.is_nil()) + cond.push_back(summary.fw_precondition); + cond.push_back(ssa_inliner.get_summaries(SSA)); + + status() << "Synthesizing ranking function to prove termination" << eom; + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + template_generator_rankingt template_generator1( + options, ssa_db, ssa_unwinder.get(function_name)); + template_generator1.set_message_handler(get_message_handler()); + template_generator1(solver.next_domain_number(), SSA, true); + + if(template_generator1.all_vars().empty()) + return; // nothing to do + + get_assertions(SSA, cond); // add assertions as assumptions + + // compute ranking functions + ssa_analyzert analyzer1; + analyzer1.set_message_handler(get_message_handler()); + analyzer1(solver, SSA, conjunction(cond), template_generator1); + analyzer1.get_result( + summary.termination_argument, template_generator1.all_vars()); + + // extract information whether a ranking function was found for all loops + summary.terminates=check_termination_argument(summary.termination_argument); + if(!summary.fw_precondition.is_true()) + summary.termination_argument= + implies_exprt(summary.fw_precondition, summary.termination_argument); + + // statistics + solver_instances+=analyzer1.get_number_of_solver_instances(); + solver_calls+=analyzer1.get_number_of_solver_calls(); + termargs_computed++; +} + +/*******************************************************************\ + +Function: summarizer_fw_termt::check_termination_argument + + Inputs: + + Outputs: + + Purpose: checks whether a termination argument implies termination + +\*******************************************************************/ + +threevalt summarizer_fw_termt::check_termination_argument(exprt expr) +{ + if(expr.is_false()) + return YES; + + // should be of the form /\_i g_i=> R_i + if(expr.id()==ID_and) + { + threevalt result=YES; + for(exprt::operandst::iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) + { + if(it->is_true()) + result=UNKNOWN; + if(it->id()==ID_implies) + { + if(it->op1().is_true()) + result=UNKNOWN; + } + } + return result; + } + else + { + if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + return UNKNOWN; + } + else + return !expr.is_true() ? YES : UNKNOWN; + } + return YES; +} + diff --git a/src/solver/summarizer_fw_term.h b/src/solver/summarizer_fw_term.h new file mode 100644 index 000000000..bc8ad45aa --- /dev/null +++ b/src/solver/summarizer_fw_term.h @@ -0,0 +1,63 @@ +/*******************************************************************\ + +Module: Summarizer for Forward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_FW_TERM_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_FW_TERM_H + +#include +#include +#include + +#include +#include +#include +#include + +#include "summarizer_fw.h" + +class summarizer_fw_termt:public summarizer_fwt +{ +public: + explicit summarizer_fw_termt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner): + summarizer_fwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner) + { + } + + static threevalt check_termination_argument(exprt expr); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const exprt &precondition, + bool context_sensitive); + + void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + exprt precondition, + bool context_sensitive, + threevalt &calls_terminate, + bool &has_function_calls); + + void do_termination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary); + + void do_nontermination( + const function_namet &function_name, + local_SSAt &SSA, + summaryt &summary); +}; + +#endif diff --git a/src/solver/summary.cpp b/src/solver/summary.cpp new file mode 100644 index 000000000..0539d9ecd --- /dev/null +++ b/src/solver/summary.cpp @@ -0,0 +1,197 @@ +/*******************************************************************\ + +Module: Summary + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include + +#include "summary.h" + +const summaryt::call_sitet summaryt::entry_call_site; + +// #define PRETTY_PRINT + +/*******************************************************************\ + +Function: summaryt::output + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::output(std::ostream &out, const namespacet &ns) const +{ + out << "params: "; + for(summaryt::var_listt::const_iterator it=params.begin(); + it!=params.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "globals_in: "; + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "globals_out: "; + for(summaryt::var_sett::const_iterator it=globals_out.begin(); + it!=globals_out.end(); it++) + out << from_expr(ns, "", *it) << " "; + out << std::endl; + out << "forward precondition: " + << (fw_precondition.is_nil() ? "not computed" : + from_expr(ns, "", fw_precondition)) << std::endl; + out << "forward transformer: " + << (fw_transformer.is_nil() ? "not computed" : + from_expr(ns, "", fw_transformer)) << std::endl; + out << "forward invariant: " + << (fw_invariant.is_nil() ? "not computed" : + from_expr(ns, "", fw_invariant)) << std::endl; + out << "backward precondition: " + << (bw_precondition.is_nil() ? "not computed" : + from_expr(ns, "", bw_precondition)) << std::endl; + out << "backward postcondition: " + << (bw_postcondition.is_nil() ? "not computed" : + from_expr(ns, "", bw_postcondition)) << std::endl; + out << "backward transformer: " + << (bw_transformer.is_nil() ? "not computed" : + from_expr(ns, "", bw_transformer)) << std::endl; + out << "backward invariant: " + << (bw_invariant.is_nil() ? "not computed" : + from_expr(ns, "", bw_invariant)) << std::endl; + out << "termination argument: "; + if(termination_argument.is_nil()) + out << "not computed"; + else +#if PRETTY_PRINT + pretty_print_termination_argument(out, ns, termination_argument); +#else + out << from_expr(ns, "", termination_argument) << std::endl; +#endif + out << std::endl; + out << "terminates: " << threeval2string(terminates) << std::endl; +} + +/*******************************************************************\ + +Function: summaryt::combine_and + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::combine_and(exprt &olde, const exprt &newe) +{ + if(olde.is_nil()) + { + olde=newe; + } + else + { + if(newe.is_nil()) + return; + + olde=and_exprt(olde, newe); + } +} + +/*******************************************************************\ + +Function: summaryt::combine_or + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::combine_or(exprt &olde, const exprt &newe) +{ + if(olde.is_nil()) + { + olde=newe; + } + else + { + if(newe.is_nil()) + return; + olde=or_exprt(olde, newe); + } +} + +/*******************************************************************\ + +Function: summaryt::join + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summaryt::join(const summaryt &new_summary) +{ + assert(params==new_summary.params); + assert(globals_in==new_summary.globals_in); + assert(globals_out==new_summary.globals_out); + combine_or(fw_precondition, new_summary.fw_precondition); + combine_and(fw_transformer, new_summary.fw_transformer); + combine_and(fw_invariant, new_summary.fw_invariant); + combine_and(bw_precondition, new_summary.bw_precondition); + combine_or(bw_postcondition, new_summary.bw_postcondition); + combine_and(bw_transformer, new_summary.bw_transformer); + combine_and(bw_invariant, new_summary.bw_invariant); + combine_and(termination_argument, new_summary.termination_argument); + switch(new_summary.terminates) + { + case YES: + break; + case NO: terminates=NO; + break; + case UNKNOWN: + if(terminates!=NO) + terminates=UNKNOWN; + break; + default: assert(false); + } +} + +/*******************************************************************\ + +Function: threeval2string + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string threeval2string(threevalt v) +{ + switch(v) + { + case YES: return "yes"; + case NO: return "no"; + case UNKNOWN: return "unknown"; + } + assert(false); +} diff --git a/src/summarizer/summary.h b/src/solver/summary.h similarity index 84% rename from src/summarizer/summary.h rename to src/solver/summary.h index 3aba80933..24f142d35 100644 --- a/src/summarizer/summary.h +++ b/src/solver/summary.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_DELTACHECK_SUMMARY_H -#define CPROVER_DELTACHECK_SUMMARY_H +#ifndef CPROVER_2LS_SOLVER_SUMMARY_H +#define CPROVER_2LS_SOLVER_SUMMARY_H #include #include @@ -17,7 +17,7 @@ Author: Daniel Kroening, kroening@kroening.com #include "../ssa/local_ssa.h" -typedef enum{YES, NO, UNKNOWN} threevalt; +typedef enum {YES, NO, UNKNOWN} threevalt; class summaryt { @@ -28,28 +28,31 @@ class summaryt typedef std::set var_sett; typedef std::set expr_sett; - summaryt() : - fw_precondition(nil_exprt()), - fw_transformer(nil_exprt()), - fw_invariant(nil_exprt()), - bw_precondition(nil_exprt()), - bw_postcondition(nil_exprt()), - bw_transformer(nil_exprt()), + summaryt() : + fw_precondition(nil_exprt()), + fw_transformer(nil_exprt()), + fw_invariant(nil_exprt()), + bw_precondition(nil_exprt()), + bw_postcondition(nil_exprt()), + bw_transformer(nil_exprt()), bw_invariant(nil_exprt()), - termination_argument(nil_exprt()), + termination_argument(nil_exprt()), terminates(UNKNOWN), - mark_recompute(false) {} + mark_recompute(false), + has_assertion(false) + { + } var_listt params; var_sett globals_in, globals_out; expr_sett nondets; - + predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently predicatet fw_transformer; // forward summary (over-approx) predicatet fw_invariant; // forward invariant (over-approx) - predicatet bw_precondition; // accumulated preconditions (over- or under-approx) - predicatet bw_postcondition; // accumulated postconditions (over- or under-approx) + predicatet bw_precondition; // accumulated preconditions (over/under-approx) + predicatet bw_postcondition; // accumulated postconditions (over/under-approx) predicatet bw_transformer; // backward summary (over- or under-approx) predicatet bw_invariant; // backward invariant (over- or under-approx) @@ -82,19 +85,17 @@ class summaryt const static call_sitet entry_call_site; typedef std::map error_summariest; error_summariest error_summaries; - //-------------- void output(std::ostream &out, const namespacet &ns) const; void join(const summaryt &new_summary); protected: - void combine_or(exprt &olde, const exprt &newe); void combine_and(exprt &olde, const exprt &newe); - }; std::string threeval2string(threevalt v); + #endif diff --git a/src/solver/summary_db.cpp b/src/solver/summary_db.cpp new file mode 100644 index 000000000..a04e8d809 --- /dev/null +++ b/src/solver/summary_db.cpp @@ -0,0 +1,111 @@ +/*******************************************************************\ + +Module: Storage for Function Summaries + +Author: Daniel Kroening, kroening@kroening.com + +\*******************************************************************/ + +#include + +#include + +#include "summary_db.h" + +/*******************************************************************\ + +Function: summary_dbt::put + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::put( + const function_namet &function_name, + const summaryt &summary) +{ + if(store.find(function_name)==store.end() || + store[function_name].mark_recompute) + store[function_name]=summary; + else + store[function_name].join(summary); +} + +/*******************************************************************\ + +Function: summary_dbt::mark_recompute_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::mark_recompute_all() +{ + for(std::map::iterator it=store.begin(); + it!=store.end(); it++) + it->second.mark_recompute=true; +} + +/*******************************************************************\ + +Function: summary_dbt::file_name + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +std::string summary_dbt::file_name(const std::string &id) +{ + return "summary."+id; +} + +/*******************************************************************\ + +Function: summary_dbt::read + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::read(const std::string &id) +{ + current=id; + + summary.make_object(); + + parse_json(file_name(id), get_message_handler(), summary); +} + +/*******************************************************************\ + +Function: summary_dbt::write + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summary_dbt::write() +{ + std::ofstream out(file_name(current).c_str()); + out << summary << '\n'; +} diff --git a/src/summarizer/summary_db.h b/src/solver/summary_db.h similarity index 83% rename from src/summarizer/summary_db.h rename to src/solver/summary_db.h index 3dfe0341f..34c761335 100644 --- a/src/summarizer/summary_db.h +++ b/src/solver/summary_db.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SUMMARIZER_SUMMARY_DB_H -#define CPROVER_SUMMARIZER_SUMMARY_DB_H +#ifndef CPROVER_2LS_SOLVER_SUMMARY_DB_H +#define CPROVER_2LS_SOLVER_SUMMARY_DB_H #include "summary.h" #include @@ -23,11 +23,11 @@ class summary_dbt:public messaget void write(); void clear() { store.clear(); } - summaryt get(const function_namet &function_name) const + summaryt get(const function_namet &function_name) const { return store.at(function_name); } void set(const function_namet &function_name, const summaryt &summary) { store[function_name] = summary; } - bool exists(const function_namet &function_name) const + bool exists(const function_namet &function_name) const { return store.find(function_name)!=store.end(); } void put(const function_namet &function_name, const summaryt &summary); diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 52023ab4b..91f1b3d96 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,25 +1,26 @@ -include ../config.inc -CBMC ?= ../.. - SRC = local_ssa.cpp \ ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ - unwindable_local_ssa.cpp split_loopheads.cpp const_propagator.cpp ssa_const_propagator.cpp \ - replace_symbol_ext.cpp ssa_dependency_graph.cpp \ - ssa_refiner_monolithic.cpp ssa_refiner_selective.cpp + unwindable_local_ssa.cpp ssa_db.cpp \ + ssa_dependency_graph.cpp \ + ssa_const_propagator.cpp \ +include ../config.inc include $(CBMC)/src/config.inc include $(CBMC)/src/common +CBMC ?= ../.. -CP_CXXFLAGS += $(SUMMARIZERFLAGS) +CP_CXXFLAGS += $(TWOLSFLAGS) -INCLUDES= -I $(CBMC)/src +INCLUDES= -I $(CBMC)/src -I .. -CLEANFILES = +CLEANFILES = ssa$(LIBEXT) -all: $(OBJ) +all: ssa$(LIBEXT) ############################################################################### +ssa$(LIBEXT): $(OBJ) + $(LINKLIB) diff --git a/src/ssa/address_canonizer.cpp b/src/ssa/address_canonizer.cpp index 4b64b4378..2ffab3ff1 100644 --- a/src/ssa/address_canonizer.cpp +++ b/src/ssa/address_canonizer.cpp @@ -46,17 +46,18 @@ exprt address_canonizer( { // get offset exprt offset=member_offset_expr(to_member_expr(object), ns); - + // &x.m ---> (&x)+offset - + address_of_exprt address_of_expr(to_member_expr(object).struct_op()); exprt rec_result=address_canonizer(address_of_expr, ns); // rec. call pointer_typet byte_pointer(unsigned_char_type()); typecast_exprt typecast_expr(rec_result, byte_pointer); plus_exprt sum(typecast_expr, offset); - if(sum.type()!=address.type()) sum.make_typecast(address.type()); - + if(sum.type()!=address.type()) + sum.make_typecast(address.type()); + return sum; } else if(object.id()==ID_index) @@ -69,8 +70,9 @@ exprt address_canonizer( pointer_type.subtype()=object.type(); typecast_exprt typecast_expr(rec_result, pointer_type); plus_exprt sum(typecast_expr, to_index_expr(object).index()); - if(sum.type()!=address.type()) sum.make_typecast(address.type()); - + if(sum.type()!=address.type()) + sum.make_typecast(address.type()); + return sum; } else @@ -105,7 +107,7 @@ exprt address_canonizer( else if(address.id()==ID_typecast) { typecast_exprt tmp=to_typecast_expr(address); - + // cast from another pointer? if(tmp.op().type().id()==ID_pointer) { diff --git a/src/ssa/address_canonizer.h b/src/ssa/address_canonizer.h index f917adc79..72281d792 100644 --- a/src/ssa/address_canonizer.h +++ b/src/ssa/address_canonizer.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_ADDRESS_CANONIZER_H -#define CPROVER_ADDRESS_CANONIZER_H +#ifndef CPROVER_2LS_SSA_ADDRESS_CANONIZER_H +#define CPROVER_2LS_SSA_ADDRESS_CANONIZER_H #include #include diff --git a/src/ssa/assignments.cpp b/src/ssa/assignments.cpp index bcae6e998..2a5bc1c6b 100644 --- a/src/ssa/assignments.cpp +++ b/src/ssa/assignments.cpp @@ -31,7 +31,7 @@ void assignmentst::build_assignment_map( { // make sure we have the location in the map assignment_map[it]; - + // now fill it if(it->is_assign()) { @@ -46,12 +46,13 @@ void assignmentst::build_assignment_map( } else if(it->is_function_call()) { - const code_function_callt &code_function_call=to_code_function_call(it->code); + const code_function_callt &code_function_call= + to_code_function_call(it->code); // functions may alter state almost arbitrarily: // * any global-scoped variables // * any dirty locals - + for(objectst::const_iterator o_it=ssa_objects.dirty_locals.begin(); o_it!=ssa_objects.dirty_locals.end(); o_it++) @@ -65,7 +66,8 @@ void assignmentst::build_assignment_map( // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) { - exprt lhs_deref=dereference(code_function_call.lhs(), ssa_value_ai[it], "", ns); + exprt lhs_deref= + dereference(code_function_call.lhs(), ssa_value_ai[it], "", ns); assign(lhs_deref, it, ns); } } @@ -95,15 +97,15 @@ void assignmentst::assign( if(is_symbol_struct_member(lhs, ns)) { const typet &lhs_type=ns.follow(lhs.type()); - + if(lhs_type.id()==ID_struct) { // Are we assigning an entire struct? // If so, need to split into pieces, recursively. - + const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -112,13 +114,13 @@ void assignmentst::assign( member_exprt new_lhs(lhs, it->get_name(), it->type()); assign(new_lhs, loc, ns); // recursive call } - + return; // done } - + // object? ssa_objectt ssa_object(lhs, ns); - + if(ssa_object) { assign(ssa_object, loc, ns); @@ -201,12 +203,13 @@ void assignmentst::output( { out << "**** " << i_it->location_number << " " << i_it->source_location << "\n"; - + assignment_mapt::const_iterator m_it=assignment_map.find(i_it); - if(m_it==assignment_map.end()) throw "location not found"; - + if(m_it==assignment_map.end()) + throw "location not found"; + const objectst &objects=m_it->second; - + for(objectst::const_iterator o_it=objects.begin(); o_it!=objects.end(); @@ -214,7 +217,7 @@ void assignmentst::output( { out << o_it->get_identifier() << "\n"; } - + out << "\n"; } } diff --git a/src/ssa/assignments.h b/src/ssa/assignments.h index e21462cfe..28b36d608 100644 --- a/src/ssa/assignments.h +++ b/src/ssa/assignments.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_ASSIGNMENTS_H -#define CPROVER_ASSIGNMENTS_H +#ifndef CPROVER_2LS_SSA_ASSIGNMENTS_H +#define CPROVER_2LS_SSA_ASSIGNMENTS_H #include @@ -26,14 +26,15 @@ class assignmentst typedef std::map assignment_mapt; assignment_mapt assignment_map; - + bool assigns(locationt loc, const ssa_objectt &object) const { assignment_mapt::const_iterator it=assignment_map.find(loc); - if(it==assignment_map.end()) return false; + if(it==assignment_map.end()) + return false; return it->second.find(object)!=it->second.end(); } - + inline const objectst &get(locationt loc) const { assignment_mapt::const_iterator it=assignment_map.find(loc); @@ -51,22 +52,22 @@ class assignmentst { build_assignment_map(_goto_program, _ns); } - + void output( const namespacet &ns, const goto_programt &_goto_program, std::ostream &); - + protected: void build_assignment_map(const goto_programt &, const namespacet &); void assign( const exprt &lhs, locationt, const namespacet &ns); - + void assign( const ssa_objectt &lhs, locationt, - const namespacet &ns); + const namespacet &ns); }; #endif diff --git a/src/ssa/const_propagator.cpp b/src/ssa/const_propagator.cpp deleted file mode 100644 index 747f2af93..000000000 --- a/src/ssa/const_propagator.cpp +++ /dev/null @@ -1,467 +0,0 @@ -/*******************************************************************\ - -Module: Constant Propagation - -Author: Peter Schrammel - -\*******************************************************************/ - -//#define DEBUG - -#include - -#include -#include - -#include "const_propagator.h" - -/*******************************************************************\ - -Function: const_propagator_domaint::assign_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::assign_rec(const exprt &lhs, const exprt &rhs, - const namespacet &ns) -{ - const typet & rhs_type = ns.follow(rhs.type()); - -#ifdef DEBUG - std::cout << "assign: " << from_expr(ns, "", lhs) - << " := " << from_type(ns, "", rhs_type) << std::endl; -#endif - - if(lhs.id()==ID_symbol && rhs_type.id()!=ID_array - && rhs_type.id()!=ID_struct - && rhs_type.id()!=ID_union) - { - if(!values.maps_to_top(rhs)) - assign(values,lhs,rhs,ns); - else - values.set_to_top(lhs); - } -#if 0 - else //TODO: could make field or array element-sensitive - { - } -#endif -} - -/*******************************************************************\ - -Function: const_propagator_domaint::transform - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::transform( - locationt from, - locationt to, - ai_baset &ai, - const namespacet &ns) -{ -#ifdef DEBUG - std::cout << from->location_number << " --> " - << to->location_number << std::endl; -#endif - - if(from->is_decl()) - { - values.set_to_top(to_code_decl(from->code).symbol()); - } - else if(from->is_assign()) - { - const code_assignt &assignment=to_code_assign(from->code); - const exprt &lhs = assignment.lhs(); - const exprt &rhs = assignment.rhs(); - assign_rec(lhs,rhs,ns); - } - else if(from->is_goto()) - { - if(from->guard.id()==ID_equal && from->get_target()==to) - { - const exprt &lhs = from->guard.op0(); - const exprt &rhs = from->guard.op1(); - - assign_rec(lhs,rhs,ns); - assign_rec(rhs,lhs,ns); - } - } - else if(from->is_dead()) - { - const code_deadt &code_dead=to_code_dead(from->code); - values.set_to_top(code_dead.symbol()); - } - else if(from->is_function_call()) - { - values.set_all_to_top(); - } - -#ifdef DEBUG - output(std::cout,ai,ns); -#endif -} - -/*******************************************************************\ - -Function: const_propagator_domaint::assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::assign( - valuest &dest, - const exprt &lhs, - exprt rhs, - const namespacet &ns) const -{ -#ifdef DEBUG - std::cout << "assign: " << from_expr(ns, "", lhs) - << " := " << from_expr(ns, "", rhs) << std::endl; -#endif - - values.replace_const(rhs); - - //this is to remove casts in constants propagated into the size of array types - bool valid = true; - exprt rhs_val = evaluate_casts_in_constants(rhs,lhs.type(),valid); - if(valid) - dest.set_to(lhs,rhs_val); -} - -/*******************************************************************\ - -Function: const_propagator_domaint::valuest::maps_to_top - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool const_propagator_domaint::valuest::maps_to_top(const exprt &expr) const -{ - if(expr.id()==ID_side_effect && - to_side_effect_expr(expr).get_statement()==ID_nondet) - return true; - if(expr.id()==ID_symbol) - if(replace_const.expr_map.find(expr.get(ID_identifier)) - == replace_const.expr_map.end()) - return true; - forall_operands(it,expr) - { - if(maps_to_top(*it)) - return true; - } - return false; -} - -/*******************************************************************\ - -Function: const_propagator_domaint::valuest::set_to_top - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool const_propagator_domaint::valuest::set_to_top(const irep_idt &id) -{ - bool result = false; - replace_symbolt::expr_mapt::iterator r_it = - replace_const.expr_map.find(id); - if(r_it != replace_const.expr_map.end()) - { - replace_const.expr_map.erase(r_it); - result = true; - } - if(top_ids.find(id)==top_ids.end()) - { - top_ids.insert(id); - result = true; - } - return result; -} - -bool const_propagator_domaint::valuest::set_to_top(const exprt &expr) -{ - return set_to_top(to_symbol_expr(expr).get_identifier()); -} - -void const_propagator_domaint::valuest::set_all_to_top() -{ - for(replace_symbolt::expr_mapt::iterator it = - replace_const.expr_map.begin(); - it != replace_const.expr_map.end(); ++it) - top_ids.insert(it->first); - replace_const.expr_map.clear(); -} - - -/*******************************************************************\ - -Function: const_propagator_domaint::valuest::add - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::valuest::set_to(const irep_idt &lhs_id, - const exprt &rhs_val) -{ - replace_const.expr_map[lhs_id] = rhs_val; - std::set::iterator it = top_ids.find(lhs_id); - if(it!=top_ids.end()) top_ids.erase(it); -} - -void const_propagator_domaint::valuest::set_to(const exprt &lhs, - const exprt &rhs_val) -{ - const irep_idt &lhs_id = to_symbol_expr(lhs).get_identifier(); - set_to(lhs_id,rhs_val); -} - -/*******************************************************************\ - -Function: const_propagator_domaint::valuest::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::valuest::output( - std::ostream &out, - const namespacet &ns) const -{ - out << "const map: " << std::endl; - for(replace_symbolt::expr_mapt::const_iterator - it=replace_const.expr_map.begin(); - it!=replace_const.expr_map.end(); - ++it) - out << ' ' << it->first << "=" << - from_expr(ns, "", it->second) << std::endl; - out << "top ids: " << std::endl; - for(std::set::const_iterator - it=top_ids.begin(); - it!=top_ids.end(); - ++it) - out << ' ' << *it << std::endl; -} - -/*******************************************************************\ - -Function: const_propagator_domaint::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_domaint::output( - std::ostream &out, - const ai_baset &ai, - const namespacet &ns) const -{ - values.output(out,ns); -} - -/*******************************************************************\ - -Function: const_propagator_domaint::valuest::merge - - Inputs: - - Outputs: Return true if "this" has changed. - - Purpose: - -\*******************************************************************/ - -bool const_propagator_domaint::valuest::merge(const valuest &src) -{ - bool changed = false; - for(replace_symbolt::expr_mapt::const_iterator - it=src.replace_const.expr_map.begin(); - it!=src.replace_const.expr_map.end(); ++it) - { - replace_symbolt::expr_mapt::iterator - c_it = replace_const.expr_map.find(it->first); - if(c_it != replace_const.expr_map.end()) - { - if(c_it->second != it->second) - { - set_to_top(it->first); - changed = true; - } - } - else if(top_ids.find(it->first)==top_ids.end()) - { - set_to(it->first,it->second); - changed = true; - } - } - for(std::set::const_iterator it=src.top_ids.begin(); - it!=src.top_ids.end(); ++it) - { - bool c = set_to_top(*it); - changed = changed || c; - } - - return changed; -} - -/*******************************************************************\ - -Function: const_propagator_domaint::merge - - Inputs: - - Outputs: Return true if "this" has changed. - - Purpose: - -\*******************************************************************/ - -bool const_propagator_domaint::merge( - const const_propagator_domaint &other, - locationt from, - locationt to) -{ - return values.merge(other.values); -} - -/*******************************************************************\ - -Function: const_propagator_domaint::evaluate_casts_in_constants - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt const_propagator_domaint::evaluate_casts_in_constants(exprt expr, - const typet& parent_type, bool &valid) const -{ - if(expr.id()==ID_side_effect) - { - valid = false; - return expr; - } - if(expr.type().id()!=ID_signedbv && expr.type().id()!=ID_unsignedbv) - return expr; - if(expr.id()==ID_typecast) - expr = evaluate_casts_in_constants(expr.op0(),expr.type(),valid); - if(expr.id()!=ID_constant) - { - if(expr.type()!=parent_type) - return typecast_exprt(expr,parent_type); - else - return expr; - } - //TODO: could be improved to resolve float casts as well... - if(expr.type().id()!=ID_signedbv && expr.type().id()!=ID_unsignedbv) - return expr; - mp_integer v; - to_integer(to_constant_expr(expr), v); - return from_integer(v,parent_type); -} - -/*******************************************************************\ - -Function: const_propagator_ait::replace - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_ait::replace( - goto_functionst::goto_functiont &goto_function, - const namespacet &ns) -{ - Forall_goto_program_instructions(it, goto_function.body) - { - state_mapt::iterator s_it = state_map.find(it); - if(s_it == state_map.end()) - continue; - replace_types_rec(s_it->second.values.replace_const, it->code); - replace_types_rec(s_it->second.values.replace_const, it->guard); - if(it->is_goto() || it->is_assume() || it->is_assert()) - { - s_it->second.values.replace_const(it->guard); - } - else if(it->is_assign()) - { - exprt &rhs = to_code_assign(it->code).rhs(); - s_it->second.values.replace_const(rhs); - } - else if(it->is_function_call()) - { - exprt::operandst &args = - to_code_function_call(it->code).arguments(); - for(exprt::operandst::iterator o_it = args.begin(); - o_it != args.end(); ++o_it) - s_it->second.values.replace_const(*o_it); - } - } -} - -/*******************************************************************\ - -Function: const_propagator_ait::replace_types_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void const_propagator_ait::replace_types_rec( - const replace_symbolt &replace_const, - exprt &expr) -{ - replace_const(expr.type()); - Forall_operands(it,expr) - replace_types_rec(replace_const,*it); -} - diff --git a/src/ssa/const_propagator.h b/src/ssa/const_propagator.h deleted file mode 100644 index 6d017f8b9..000000000 --- a/src/ssa/const_propagator.h +++ /dev/null @@ -1,101 +0,0 @@ -/*******************************************************************\ - -Module: Constant propagation - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_CONST_PROPAGATOR_H -#define CPROVER_CONST_PROPAGATOR_H - -#include - -#include -#include "replace_symbol_ext.h" - -class const_propagator_domaint:public ai_domain_baset -{ -public: - virtual void transform(locationt, locationt, ai_baset &, const namespacet &); - virtual void output(std::ostream &, const ai_baset &, const namespacet &) const; - bool merge(const const_propagator_domaint &, locationt, locationt); - - struct valuest - { - public: - // maps variables to constants - replace_symbol_extt replace_const; - std::set top_ids; - - void output(std::ostream &, const namespacet &) const; - - bool merge(const valuest &src); - - inline void clear() - { - replace_const.expr_map.clear(); - replace_const.type_map.clear(); - top_ids.clear(); - } - - bool empty() const - { - return replace_const.expr_map.empty() && - replace_const.type_map.empty() && - top_ids.empty(); - } - - void set_to(const exprt &lhs, const exprt &rhs_val); - void set_to(const irep_idt &lhs_id, const exprt &rhs_val); - - bool maps_to_top(const exprt &expr) const; - bool set_to_top(const exprt &expr); - bool set_to_top(const irep_idt &id); - void set_all_to_top(); - }; - - valuest values; - -protected: - void assign( - valuest &dest, - const exprt &lhs, - exprt rhs, - const namespacet &ns) const; - - void assign_rec(const exprt &lhs, const exprt &rhs, - const namespacet &ns); - - exprt evaluate_casts_in_constants( - exprt expr, - const typet& parent_type, - bool &valid) const; - -}; - -class const_propagator_ait:public ait -{ -public: - const_propagator_ait( - goto_functionst::goto_functiont &goto_function, - const namespacet &ns) - { - operator()(goto_function, ns); -// output(ns,goto_function.body,"",std::cout); - replace(goto_function, ns); - } - -protected: - friend class const_propagator_domaint; - -void replace( - goto_functionst::goto_functiont &goto_function, - const namespacet &ns); - -void replace_types_rec( - const replace_symbolt &replace_const, - exprt &expr); -}; - -#endif diff --git a/src/ssa/guard_domain.cpp b/src/ssa/guard_domain.cpp deleted file mode 100644 index d7c8b8106..000000000 --- a/src/ssa/guard_domain.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/*******************************************************************\ - -Module: Definition Domain - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#ifdef DEBUG -#include -#endif - -#include "guard_domain.h" - -/*******************************************************************\ - -Function: guard_domaint::output - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void guard_domaint::output( - std::ostream &out, - const ai_baset &, - const namespacet &ns) const -{ - if(unreachable) - { - out << "UNREACHABLE\n"; - return; - } - - for(guardst::const_iterator - g_it=guards.begin(); - g_it!=guards.end(); - g_it++) - { - if(g_it!=guards.begin()) out << " "; - switch(g_it->kind) - { - case guardt::NONE: assert(false); break; - case guardt::BRANCH_TAKEN: out << "bt"; break; - case guardt::BRANCH_NOT_TAKEN: out << "bnt"; break; - case guardt::MERGED: break; - } - - out << g_it->loc->location_number; - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: guard_domaint::transform - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void guard_domaint::transform( - locationt from, - locationt to, - ai_baset &, - const namespacet &ns) -{ - if(unreachable) return; - - if(from->is_goto()) - { - if(from->get_target()==to) - { - // taken - if(!from->guard.is_true()) - guards.push_back(guardt(from, true)); - } - else - { - // not taken - guards.push_back(guardt(from, false)); - } - } - else if(from->is_assume()) - { - guards.push_back(guardt(from)); - } - else if(from->is_function_call()) - { - // Functions might not return, but we will assume that - // for now. - //guards.push_back(guardt(from)); - } -} - -/*******************************************************************\ - -Function: guard_domaint::merge - - Inputs: - - Outputs: return true if "this" has changed - - Purpose: - -\*******************************************************************/ - -bool guard_domaint::merge( - const guard_domaint &b, - locationt from, - locationt to) -{ - // Merging a blank state doesn't change anything. - if(b.unreachable) return false; - - // update 'from' - incoming[from]=b.guards; - - if(unreachable) - { - // copy guards of 'b' - unreachable=false; - guards=b.guards; - return true; - } - - guardst new_guards; - - // Just one incoming edge? - if(incoming.size()==1) - { - new_guards=incoming.begin()->second; - } - else if(incoming.size()==2) - { - // This is the 'OR' between the two conjunctions. - // If this is something simple, we use it. - const guardst &g1=incoming.begin()->second; - const guardst &g2=incoming.rbegin()->second; - - if(prefix_match(g1, g2) && - g1.back().is_branch() && g2.back().is_branch() && - g1.back().loc==g2.back().loc) - { - // We have PREFIX bt loc and PREFIX bnt loc. - // The 'OR' is PREFIX. - new_guards=g1; - new_guards.resize(new_guards.size()-1); - } - else - { - // introduce merge guard - new_guards.push_back(guardt(to)); - } - } - else - { - // Otherwise, we introduce a brand-new merge guard. - new_guards.push_back(guardt(to)); - } - - if(new_guards==guards) - return false; - - guards.swap(new_guards); - - return true; -} diff --git a/src/ssa/guard_domain.h b/src/ssa/guard_domain.h deleted file mode 100644 index 5cd2a6811..000000000 --- a/src/ssa/guard_domain.h +++ /dev/null @@ -1,113 +0,0 @@ -/*******************************************************************\ - -Module: Discover the Guards of Basic Blocks - -Author: Daniel Kroening, kroening@kroening.com - -SEEMS OBSOLETE - -\*******************************************************************/ - -#ifndef CPROVER_GUARD_DOMAIN_H -#define CPROVER_GUARD_DOMAIN_H - -#include - -class guard_domaint:public ai_domain_baset -{ -public: - // the constructor builds 'bottom' - inline guard_domaint():unreachable(true) - { - } - - // A guard may be one of the following: - // 1) a location of a branch, possibly negated for the else-case - // 2) a location of a merged guard - struct guardt - { - public: - locationt loc; - - enum { NONE, BRANCH_TAKEN, BRANCH_NOT_TAKEN, MERGED } kind; - - guardt():kind(NONE) - { - } - - explicit guardt(locationt _loc):loc(_loc), kind(MERGED) - { - } - - guardt(locationt branch, bool truth): - loc(branch), kind(truth?BRANCH_TAKEN:BRANCH_NOT_TAKEN) - { - } - - inline bool is_branch() const - { - return kind==BRANCH_TAKEN || kind==BRANCH_NOT_TAKEN; - } - }; - - inline friend bool operator==(const guardt &a, const guardt &b) - { - return a.kind==b.kind && - a.loc->location_number==b.loc->location_number; - } - - inline friend bool operator!=(const guardt &a, const guardt &b) - { - return !(a==b); - } - - // We may be under some set of guards. - typedef std::vector guardst; - guardst guards; - - bool unreachable; - - // Keep the guards for all incoming edges. - typedef std::map incomingt; - incomingt incoming; - - // returns true iff 'a' and 'b' match in all but the last place - static bool prefix_match(const guardst &a, const guardst &b) - { - if(a.size()!=b.size() || a.empty()) return false; - for(unsigned i=0; i -{ -protected: - virtual void initialize(const goto_programt &goto_program) - { - ait::initialize(goto_program); - - // make entry instruction reachable - if(!goto_program.instructions.empty()) - operator[](goto_program.instructions.begin()).unreachable=false; - } -}; - -#endif diff --git a/src/ssa/guard_map.cpp b/src/ssa/guard_map.cpp index d039438c9..6b5de89ab 100644 --- a/src/ssa/guard_map.cpp +++ b/src/ssa/guard_map.cpp @@ -38,7 +38,7 @@ void guard_mapt::output( in_it++) out << " " << in_it->guard_source->location_number << " (" << in_it->kind << ")"; - + out << "\n"; } } @@ -63,11 +63,11 @@ void guard_mapt::build(const goto_programt &src) { locationt next=it; next++; - + if(it->is_goto()) { map[it->get_target()->location_number].add_in(it, TAKEN); - + if(!it->guard.is_true()) map[next->location_number].add_in(it, NOT_TAKEN); else @@ -88,7 +88,7 @@ void guard_mapt::build(const goto_programt &src) // Also make the function entry location have a guard if(!src.instructions.empty()) map[src.instructions.begin()->location_number].has_guard=true; - + // now assign the guard sources accordingly locationt g; @@ -96,7 +96,7 @@ void guard_mapt::build(const goto_programt &src) forall_goto_program_instructions(it, src) { entryt &entry=map[it->location_number]; - + if(entry.has_guard) { entry.guard_source=it; // self-pointer @@ -105,19 +105,19 @@ void guard_mapt::build(const goto_programt &src) else entry.guard_source=g; // previous } - + // Locations with guards get the successor edge // in the CFG. locationt previous; - + forall_goto_program_instructions(it, src) { // skip first, which has no predecessor if(it!=src.instructions.begin()) { entryt &entry=map[it->location_number]; - + // no need if previous is a goto if(entry.has_guard && !previous->is_goto() && @@ -125,10 +125,10 @@ void guard_mapt::build(const goto_programt &src) !previous->is_function_call()) entry.add_in(previous, SUCCESSOR); } - + previous=it; } - + // now do guard sources of edges for(mapt::iterator m_it=map.begin(); m_it!=map.end(); m_it++) diff --git a/src/ssa/guard_map.h b/src/ssa/guard_map.h index 2e7ca7cce..e59a725cb 100644 --- a/src/ssa/guard_map.h +++ b/src/ssa/guard_map.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_GUARD_MAP_H -#define CPROVER_GUARD_MAP_H +#ifndef CPROVER_2LS_SSA_GUARD_MAP_H +#define CPROVER_2LS_SSA_GUARD_MAP_H #include #include @@ -22,11 +22,11 @@ class guard_mapt { build(src); } - - typedef goto_programt::const_targett locationt; + + typedef goto_programt::const_targett locationt; enum kindt { SUCCESSOR, TAKEN, NOT_TAKEN, ASSUME, FUNCTION_CALL } kind; - + friend std::ostream & operator << (std::ostream &out, kindt kind) { switch(kind) @@ -44,7 +44,7 @@ class guard_mapt { locationt from, guard_source; kindt kind; - + bool is_branch_not_taken() const { return kind==NOT_TAKEN; @@ -82,10 +82,10 @@ class guard_mapt public: inline entryt():has_guard(false) { } bool has_guard; - + // if location has a guard of its own this is a self-pointer locationt guard_source; - + // if it has a guard of its own: incomingt incoming; @@ -95,7 +95,7 @@ class guard_mapt incoming.push_back(edget(l, k)); } }; - + // Query me! I return the entry for any program location. inline const entryt &operator[](const locationt location) const { @@ -103,15 +103,15 @@ class guard_mapt assert(it!=map.end()); return it->second; } - + void output( const goto_programt &goto_program, std::ostream &) const; - + protected: void build(const goto_programt &src); - - //use location number as key to make iteration deterministic + + // use location number as key to make iteration deterministic typedef std::map mapt; mapt map; }; diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index cb8722778..5102df7a5 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -19,14 +19,8 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include - - #include -#include - -#include "ssa_slicer.h" #include "local_ssa.h" #include "malloc_ssa.h" #include "ssa_dereference.h" @@ -48,15 +42,15 @@ void local_SSAt::build_SSA() { // perform SSA data-flow analysis ssa_analysis(goto_function, ns); - + forall_goto_program_instructions(i_it, goto_function.body) { - nodest::iterator loophead_node = nodes.end(); + nodest::iterator loophead_node=nodes.end(); if(i_it->is_backwards_goto()) { - loophead_node = find_node(i_it->get_target()); + loophead_node=find_node(i_it->get_target()); } - nodes.push_back(nodet(i_it,loophead_node)); + nodes.push_back(nodet(i_it, loophead_node)); build_transfer(i_it); build_phi_nodes(i_it); @@ -93,10 +87,10 @@ Function: local_SSAt::get_entry_exit_vars void local_SSAt::get_entry_exit_vars() { - goto_programt::const_targett first = goto_function.body.instructions.begin(); + goto_programt::const_targett first=goto_function.body.instructions.begin(); - //get parameters - const code_typet::parameterst ¶meter_types = + // get parameters + const code_typet::parameterst ¶meter_types= goto_function.type.parameters(); for(code_typet::parameterst::const_iterator it=parameter_types.begin(); it!=parameter_types.end(); it++) @@ -105,7 +99,8 @@ void local_SSAt::get_entry_exit_vars() const irep_idt &identifier=parameter.get_identifier(); const symbolt *symbol; - if(ns.lookup(identifier,symbol)) continue; + if(ns.lookup(identifier, symbol)) + continue; if(ns.follow(symbol->type).id()==ID_struct) { @@ -118,68 +113,18 @@ void local_SSAt::get_entry_exit_vars() params.push_back(to_symbol_expr(*it)); } else - params.push_back(symbol->symbol_expr()); + params.push_back(symbol->symbol_expr()); } - //get globals in - get_globals(first,globals_in,true,false); //filters out #return_value - - //get globals out (includes return value) - goto_programt::const_targett - last = goto_function.body.instructions.end(); last--; - get_globals(last,globals_out,true,true,last->function); + // get globals in + get_globals(first, globals_in, true, false); // filters out #return_value - //get nondeterministic variables - get_nondet_vars(); + // get globals out (includes return value) + goto_programt::const_targett + last=goto_function.body.instructions.end(); last--; + get_globals(last, globals_out, true, true, last->function); } -/*******************************************************************\ - -Function: local_SSAt::get_nondet_vars - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void local_SSAt::get_nondet_vars(const exprt &expr) -{ - if(expr.id()==ID_nondet_symbol) - nondets.insert(expr); - else - forall_operands(it, expr) - get_nondet_vars(*it); -} - -void local_SSAt::get_nondet_vars() -{ - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) - { - for(nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); - e_it!=n_it->equalities.end(); - e_it++) - get_nondet_vars(*e_it); - - for(nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); - c_it!=n_it->constraints.end(); - c_it++) - get_nondet_vars(*c_it); - - for(nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) - get_nondet_vars(*a_it); - } -} - - /*******************************************************************\ Function: local_SSAt::get_globals @@ -192,57 +137,49 @@ Function: local_SSAt::get_globals \*******************************************************************/ -void local_SSAt::get_globals(locationt loc, std::set &globals, - bool rhs_value, bool with_returns, - const irep_idt &returns_for_function) const +void local_SSAt::get_globals( + locationt loc, + std::set &globals, + bool rhs_value, + bool with_returns, + const irep_idt &returns_for_function) const { { - const std::set &ssa_globals = assignments.ssa_objects.globals; - for(std::set::const_iterator it = ssa_globals.begin(); - it != ssa_globals.end(); it++) + const std::set &ssa_globals=assignments.ssa_objects.globals; + for(std::set::const_iterator it=ssa_globals.begin(); + it!=ssa_globals.end(); it++) { #if 0 - std::cout << "global: " - << from_expr(ns, "", read_lhs(it->get_expr(),loc)) << std::endl; + std::cout << "global: " + << from_expr(ns, "", read_lhs(it->get_expr(), loc)) + << std::endl; #endif - bool is_return = id2string(it->get_identifier()).find( - "#return_value") != std::string::npos; - if(!with_returns && is_return) + if(!with_returns && + id2string(it->get_identifier()).find( + "#return_value")!=std::string::npos) continue; - //filter out return values of other functions + // filter out return values of other functions if(with_returns && returns_for_function!="" && - is_return && + id2string(it->get_identifier()).find( + "#return_value")!=std::string::npos && id2string(it->get_identifier()).find( id2string(returns_for_function)+"#return_value")==std::string::npos) continue; if(rhs_value) { - //workaround for the problem that - // rhs() for a return value is always the "input" return value - if(is_return) - { - if(loc != goto_function.body.instructions.begin()) - { - const exprt &expr = read_lhs(it->get_expr(),--loc); - globals.insert(to_symbol_expr(expr)); - } - } - else - { - const exprt &expr = read_rhs(it->get_expr(),loc); - globals.insert(to_symbol_expr(expr)); - } + const exprt &expr=read_rhs(it->get_expr(), loc); + globals.insert(to_symbol_expr(expr)); } else { - const exprt &expr = read_lhs(it->get_expr(),loc); + const exprt &expr=read_lhs(it->get_expr(), loc); globals.insert(to_symbol_expr(expr)); } } } -} +} /*******************************************************************\ @@ -259,16 +196,18 @@ Function: local_SSAt::collect_custom_templates void local_SSAt::collect_custom_templates() { - for(local_SSAt::nodest::iterator n_it=nodes.begin(); + for(local_SSAt::nodest::iterator n_it=nodes.begin(); n_it!=nodes.end(); n_it++) { - if(n_it->loophead != nodes.end()) //we've found a loop + if(n_it->loophead!=nodes.end()) // we've found a loop { - //search for templates in the loop - for(local_SSAt::nodest::iterator nn_it=n_it->loophead; + // search for templates in the loop + for(local_SSAt::nodest::iterator nn_it=n_it->loophead; nn_it!=n_it; nn_it++) { - if(nn_it->templates.empty()) continue; + if(nn_it->templates.empty()) + continue; + n_it->loophead->templates.insert(n_it->loophead->templates.end(), nn_it->templates.begin(), nn_it->templates.end()); @@ -292,10 +231,11 @@ Function: local_SSAt::find_node local_SSAt::nodest::iterator local_SSAt::find_node(locationt loc) { - nodest::iterator n_it = nodes.begin(); - for(; n_it != nodes.end(); n_it++) + nodest::iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) { - if(n_it->location == loc) break; + if(n_it->location==loc) + break; } return n_it; } @@ -314,10 +254,11 @@ Function: local_SSAt::find_node local_SSAt::nodest::const_iterator local_SSAt::find_node(locationt loc) const { - nodest::const_iterator n_it = nodes.begin(); - for(; n_it != nodes.end(); n_it++) + nodest::const_iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) { - if(n_it->location == loc) break; + if(n_it->location==loc) + break; } return n_it; } @@ -334,13 +275,38 @@ Function: local_SSAt::find_nodes \*******************************************************************/ -void local_SSAt::find_nodes(locationt loc, std::list &_nodes) const +void local_SSAt::find_nodes( + locationt loc, + std::list &_nodes) const +{ + nodest::const_iterator n_it=nodes.begin(); + for(; n_it!=nodes.end(); n_it++) + { + if(n_it->location==loc) + _nodes.push_back(n_it); + } +} + +/*******************************************************************\ + +Function: local_SSAt::find_location_by_number + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +local_SSAt::locationt local_SSAt::find_location_by_number(unsigned location_number) const { - nodest::const_iterator n_it = nodes.begin(); + local_SSAt::nodest::const_iterator n_it =nodes.begin(); for(; n_it != nodes.end(); n_it++) { - if(n_it->location == loc) _nodes.push_back(n_it); + if(n_it->location->location_number == location_number) break; } + return n_it->location; } /*******************************************************************\ @@ -388,21 +354,30 @@ Function: local_SSAt::build_phi_nodes void local_SSAt::build_phi_nodes(locationt loc) { const ssa_domaint::phi_nodest &phi_nodes=ssa_analysis[loc].phi_nodes; - nodet &node= *(--nodes.end()); + nodet &node=*(--nodes.end()); for(objectst::const_iterator o_it=ssa_objects.objects.begin(); - o_it!=ssa_objects.objects.end(); o_it++) + o_it!=ssa_objects.objects.end(); ++o_it) { // phi-node for this object here? ssa_domaint::phi_nodest::const_iterator p_it= phi_nodes.find(o_it->get_identifier()); - - if(p_it==phi_nodes.end()) continue; // none + + if(p_it==phi_nodes.end()) // none + continue; #ifdef DEBUG std::cout << "PHI " << o_it->get_identifier() << "\n"; #endif + + // ignore custom template variables + if(id2string(o_it->get_identifier()). + find(TEMPLATE_PREFIX)!=std::string::npos) continue; + + #ifdef DEBUG + std::cout << "PHI " << o_it->get_identifier() << "\n"; + #endif //ignore custom template variables if(id2string(o_it->get_identifier()). @@ -417,28 +392,28 @@ void local_SSAt::build_phi_nodes(locationt loc) // and do forwards-edges first, which gives them // _lower_ priority in the ITE. Inputs are always // forward edges. - + for(ssa_domaint::loc_def_mapt::const_iterator incoming_it=incoming.begin(); incoming_it!=incoming.end(); incoming_it++) if(incoming_it->second.is_input() || - incoming_it->first < loc->location_number) + incoming_it->firstlocation_number) { // it's a forward edge exprt incoming_value=name(*o_it, incoming_it->second); - //TODO: investigate: here is some nondeterminism + // TODO: investigate: here is some nondeterminism // whether g2 (=g1&c1 (maybe)) or (g1&c1) is used, // not sure whether this has consequences // (further than the SSA looking different each time you generate it) - exprt incoming_guard=edge_guard(get_location(incoming_it->first),loc); + exprt incoming_guard=edge_guard(get_location(incoming_it->first), loc); if(rhs.is_nil()) // first rhs=incoming_value; else rhs=if_exprt(incoming_guard, incoming_value, rhs); } - + // now do backwards for(ssa_domaint::loc_def_mapt::const_iterator @@ -446,10 +421,10 @@ void local_SSAt::build_phi_nodes(locationt loc) incoming_it!=incoming.end(); incoming_it++) if(!incoming_it->second.is_input() && - incoming_it->first >= loc->location_number) + incoming_it->first>=loc->location_number) { // it's a backwards edge - const locationt &iloc = get_location(incoming_it->first); + const locationt &iloc=get_location(incoming_it->first); exprt incoming_value=name(*o_it, LOOP_BACK, iloc); exprt incoming_select=name(guard_symbol(), LOOP_SELECT, iloc); @@ -460,7 +435,7 @@ void local_SSAt::build_phi_nodes(locationt loc) } symbol_exprt lhs=name(*o_it, PHI, loc); - + equal_exprt equality(lhs, rhs); node.equalities.push_back(equality); } @@ -503,23 +478,23 @@ void local_SSAt::build_transfer(locationt loc) { const code_assignt &code_assign=to_code_assign(loc->code); - // template declarations - if(code_assign.lhs().id()==ID_symbol && + // template declarations + if(code_assign.lhs().id()==ID_symbol && id2string(code_assign.lhs().get(ID_identifier)). - find("return_value_" TEMPLATE_NEWVAR) != std::string::npos) + find("return_value_" TEMPLATE_NEWVAR)!=std::string::npos) { - //propagate equalities through replace map - exprt lhs = code_assign.lhs(); - template_newvars[lhs] = template_newvars[template_last_newvar]; - template_last_newvar = lhs; + // propagate equalities through replace map + exprt lhs=code_assign.lhs(); + template_newvars[lhs]=template_newvars[template_last_newvar]; + template_last_newvar=lhs; return; } - if(code_assign.lhs().id()==ID_symbol && + if(code_assign.lhs().id()==ID_symbol && id2string(code_assign.lhs().get(ID_identifier)). - find(TEMPLATE_PREFIX)!=std::string::npos) return; - if(code_assign.rhs().id()==ID_symbol && + find(TEMPLATE_PREFIX)!=std::string::npos) return; + if(code_assign.rhs().id()==ID_symbol && id2string(code_assign.rhs().get(ID_identifier)). - find(TEMPLATE_PREFIX)!=std::string::npos) return; + find(TEMPLATE_PREFIX)!=std::string::npos) return; exprt deref_lhs=dereference(code_assign.lhs(), loc); exprt deref_rhs=dereference(code_assign.rhs(), loc); @@ -527,7 +502,7 @@ void local_SSAt::build_transfer(locationt loc) assign_rec(deref_lhs, deref_rhs, true_exprt(), loc); } } - + /*******************************************************************\ Function: local_SSAt::build_function_call @@ -548,81 +523,82 @@ void local_SSAt::build_function_call(locationt loc) to_code_function_call(loc->code); const exprt &lhs=code_function_call.lhs(); - + if(lhs.is_not_nil()) { exprt deref_lhs=dereference(lhs, loc); - + // generate a symbol for rhs irep_idt identifier="ssa::return_value"+i2string(loc->location_number); symbol_exprt rhs(identifier, code_function_call.lhs().type()); - + assign_rec(deref_lhs, rhs, true_exprt(), loc); } - nodest::iterator n_it = --nodes.end(); + nodest::iterator n_it=--nodes.end(); - //template declarations + // template declarations if(code_function_call.function().id()==ID_symbol && - has_prefix(TEMPLATE_DECL, - id2string(code_function_call.function().get(ID_identifier)))) + has_prefix( + TEMPLATE_DECL, + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); n_it->templates.push_back(code_function_call.arguments()[0]); // replace "new" vars - replace_expr(template_newvars,n_it->templates.back()); + replace_expr(template_newvars, n_it->templates.back()); #if 0 - std::cout << "found template declaration: " - << from_expr(ns,"",code_function_call.arguments()[0]) + std::cout << "found template declaration: " + << from_expr(ns, "", code_function_call.arguments()[0]) << std::endl; #endif template_newvars.clear(); return; } - //turn function call into expression + // turn function call into expression function_application_exprt f; - f.function() = code_function_call.function(); - f.type() = code_function_call.lhs().type(); - f.arguments() = code_function_call.arguments(); + f.function()=code_function_call.function(); + f.type()=code_function_call.lhs().type(); + f.arguments()=code_function_call.arguments(); - //access to "new" value in template declarations + // access to "new" value in template declarations if(code_function_call.function().id()==ID_symbol && - has_prefix(TEMPLATE_NEWVAR, - id2string(code_function_call.function().get(ID_identifier)))) + has_prefix( + TEMPLATE_NEWVAR, + id2string(code_function_call.function().get(ID_identifier)))) { assert(code_function_call.arguments().size()==1); - template_last_newvar = f; - template_newvars[template_last_newvar] = template_last_newvar; + template_last_newvar=f; + template_newvars[template_last_newvar]=template_last_newvar; return; } - f = to_function_application_expr(read_rhs(f, loc)); - assert(f.function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f.function()).get_identifier(); - //add equalities for arguments + f=to_function_application_expr(read_rhs(f, loc)); + assert(f.function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f.function()).get_identifier(); + // add equalities for arguments unsigned i=0; - for(exprt::operandst::iterator it = f.arguments().begin(); - it != f.arguments().end(); it++, i++) - { - //symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - // "#arg"+i2string(i),it->type()); - symbol_exprt arg(id2string(fname)+"#arg"+i2string(i)+"#"+i2string(loc->location_number),it->type()); + for(exprt::operandst::iterator it=f.arguments().begin(); + it!=f.arguments().end(); ++it, ++i) + { + symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ + "#arg"+i2string(i),it->type()); const typet &argtype = ns.follow(it->type()); if(argtype.id()==ID_struct) { - exprt lhs = read_rhs(arg, loc); - for(size_t j=0; jequalities.push_back(equal_exprt(lhs.operands()[j], - it->operands()[j])); - } + n_it->equalities.push_back(equal_exprt(lhs.operands()[j], + it->operands()[j])); + } } else { - n_it->equalities.push_back(equal_exprt(arg,*it)); + n_it->equalities.push_back(equal_exprt(arg,*it)); } *it = arg; } @@ -645,7 +621,7 @@ Function: local_SSAt::build_cond void local_SSAt::build_cond(locationt loc) { // anything to be built? - if(loc->is_goto() || loc->is_assume()) + if(loc->is_goto() || loc->is_assume()) { // produce a symbol for the renamed branching condition equal_exprt equality(cond_symbol(loc), read_rhs(loc->guard, loc)); @@ -672,25 +648,26 @@ Function: local_SSAt::build_guard void local_SSAt::build_guard(locationt loc) { const guard_mapt::entryt &entry=guard_map[loc]; - + // anything to be built? - if(!entry.has_guard) return; - + if(!entry.has_guard) + return; + exprt::operandst sources; // the very first 'loc' trivially gets 'true' as source if(loc==goto_function.body.instructions.begin()) sources.push_back(true_exprt()); - + for(guard_mapt::incomingt::const_iterator i_it=entry.incoming.begin(); i_it!=entry.incoming.end(); i_it++) { const guard_mapt::edget &edge=*i_it; - + exprt source; - + // might be backwards branch taken edge if(edge.is_branch_taken() && edge.from->is_backwards_goto()) @@ -707,11 +684,11 @@ void local_SSAt::build_guard(locationt loc) else { // the other cases are basically similar - + symbol_exprt gs=name(guard_symbol(), OUT, edge.guard_source); exprt cond; - + if(edge.is_branch_taken() || edge.is_assume() || edge.is_function_call()) @@ -725,13 +702,13 @@ void local_SSAt::build_guard(locationt loc) source=and_exprt(gs, cond); } - + sources.push_back(source); } - + // the below produces 'false' if there is no source exprt rhs=disjunction(sources); - + equal_exprt equality(guard_symbol(loc), rhs); (--nodes.end())->equalities.push_back(equality); } @@ -753,7 +730,7 @@ void local_SSAt::build_assertions(locationt loc) if(loc->is_assert()) { exprt c=read_rhs(loc->guard, loc); - exprt g=guard_symbol(loc); + exprt g=guard_symbol(loc); (--nodes.end())->assertions.push_back(implies_exprt(g, c)); } } @@ -799,8 +776,8 @@ void local_SSAt::assertions_to_constraints() n_it++) { n_it->constraints.insert(n_it->constraints.end(), - n_it->assertions.begin(),n_it->assertions.end()); - } + n_it->assertions.begin(), n_it->assertions.end()); + } } /*******************************************************************\ @@ -840,12 +817,12 @@ void local_SSAt::assertions_after_loop() const exprt::operandst &assertions = assertion_map[loopheads.back()->location]; n_it->assertions_after_loop.insert(n_it->assertions_after_loop.end(), - assertions.begin(),assertions.end()); + assertions.begin(),assertions.end()); assertion_map[loopheads.back()->location].clear(); - //do not consider assertions after another loop + //do not consider assertions after another loop } else // we should have reached the beginning - assert(n_it==nodes.begin()); + assert(n_it==nodes.begin()); } if(n_it->loophead!=nodes.end()) { @@ -950,18 +927,17 @@ exprt local_SSAt::read_lhs( #ifdef DEBUG std::cout << "read_lhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; #endif - + ssa_objectt object(tmp1, ns); // is this an object we track? if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { - #ifdef DEBUG std::cout << from_expr(ns, "", tmp1) << "is_object" << '\n'; #endif - + // yes, it is if(assignments.assigns(loc, object)) return name(object, OUT, loc); @@ -1005,23 +981,20 @@ exprt local_SSAt::read_node_in( ssa_domaint::phi_nodest::const_iterator p_it= phi_nodes.find(identifier); - + bool has_phi=false; - + if(p_it!=phi_nodes.end()) { const ssa_domaint::loc_def_mapt &incoming=p_it->second; - for(ssa_domaint::loc_def_mapt::const_iterator - incoming_it=incoming.begin(); - incoming_it!=incoming.end(); - incoming_it++) + for(const auto &incoming_it : incoming) { - if(incoming_it->first > loc->location_number) + if(incoming_it.first>loc->location_number) has_phi=true; } } - + if(has_phi) return name(object, LOOP_BACK, loc); else @@ -1041,11 +1014,12 @@ Function: local_SSAt::get_def_loc \*******************************************************************/ local_SSAt::locationt local_SSAt::get_def_loc( - const symbol_exprt &expr, + const symbol_exprt &expr, locationt loc) const { - ssa_objectt object(expr,ns); - if(!object) assert(false); + ssa_objectt object(expr, ns); + if(!object) + assert(false); if(ssa_objects.objects.find(object)!= ssa_objects.objects.end()) { @@ -1055,14 +1029,13 @@ local_SSAt::locationt local_SSAt::get_def_loc( ssa_domaint::def_mapt::const_iterator d_it= ssa_domain.def_map.find(identifier); - if(d_it==ssa_domain.def_map.end()) //input + if(d_it==ssa_domain.def_map.end()) // input return goto_function.body.instructions.begin(); else - return d_it->second.def.loc; //last definition + return d_it->second.def.loc; // last definition } - else //input + else // input return goto_function.body.instructions.begin(); - } /*******************************************************************\ @@ -1081,26 +1054,26 @@ exprt local_SSAt::read_rhs(const exprt &expr, locationt loc) const { exprt tmp1=expr; adjust_float_expressions(tmp1, ns); - + unsigned counter=0; replace_side_effects_rec(tmp1, loc, counter); #ifdef DEBUG std::cout << "read_rhs tmp1: " << from_expr(ns, "", tmp1) << '\n'; #endif - + exprt tmp2=dereference(tmp1, loc); #ifdef DEBUG std::cout << "read_rhs tmp2: " << from_expr(ns, "", tmp2) << '\n'; #endif - + exprt result=read_rhs_rec(tmp2, loc); - + #ifdef DEBUG std::cout << "read_rhs result: " << from_expr(ns, "", result) << '\n'; #endif - + return result; } @@ -1170,7 +1143,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { // ignore - //throw "unexpected side effect in read_rhs_rec"; + // throw "unexpected side effect in read_rhs_rec"; } else if(expr.id()==ID_address_of) { @@ -1189,11 +1162,11 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const read_rhs(index_expr.index(), loc), expr.type()); } - + ssa_objectt object(expr, ns); - + // is it an object identifier? - + if(!object) { exprt tmp=expr; // copy @@ -1201,7 +1174,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const *it=read_rhs(*it, loc); return tmp; } - + // Argument is a struct-typed ssa object? // May need to split up into members. const typet &type=ns.follow(expr.type()); @@ -1210,12 +1183,12 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const { // build struct constructor struct_exprt result(expr.type()); - + const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + result.operands().resize(components.size()); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -1224,7 +1197,7 @@ exprt local_SSAt::read_rhs_rec(const exprt &expr, locationt loc) const result.operands()[it-components.begin()]= read_rhs(member_exprt(expr, it->get_name(), it->type()), loc); } - + return result; } @@ -1268,7 +1241,7 @@ void local_SSAt::replace_side_effects_rec( { // turn into nondet_symbol counter++; - exprt s = nondet_symbol("ssa::nondet",expr.type(),loc,counter); + exprt s=nondet_symbol("ssa::nondet", expr.type(), loc, counter); expr.swap(s); } else if(statement==ID_malloc) @@ -1282,7 +1255,7 @@ void local_SSAt::replace_side_effects_rec( } else { - //throw "unexpected side effect: "+id2string(statement); + // throw "unexpected side effect: "+id2string(statement); // ignore } } @@ -1308,12 +1281,9 @@ symbol_exprt local_SSAt::name( symbol_exprt new_symbol_expr(object.get_expr().type()); const irep_idt &id=object.get_identifier(); unsigned cnt=loc->location_number; - + irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi": - kind==LOOP_BACK?"lb": - kind==LOOP_SELECT?"ls": - "")+ + (kind==PHI?"phi":kind==LOOP_BACK?"lb":kind==LOOP_SELECT?"ls":"")+ i2string(cnt)+ (kind==LOOP_SELECT?std::string(""):suffix); @@ -1322,10 +1292,10 @@ symbol_exprt local_SSAt::name( #endif new_symbol_expr.set_identifier(new_id); - + if(object.get_expr().source_location().is_not_nil()) new_symbol_expr.add_source_location()=object.get_expr().source_location(); - + return new_symbol_expr; } @@ -1369,7 +1339,7 @@ symbol_exprt local_SSAt::name_input(const ssa_objectt &object) const { symbol_exprt new_symbol_expr(object.get_expr().type()); // copy const irep_idt old_id=object.get_identifier(); - irep_idt new_id=id2string(old_id)+suffix; //+"#in" + irep_idt new_id=id2string(old_id)+suffix; // +"#in" new_symbol_expr.set_identifier(new_id); if(object.get_expr().source_location().is_not_nil()) @@ -1390,8 +1360,11 @@ Function: local_SSAt::nondet_symbol \*******************************************************************/ -exprt local_SSAt::nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const +exprt local_SSAt::nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const { exprt s(ID_nondet_symbol, type); const irep_idt identifier= @@ -1430,7 +1403,7 @@ void local_SSAt::assign_rec( const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -1445,7 +1418,7 @@ void local_SSAt::assign_rec( } ssa_objectt lhs_object(lhs, ns); - + const std::set &assigned= assignments.get(loc); @@ -1454,7 +1427,7 @@ void local_SSAt::assign_rec( exprt ssa_rhs=read_rhs(rhs, loc); const symbol_exprt ssa_symbol=name(lhs_object, OUT, loc); - + equal_exprt equality(ssa_symbol, ssa_rhs); (--nodes.end())->equalities.push_back(equality); } @@ -1475,7 +1448,8 @@ void local_SSAt::assign_rec( if(compound_type.id()==ID_union) { - union_exprt new_rhs(member_expr.get_component_name(), rhs, compound.type()); + union_exprt new_rhs( + member_expr.get_component_name(), rhs, compound.type()); assign_rec(member_expr.struct_op(), new_rhs, guard, loc); } else if(compound_type.id()==ID_struct) @@ -1508,7 +1482,11 @@ void local_SSAt::assign_rec( { const if_exprt &if_expr=to_if_expr(lhs); assign_rec(if_expr.true_case(), rhs, and_exprt(guard, if_expr.cond()), loc); - assign_rec(if_expr.false_case(), rhs, and_exprt(guard, not_exprt(if_expr.cond())), loc); + assign_rec( + if_expr.false_case(), + rhs, + and_exprt(guard, not_exprt(if_expr.cond())), + loc); } else if(lhs.id()==ID_byte_extract_little_endian || lhs.id()==ID_byte_extract_big_endian) @@ -1540,11 +1518,12 @@ Function: local_SSAt::output void local_SSAt::output(std::ostream &out) const { - for(nodest::const_iterator - n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + for(nodest::const_iterator + n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) { - if(n_it->empty()) continue; + if(n_it->empty()) + continue; n_it->output(out, ns); out << '\n'; } @@ -1564,18 +1543,19 @@ Function: local_SSAt::output_verbose void local_SSAt::output_verbose(std::ostream &out) const { - for(nodest::const_iterator - n_it = nodes.begin(); - n_it != nodes.end(); n_it++) + for(nodest::const_iterator + n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) { - if(n_it->empty()) continue; + if(n_it->empty()) + continue; out << "*** " << n_it->location->location_number << " " << n_it->location->source_location << "\n"; n_it->output(out, ns); - if(n_it->loophead!=nodes.end()) + if(n_it->loophead!=nodes.end()) out << "loop back to location " << n_it->loophead->location->location_number << "\n"; - if(!n_it->enabling_expr.is_true()) + if(!n_it->enabling_expr.is_true()) out << "enabled if " << from_expr(ns, "", n_it->enabling_expr) << "\n"; out << "\n"; @@ -1585,7 +1565,7 @@ void local_SSAt::output_verbose(std::ostream &out) const /*******************************************************************\ -Function: local_SSAt::output +Function: local_SSAt::nodet::output Inputs: @@ -1602,7 +1582,7 @@ void local_SSAt::nodet::output( if(!enabling_expr.is_true()) out << "(enable) " << from_expr(ns, "", enabling_expr) << "\n"; #if 0 - if(!marked) + if(!marked) out << "(not marked)" << "\n"; #endif for(equalitiest::const_iterator @@ -1612,10 +1592,10 @@ void local_SSAt::nodet::output( out << "(E) " << from_expr(ns, "", *e_it) << "\n"; for(constraintst::const_iterator - c_it=constraints.begin(); - c_it!=constraints.end(); - c_it++) - out << "(C) " << from_expr(ns, "", *c_it) << "\n"; + e_it=constraints.begin(); + e_it!=constraints.end(); + e_it++) + out << "(C) " << from_expr(ns, "", *e_it) << "\n"; for(assertionst::const_iterator a_it=assertions.begin(); @@ -1632,7 +1612,7 @@ void local_SSAt::nodet::output( #if 0 if(!assertions_after_loop.empty()) out << "(assertions-after-loop) " - << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; + << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; #endif } @@ -1676,7 +1656,8 @@ bool local_SSAt::has_static_lifetime(const exprt &src) const else if(src.id()==ID_symbol) { const symbolt *s; - if(ns.lookup(to_symbol_expr(src).get_identifier(),s)) return false; + if(ns.lookup(to_symbol_expr(src).get_identifier(), s)) + return false; return s->is_static_lifetime; } else @@ -1704,27 +1685,27 @@ std::vector & operator << ( ssa_slicer(dest,src); #else for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + n_it != src.nodes.end(); n_it++) { if(n_it->marked) continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); else dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); else dest.push_back(*c_it); } @@ -1752,21 +1733,22 @@ std::list & operator << ( { #ifdef SLICING ssa_slicert ssa_slicer; - ssa_slicer(dest,src); + ssa_slicer(dest, src); #else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); else - dest.push_back(*e_it); + dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator @@ -1775,19 +1757,19 @@ std::list & operator << ( c_it++) { if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); else - dest.push_back(*c_it); + dest.push_back(*c_it); } } #endif - + return dest; } /*******************************************************************\ -Function: local_SSAt::operator << +Function: local_SSAt::operator<< Inputs: @@ -1797,30 +1779,31 @@ Function: local_SSAt::operator << \*******************************************************************/ -decision_proceduret & operator << ( +decision_proceduret &operator<<( decision_proceduret &dest, const local_SSAt &src) { #ifdef SLICING std::list tmp; tmp << src; - for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) + for(std::list::const_iterator it=tmp.begin(); + it!=tmp.end(); it++) dest << *it; #else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + dest << implies_exprt(n_it->enabling_expr,*e_it); else - dest << *e_it; + dest << *e_it; } for(local_SSAt::nodet::constraintst::const_iterator @@ -1829,18 +1812,18 @@ decision_proceduret & operator << ( c_it++) { if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + dest << implies_exprt(n_it->enabling_expr,*c_it); else - dest << *c_it; + dest << *c_it; } } -#endif +#endif return dest; } /*******************************************************************\ -Function: local_SSAt::operator << +Function: local_SSAt::operator<< Inputs: @@ -1850,30 +1833,45 @@ Function: local_SSAt::operator << \*******************************************************************/ -incremental_solvert & operator << ( +incremental_solvert &operator<<( incremental_solvert &dest, const local_SSAt &src) { #ifdef SLICING std::list tmp; tmp << src; - for(std::list::const_iterator it = tmp.begin(); - it != tmp.end(); it++) + for(std::list::const_iterator it=tmp.begin(); + it!=tmp.end(); it++) dest << *it; #else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *e_it); else dest << *e_it; + +#if 0 + // freeze cond variables + if(e_it->op0().id()==ID_symbol && + e_it->op0().type().id()==ID_bool) + { + const symbol_exprt &symbol=to_symbol_expr(e_it->op0()); + if(id2string(symbol.get_identifier()).find("ssa::$cond")!= + std::string::npos) + { + dest.solver->set_frozen(dest.solver->convert(symbol)); + } + } +#endif } for(local_SSAt::nodet::constraintst::const_iterator @@ -1881,19 +1879,19 @@ incremental_solvert & operator << ( c_it!=n_it->constraints.end(); c_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *c_it); else dest << *c_it; } } -#endif +#endif return dest; } /*******************************************************************\ -Function: local_SSAt::get_enabling_expr +Function: local_SSAt::get_enabling_exprs Inputs: @@ -1905,11 +1903,44 @@ Function: local_SSAt::get_enabling_expr exprt local_SSAt::get_enabling_exprs() const { - exprt result = conjunction(enabling_exprs); + exprt::operandst result; + result.reserve(enabling_exprs.size()); + for(std::list::const_iterator it=enabling_exprs.begin(); + it!=enabling_exprs.end(); ++it) + { + std::list::const_iterator lh=it; ++lh; + if(lh!=enabling_exprs.end()) + result.push_back(not_exprt(*it)); + else + result.push_back(*it); + } + return conjunction(result); +} -#if 0 - std::cout << "current enabling expr:" << from_expr(ns, "", result) << "\n"; -#endif +/*******************************************************************\ - return result; +Function: local_SSAt::has_function_calls + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool local_SSAt::has_function_calls() const +{ + bool found=false; + for(local_SSAt::nodest::const_iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + { + if(!n_it->function_calls.empty()) + { + found=true; + break; + } + } + return found; } + diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 314d6cc36..0c87c160d 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_LOCAL_SSA_H -#define CPROVER_LOCAL_SSA_H +#ifndef CPROVER_2LS_SSA_LOCAL_SSA_H +#define CPROVER_2LS_SSA_LOCAL_SSA_H #include @@ -33,21 +33,21 @@ class local_SSAt const goto_functiont &_goto_function, const namespacet &_ns, const std::string &_suffix=""): - ns(_ns), goto_function(_goto_function), + ns(_ns), goto_function(_goto_function), ssa_objects(_goto_function, ns), ssa_value_ai(_goto_function, ns), assignments(_goto_function.body, ns, ssa_objects, ssa_value_ai), guard_map(_goto_function.body), ssa_analysis(assignments), - suffix(_suffix) + suffix(_suffix) { - //ENHANCE: in future locst will be used (currently in path-symex/locs.h) - forall_goto_program_instructions(it,_goto_function.body) - location_map[it->location_number] = it; + // ENHANCE: in future locst will be used (currently in path-symex/locs.h) + forall_goto_program_instructions(it, _goto_function.body) + location_map[it->location_number]=it; build_SSA(); } - + void output(std::ostream &) const; void output_verbose(std::ostream &) const; @@ -57,18 +57,17 @@ class local_SSAt public: inline nodet( locationt _location, - std::list::iterator _loophead) - : + std::list::iterator _loophead) + : enabling_expr(true_exprt()), - marked(false), function_calls_inlined(false), - location(_location), + marked(false), function_calls_inlined(false), + location(_location), loophead(_loophead) - { + { } - exprt enabling_expr; //for incremental unwinding - - bool marked; //for incremental solving + exprt enabling_expr; // for incremental unwinding + bool marked; // for incremental unwinding typedef std::vector equalitiest; equalitiest equalities; @@ -82,61 +81,63 @@ class local_SSAt typedef std::vector assumptionst; assertionst assumptions; - + typedef std::vector function_callst; function_callst function_calls; bool function_calls_inlined; - //custom invariant templates + + // custom invariant templates typedef std::vector templatest; templatest templates; - locationt location; //link to goto instruction - std::list::iterator loophead; //link to loop head node + locationt location; // link to goto instruction + std::list::iterator loophead; // link to loop head node + // otherwise points to nodes.end() void output(std::ostream &, const namespacet &) const; inline bool empty() const { - return equalities.empty() && constraints.empty() && - assertions.empty() && function_calls.empty(); + return equalities.empty() && constraints.empty() && + assertions.empty() && function_calls.empty(); } }; - + // turns the assertions in the function into constraints void assertions_to_constraints(); - // all the SSA nodes + // all the SSA nodes typedef std::list nodest; nodest nodes; void mark_nodes() { for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = true; + n_it!=nodes.end(); n_it++) n_it->marked=true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked = false; + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) n_it->marked=false; } // for incremental unwinding - std::vector enabling_exprs; + std::list enabling_exprs; exprt get_enabling_exprs() const; // function entry and exit variables typedef std::list var_listt; typedef std::set var_sett; - var_listt params; + var_listt params; var_sett globals_in, globals_out; - std::set nondets; + std::set nondets; bool has_function_calls() const; const namespacet &ns; const goto_functiont &goto_function; - + // guards ssa_objectt cond_symbol() const; symbol_exprt cond_symbol(locationt loc) const @@ -145,14 +146,18 @@ class local_SSAt symbol_exprt guard_symbol(locationt loc) const { return name(guard_symbol(), OUT, guard_map[loc].guard_source); } exprt edge_guard(locationt from, locationt to) const; - + // auxiliary functions enum kindt { PHI, OUT, LOOP_BACK, LOOP_SELECT }; - virtual symbol_exprt name(const ssa_objectt &, kindt kind, locationt loc) const; + virtual symbol_exprt name( + const ssa_objectt &, kindt kind, locationt loc) const; symbol_exprt name(const ssa_objectt &, const ssa_domaint::deft &) const; symbol_exprt name_input(const ssa_objectt &) const; - virtual exprt nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const; + virtual exprt nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const; locationt get_def_loc(const symbol_exprt &, locationt loc) const; void replace_side_effects_rec(exprt &, locationt, unsigned &) const; exprt read_lhs(const exprt &, locationt loc) const; @@ -161,34 +166,39 @@ class local_SSAt exprt read_rhs_rec(const exprt &, locationt loc) const; symbol_exprt read_rhs(const ssa_objectt &, locationt loc) const; exprt read_node_in(const ssa_objectt &, locationt loc) const; - void assign_rec(const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); + void assign_rec( + const exprt &lhs, const exprt &rhs, const exprt &guard, locationt loc); void get_entry_exit_vars(); - + bool has_static_lifetime(const ssa_objectt &) const; bool has_static_lifetime(const exprt &) const; - + exprt dereference(const exprt &expr, locationt loc) const; ssa_objectst ssa_objects; typedef ssa_objectst::objectst objectst; ssa_value_ait ssa_value_ai; assignmentst assignments; - -//protected: + +// protected: guard_mapt guard_map; ssa_ait ssa_analysis; std::string suffix; // an extra suffix - void get_globals(locationt loc, std::set &globals, - bool rhs_value=true, - bool with_returns=true, - const irep_idt &returns_for_function="") const; + void get_globals( + locationt loc, + std::set &globals, + bool rhs_value=true, + bool with_returns=true, + const irep_idt &returns_for_function="") const; nodest::iterator find_node(locationt loc); nodest::const_iterator find_node(locationt loc) const; - void find_nodes(locationt loc, std::list &_nodes) const; + void find_nodes( + locationt loc, std::list &_nodes) const; + inline locationt get_location(unsigned location_number) const { location_mapt::const_iterator it=location_map.find(location_number); @@ -196,6 +206,8 @@ class local_SSAt return it->second; } + locationt find_location_by_number(unsigned location_number) const; + protected: typedef std::map location_mapt; location_mapt location_map; @@ -216,9 +228,6 @@ class local_SSAt void collect_custom_templates(); replace_mapt template_newvars; exprt template_last_newvar; - - void get_nondet_vars(const exprt &expr); - void get_nondet_vars(); }; std::vector & operator << diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index 13adc92c2..9e93714b8 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -6,6 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ +#include + #include #include #include @@ -20,7 +22,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: +Function: c_sizeof_type_rec Inputs: @@ -43,10 +45,11 @@ inline static typet c_sizeof_type_rec(const exprt &expr) forall_operands(it, expr) { typet t=c_sizeof_type_rec(*it); - if(t.is_not_nil()) return t; + if(t.is_not_nil()) + return t; } } - + return nil_typet(); } @@ -73,7 +76,7 @@ exprt malloc_ssa( namespacet ns(symbol_table); exprt size=code.op0(); typet object_type=nil_typet(); - + { // special treatment for sizeof(T)*x if(size.id()==ID_mult && @@ -82,20 +85,20 @@ exprt malloc_ssa( { object_type=array_typet( c_sizeof_type_rec(size.op0()), - size.op1()); + size.op1()); } else if(size.id()==ID_mult && - size.operands().size()==2 && - size.op1().find(ID_C_c_sizeof_type).is_not_nil()) + size.operands().size()==2 && + size.op1().find(ID_C_c_sizeof_type).is_not_nil()) { object_type=array_typet( c_sizeof_type_rec(size.op1()), - size.op0()); + size.op0()); } else { typet tmp_type=c_sizeof_type_rec(size); - + if(tmp_type.is_not_nil()) { // Did the size get multiplied? @@ -111,15 +114,17 @@ exprt malloc_ssa( else { mp_integer elements=alloc_size/elem_size; - + if(elements*elem_size==alloc_size) - object_type=array_typet(tmp_type, from_integer(elements, size.type())); + object_type=array_typet( + tmp_type, + from_integer(elements, size.type())); } } } } - // the fall-back is to produce a byte-array + // the fall-back is to produce a byte-array if(object_type.is_nil()) object_type=array_typet(unsigned_char_type(), size); } @@ -127,10 +132,10 @@ exprt malloc_ssa( #ifdef DEBUG std::cout << "OBJECT_TYPE: " << from_type(ns, "", object_type) << std::endl; #endif - + // value symbolt value_symbol; - + value_symbol.base_name="dynamic_object"+suffix; value_symbol.name="ssa::"+id2string(value_symbol.base_name); value_symbol.is_lvalue=true; @@ -140,7 +145,7 @@ exprt malloc_ssa( symbol_table.add(value_symbol); address_of_exprt address_of; - + if(object_type.id()==ID_array) { address_of.type()=pointer_typet(value_symbol.type.subtype()); @@ -154,9 +159,9 @@ exprt malloc_ssa( address_of.op0()=value_symbol.symbol_expr(); address_of.type()=pointer_typet(value_symbol.type); } - + exprt result=address_of; - + if(result.type()!=code.type()) result=typecast_exprt(result, code.type()); @@ -164,49 +169,78 @@ exprt malloc_ssa( } -static void replace_malloc_rec(exprt &expr, - const std::string &suffix, - symbol_tablet &symbol_table, - const exprt &malloc_size, - unsigned &counter) +/*******************************************************************\ + +Function: replace_malloc_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +static void replace_malloc_rec( + exprt &expr, + const std::string &suffix, + symbol_tablet &symbol_table, + const exprt &malloc_size, + unsigned &counter) { if(expr.id()==ID_side_effect && to_side_effect_expr(expr).get_statement()==ID_malloc) { assert(!malloc_size.is_nil()); - expr.op0() = malloc_size; - - expr = malloc_ssa(to_side_effect_expr(expr),"$"+i2string(counter++)+suffix,symbol_table); + expr.op0()=malloc_size; + + expr=malloc_ssa(to_side_effect_expr(expr), + "$"+i2string(counter++)+suffix, symbol_table); } else - Forall_operands(it,expr) - replace_malloc_rec(*it,suffix,symbol_table,malloc_size,counter); + Forall_operands(it, expr) + replace_malloc_rec(*it, suffix, symbol_table, malloc_size, counter); } -void replace_malloc(goto_modelt &goto_model, - const std::string &suffix) +/*******************************************************************\ + +Function: replace_malloc + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void replace_malloc( + goto_modelt &goto_model, + const std::string &suffix) { - unsigned counter = 0; + unsigned counter=0; Forall_goto_functions(f_it, goto_model.goto_functions) { - exprt malloc_size = nil_exprt(); + exprt malloc_size=nil_exprt(); Forall_goto_program_instructions(i_it, f_it->second.body) { if(i_it->is_assign()) { - code_assignt &code_assign = to_code_assign(i_it->code); - if(code_assign.lhs().id()==ID_symbol) - { - // we have to propagate the malloc size + code_assignt &code_assign=to_code_assign(i_it->code); + if(code_assign.lhs().id()==ID_symbol) + { + // we have to propagate the malloc size // in order to get the object type - // TODO: this only works with inlining - const irep_idt &lhs_id = - to_symbol_expr(code_assign.lhs()).get_identifier(); - if(lhs_id == "malloc::malloc_size") - malloc_size = code_assign.rhs(); - } - replace_malloc_rec(code_assign.rhs(),suffix, - goto_model.symbol_table,malloc_size,counter); + // TODO: this only works with inlining, + // and btw, this is an ugly hack + std::string lhs_id= + id2string(to_symbol_expr(code_assign.lhs()).get_identifier()); + if(lhs_id=="malloc::malloc_size" || + lhs_id=="__builtin_alloca::alloca_size") + malloc_size=code_assign.rhs(); + } + replace_malloc_rec(code_assign.rhs(), suffix, + goto_model.symbol_table, malloc_size, counter); } } } diff --git a/src/ssa/malloc_ssa.h b/src/ssa/malloc_ssa.h index d9ef8d6b1..5ef4e5ed4 100644 --- a/src/ssa/malloc_ssa.h +++ b/src/ssa/malloc_ssa.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_MALLOC_SSA_H -#define CPROVER_MALLOC_SSA_H +#ifndef CPROVER_2LS_SSA_MALLOC_SSA_H +#define CPROVER_2LS_SSA_MALLOC_SSA_H #include #include @@ -17,10 +17,8 @@ exprt malloc_ssa( const std::string &suffix, symbol_tablet &); - -#if 1 -void replace_malloc(goto_modelt &goto_model, - const std::string &suffix); -#endif +void replace_malloc( + goto_modelt &goto_model, + const std::string &suffix); #endif diff --git a/src/ssa/replace_symbol_ext.cpp b/src/ssa/replace_symbol_ext.cpp deleted file mode 100644 index 7d77633aa..000000000 --- a/src/ssa/replace_symbol_ext.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/*******************************************************************\ - -Module: Modified expression replacement for constant propagator - -Author: Peter Schrammel - -\*******************************************************************/ - -#include -#include - -#include "replace_symbol_ext.h" - -/*******************************************************************\ - -Function: replace_symbol_extt::replace - - Inputs: - - Outputs: - - Purpose: does not replace object in address_of expressions - -\*******************************************************************/ - -bool replace_symbol_extt::replace(exprt &dest) const -{ - bool result=true; - - // first look at type - - if(have_to_replace(dest.type())) - if(!replace_symbolt::replace(dest.type())) - result=false; - - // now do expression itself - - if(!have_to_replace(dest)) - return result; - - if(dest.id()==ID_address_of) - { - const exprt &object = to_address_of_expr(dest).object(); - if(object.id()==ID_symbol) - { - expr_mapt::const_iterator it= - expr_map.find(object.get(ID_identifier)); - - if(it!=expr_map.end()) - return false; - } - } - else if(dest.id()==ID_symbol) - { - expr_mapt::const_iterator it= - expr_map.find(dest.get(ID_identifier)); - - if(it!=expr_map.end()) - { - dest=it->second; - return false; - } - } - - Forall_operands(it, dest) - if(!replace(*it)) - result=false; - - const irept &c_sizeof_type=dest.find(ID_C_c_sizeof_type); - - if(c_sizeof_type.is_not_nil() && - !replace_symbolt::replace(static_cast(dest.add(ID_C_c_sizeof_type)))) - result=false; - - const irept &va_arg_type=dest.find(ID_C_va_arg_type); - - if(va_arg_type.is_not_nil() && - !replace_symbolt::replace(static_cast(dest.add(ID_C_va_arg_type)))) - result=false; - - return result; -} diff --git a/src/ssa/replace_symbol_ext.h b/src/ssa/replace_symbol_ext.h deleted file mode 100644 index 2aac30957..000000000 --- a/src/ssa/replace_symbol_ext.h +++ /dev/null @@ -1,20 +0,0 @@ -/*******************************************************************\ - -Module: Modified expression replacement for constant propagator - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_REPLACE_SYMBOL_EXT_H -#define CPROVER_REPLACE_SYMBOL_EXT_H - -#include - -class replace_symbol_extt : public replace_symbolt -{ -public: - virtual bool replace(exprt &dest) const; -}; - -#endif diff --git a/src/ssa/simplify_ssa.cpp b/src/ssa/simplify_ssa.cpp index b6a5561c3..d3ab25979 100644 --- a/src/ssa/simplify_ssa.cpp +++ b/src/ssa/simplify_ssa.cpp @@ -12,7 +12,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: simplify_ssa +Function: simplify Inputs: @@ -30,7 +30,7 @@ void simplify(local_SSAt &ssa, const namespacet &ns) n_it++) { local_SSAt::nodet &node=*n_it; - + for(local_SSAt::nodet::equalitiest::iterator e_it=node.equalities.begin(); e_it!=node.equalities.end(); @@ -47,7 +47,7 @@ void simplify(local_SSAt &ssa, const namespacet &ns) { *c_it=simplify_expr(*c_it, ns); } - + for(local_SSAt::nodet::assertionst::iterator a_it=node.assertions.begin(); a_it!=node.assertions.end(); diff --git a/src/ssa/simplify_ssa.h b/src/ssa/simplify_ssa.h index 75693c8c1..1d4efe1bd 100644 --- a/src/ssa/simplify_ssa.h +++ b/src/ssa/simplify_ssa.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SIMPLIFY_SSA_H -#define CPROVER_SIMPLIFY_SSA_H +#ifndef CPROVER_2LS_SSA_SIMPLIFY_SSA_H +#define CPROVER_2LS_SSA_SIMPLIFY_SSA_H #include diff --git a/src/ssa/split_loopheads.cpp b/src/ssa/split_loopheads.cpp deleted file mode 100644 index 73c179d5f..000000000 --- a/src/ssa/split_loopheads.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include -#include - -#include "split_loopheads.h" - -/*******************************************************************\ - -Function: split_loopheads - -Inputs: - -Outputs: - -Purpose: insert skip at jump targets if they are goto, - assume or assert instructions - -\*******************************************************************/ - -void split_loopheads(goto_modelt &goto_model) -{ - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(!i_it->is_backwards_goto()) continue; - goto_programt::targett loophead = i_it->get_target(); - if(i_it->guard.is_true() && !loophead->is_assert()) continue; - - // inserts the skip - goto_programt::targett new_loophead = - f_it->second.body.insert_before(loophead); - new_loophead->make_skip(); - new_loophead->source_location=loophead->source_location; - new_loophead->function=i_it->function; - - // update jumps to loophead - for(std::set::iterator j_it = loophead->incoming_edges.begin(); - j_it != loophead->incoming_edges.end(); j_it++) - { - if(!(*j_it)->is_goto() || (*j_it)->get_target()!=loophead) continue; - (*j_it)->targets.clear(); - (*j_it)->targets.push_back(new_loophead); - } - } - } -} diff --git a/src/ssa/split_loopheads.h b/src/ssa/split_loopheads.h deleted file mode 100644 index c3a220c20..000000000 --- a/src/ssa/split_loopheads.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CPROVER_SPLIT_LOOPHEADS_H -#define CPROVER_SPLIT_LOOPHEADS_H - -#include - -void split_loopheads(goto_modelt &goto_model); - -#endif diff --git a/src/ssa/ssa_build_goto_trace.cpp b/src/ssa/ssa_build_goto_trace.cpp index a257e3134..22891beff 100644 --- a/src/ssa/ssa_build_goto_trace.cpp +++ b/src/ssa/ssa_build_goto_trace.cpp @@ -8,9 +8,14 @@ Author: Daniel Kroening, Peter Schrammel #include #include +#include +#include +#include #include "ssa_build_goto_trace.h" +#define TERM_CEX 1 + /*******************************************************************\ Function: ssa_build_goto_tracet::finalize_lhs @@ -31,20 +36,20 @@ exprt ssa_build_goto_tracet::finalize_lhs(const exprt &src) { index_exprt tmp=to_index_expr(src); tmp.array()=finalize_lhs(tmp.array()); - exprt index = unwindable_local_SSA.read_rhs(tmp.index(),current_pc); + exprt index=unwindable_local_SSA.read_rhs(tmp.index(), current_pc); tmp.index()=simplify_expr(prop_conv.get(index), unwindable_local_SSA.ns); return tmp; } else if(src.id()==ID_dereference) { address_of_exprt tmp1(src); - exprt tmp2 = unwindable_local_SSA.read_rhs(tmp1,current_pc); + exprt tmp2=unwindable_local_SSA.read_rhs(tmp1, current_pc); exprt tmp3=prop_conv.get(tmp2); exprt tmp4=tmp3; if(tmp4.id()==ID_constant && tmp4.type().id()==ID_pointer && tmp4.operands().size()==1 && tmp4.op0().id()==ID_address_of) tmp4=to_address_of_expr(tmp4.op0()).object(); - //TODO: investigate: produces nil sometimes + // TODO: investigate: produces nil sometimes return tmp4; } else if(src.id()==ID_member) @@ -59,6 +64,44 @@ exprt ssa_build_goto_tracet::finalize_lhs(const exprt &src) /*******************************************************************\ +Function: ssa_build_goto_tracet::can_convert_ssa_expr + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_build_goto_tracet::can_convert_ssa_expr(const exprt &expr) +{ + if(expr.id()==ID_member) + { + const member_exprt &member=to_member_expr(expr); + can_convert_ssa_expr(member.struct_op()); + return true; + } + else if(expr.id()==ID_index) + { + const index_exprt &index=to_index_expr(expr); + can_convert_ssa_expr(index.array()); + + mp_integer idx; + if(to_integer(to_constant_expr(index.index()), idx)) + return false; + return true; + } + else if(expr.id()==ID_symbol) + { + return true; + } + + return false; +} + +/*******************************************************************\ + Function: ssa_build_goto_tracet::record_step Inputs: @@ -73,7 +116,7 @@ bool ssa_build_goto_tracet::record_step( goto_tracet &goto_trace, unsigned &step_nr) { - bool taken = true; + bool taken=true; goto_trace_stept step; step.pc=current_pc; step.step_nr=step_nr; @@ -101,98 +144,142 @@ bool ssa_build_goto_tracet::record_step( goto_trace.add_step(step); step_nr++; break; - + case GOTO: - { - exprt cond=current_pc->guard; - exprt cond_read = unwindable_local_SSA.read_rhs(cond,current_pc); - exprt cond_value=simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); - step.type=goto_trace_stept::GOTO; - step.cond_expr = cond_value; //cond - step.cond_value = cond_value.is_true(); + { + exprt cond=current_pc->guard; + exprt cond_read=unwindable_local_SSA.read_rhs(cond, current_pc); + unwindable_local_SSA.rename(cond_read, current_pc); + exprt cond_value= + simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); + step.type=goto_trace_stept::GOTO; + step.cond_expr=cond_value; // cond #if 0 - std::cout << "COND " << from_expr(unwindable_local_SSA.ns, "", cond) - << ": " << from_expr(unwindable_local_SSA.ns, "", cond_read) - << " == " << cond_value.is_true() << std::endl; + assert(cond_value.is_true() || cond_value.is_false()); #endif - if(step.cond_value) - { - goto_trace.add_step(step); - step_nr++; - } - else - taken = false; + step.cond_value=cond_value.is_true(); +#if 0 + std::cout << "COND " << from_expr(unwindable_local_SSA.ns, "", cond) + << ": (" << from_expr(unwindable_local_SSA.ns, "", cond_read) + << ")==" << cond_value.is_true() << std::endl; +#endif + + taken=step.cond_value; + + if(taken) + { + goto_trace.add_step(step); + step_nr++; } - break; + } + break; case ASSERT: + { + // failed or not? + exprt cond=current_pc->guard; + exprt cond_read=unwindable_local_SSA.read_rhs(cond, current_pc); + unwindable_local_SSA.rename(cond_read, current_pc); + exprt cond_value= + simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); + if(cond_value.is_false()) { - // failed or not? - exprt cond=current_pc->guard; - exprt cond_read=unwindable_local_SSA.read_rhs(cond,current_pc); - exprt cond_value=simplify_expr(prop_conv.get(cond_read), unwindable_local_SSA.ns); - if(cond_value.is_false()) - { - step.type=goto_trace_stept::ASSERT; - step.comment=id2string(current_pc->source_location.get_comment()); - step.cond_expr=cond; - step.cond_value=false; - goto_trace.add_step(step); - step_nr++; - } + step.type=goto_trace_stept::ASSERT; + step.comment=id2string(current_pc->source_location.get_comment()); + step.cond_expr=cond; + step.cond_value=false; + goto_trace.add_step(step); + step_nr++; } - break; + } + break; case ATOMIC_BEGIN: case ATOMIC_END: case DECL: case DEAD: break; // ignore - + case ASSIGN: - { - const code_assignt &code_assign= - to_code_assign(current_pc->code); - exprt rhs_ssa=unwindable_local_SSA.read_rhs(code_assign.rhs(),current_pc); - exprt rhs_value=prop_conv.get(rhs_ssa); - exprt rhs_simplified=simplify_expr(rhs_value, unwindable_local_SSA.ns); - exprt lhs_ssa=finalize_lhs(code_assign.lhs()); - exprt lhs_simplified=simplify_expr(lhs_ssa, unwindable_local_SSA.ns); - - step.type=goto_trace_stept::ASSIGNMENT; - step.full_lhs=lhs_simplified; - step.full_lhs_value=rhs_simplified; - if(lhs_simplified.id()==ID_symbol) - { - //filter out internal stuff - if(id2string(to_symbol_expr(lhs_simplified).get_identifier()).find("#") - != std::string::npos) - break; - //filter out undetermined values - if(rhs_simplified.id()!=ID_constant) - break; - step.lhs_object = ssa_exprt(lhs_simplified); - step.lhs_object_value=rhs_simplified; - } - if(step.lhs_object.is_nil()) - break; + { + if(has_prefix(id2string(current_pc->function), CPROVER_PREFIX)) + break; + + const code_assignt &code_assign= + to_code_assign(current_pc->code); + + exprt rhs_ssa=unwindable_local_SSA.read_rhs(code_assign.rhs(), current_pc); + unwindable_local_SSA.rename(rhs_ssa, current_pc); + exprt rhs_value=prop_conv.get(rhs_ssa); + exprt rhs_simplified=simplify_expr(rhs_value, unwindable_local_SSA.ns); + exprt lhs_ssa=finalize_lhs(code_assign.lhs()); + exprt lhs_simplified=simplify_expr(lhs_ssa, unwindable_local_SSA.ns); + #if 0 - std::cout << "ASSIGN " << from_expr(unwindable_local_SSA.ns, "", code_assign) - << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) - << " == " << from_expr(unwindable_local_SSA.ns, - "", step.full_lhs_value) << std::endl; + std::cout << "ASSIGN " + << from_expr(unwindable_local_SSA.ns, "", code_assign) + << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) + << "==" << from_expr(unwindable_local_SSA.ns, "", rhs_simplified) + << std::endl; #endif - goto_trace.add_step(step); - step_nr++; + step.type=goto_trace_stept::ASSIGNMENT; + step.full_lhs=lhs_simplified; + step.full_lhs_value=rhs_simplified; + + // filter out internal stuff + if(lhs_simplified.id()==ID_symbol) + { + std::string identifier=id2string(lhs_simplified.get(ID_identifier)); + if(has_prefix(identifier, CPROVER_PREFIX)) + break; + if(identifier.find("#")!=std::string::npos) + break; + if(identifier.find("$")!=std::string::npos) + break; + if(identifier.find("'")!=std::string::npos) + break; } - break; + + if(!can_convert_ssa_expr(lhs_simplified)) + break; + + step.lhs_object=ssa_exprt(lhs_simplified); + step.lhs_object_value=rhs_simplified; + + // skip unresolved lhs + if(step.lhs_object.is_nil()) + break; + + // skip strings (for SV-COMP) + if(step.lhs_object.type().id()==ID_pointer && + to_pointer_type(step.lhs_object.type()).subtype().id()==ID_signedbv) + break; + + // skip undetermined rhs + find_symbols_sett rhs_symbols; + find_symbols(rhs_simplified, rhs_symbols); + if(!rhs_symbols.empty() || rhs_simplified.id()==ID_nondet_symbol) + break; + +#if 0 + std::cout << "ASSIGNMENT ADDED: " + << from_expr(unwindable_local_SSA.ns, "", code_assign) + << ": " << from_expr(unwindable_local_SSA.ns, "", rhs_ssa) + << "==" + << from_expr(unwindable_local_SSA.ns, "", step.full_lhs_value) + << std::endl; +#endif + goto_trace.add_step(step); + step_nr++; + } + break; case OTHER: step.type=goto_trace_stept::LOCATION; goto_trace.add_step(step); step_nr++; break; - + case NO_INSTRUCTION_TYPE: assert(false); break; @@ -219,49 +306,77 @@ void ssa_build_goto_tracet::operator()( return; current_pc=unwindable_local_SSA.goto_function.body.instructions.begin(); - unwindable_local_SSA.current_unwindings.clear(); - unsigned last_level = 0; + unwindable_local_SSA.current_unwindings.clear(); + unsigned last_level=0; unsigned step_nr=1; - +#if TERM_CEX + bool stop_next=false; +#endif + while(current_pc!=unwindable_local_SSA.goto_function.body.instructions.end()) { -#if 0 - if(current_pc->is_assign()) - { - const code_assignt &code_assign = to_code_assign(current_pc->code); - if(code_assign.rhs().id()==ID_side_effect && - to_side_effect_expr(code_assign.rhs()).get_statement()==ID_nondet) - { - current_pc++; - continue; - } - } -#endif - unsigned current_level = + unsigned current_level= unwindable_local_SSA.loop_hierarchy_level[current_pc].level; - int level_diff = current_level - last_level; - last_level = current_level; - if(level_diff!=0) - unwindable_local_SSA.decrement_unwindings(level_diff); + long level_diff=(long)current_level-(long)last_level; + #if 0 std::cout << "location: " << current_pc->location_number << std::endl; + std::cout << "current_level: " << current_level << std::endl; + std::cout << "last_level: " << last_level << std::endl; std::cout << "level_diff: " << level_diff << std::endl; - std::cout << "unwindings: " - << unwindable_local_SSA.odometer_to_string(unwindable_local_SSA.current_unwindings,100) << std::endl; #endif - bool taken = record_step(goto_trace, step_nr); - + last_level=current_level; + // we enter a loop if >0 and exit a loop if <0 + if(level_diff!=0l) + { + unwindable_local_SSA.decrement_unwindings(level_diff); + +#if 0 + std::cout << "loop-head : " << current_pc->location_number << std::endl; + std::cout << "unwindings: " + << unwindable_local_SSA.odometer_to_string( + unwindable_local_SSA.current_unwindings, 100) + << std::endl; +#endif + } + + bool taken=record_step(goto_trace, step_nr); + if(!goto_trace.steps.empty() && goto_trace.steps.back().is_assert()) break; // done - + +#if 0 + std::cout << "is_goto: " << current_pc->is_goto() << std::endl; + std::cout << "is_backwards_goto: " << current_pc->is_backwards_goto() + << std::endl; + std::cout << "taken: " << taken << std::endl; +#endif + // get successor if(current_pc->is_goto() && taken) { +#if TERM_CEX + if(termination && stop_next) + { + break; + } +#endif if(current_pc->is_backwards_goto()) { - unwindable_local_SSA.decrement_unwindings(0); + // we de-(!)-crement the unwinding counter + unwindable_local_SSA.decrement_unwindings(0); +#if 0 + std::cout << "loop-end : " << current_pc->location_number << std::endl; + std::cout << "unwindings: " + << unwindable_local_SSA.odometer_to_string( + unwindable_local_SSA.current_unwindings, 100) + << std::endl; +#endif +#if TERM_CEX + stop_next=true; +#endif } current_pc=current_pc->get_target(); } diff --git a/src/ssa/ssa_build_goto_trace.h b/src/ssa/ssa_build_goto_trace.h index dbace61e2..435d0af35 100644 --- a/src/ssa/ssa_build_goto_trace.h +++ b/src/ssa/ssa_build_goto_trace.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SSA_BUILD_GOTO_TRACE_H -#define CPROVER_SSA_BUILD_GOTO_TRACE_H +#ifndef CPROVER_2LS_SSA_SSA_BUILD_GOTO_TRACE_H +#define CPROVER_2LS_SSA_SSA_BUILD_GOTO_TRACE_H #include #include @@ -15,14 +15,17 @@ Author: Daniel Kroening, Peter Schrammel #include "local_ssa.h" #include "unwindable_local_ssa.h" -class ssa_build_goto_tracet { +class ssa_build_goto_tracet +{ public: ssa_build_goto_tracet( unwindable_local_SSAt &_unwindable_local_SSA, - const prop_convt &_prop_conv) - : - unwindable_local_SSA(_unwindable_local_SSA), - prop_conv(_prop_conv) + const prop_convt &_prop_conv, + bool _termination=false) + : + unwindable_local_SSA(_unwindable_local_SSA), + prop_conv(_prop_conv), + termination(_termination) {} void operator()(goto_tracet &); @@ -31,8 +34,10 @@ class ssa_build_goto_tracet { unwindable_local_SSAt &unwindable_local_SSA; const prop_convt &prop_conv; goto_programt::const_targett current_pc; + bool termination; exprt finalize_lhs(const exprt &src); + bool can_convert_ssa_expr(const exprt &expr); bool record_step( goto_tracet &goto_trace, diff --git a/src/summarizer/ssa_db.cpp b/src/ssa/ssa_db.cpp similarity index 93% rename from src/summarizer/ssa_db.cpp rename to src/ssa/ssa_db.cpp index 8e77f0cf3..fbbb80ef3 100644 --- a/src/summarizer/ssa_db.cpp +++ b/src/ssa/ssa_db.cpp @@ -2,7 +2,7 @@ Module: Storage for Function SSAs -Author: Daniel Kroening, kroening@kroening.com +Author: Peter Schrammel \*******************************************************************/ diff --git a/src/ssa/ssa_db.h b/src/ssa/ssa_db.h new file mode 100644 index 000000000..7b4ee9237 --- /dev/null +++ b/src/ssa/ssa_db.h @@ -0,0 +1,97 @@ +/*******************************************************************\ + +Module: Storage for Function SSAs + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_DB_H +#define CPROVER_2LS_SSA_SSA_DB_H + +#include + +#include +#include +#include + +#include "../ssa/local_ssa.h" +#include "../ssa/ssa_inliner.h" +#include "../ssa/ssa_dependency_graph.h" + +class ssa_inlinert; +class ssa_dependency_grapht; + +class ssa_dbt +{ +public: + typedef irep_idt function_namet; + typedef std::map functionst; + typedef std::map depgrapht; + typedef std::map solverst; + + explicit ssa_dbt(const optionst &_options): + options(_options) + { + } + + ~ssa_dbt() + { + for(auto &item : store) + delete item.second; + for(auto &item : the_solvers) + delete item.second; + } + + inline local_SSAt &get(const function_namet &function_name) const + { + return *store.at(function_name); + } + + ssa_dependency_grapht &get_depgraph(const function_namet &function_name) const + { + return *depgraph_store.at(function_name); + } + + inline incremental_solvert &get_solver(const function_namet &function_name) + { + solverst::iterator it=the_solvers.find(function_name); + if(it!=the_solvers.end()) + return *(it->second); + + the_solvers[function_name]= + incremental_solvert::allocate( + store.at(function_name)->ns, + options.get_bool_option("refine")); + return *the_solvers.at(function_name); + } + + inline functionst &functions() { return store; } + inline solverst &solvers() { return the_solvers; } + + inline bool exists(const function_namet &function_name) const + { + return store.find(function_name)!=store.end(); + } + + inline void create( + const function_namet &function_name, + const goto_functionst::goto_functiont &goto_function, + const namespacet &ns) + { + store[function_name]=new unwindable_local_SSAt(goto_function, ns); + } + + void depgraph_create(const function_namet &function_name, + const namespacet &ns, + ssa_inlinert &ssa_inliner, + bool entry); + +protected: + const optionst &options; + functionst store; + depgrapht depgraph_store; + solverst the_solvers; +}; + +#endif diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp index 651563082..c737a7ed7 100644 --- a/src/ssa/ssa_dependency_graph.cpp +++ b/src/ssa/ssa_dependency_graph.cpp @@ -84,11 +84,16 @@ void ssa_dependency_grapht::create(const local_SSAt &SSA, ssa_inlinert &ssa_inli { bool enabled=true; const irep_idt &enable=to_symbol_expr(node.enabling_expr).get_identifier(); - for(size_t i=0; i::const_iterator it=SSA.enabling_exprs.begin(); + it!=SSA.enabling_exprs.end(); ++it) { - if(SSA.enabling_exprs[i].id()==ID_not) + //if(SSA.enabling_exprs[i].id()==ID_not) + if((*it).id()==ID_not) { - if(to_symbol_expr(SSA.enabling_exprs[i].op0()).get_identifier()==enable) + //if(to_symbol_expr(SSA.enabling_exprs[i].op0()).get_identifier()==enable) + if(to_symbol_expr((*it).op0()).get_identifier()==enable) { enabled=false; break; diff --git a/src/ssa/ssa_dependency_graph.h b/src/ssa/ssa_dependency_graph.h index 1acb50978..e0617c8de 100644 --- a/src/ssa/ssa_dependency_graph.h +++ b/src/ssa/ssa_dependency_graph.h @@ -1,13 +1,13 @@ -#ifndef CPROVER_DELTACHECK_SSA_DEPENDENCY_GRAPH_H -#define CPROVER_DELTACHECK_SSA_DEPENDENCY_GRAPH_H +#ifndef CPROVER_2LS_SSA_DEPENDENCY_GRAPH_H +#define CPROVER_2LS_SSA_DEPENDENCY_GRAPH_H #include #include -#include "../summarizer/ssa_db.h" -#include "ssa_inliner.h" -#include "local_ssa.h" +#include "../ssa/ssa_db.h" +#include "../ssa/ssa_inliner.h" +#include "../ssa/local_ssa.h" class ssa_inlinert; class ssa_dbt; diff --git a/src/ssa/ssa_dereference.cpp b/src/ssa/ssa_dereference.cpp index 27db94362..0425e7d54 100644 --- a/src/ssa/ssa_dereference.cpp +++ b/src/ssa/ssa_dereference.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -56,7 +56,7 @@ exprt lift_if(const exprt &src) if_expr.true_case()=src; if_expr.true_case().op0()=previous; } - + if(if_expr.false_case().is_not_nil()) { exprt previous=if_expr.false_case(); @@ -103,22 +103,26 @@ bool ssa_may_alias( return to_symbol_expr(e1).get_identifier()== to_symbol_expr(e2).get_identifier(); } - + // __CPROVER symbols if(e1.id()==ID_symbol && - has_prefix(id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) + has_prefix( + id2string(to_symbol_expr(e1).get_identifier()), CPROVER_PREFIX)) return false; if(e2.id()==ID_symbol && - has_prefix(id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) + has_prefix( + id2string(to_symbol_expr(e2).get_identifier()), CPROVER_PREFIX)) return false; if(e1.id()==ID_symbol && - has_suffix(id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) + has_suffix( + id2string(to_symbol_expr(e1).get_identifier()), "#return_value")) return false; if(e2.id()==ID_symbol && - has_suffix(id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) + has_suffix( + id2string(to_symbol_expr(e2).get_identifier()), "#return_value")) return false; // Both member? @@ -127,11 +131,11 @@ bool ssa_may_alias( { const member_exprt &m1=to_member_expr(e1); const member_exprt &m2=to_member_expr(e2); - + // same component? if(m1.get_component_name()!=m2.get_component_name()) return false; - + return ssa_may_alias(m1.struct_op(), m2.struct_op(), ns); } @@ -147,33 +151,35 @@ bool ssa_may_alias( const typet &t1=ns.follow(e1.type()); const typet &t2=ns.follow(e2.type()); - + // If one is an array and the other not, consider the elements if(t1.id()==ID_array && t2.id()!=ID_array) - if(ssa_may_alias(index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) + if(ssa_may_alias( + index_exprt(e1, gen_zero(index_type()), t1.subtype()), e2, ns)) return true; - + if(t2.id()==ID_array && t2.id()!=ID_array) - if(ssa_may_alias(e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) + if(ssa_may_alias( + e1, index_exprt(e2, gen_zero(index_type()), t2.subtype()), ns)) return true; - + // Pointers only alias with other pointers, // which is a restriction. if(t1.id()==ID_pointer) return t2.id()==ID_pointer; - + if(t2.id()==ID_pointer) return t1.id()==ID_pointer; - + // Is one a scalar pointer? if(e1.id()==ID_dereference && (t1.id()==ID_signedbv || t1.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; - + if(e2.id()==ID_dereference && (t2.id()==ID_signedbv || t2.id()==ID_unsignedbv || t1.id()==ID_floatbv)) return true; - + // Is one a pointer? if(e1.id()==ID_dereference || e2.id()==ID_dereference) @@ -185,8 +191,8 @@ bool ssa_may_alias( { return true; } - - // should consider further options, e.g., struct prefixes + + // should consider further options, e.g., struct prefixes return false; } @@ -211,30 +217,31 @@ exprt ssa_alias_guard( const namespacet &ns) { exprt a1=address_canonizer(address_of_exprt(e1), ns); - //TODO: We should compare 'base' pointers here because + // TODO: We should compare 'base' pointers here because // we have a higher chance that there was no pointer arithmetic // on the base pointer than that the result of the pointer // arithmetic points to a base pointer. // The following hack does that: - if(a1.id()==ID_plus) a1 = a1.op0(); - + if(a1.id()==ID_plus) + a1=a1.op0(); + exprt a2=address_canonizer(address_of_exprt(e2), ns); - + // in some cases, we can use plain address equality, // as we assume well-aligned-ness mp_integer size1=pointer_offset_size(e1.type(), ns); mp_integer size2=pointer_offset_size(e2.type(), ns); - + if(size1>=size2) { exprt lhs=a1; exprt rhs=a2; if(ns.follow(rhs.type())!=ns.follow(lhs.type())) rhs=typecast_exprt(rhs, lhs.type()); - + return equal_exprt(lhs, rhs); } - + return same_object(a1, a2); } @@ -264,7 +271,7 @@ exprt ssa_alias_value( exprt a1=address_canonizer(address_of_exprt(e1), ns); exprt a2=address_canonizer(address_of_exprt(e2), ns); - + exprt offset1=pointer_offset(a1); // array index possible? @@ -279,7 +286,8 @@ exprt ssa_alias_value( return index_exprt(e2, offset1, e1.type()); else if(element_size>1) { - exprt index=div_exprt(offset1, from_integer(element_size, offset1.type())); + exprt index= + div_exprt(offset1, from_integer(element_size, offset1.type())); return index_exprt(e2, index, e1.type()); } } @@ -287,8 +295,8 @@ exprt ssa_alias_value( byte_extract_exprt byte_extract(byte_extract_id(), e1.type()); byte_extract.op()=e2; byte_extract.offset()=offset1; - - return byte_extract; + + return byte_extract; } /*******************************************************************\ @@ -304,15 +312,16 @@ Function: dereference_rec \*******************************************************************/ exprt dereference_rec( - const exprt &src, - const ssa_value_domaint &ssa_value_domain, - const std::string &nondet_prefix, - const namespacet &ns) + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) { if(src.id()==ID_dereference) { const exprt &pointer=to_dereference_expr(src).pointer(); - exprt pointer_deref=dereference(pointer, ssa_value_domain, nondet_prefix, ns); + exprt pointer_deref= + dereference(pointer, ssa_value_domain, nondet_prefix, ns); // We use the identifier produced by // local_SSAt::replace_side_effects_rec @@ -337,25 +346,27 @@ exprt dereference_rec( else if(src.id()==ID_member) { member_exprt tmp=to_member_expr(src); - tmp.struct_op()=dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); - + tmp.struct_op()= + dereference_rec(tmp.struct_op(), ssa_value_domain, nondet_prefix, ns); + #ifdef DEBUG std::cout << "dereference_rec tmp: " << from_expr(ns, "", tmp) << '\n'; #endif if(tmp.struct_op().is_nil()) return nil_exprt(); - + return lift_if(tmp); } else if(src.id()==ID_address_of) { address_of_exprt tmp=to_address_of_expr(src); - tmp.object()=dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); + tmp.object()= + dereference_rec(tmp.object(), ssa_value_domain, nondet_prefix, ns); if(tmp.object().is_nil()) return nil_exprt(); - + return lift_if(tmp); } else @@ -380,10 +391,10 @@ Function: dereference \*******************************************************************/ exprt dereference( - const exprt &src, - const ssa_value_domaint &ssa_value_domain, - const std::string &nondet_prefix, - const namespacet &ns) + const exprt &src, + const ssa_value_domaint &ssa_value_domain, + const std::string &nondet_prefix, + const namespacet &ns) { #ifdef DEBUG std::cout << "dereference src: " << from_expr(ns, "", src) << '\n'; diff --git a/src/ssa/ssa_dereference.h b/src/ssa/ssa_dereference.h index 2494d5973..c020df6e5 100644 --- a/src/ssa/ssa_dereference.h +++ b/src/ssa/ssa_dereference.h @@ -6,18 +6,14 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_ALIASING_H -#define CPROVER_SSA_ALIASING_H +#ifndef CPROVER_2LS_SSA_SSA_DEREFERENCE_H +#define CPROVER_2LS_SSA_SSA_DEREFERENCE_H #include #include #include "ssa_value_set.h" -//bool ssa_may_alias(const exprt &, const exprt &, const namespacet &); -//exprt ssa_alias_guard(const exprt &, const exprt &, const namespacet &); -//exprt ssa_alias_value(const exprt &, const exprt &, const namespacet &); - exprt dereference( const exprt &, const ssa_value_domaint &, diff --git a/src/ssa/ssa_domain.cpp b/src/ssa/ssa_domain.cpp index 0036e5f6f..a9848e8af 100644 --- a/src/ssa/ssa_domain.cpp +++ b/src/ssa/ssa_domain.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -54,7 +54,7 @@ void ssa_domaint::output( // this maps source -> def out << "PHI " << p_it->first << ": " << (*n_it).second - << " from " + << " from " << (*n_it).first << "\n"; } } @@ -102,7 +102,7 @@ void ssa_domaint::transform( const irep_idt &id=code_dead.get_identifier(); def_map.erase(id); } - + // update source in all defs for(def_mapt::iterator d_it=def_map.begin(); d_it!=def_map.end(); d_it++) @@ -127,7 +127,7 @@ bool ssa_domaint::merge( locationt to) { bool result=false; - + // should traverse both maps simultaneously for(def_mapt::const_iterator d_it_b=b.def_map.begin(); @@ -135,15 +135,15 @@ bool ssa_domaint::merge( d_it_b++) { const irep_idt &id=d_it_b->first; - + // check if we have a phi node for 'id' - + phi_nodest::iterator p_it=phi_nodes.find(id); if(p_it!=phi_nodes.end()) { // yes, simply add to existing phi node loc_def_mapt &phi_node=p_it->second; - phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; + phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; // doesn't get propagated, don't set result to 'true' continue; } @@ -190,7 +190,7 @@ bool ssa_domaint::merge( phi_node[d_it_a->second.source->location_number]=d_it_a->second.def; phi_node[d_it_b->second.source->location_number]=d_it_b->second.def; - + // This phi node is now the new source. d_it_a->second.def.loc=to; d_it_a->second.def.kind=deft::PHI; @@ -203,7 +203,7 @@ bool ssa_domaint::merge( #endif } } - + return result; } @@ -229,7 +229,7 @@ void ssa_ait::initialize(const goto_functionst::goto_functiont &goto_function) { locationt e=goto_function.body.instructions.begin(); ssa_domaint &entry=operator[](e); - + #if 0 // parameters const code_typet::parameterst ¶meters=goto_function.type.parameters(); @@ -243,7 +243,7 @@ void ssa_ait::initialize(const goto_functionst::goto_functiont &goto_function) entry.def_map[id].def.kind=ssa_domaint::deft::INPUT; } #endif - + for(ssa_objectst::objectst::const_iterator o_it=assignments.ssa_objects.objects.begin(); o_it!=assignments.ssa_objects.objects.end(); diff --git a/src/ssa/ssa_domain.h b/src/ssa/ssa_domain.h index 5f3127c70..679fdbc59 100644 --- a/src/ssa/ssa_domain.h +++ b/src/ssa/ssa_domain.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_DOMAIN_H -#define CPROVER_SSA_DOMAIN_H +#ifndef CPROVER_2LS_SSA_SSA_DOMAIN_H +#define CPROVER_2LS_SSA_SSA_DOMAIN_H #include @@ -23,13 +23,13 @@ class ssa_domaint:public ai_domain_baset typedef enum { INPUT, ASSIGNMENT, PHI } kindt; kindt kind; locationt loc; - + inline bool is_input() const { return kind==INPUT; } inline bool is_assignment() const { return kind==ASSIGNMENT; } inline bool is_phi() const { return kind==PHI; } }; - friend inline bool operator == (const deft &a, const deft &b) + friend inline bool operator==(const deft &a, const deft &b) { return a.kind==b.kind && a.loc==b.loc; } @@ -59,10 +59,10 @@ class ssa_domaint:public ai_domain_baset { return out << d.def << " from " << d.source->location_number; } - + typedef std::map def_mapt; def_mapt def_map; - + // The phi nodes map identifiers to incoming branches: // map from source to definition. typedef std::map loc_def_mapt; @@ -74,7 +74,7 @@ class ssa_domaint:public ai_domain_baset locationt to, ai_baset &ai, const namespacet &ns); - + virtual void output( std::ostream &out, const ai_baset &ai, @@ -96,7 +96,7 @@ class ssa_ait:public ait protected: const assignmentst &assignments; - + friend class ssa_domaint; // The overload below is needed to make the entry point get a source diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 735dfec87..71bd8ac6f 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -2,7 +2,7 @@ Module: SSA Inliner -Author: Peter Schrammel, Madhukar Kumar +Author: Peter Schrammel \*******************************************************************/ @@ -24,11 +24,11 @@ Function: ssa_inlinert::get_guard_binding \*******************************************************************/ void ssa_inlinert::get_guard_binding( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - exprt &guard_binding, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter) { exprt callee_guard, caller_guard; callee_guard = fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); @@ -51,13 +51,13 @@ Function: ssa_inlinert::get_bindings \*******************************************************************/ void ssa_inlinert::get_bindings( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - exprt::operandst &bindings_in, - exprt::operandst &bindings_out, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter) { //getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; @@ -81,144 +81,71 @@ void ssa_inlinert::get_bindings( #endif //equalities for arguments + //bindings_in.push_back(get_replace_params(SSA.params,*f_it)); get_replace_params(SSA,fSSA.params,n_it,*f_it,bindings_in,counter); //equalities for globals_in + //bindings_in.push_back(get_replace_globals_in(SSA.globals_in,cs_globals_in)); + + //get_replace_globals_in(fSSA.globals_in,cs_globals_in,bindings_in,counter); get_replace_globals_in(fSSA.globals_in,*f_it,cs_globals_in,bindings_in,counter); //equalities for globals out (including unmodified globals) + //bindings_out.push_back(get_replace_globals_out(SSA.globals_out,cs_globals_in,cs_globals_out)); + + //get_replace_globals_out(fSSA.globals_out,cs_globals_in,cs_globals_out,bindings_out,counter); get_replace_globals_out(fSSA.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings_out,counter); } /*******************************************************************\ -Function: ssa_inlinert::get_inlined +Function: ssa_inlinert::get_summary Inputs: - Outputs: - - Purpose: get inlined function call + Outputs: + Purpose: get summary for function call \*******************************************************************/ -bool ssa_inlinert::get_inlined( +void ssa_inlinert::get_summary( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, + local_SSAt::nodet::function_callst::const_iterator f_it, + const summaryt &summary, + bool forward, + exprt::operandst &summaries, exprt::operandst &bindings, - assertion_mapt &assertion_map, int counter, bool error_summ) { - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - const local_SSAt &fSSA = ssa_db.get(fname); - - bool assertion_flag = get_summaries(fSSA, - summaryt::call_sitet(fSSA.goto_function.body.instructions.end()), - forward,assert_summaries,noassert_summaries, - bindings,assertion_map,error_summ); - - //bindings - exprt guard_binding; - get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); - bindings.push_back(guard_binding); - get_bindings(SSA,fSSA,n_it,f_it,bindings,bindings,counter); - - bool first_equality = true; - for(local_SSAt::nodest::const_iterator n_it = fSSA.nodes.begin(); - n_it != fSSA.nodes.end(); n_it++) + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + if(forward) { - const local_SSAt::nodet &fnode=*n_it; - - for(local_SSAt::nodet::equalitiest::const_iterator e_it = - fnode.equalities.begin(); e_it!=fnode.equalities.end(); e_it++) - { - // unless lhs starts with "ssa::guard" and rhs is true - // because that one is replaced by the guard binding - const equal_exprt &e = to_equal_expr(*e_it); - const exprt &lhs = e.lhs(); const exprt &rhs = e.rhs(); - std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if((var_string.substr(0,11) == "ssa::$guard") && - rhs.is_true() && first_equality) - { - first_equality = false; - } - else - { - noassert_summaries.push_back(*e_it); - rename(noassert_summaries.back(), counter); - } - } - for(local_SSAt::nodet::constraintst::const_iterator c_it = - fnode.constraints.begin(); c_it!=fnode.constraints.end(); c_it++) - { - noassert_summaries.push_back(*c_it); - rename(noassert_summaries.back(), counter); - } - for(local_SSAt::nodet::assertionst::const_iterator a_it = - fnode.assertions.begin(); a_it!=fnode.assertions.end(); a_it++) - { -#if 0 - assert_summaries.push_back(*a_it); - rename(assert_summaries.back(), counter); -#endif - assertion_flag = true; - } + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + } + else + { + SSA.get_globals(loc, cs_globals_out); + SSA.get_globals(loc, cs_globals_in, false); } - - return assertion_flag; -} - - -/*******************************************************************\ - -Function: ssa_inlinert::get_summary - - Inputs: - - Outputs: - - Purpose: get summary for non-inlined function calls - -\*******************************************************************/ - -bool ssa_inlinert::get_summary( - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - int counter, - bool error_summ) -{ - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - const summaryt &summary = summary_db.get(fname); - - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); #if 0 std::cout << "cs_globals_in: "; - for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); - it != cs_globals_in.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; std::cout << "cs_globals_out: "; - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; #endif @@ -226,50 +153,54 @@ bool ssa_inlinert::get_summary( get_replace_params(SSA,summary.params,n_it,*f_it,bindings,counter); //equalities for globals_in - get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); + if(forward){ + //get_replace_globals_in(summary.globals_in,cs_globals_in,bindings,counter); + get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); + } + else{ + //get_replace_globals_in(summary.globals_out,cs_globals_out,bindings,counter); + get_replace_globals_in(summary.globals_out,*f_it,cs_globals_out,bindings,counter); + } //constraints for transformer exprt transformer; if(error_summ) - { - // update transformer using the error_summaries map - summaryt::call_sitet call_site(loc); - summaryt::error_summariest::const_iterator e_it = - summary.error_summaries.find(call_site); - if(e_it != summary.error_summaries.end() && - !e_it->second.is_nil()) - transformer = e_it->second; - else - transformer = true_exprt(); - } + { + // update transformer using the error_summaries map + summaryt::call_sitet call_site(loc); + summaryt::error_summariest::const_iterator e_it = + summary.error_summaries.find(call_site); + if(e_it != summary.error_summaries.end() && + !e_it->second.is_nil()) + transformer = e_it->second; + else + transformer = true_exprt(); + } else - { - if(forward) - transformer = summary.fw_transformer.is_nil() ? true_exprt() : - summary.fw_transformer; - else - transformer = summary.bw_transformer.is_nil() ? true_exprt() : - summary.bw_transformer; - } + { + if(forward) + transformer=summary.fw_transformer.is_nil() ? true_exprt() : + summary.fw_transformer; + else + transformer = summary.bw_transformer.is_nil() ? true_exprt() : + summary.bw_transformer; + } rename(transformer,counter); - if(summary.has_assertion) - { - assert_summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), - transformer)); - } - else - { - noassert_summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), - transformer)); - } + summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), + transformer)); //equalities for globals out (including unmodified globals) - get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); - - return summary.has_assertion; + if(forward){ + //get_replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,bindings,counter); + get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); + } + else{ + //get_replace_globals_out(summary.globals_in,cs_globals_out,cs_globals_in,bindings,counter); + get_replace_globals_out(summary.globals_in,*f_it,cs_globals_out,cs_globals_in,bindings,counter); + } } /*******************************************************************\ @@ -278,7 +209,7 @@ Function: ssa_inlinert::get_summaries Inputs: - Outputs: + Outputs: Purpose: get summary for all function calls @@ -286,11 +217,12 @@ Function: ssa_inlinert::get_summaries exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) { - exprt::operandst summaries,bindings; - get_summaries(SSA,true,summaries,bindings); - return and_exprt(conjunction(bindings),conjunction(summaries)); + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings); + return and_exprt(conjunction(bindings), conjunction(summaries)); } + exprt ssa_inlinert::get_summaries(const local_SSAt &SSA, assertion_mapt &assertion_map) { @@ -299,17 +231,47 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA, return and_exprt(conjunction(bindings),conjunction(summaries)); } -void ssa_inlinert::get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) + + +/*******************************************************************\ + +Function: ssa_inlinert::get_summaries + + Inputs: + + Outputs: + + Purpose: get summary for all function calls + +\*******************************************************************/ + +void ssa_inlinert::get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings) { - assertion_mapt assertion_map; - get_summaries(SSA, - summaryt::call_sitet(SSA.goto_function.body.instructions.end()), - forward,summaries,summaries,bindings,assertion_map); + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(summary_db.exists(fname)) + { + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname), + forward,summaries,bindings,counter); + } + } + } } + void ssa_inlinert::get_summaries(const local_SSAt &SSA, bool forward, exprt::operandst &summaries, @@ -322,33 +284,61 @@ void ssa_inlinert::get_summaries(const local_SSAt &SSA, } bool ssa_inlinert::get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - bool error_summ) + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings) { - assertion_mapt assertion_map; - return get_summaries(SSA, - summaryt::call_sitet(SSA.goto_function.body.instructions.end()), - forward,assert_summaries,noassert_summaries,bindings,assertion_map); + bool assertion_flag = false; + for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); + n_it != SSA.nodes.end(); n_it++) + { + for(local_SSAt::nodet::function_callst::const_iterator f_it = + n_it->function_calls.begin(); + f_it != n_it->function_calls.end(); f_it++) + { + //do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site == this_call_site) + continue; + + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries + if(summary_db.exists(fname)) + { + summaryt summary = summary_db.get(fname); + if(summary.has_assertion == true){ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); + assertion_flag = true; + } + else{ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); + } + } + } + } + return assertion_flag; } + bool ssa_inlinert::get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map, - bool error_summ) + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map) { bool assertion_flag = false; for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::assertionst::const_iterator a_it = + for(local_SSAt::nodet::assertionst::const_iterator a_it = n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) { @@ -356,49 +346,43 @@ bool ssa_inlinert::get_summaries(const local_SSAt &SSA, rename(assertion_map[n_it->location].back(), counter); } for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); + n_it->function_calls.begin(); f_it != n_it->function_calls.end(); f_it++) - { - //do not use summary for current call site - summaryt::call_sitet this_call_site(n_it->location); - if(current_call_site == this_call_site) - continue; - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - //get inlined function - if(n_it->function_calls_inlined) - { - counter++; - bool new_assertion_flag = - get_inlined(SSA,n_it,f_it, - forward,assert_summaries,noassert_summaries, - bindings,assertion_map,counter,error_summ); - assertion_flag = assertion_flag || new_assertion_flag; - } - //get summary - else if(summary_db.exists(fname)) { - counter++; - bool new_assertion_flag = - get_summary(SSA,n_it,f_it, - forward,assert_summaries,noassert_summaries, - bindings,counter,error_summ); - assertion_flag = assertion_flag || new_assertion_flag; + //do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site == this_call_site) + continue; + + assert(f_it->function().id()==ID_symbol); //no function pointers + irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + + //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries + if(summary_db.exists(fname)) + { + summaryt summary = summary_db.get(fname); + if(summary.has_assertion == true){ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); + assertion_flag = true; } + else{ + counter++; + get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); } + } + } } return assertion_flag; } - /*******************************************************************\ Function: ssa_inlinert::replace Inputs: - Outputs: + Outputs: Purpose: replaces function calls by summaries if available in the summary store @@ -406,43 +390,46 @@ Function: ssa_inlinert::replace \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - bool forward, - bool preconditions_as_assertions, - int counter) +void ssa_inlinert::replace( + local_SSAt &SSA, + bool forward, + bool preconditions_as_assertions, + int counter) { - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + for(local_SSAt::nodet::function_callst::iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) { - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); - if(summary_db.exists(fname)) + if(summary_db.exists(fname)) { - summaryt summary = summary_db.get(fname); + summaryt summary=summary_db.get(fname); status() << "Replacing function " << fname << " by summary" << eom; - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); //replace replace(SSA,n_it,f_it,cs_globals_in,cs_globals_out,summary, - forward,preconditions_as_assertions,counter); + forward,preconditions_as_assertions,counter); - //remove function_call + // remove function_call rm_function_calls.insert(f_it); } - else debug() << "No summary available for function " << fname << eom; + else + debug() << "No summary available for function " << fname << eom; commit_node(n_it); } - commit_nodes(SSA.nodes,n_it); + commit_nodes(SSA.nodes, n_it); } } @@ -452,72 +439,73 @@ Function: ssa_inlinert::replace Inputs: - Outputs: + Outputs: - Purpose: replaces inlines functions + Purpose: replaces inlines functions if SSA is available in functions and does nothing otherwise \*******************************************************************/ void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - int counter, - bool recursive, bool rename) + const ssa_dbt &ssa_db, + int counter, + bool recursive, bool rename) { - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) { - for(local_SSAt::nodet::function_callst::iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + for(local_SSAt::nodet::function_callst::iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(ssa_db.exists(fname)) - { - status() << "Inlining function " << fname << eom; - local_SSAt fSSA = ssa_db.get(fname); //copy - - if(rename) + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + if(ssa_db.exists(fname)) + { + status() << "Inlining function " << fname << eom; + local_SSAt fSSA=ssa_db.get(fname); // copy + + if(rename) { - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + if(recursive) { - replace(fSSA,ssa_db,true,counter); - } + replace(fSSA,ssa_db,true,counter); + } - //replace - replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); + //replace + replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); } - else // just add to nodes + else // just add to nodes { - for(local_SSAt::nodest::const_iterator fn_it = fSSA.nodes.begin(); - fn_it != fSSA.nodes.end(); fn_it++) + for(local_SSAt::nodest::const_iterator fn_it=fSSA.nodes.begin(); + fn_it!=fSSA.nodes.end(); fn_it++) { - debug() << "new node: "; fn_it->output(debug(),fSSA.ns); + debug() << "new node: "; fn_it->output(debug(), fSSA.ns); debug() << eom; - + new_nodes.push_back(*fn_it); } } - } - else debug() << "No body available for function " << fname << eom; + } + else + debug() << "No body available for function " << fname << eom; commit_node(n_it); } - commit_nodes(SSA.nodes,n_it); + commit_nodes(SSA.nodes, n_it); } } /*******************************************************************\ -Function: ssa_inlinert::replace() +Function: ssa_inlinert::replace Inputs: @@ -527,15 +515,16 @@ Function: ssa_inlinert::replace() \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const summaryt &summary, - bool forward, - bool preconditions_as_assertions, - int counter) +void ssa_inlinert::replace( + local_SSAt &SSA, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const summaryt &summary, + bool forward, + bool preconditions_as_assertions, + int counter) { //equalities for arguments replace_params(summary.params,*f_it,counter); @@ -543,23 +532,25 @@ void ssa_inlinert::replace(local_SSAt &SSA, //equalities for globals_in replace_globals_in(summary.globals_in,cs_globals_in,counter); - //constraints for precondition and transformer + // constraints for precondition and transformer exprt precondition; - if(forward) precondition = summary.fw_precondition; - else precondition = summary.bw_precondition; + if(forward) + precondition=summary.fw_precondition; + else + precondition=summary.bw_precondition; if(!preconditions_as_assertions) { - rename(precondition,counter); + rename(precondition,counter); node->constraints.push_back( implies_exprt(SSA.guard_symbol(node->location), - precondition)); + precondition)); } else { - rename(precondition,counter); + rename(precondition,counter); node->assertions.push_back( implies_exprt(SSA.guard_symbol(node->location), - precondition)); + precondition)); } exprt transformer; if(forward) transformer = summary.fw_transformer; @@ -567,8 +558,8 @@ void ssa_inlinert::replace(local_SSAt &SSA, node->constraints.push_back(transformer); //copy exprt &_transformer = node->constraints.back(); rename(_transformer,counter); - - //remove function call + + // remove function call rm_function_calls.insert(f_it); //equalities for globals out (including unmodified globals) @@ -577,53 +568,54 @@ void ssa_inlinert::replace(local_SSAt &SSA, /*******************************************************************\ - Function: ssa_inlinert::replace() + Function: ssa_inlinert::replace Inputs: Outputs: - Purpose: inline function + Purpose: inline function Remark: local_SSAt::nodest maps a goto program target to a single SSA node, - when inlining several calls to the same function - instructions appear factorized by the goto program targets + when inlining several calls to the same function + instructions appear factorized by the goto program targets \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - const local_SSAt &function, - int counter) +void ssa_inlinert::replace( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + const local_SSAt &function, + int counter) { //equalities for arguments replace_params(function.params,*f_it,counter); //equalities for globals_in replace_globals_in(function.globals_in,cs_globals_in,counter); - - //add function body - for(local_SSAt::nodest::const_iterator n_it = function.nodes.begin(); - n_it != function.nodes.end(); n_it++) + + // add function body + for(local_SSAt::nodest::const_iterator n_it=function.nodes.begin(); + n_it!=function.nodes.end(); n_it++) { local_SSAt::nodet n = *n_it; //copy rename(n,counter); new_nodes.push_back(n); } - - //remove function call + + // remove function call rm_function_calls.insert(f_it); - + //equalities for globals out (including unmodified globals) replace_globals_out(function.globals_out,cs_globals_in,cs_globals_out,counter); } /*******************************************************************\ -Function: ssa_inlinert::replace_globals_in() +Function: ssa_inlinert::get_replace_globals_in Inputs: @@ -634,31 +626,31 @@ Function: ssa_inlinert::replace_globals_in() \*******************************************************************/ void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &globals, - exprt::operandst &c, - int counter) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter) { std::string suffix = id2string(funapp_expr.get(ID_suffix)); //equalities for globals_in for(summaryt::var_sett::const_iterator it = globals_in.begin(); it != globals_in.end(); it++) - { - symbol_exprt lhs = *it; //copy - rename(lhs,counter); + { + symbol_exprt lhs = *it; //copy + rename(lhs,counter); symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) + if(find_corresponding_symbol(*it, globals, rhs)) { - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - debug() << "binding: " << lhs.get_identifier() << " == " + debug() << "binding: " << lhs.get_identifier() << " == " << rhs.get_identifier() << eom; - c.push_back(equal_exprt(lhs,rhs)); + c.push_back(equal_exprt(lhs, rhs)); } #if 0 else - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif } @@ -666,25 +658,25 @@ void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in } void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals, - int counter) + const local_SSAt::var_sett &globals, + int counter) { - //equalities for globals_in - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) + // equalities for globals_in + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) { symbol_exprt lhs = *it; //copy rename(lhs,counter); symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals,rhs)) + if(find_corresponding_symbol(*it, globals, rhs)) { - debug() << "binding: " << lhs.get_identifier() << " == " + debug() << "binding: " << lhs.get_identifier() << "==" << rhs.get_identifier() << eom; - new_equs.push_back(equal_exprt(lhs,rhs)); + new_equs.push_back(equal_exprt(lhs, rhs)); } #if 0 else - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif } @@ -692,7 +684,7 @@ void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, /*******************************************************************\ -Function: ssa_inlinert::replace_params() +Function: ssa_inlinert::get_replace_params Inputs: @@ -703,87 +695,92 @@ Function: ssa_inlinert::replace_params() \*******************************************************************/ void ssa_inlinert::get_replace_params(const local_SSAt &SSA, - const local_SSAt::var_listt ¶ms, - local_SSAt::nodest::const_iterator n_it, - const function_application_exprt &funapp_expr, - exprt::operandst &c, - int counter) + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter) { + //std::string suffix = id2string(funapp_expr.get(ID_suffix)); + //equalities for arguments local_SSAt::var_listt::const_iterator p_it = params.begin(); for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); it != funapp_expr.arguments().end(); it++, p_it++) - { + { #if 0 - std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*it) << std::endl; + std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*it) << std::endl; #endif #if 0 - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it = p_it; + if(funapp_expr.arguments().size() != params.size() && + ++next_p_it==params.end()) //TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } #endif - if(SSA.ns.follow(it->type()).id()==ID_struct) - { - exprt rhs = SSA.read_rhs(*it, n_it->location); //copy + if(SSA.ns.follow(it->type()).id()==ID_struct) + { + exprt rhs = SSA.read_rhs(*it, n_it->location); //copy #if 0 - std::cout << "split param " << from_expr(SSA.ns,"",*it) - << " into " << from_expr(SSA.ns,"",rhs) << std::endl; + std::cout << "split param " << from_expr(SSA.ns,"",*it) + << " into " << from_expr(SSA.ns,"",rhs) << std::endl; #endif - forall_operands(o_it, rhs) - { - assert(p_it!=params.end()); - exprt lhs = *p_it; //copy - rename(lhs,counter); + forall_operands(o_it, rhs) + { + assert(p_it!=params.end()); + exprt lhs = *p_it; //copy + rename(lhs,counter); #if 0 - std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; + std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) + << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; #endif - c.push_back(equal_exprt(lhs,*o_it)); - ++p_it; + c.push_back(equal_exprt(lhs,*o_it)); + ++p_it; + } + } + else + { + exprt lhs = *p_it; //copy + rename(lhs,counter); + c.push_back(equal_exprt(lhs,*it)); + //symbol_exprt sexpr = to_symbol_expr(*it); + //sexpr.set_identifier(id2string(sexpr.get_identifier())+suffix); + //c.push_back(equal_exprt(lhs,sexpr)); } - } - else - { - exprt lhs = *p_it; //copy - rename(lhs,counter); - c.push_back(equal_exprt(lhs,*it)); - } } } void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr, - int counter) + const function_application_exprt &funapp_expr, + int counter) { - //equalities for arguments - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); - it != funapp_expr.arguments().end(); it++, p_it++) + // equalities for arguments + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=funapp_expr.arguments().begin(); + it!=funapp_expr.arguments().end(); it++, p_it++) { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(funapp_expr.arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - - exprt lhs = *p_it; //copy - rename(lhs,counter); - new_equs.push_back(equal_exprt(lhs,*it)); + + exprt lhs = *p_it; //copy + rename(lhs,counter); + new_equs.push_back(equal_exprt(lhs,*it)); } } /*******************************************************************\ -Function: ssa_inlinert::replace_globals_out() +Function: ssa_inlinert::get_replace_globals_out Inputs: @@ -794,57 +791,56 @@ Function: ssa_inlinert::replace_globals_out() \*******************************************************************/ void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - exprt::operandst &c, - int counter) + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter) { std::string suffix = id2string(funapp_expr.get(ID_suffix)); - + //equalities for globals_out for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); it != cs_globals_out.end(); it++) - { - symbol_exprt lhs = *it; //copy + { + symbol_exprt lhs = *it; //copy - lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + lhs.set_identifier(id2string(lhs.get_identifier())+suffix); - symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals_out,rhs)) - rename(rhs,counter); - else - { - bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); - assert(found); - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - } - c.push_back(equal_exprt(lhs,rhs)); + symbol_exprt rhs; + if(find_corresponding_symbol(*it,globals_out,rhs)) + rename(rhs,counter); + else{ + bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); + assert(found); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + } + c.push_back(equal_exprt(lhs,rhs)); } } void ssa_inlinert::replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - int counter) + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter) { - //equalities for globals_out - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) + // equalities for globals_out + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) { - symbol_exprt rhs = *it; //copy + symbol_exprt rhs=*it; // copy symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs,counter); + if(find_corresponding_symbol(*it,globals_out,lhs)) + rename(lhs,counter); else - assert(find_corresponding_symbol(*it,cs_globals_in,lhs)); - new_equs.push_back(equal_exprt(lhs,rhs)); + assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); + new_equs.push_back(equal_exprt(lhs, rhs)); } } /*******************************************************************\ -Function: ssa_inlinert::havoc() +Function: ssa_inlinert::havoc Inputs: @@ -854,13 +850,32 @@ Function: ssa_inlinert::havoc() \*******************************************************************/ -void ssa_inlinert::havoc(local_SSAt::nodet &node, - local_SSAt::nodet::function_callst::iterator f_it) +void ssa_inlinert::havoc( + local_SSAt::nodet &node, + local_SSAt::nodet::function_callst::iterator f_it) { - //remove function call + // remove function call rm_function_calls.insert(f_it); } +/*******************************************************************\ + +Function: ssa_inlinert::rename + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +irep_idt ssa_inlinert::rename(irep_idt &id, int counter) +{ + return id2string(id)+"@"+i2string(counter); +} + + /*******************************************************************\ Function: ssa_inlinert::rename() @@ -873,35 +888,37 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ +irep_idt ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ std::string id_str = id2string(id); - if(!attach) - { + if(attach == false){ //find first @ where afterwards there are no letters size_t pos = std::string::npos; for(size_t i=0;i=0) - id = id_str+"@"+i2string(counter); + else{ + if(id_str.find('@') != std::string::npos) + return id; + else + return rename(id, counter); } } @@ -917,9 +934,43 @@ Function: ssa_inlinert::rename() \*******************************************************************/ -void ssa_inlinert::rename(exprt &expr, int counter, bool attach) +void ssa_inlinert::rename(exprt &expr, int counter) { - if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) + if(expr.id()==ID_symbol) + { + symbol_exprt &sexpr = to_symbol_expr(expr); + std::string id_str = id2string(sexpr.get_identifier()); + irep_idt id; + + if(id_str.find('@') != std::string::npos) + id = id_str; + else + id = id_str+"@"+i2string(counter); + + sexpr.set_identifier(id); + } + for(exprt::operandst::iterator it = expr.operands().begin(); + it != expr.operands().end(); it++) + { + rename(*it,counter); + } +} + +/*******************************************************************\ + +Function: ssa_inlinert::rename() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_inlinert::rename(exprt &expr, int counter, bool attach) +{ + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { irep_idt id = expr.get(ID_identifier); rename(id, counter, attach); @@ -933,9 +984,10 @@ void ssa_inlinert::rename(exprt &expr, int counter, bool attach) } } + /*******************************************************************\ -Function: ssa_inlinert::rename() +Function: ssa_inlinert::rename Inputs: @@ -947,27 +999,14 @@ Function: ssa_inlinert::rename() void ssa_inlinert::rename(local_SSAt::nodet &node, int counter) { - for(local_SSAt::nodet::equalitiest::iterator e_it = node.equalities.begin(); - e_it != node.equalities.end(); e_it++) - { - rename(*e_it, counter); - } - for(local_SSAt::nodet::constraintst::iterator c_it = node.constraints.begin(); - c_it != node.constraints.end(); c_it++) - { - rename(*c_it, counter); - } - for(local_SSAt::nodet::assertionst::iterator a_it = node.assertions.begin(); - a_it != node.assertions.end(); a_it++) - { - rename(*a_it, counter); - } - for(local_SSAt::nodet::function_callst::iterator - f_it = node.function_calls.begin(); - f_it != node.function_calls.end(); f_it++) - { - rename(*f_it, counter); - } + for(auto &e : node.equalities) + rename(e, counter); + for(auto &c : node.constraints) + rename(c, counter); + for(auto &a : node.assertions) + rename(a, counter); + for(auto &f : node.function_calls) + rename(f, counter); } /*******************************************************************\ @@ -983,49 +1022,49 @@ Function: ssa_inlinert::rename_to_caller \*******************************************************************/ void ssa_inlinert::rename_to_caller( - local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, exprt &expr) { assert(params.size()==f_it->arguments().size()); replace_mapt replace_map; - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = f_it->arguments().begin(); - it != f_it->arguments().end(); it++, p_it++) + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=f_it->arguments().begin(); + it!=f_it->arguments().end(); ++it, ++p_it) { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(f_it->arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(f_it->arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - replace_map[*p_it] = *it; + replace_map[*p_it]=*it; } - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) { symbol_exprt cg; - if(find_corresponding_symbol(*it,cs_globals_in,cg)) - replace_map[*it] = cg; - else + if(find_corresponding_symbol(*it, cs_globals_in, cg)) + replace_map[*it]=cg; + else { #if 0 - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + replace_map[*it]= + symbol_exprt( + id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); } } - replace_expr(replace_map,expr); + replace_expr(replace_map, expr); } /*******************************************************************\ @@ -1041,64 +1080,59 @@ Function: ssa_inlinert::rename_to_callee \*******************************************************************/ void ssa_inlinert::rename_to_callee( - local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, exprt &expr) { replace_mapt replace_map; - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = f_it->arguments().begin(); - it != f_it->arguments().end(); it++, p_it++) + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=f_it->arguments().begin(); + it!=f_it->arguments().end(); ++it, ++p_it) { - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(f_it->arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(f_it->arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { - warning() << "ignoring excess function arguments" << eom; + warning() << "ignoring excess function arguments" << eom; break; } - replace_map[*it] = *p_it; + replace_map[*it]=*p_it; } - /* replace_expr(replace_map,expr); - - replace_map.clear(); //arguments might contain globals, - // thus, we have to replace them separately - */ for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); it != cs_globals_in.end(); it++) { symbol_exprt cg; - if(find_corresponding_symbol(*it,globals_in,cg)) - replace_map[*it] = cg; + if(find_corresponding_symbol(*it, globals_in, cg)) + replace_map[*it]=cg; else { #if 0 - warning() << "'" << it->get_identifier() + warning() << "'" << it->get_identifier() << "' not bound in caller" << eom; #endif - replace_map[*it] = - symbol_exprt(id2string(it->get_identifier())+ - "@"+i2string(++counter),it->type()); + replace_map[*it]= + symbol_exprt( + id2string(it->get_identifier())+"@"+i2string(++counter), it->type()); } } - - replace_expr(replace_map,expr); + + replace_expr(replace_map, expr); } /*******************************************************************\ -Function: ssa_inlinert::commit_node() +Function: ssa_inlinert::commit_node Inputs: Outputs: Purpose: apply changes to node, must be called after replace and havoc - (needed because replace and havoc usually called while + (needed because replace and havoc usually called while iterating over equalities, and hence we cannot modify them) @@ -1106,24 +1140,24 @@ Function: ssa_inlinert::commit_node() void ssa_inlinert::commit_node(local_SSAt::nodest::iterator node) { - //remove obsolete function calls - for(std::set::iterator - it = rm_function_calls.begin(); - it != rm_function_calls.end(); it++) + // remove obsolete function calls + for(std::set::iterator + it=rm_function_calls.begin(); + it!=rm_function_calls.end(); it++) { node->function_calls.erase(*it); } rm_function_calls.clear(); - //insert new equalities - node->equalities.insert(node->equalities.end(), - new_equs.begin(),new_equs.end()); + // insert new equalities + node->equalities.insert( + node->equalities.end(), new_equs.begin(), new_equs.end()); new_equs.clear(); } /*******************************************************************\ -Function: ssa_inlinert::commit_nodes() +Function: ssa_inlinert::commit_nodes Inputs: @@ -1133,11 +1167,13 @@ Function: ssa_inlinert::commit_nodes() \*******************************************************************/ -bool ssa_inlinert::commit_nodes(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator n_pos) +bool ssa_inlinert::commit_nodes( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator n_pos) { - if(new_nodes.empty()) return true; - nodes.splice(n_pos,new_nodes,new_nodes.begin(),new_nodes.end()); + if(new_nodes.empty()) + return true; + nodes.splice(n_pos, new_nodes, new_nodes.begin(), new_nodes.end()); return false; } @@ -1153,23 +1189,22 @@ Function: ssa_inlinert::find_corresponding_symbol \*******************************************************************/ -bool ssa_inlinert::find_corresponding_symbol(const symbol_exprt &s, - const local_SSAt::var_sett &globals, - symbol_exprt &s_found) +bool ssa_inlinert::find_corresponding_symbol( + const symbol_exprt &s, + const local_SSAt::var_sett &globals, + symbol_exprt &s_found) { - const irep_idt &s_orig_id = get_original_identifier(s); - for(local_SSAt::var_sett::const_iterator it = globals.begin(); - it != globals.end(); it++) + const irep_idt &s_orig_id=get_original_identifier(s); + for(local_SSAt::var_sett::const_iterator it=globals.begin(); + it!=globals.end(); it++) { #if 0 - std::cout << s.get_identifier() << " =?= " << it->get_identifier() << std::endl; + std::cout << s_orig_id << "=?= " + << get_original_identifier(*it) << std::endl; #endif - if(s_orig_id == get_original_identifier(*it)) + if(s_orig_id==get_original_identifier(*it)) { -#if 0 - std::cout << s.get_identifier() << " == " << it->get_identifier() << std::endl; -#endif - s_found = *it; + s_found=*it; return true; } } @@ -1182,7 +1217,7 @@ Function: ssa_inlinert::get_original_identifier Inputs: - Outputs: + Outputs: Purpose: TODO: this is a potential source of bugs. Better way to do that? @@ -1190,29 +1225,28 @@ Function: ssa_inlinert::get_original_identifier irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) { - std::string id = id2string(s.get_identifier()); + std::string id=id2string(s.get_identifier()); - //find first #@%!$ where afterwards there are no letters - size_t pos = std::string::npos; - for(size_t i=0;i -#include "../summarizer/summary_db.h" -#include "../summarizer/ssa_db.h" -#include "../ssa/local_ssa.h" +#include +#include + +#include "ssa_db.h" +#include "local_ssa.h" class summary_dbt; class ssa_dbt; -class ssa_inlinert : public messaget +class ssa_inlinert:public messaget { - public: - explicit ssa_inlinert(summary_dbt &_summary_db, - ssa_dbt &_ssa_db) : - counter(-1), - summary_db(_summary_db), - ssa_db(_ssa_db) - {} +public: + explicit ssa_inlinert(summary_dbt &_summary_db, + ssa_dbt &_ssa_db): + counter(0), + summary_db(_summary_db), + ssa_db(_ssa_db) + { + } typedef std::map assertion_mapt; + void get_guard_binding(const local_SSAt &SSA, const local_SSAt &fSSA, local_SSAt::nodest::const_iterator n_it, exprt &guard_binding, int counter); + void get_bindings(const local_SSAt &SSA, const local_SSAt &fSSA, local_SSAt::nodest::const_iterator n_it, @@ -42,126 +47,131 @@ class ssa_inlinert : public messaget exprt::operandst &bindings_in, exprt::operandst &bindings_out, int counter); - bool get_summary(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, + + void get_summary(const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const summaryt &summary, + bool forward, + exprt::operandst &summaries, exprt::operandst &bindings, int counter, bool error_summ = false); - bool get_inlined(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map, - int counter, - bool error_summ); - void get_summaries( - const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter - void get_summaries( - const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter - bool get_summaries( - const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - bool error_summ = false); //TODO: need to explicitly pass the correct counter - bool get_summaries( - const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map, - bool error_summ = false); //TODO: need to explicitly pass the correct counter + + void get_summaries(const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + + void get_summaries(const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter + + bool get_summaries(const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter + + bool get_summaries(const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter exprt get_summaries(const local_SSAt &SSA); //TODO: need to explicitly pass the correct counter + exprt get_summaries(const local_SSAt &SSA, - assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter - - void replace(local_SSAt &SSA, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site - const summaryt &summary, - bool forward, + assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter + + void replace( + local_SSAt &SSA, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, + // outgoing globals at call site + const summaryt &summary, + bool forward, bool preconditions_as_assertions, int counter); - void replace(local_SSAt &SSA, - bool forward, + void replace( + local_SSAt &SSA, + bool forward, bool preconditions_as_assertions, int counter); - void replace(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator node, - local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - //incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, + void replace( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator node, + local_SSAt::nodet::function_callst::iterator f_it, + const local_SSAt::var_sett &cs_globals_in, + // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, //outgoing globals at call site const local_SSAt &function, int counter); - void replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, + void replace( + local_SSAt &SSA, + const ssa_dbt &ssa_db, int counter, - bool recursive=false, - bool rename=true); - - void havoc(local_SSAt::nodet &node, - local_SSAt::nodet::function_callst::iterator f_it); + bool recursive=false, + bool rename=true); - //apply changes to node, must be called after replace and havoc + void havoc( + local_SSAt::nodet &node, + local_SSAt::nodet::function_callst::iterator f_it); + + // apply changes to node, must be called after replace and havoc void commit_node(local_SSAt::nodest::iterator node); - bool commit_nodes(local_SSAt::nodest &nodes, - local_SSAt::nodest::iterator n_pos); - - //functions for renaming preconditions to calling context - void rename_to_caller(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); - void rename_to_callee(local_SSAt::nodet::function_callst::const_iterator f_it, - const local_SSAt::var_listt ¶ms, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &globals_in, - exprt &expr); - - static bool find_corresponding_symbol(const symbol_exprt &s, - const local_SSAt::var_sett &globals, - symbol_exprt &s_found); + bool commit_nodes( + local_SSAt::nodest &nodes, + local_SSAt::nodest::iterator n_pos); + + // functions for renaming preconditions to calling context + void rename_to_caller( + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); + void rename_to_callee( + local_SSAt::nodet::function_callst::const_iterator f_it, + const local_SSAt::var_listt ¶ms, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &globals_in, + exprt &expr); + + static bool find_corresponding_symbol( + const symbol_exprt &s, + const local_SSAt::var_sett &globals, + symbol_exprt &s_found); static irep_idt get_original_identifier(const symbol_exprt &s); - - void rename(irep_idt &id, int counter, bool attach=true); - int get_rename_counter() { return ++counter; } + irep_idt rename(irep_idt &id, int counter); + + irep_idt rename(irep_idt &id, int counter, bool attach); + + int get_rename_counter(){ + counter++; + return counter; + } // moved from protected to public, for use in summarizer_bw_cex_complete - void rename(exprt &expr, int counter, bool attach=true); + void rename(exprt &expr, int counter); + void rename(exprt &expr, int counter, bool attach); protected: - int counter; + unsigned counter; summary_dbt &summary_db; ssa_dbt &ssa_db; @@ -176,7 +186,7 @@ class ssa_inlinert : public messaget const function_application_exprt &funapp_expr, int counter); void replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_in, const local_SSAt::var_sett &cs_globals_out, int counter); @@ -193,7 +203,7 @@ class ssa_inlinert : public messaget int counter); void get_replace_globals_out(const local_SSAt::var_sett &globals_out, const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_in, const local_SSAt::var_sett &cs_globals_out, exprt::operandst &c, int counter); @@ -202,5 +212,4 @@ class ssa_inlinert : public messaget }; - #endif diff --git a/src/ssa/ssa_object.cpp b/src/ssa/ssa_object.cpp index 834db068a..4f27c722c 100644 --- a/src/ssa/ssa_object.cpp +++ b/src/ssa/ssa_object.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -55,6 +55,18 @@ void collect_objects_rec( std::set &objects, std::set &literals); +/*******************************************************************\ + +Function: collect_objects_address_of_rec + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void collect_objects_address_of_rec( const exprt &src, const namespacet &ns, @@ -62,9 +74,9 @@ void collect_objects_address_of_rec( std::set &literals) { #ifdef DEBUG - std::cout << "COLLECT ADDRESS OF " << from_expr(ns,"",src) << "\n"; + std::cout << "COLLECT ADDRESS OF " << from_expr(ns, "", src) << "\n"; #endif - + if(src.id()==ID_index) { collect_objects_address_of_rec( @@ -112,10 +124,9 @@ void collect_objects_rec( std::set &objects, std::set &literals) { - - #ifdef DEBUG - std::cout << "COLLECT " << from_expr(ns,"",src) << "\n"; - #endif +#ifdef DEBUG + std::cout << "COLLECT " << from_expr(ns, "", src) << "\n"; +#endif if(src.id()==ID_code) { @@ -146,7 +157,7 @@ void collect_objects_rec( const struct_typet &struct_type=to_struct_type(type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator it=components.begin(); it!=components.end(); @@ -158,10 +169,9 @@ void collect_objects_rec( } else { - - #ifdef DEBUG +#ifdef DEBUG std::cout << "OBJECT " << ssa_object.get_identifier() << "\n"; - #endif +#endif objects.insert(ssa_object); } @@ -239,7 +249,7 @@ void ssa_objectst::add_ptr_objects( } } } - + for(objectst::const_iterator o_it=tmp.begin(); o_it!=tmp.end(); o_it++) @@ -277,7 +287,7 @@ void ssa_objectst::categorize_objects( exprt root_object=o_it->get_root_object(); #ifdef DEBUG - std::cout << "CATEGORIZE " << from_expr(ns,"",root_object) << "\n"; + std::cout << "CATEGORIZE " << from_expr(ns, "", root_object) << "\n"; #endif if(root_object.id()==ID_symbol) @@ -358,13 +368,14 @@ ssa_objectt::identifiert ssa_objectt::object_id_rec( { const member_exprt &member_expr=to_member_expr(src); const exprt &compound_op=member_expr.struct_op(); - + // need to distinguish union and struct members if(is_struct_member(member_expr, ns)) { irep_idt compound_object=object_id_rec(compound_op, ns); - if(compound_object==irep_idt()) return identifiert(); - + if(compound_object==irep_idt()) + return identifiert(); + return identifiert( id2string(compound_object)+ "."+id2string(member_expr.get_component_name())); @@ -445,14 +456,13 @@ Function: is_symbol_struct_member Inputs: - Outputs: + Outputs: returns true for symbol(.member)*, where + all members are struct members. Purpose: \*******************************************************************/ -// Returns true for symbol(.member)*, where -// all members are struct members. bool is_symbol_struct_member(const exprt &src, const namespacet &ns) { return get_struct_rec(src, ns).id()==ID_symbol; @@ -466,12 +476,11 @@ Function: is_symbol_or_deref_struct_member Outputs: - Purpose: + Purpose: returns true for ((*ptr)|symbol)(.member)*, where + all members are struct members. \*******************************************************************/ -// Returns true for ((*ptr)|symbol)(.member)*, where -// all members are struct members. bool is_symbol_or_deref_struct_member(const exprt &src, const namespacet &ns) { exprt struct_op=get_struct_rec(src, ns); @@ -484,14 +493,13 @@ Function: is_deref_struct_member Inputs: - Outputs: + Outputs: returns true for (*ptr)(.member)*, where + all members are struct members. Purpose: \*******************************************************************/ -// Returns true for (*ptr)(.member)*, where -// all members are struct members. bool is_deref_struct_member(const exprt &src, const namespacet &ns) { return get_struct_rec(src, ns).id()==ID_dereference; diff --git a/src/ssa/ssa_object.h b/src/ssa/ssa_object.h index 43fa0da37..37f4b8c8e 100644 --- a/src/ssa/ssa_object.h +++ b/src/ssa/ssa_object.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_OBJECTS_H -#define CPROVER_SSA_OBJECTS_H +#ifndef CPROVER_2LS_SSA_SSA_OBJECT_H +#define CPROVER_2LS_SSA_SSA_OBJECT_H #include @@ -21,7 +21,7 @@ class ssa_objectt inline explicit identifiert(const irep_idt &_src):irep_idt(_src) { } - + inline identifiert() { } @@ -32,27 +32,27 @@ class ssa_objectt identifier(object_id_rec(expr, _ns)) { } - + inline const typet &type() const { return expr.type(); } - + inline const exprt &get_expr() const { return expr; } - + inline identifiert get_identifier() const { return identifier; } - + inline const symbol_exprt symbol_expr() const { return symbol_exprt(identifier, type()); } - + // The identifier is unique, so ordering and comparison // can be done on the identifier, which in turn is // an integer. @@ -65,20 +65,20 @@ class ssa_objectt { return identifier==other.identifier; } - + inline bool operator!=(const ssa_objectt &other) const { return identifier!=other.identifier; } - + // This is for use in if(...) tests, and // implements the 'safe bool' idiom. Shall be replaced // by C++11 explict conversion to bool one day. operator void *() const { - return identifier.empty()?0:(void *)&identifier; + return identifier.empty()?0:(void *)&identifier; // NOLINT(*) } - + exprt get_root_object() const { return get_root_object_rec(expr); @@ -98,7 +98,7 @@ class ssa_objectst // objects, plus categorization typedef std::set objectst; objectst objects, dirty_locals, clean_locals, globals, ptr_objects; - + // literals whose address is taken typedef std::set literalst; literalst literals; @@ -111,7 +111,7 @@ class ssa_objectst add_ptr_objects(ns); categorize_objects(goto_function, ns); } - + protected: void collect_objects( const goto_functionst::goto_functiont &, @@ -120,7 +120,7 @@ class ssa_objectst void categorize_objects( const goto_functionst::goto_functiont &, const namespacet &); - + void add_ptr_objects( const namespacet &); }; diff --git a/src/ssa/ssa_refiner_monolithic.cpp b/src/ssa/ssa_refiner_monolithic.cpp deleted file mode 100644 index ceb26639e..000000000 --- a/src/ssa/ssa_refiner_monolithic.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/*******************************************************************\ - -Module: SSA Refiner for Monolithic Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "ssa_refiner_monolithic.h" - -/*******************************************************************\ - -Function: ssa_refiner_monolithict::operator() - - Inputs: - - Outputs: - - Purpose: refine all - -\*******************************************************************/ - -bool ssa_refiner_monolithict::operator()() -{ - status() << "Unwinding (k=" << unwind << ")" << eom; - summary_db.mark_recompute_all(); //TODO: recompute only functions with loops - ssa_unwinder.unwind_all(unwind); - - return unwind++<=max_unwind; -} diff --git a/src/ssa/ssa_refiner_monolithic.h b/src/ssa/ssa_refiner_monolithic.h deleted file mode 100644 index 77a86fde3..000000000 --- a/src/ssa/ssa_refiner_monolithic.h +++ /dev/null @@ -1,44 +0,0 @@ -/*******************************************************************\ - -Module: SSA Refiner for Monolithic Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SSA_REFINER_MONOLITHIC_H -#define CPROVER_SSA_REFINER_MONOLITHIC_H - -#include "ssa_refiner.h" - -#include "../summarizer/summary_db.h" -#include "ssa_unwinder.h" - -class summary_dbt; -class ssa_unwindert; - -class ssa_refiner_monolithict : public ssa_refinert -{ - public: - explicit ssa_refiner_monolithict( - summary_dbt &_summary_db, - ssa_unwindert &_ssa_unwinder, - unsigned _max_unwind - ) : - summary_db(_summary_db), - ssa_unwinder(_ssa_unwinder), - max_unwind(_max_unwind), - unwind(0) - {} - - virtual bool operator()(); - virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } - - protected: - summary_dbt &summary_db; - ssa_unwindert &ssa_unwinder; - const unsigned max_unwind; - unsigned unwind; -}; - -#endif diff --git a/src/ssa/ssa_refiner_selective.h b/src/ssa/ssa_refiner_selective.h index 9d2d8f352..48ec41028 100644 --- a/src/ssa/ssa_refiner_selective.h +++ b/src/ssa/ssa_refiner_selective.h @@ -11,7 +11,7 @@ Author: Peter Schrammel, Madhukar Kumar #include "ssa_refiner.h" -#include "../summarizer/ssa_db.h" +#include "../ssa/ssa_db.h" #include "ssa_inliner.h" #include "ssa_unwinder.h" diff --git a/src/ssa/ssa_slicer.cpp b/src/ssa/ssa_slicer.cpp deleted file mode 100644 index 81dde9aa6..000000000 --- a/src/ssa/ssa_slicer.cpp +++ /dev/null @@ -1,179 +0,0 @@ -#include - -#include -#include - -void print_symbols(std::string msg, const find_symbols_sett &symbols) -{ - std::cout << msg << ": " << std::endl; - for(find_symbols_sett::const_iterator - s_it=symbols.begin(); s_it!=symbols.end(); s_it++) - std::cout << " " << *s_it << std::endl; - -} - -void ssa_slicert::operator()(std::list &dest, - const local_SSAt &src) -{ - //collect symbols in assertions - find_symbols_sett new_symbols; - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) - { - if(n_it->marked) continue; - if(n_it->assertions.empty()) continue; - for(local_SSAt::nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) - { - find_symbols(*a_it,new_symbols); - } - } -#ifdef DEBUG - print_symbols("symbols in assertions",new_symbols); -#endif - if(new_symbols.empty()) return; - - //build map symbol -> (definition, constraint set) - symbol_mapt symbol_map; - sliced_equalities = 0; - sliced_constraints = 0; - - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) - { - for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); - e_it!=n_it->equalities.end(); - e_it++) - { - find_symbols_sett e_symbols; - find_symbols(e_it->lhs(),e_symbols); - - for(find_symbols_sett::const_iterator - s_it=e_symbols.begin(); s_it!=e_symbols.end(); s_it++) - { - symbol_map[*s_it]; - symbol_map[*s_it].def = e_it; - symbol_map[*s_it].def_node = n_it; - } - } - for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); - c_it!=n_it->constraints.end(); - c_it++) - { - find_symbols_sett c_symbols; - find_symbols(*c_it,c_symbols); - for(find_symbols_sett::const_iterator - s_it=c_symbols.begin(); s_it!=c_symbols.end(); s_it++) - { - if(symbol_map.find(*s_it)==symbol_map.end()) continue; - symbol_map[*s_it].constraints.push_back(constr_infot()); - constr_infot &constr_info = symbol_map[*s_it].constraints.back(); - constr_info.constr = c_it; - constr_info.node = n_it; - } - } - //statistics - if(!n_it->marked) - { - sliced_equalities += n_it->equalities.size(); - sliced_constraints += n_it->constraints.size(); - } - } - //statistics - std::cout << "Total equalities: " << sliced_equalities - << ", total constraints: " << sliced_constraints << std::endl; - - //compute backwards dependencies and add to formula on-the-fly - find_symbols_sett symbols_seen; - while(!new_symbols.empty()) - { - find_symbols_sett old_symbols = new_symbols; - -#ifdef DEBUG - print_symbols("new symbols",new_symbols); -#endif - - new_symbols.clear(); - for(find_symbols_sett::const_iterator - s_it=old_symbols.begin(); s_it!=old_symbols.end(); s_it++) - { - irep_idt sym = *s_it; - if(symbols_seen.find(sym)!=symbols_seen.end()) continue; - if(symbol_map.find(sym)==symbol_map.end()) - { - //handle loopback variables - //here it's getting a bit ugly - std::string sym_str = id2string(sym); - size_t pos1 = sym_str.find("#lb"); - if(pos1==std::string::npos) continue; - size_t pos2 = sym_str.find("%",pos1); - irep_idt basename = sym_str.substr(0,pos1); - std::string unwinder_suffix = ""; - if(pos2 - -#include "local_ssa.h" - -class ssa_slicert : public messaget -{ - public: - - void operator()(std::list &dest, - const local_SSAt &src); - - //statistics - unsigned sliced_equalities; - unsigned sliced_constraints; - - protected: - typedef struct - { - local_SSAt::nodet::constraintst::const_iterator constr; - local_SSAt::nodest::const_iterator node; - } constr_infot; - typedef std::list constraint_sett; - typedef struct - { - local_SSAt::nodet::equalitiest::const_iterator def; - local_SSAt::nodest::const_iterator def_node; - constraint_sett constraints; - } symbol_infot; - typedef hash_map_cont symbol_mapt; - -}; - -#endif diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index bc82fc1d7..08649c47f 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -1,1120 +1,985 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Peter Schrammel, Saurabh Joshi - -\*******************************************************************/ - -//#define DEBUG - -#include - -#include "ssa_unwinder.h" - -/***************************************************************************** - * - * Function : ssa_local_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : builds data structures for unwinder and transforms SSA (rename to %0) - * - * - *****************************************************************************/ - -void ssa_local_unwindert::init() -{ - build_loop_tree(); - build_pre_post_map(); - build_exit_conditions(); - unwind(0); - compute_enable_expr(); -#ifdef DEBUG - SSA.output_verbose(std::cout); -#endif -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_loop_tree - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ - -void ssa_local_unwindert::build_loop_tree() -{ - //build loop tree structure - //Assumes that initially the nodes are in the same order as in the goto program - std::list loopheads; - local_SSAt::nodest::iterator n_it = --SSA.nodes.end(); - while(n_it != SSA.nodes.begin()) - { - //end of loop found - if (n_it->loophead != SSA.nodes.end()) - { - loopt &loop = loops[n_it->loophead->location->location_number]; - if(loopheads.empty()) - { - loop.is_root = true; - } - else - { - loops[loopheads.back()->location->location_number].loop_nodes.push_back( - n_it->loophead->location->location_number); - } - loopheads.push_back(n_it->loophead); - loop.body_nodes.push_front(*n_it); -#ifdef DEBUG - std::cout << "pop " << n_it->location->location_number - << " for " << n_it->loophead->location->location_number << std::endl; -#endif - //this test is ambiguous if the loop condition is true, - // but shouldn't have any consequences - assert(n_it->location->is_backwards_goto()); - loop.is_dowhile = !n_it->location->guard.is_true(); - SSA.nodes.erase(n_it--); - } - //beginning of loop found - else if (n_it == loopheads.back()) - { -#ifdef DEBUG - std::cout << "push " << n_it->location->location_number << std::endl; -#endif - loops[n_it->location->location_number].body_nodes.push_front(*n_it); - loopheads.pop_back(); - loops[n_it->location->location_number].body_nodes.back().loophead = - loops[n_it->location->location_number].body_nodes.begin(); - SSA.nodes.erase(n_it--); - } - //collect loop body nodes - else if(!loopheads.empty()) - { -#ifdef DEBUG - std::cout << "add " << n_it->location->location_number - << " for " << loopheads.back()->location->location_number << std::endl; -#endif - loops[loopheads.back()->location->location_number].body_nodes.push_front(*n_it); - SSA.nodes.erase(n_it--); - } - else - --n_it; - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_pre_post_map - * - * Input : - * - * Output : - * - * Purpose : find variables at loop head and backedge - * - * - *****************************************************************************/ - -void ssa_local_unwindert::build_pre_post_map() -{ - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - assert(!it->second.body_nodes.empty()); - const locationt &pre_loc = it->second.body_nodes.begin()->location; - const locationt &post_loc = (--it->second.body_nodes.end())->location; - SSA.current_location = pre_loc; //TODO: must go away - - //modified variables - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[pre_loc].phi_nodes; - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) - { - ssa_domaint::phi_nodest::const_iterator p_it = phi_nodes.find( - o_it->get_identifier()); - - if (p_it == phi_nodes.end()) - continue; // object not modified in this loop - - it->second.modified_vars.push_back(o_it->get_expr()); - symbol_exprt pre = SSA.name(*o_it, local_SSAt::PHI,pre_loc); - it->second.pre_post_map[pre] = SSA.read_rhs(*o_it, post_loc); - } - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_exit_conditions - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ - -void ssa_local_unwindert::build_exit_conditions() -{ - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - unsigned location_number_end = - it->second.body_nodes.back().location->location_number; -#ifdef DEBUG - std::cout << "end: " << location_number_end << std::endl; -#endif - for(local_SSAt::nodest::iterator n_it=it->second.body_nodes.begin(); - n_it!=it->second.body_nodes.end(); n_it++) - { - if(!n_it->location->is_goto()) - continue; - local_SSAt::nodest::iterator next = n_it; ++next; - SSA.current_location = n_it->location; //TODO: must go away - if(n_it->location->get_target()->location_number>location_number_end) - { - exprt g = SSA.guard_symbol(n_it->location); - exprt c = SSA.cond_symbol(n_it->location); - //need disjunction of all exit conditions - // for each symbol name at exit location - for (exprt::operandst::const_iterator - s_it = it->second.modified_vars.begin(); - s_it != it->second.modified_vars.end(); s_it++) - { - exprt e = SSA.read_rhs(*s_it,n_it->location); - it->second.exit_map[e].push_back(and_exprt(g,c)); - } - it->second.exit_map[g].push_back(and_exprt(g,c)); - it->second.exit_map[c].push_back(and_exprt(g,c)); - //collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g,c)); - } - } - else if(next==it->second.body_nodes.end() && - !n_it->location->guard.is_true()) - { //this happens in do-whiles - //ENHANCE: transform goto-program to make SSA uniform in this respect - exprt g = SSA.guard_symbol(n_it->location); - exprt c = SSA.cond_symbol(n_it->location); - for (exprt::operandst::const_iterator - s_it = it->second.modified_vars.begin(); - s_it != it->second.modified_vars.end(); s_it++) - { - exprt e = SSA.read_rhs(*s_it,n_it->location); - it->second.exit_map[e].push_back( - and_exprt(g,not_exprt(c))); - } - it->second.exit_map[g].push_back(and_exprt(g,not_exprt(c))); - it->second.exit_map[c].push_back(and_exprt(g,not_exprt(c))); - //collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g,not_exprt(c))); - } - } - } - } - //collect assertions for hoisting - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - if(!it->second.is_root) - continue; - for(loopt::assertion_hoisting_mapt::iterator - h_it = it->second.assertion_hoisting_map.begin(); - h_it != it->second.assertion_hoisting_map.end(); ++h_it) - { - local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - //find jump target in nodes - while(n_it->location != h_it->first && - n_it != SSA.nodes.end()) ++n_it; - local_SSAt::nodest::const_iterator prev = n_it; - for(; n_it != SSA.nodes.end(); ++n_it, ++prev) - { - //we collect only on top level, but not beyond loops - // so, if there is a gap in the nodes, we would jump over a loop - if(n_it!=prev && //this would fail in the first iteration otherwise - prev->location->location_number+1 != - n_it->location->location_number) - break; - if(n_it==prev) --prev; - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) - { - h_it->second.assertions.push_back(*a_it); - } - } - } - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::unwind_loop_at_location() - * - * Input : - * - * Output : - * - * Purpose : unwind the loop at the given location, up to k starting from - * previous unwindings - * - * - *****************************************************************************/ - -unsigned ssa_local_unwindert::unwind_loop_at_location(unsigned loc) -{ - unsigned k = loops.at(loc).current_unwinding+1; - unwind_loop_at_location(loc,k); - return k; -} - -void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) -{ - if(SSA.current_unwinding >= (long)k) - return; - - loopt &loop = loops[loc]; - loop.loop_enabling_exprs.push_back(symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k),bool_typet())); - - //current_enabling_expr = - // symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), - // bool_typet()); - //SSA.enabling_exprs.push_back(current_enabling_expr); - - - SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away - //recursively unwind everything - SSA.current_unwindings.clear(); - - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it){ - if(!it->second.is_root) - continue; - - if(it->first == loc) - unwind(it->second,k,false,false); //recursive - else - unwind(it->second,it->second.current_unwinding,false,true,k,loc); //recursive - - assert(SSA.current_unwindings.empty()); - } - - //update current unwinding - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - if(it->first == loc) - it->second.current_unwinding=k; - } - - compute_enable_expr(); - return; -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::unwind - * - * Input : - * - * Output : - * - * Purpose : unwind all loops up to k starting from previous unwindings - * - * - *****************************************************************************/ - -void ssa_local_unwindert::unwind(unsigned k) -{ - if(SSA.current_unwinding >= (long)k) - return; - - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - it->second.loop_enabling_exprs.push_back(symbol_exprt("unwind$"+id2string(fname)+"$loc"+i2string(it->first)+"$enable"+i2string(k),bool_typet())); - } - SSA.current_unwinding = k; //TODO: just for exploratory integration, must go away - - //recursively unwind everything - SSA.current_unwindings.clear(); - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - if(!it->second.is_root) - continue; - //unwind(it->second,k,false,false); //recursive - unwind(it->second,k,false,true,k,0,true); //recursive - assert(SSA.current_unwindings.empty()); - } - //update current unwinding - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - it->second.current_unwinding=k; - } - - compute_enable_expr(); -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::unwind - * - * Input : - * - * Output : - * - * Purpose : unwind all instances of given loop up to k - starting from previous unwindings, - and recurse - * - *****************************************************************************/ - -void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent, - bool propagate, unsigned prop_unwind, - unsigned prop_loc, bool propagate_all) -{ - odometert context = SSA.current_unwindings; -#ifdef DEBUG - std::cout << "unwind(k=" << k << ", is_new_parent=" << is_new_parent << "), "; - std::cout << "context=" << SSA.odometer_to_string(context,context.size()) - << std::endl; -#endif - SSA.increment_unwindings(1); - for(long i = 0; i<=(long)k; ++i) - { - //add new unwindings of this loop - if(i>loop.current_unwinding || is_new_parent) - { - add_loop_body(loop); - //set new loop end node - if(i==0) - { - assert(loop.end_nodes.find(context) == loop.end_nodes.end()); - loop.end_nodes[context] = --SSA.nodes.end(); - assert(loop.end_nodes.find(context) != loop.end_nodes.end()); -#ifdef DEBUG - std::cout << "end node for context " - << SSA.odometer_to_string(context,context.size()) << ": " - << loop.end_nodes[context]->location->location_number << " == " - << loop.body_nodes.back().location->location_number << std::endl; -#endif - } - if(i>0) - { - add_loop_connector(loop); - } - //in while loops we can only hoist in iterations %2 and higher - // otherwise we would block the loop exit that is only - // reachable via !guardls - bool is_last = (loop.is_dowhile && i==0) || - (!loop.is_dowhile && (i==0 || i==1)); - add_assertions(loop,is_last); - add_hoisted_assertions(loop,is_last); - } - if(i==(long)k) - { - add_loop_head(loop); - //update loop head -#ifdef DEBUG - std::cout << "update loop head for context " - << SSA.odometer_to_string(context,context.size()) << ": " - << loop.body_nodes.begin()->location->location_number << std::endl; -#endif - assert(loop.end_nodes.find(context) != loop.end_nodes.end()); - loop.end_nodes[context]->loophead = --SSA.nodes.end(); - assert(loop.end_nodes[context]->loophead->location->location_number == - loop.body_nodes.begin()->location->location_number); - } - //recurse into child loops - for(std::vector::iterator l_it = loop.loop_nodes.begin(); - l_it != loop.loop_nodes.end(); ++l_it) - { -#ifdef DEBUG - std::cout << i << ">" << loop.current_unwinding << std::endl; -#endif - if(propagate_all == true){ - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent,false); - } - else{ - if(propagate == true){ - // if this child loop is the desired loop then unwind k and do not propagate - // else unwind loop.current_unwinding and propagate - if(*l_it == prop_loc){ - unwind(loops[*l_it],k,i>loop.current_unwinding || - is_new_parent,false); - } - else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding,i>loop.current_unwinding || - is_new_parent,true,prop_unwind,prop_loc); - } - } - else{ - unwind(loops[*l_it],loops[*l_it].current_unwinding, - i>loop.current_unwinding || is_new_parent,false); - } - } - } - SSA.increment_unwindings(0); - } - SSA.increment_unwindings(-1); -#if 0 - std::cout << "calling add_exit_merge with k = " << k << "\n"; -#endif - add_exit_merges(loop,k); -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_body - * - * Input : - * - * Output : - * - * Purpose : duplicates the loop body for the current instance - * - * - *****************************************************************************/ - -void ssa_local_unwindert::add_loop_body(loopt &loop) -{ - local_SSAt::nodest::iterator it = loop.body_nodes.begin(); - ++it; //skip loop head, we'll do that separately - for(; it != loop.body_nodes.end(); ++it) - { - if(it->equalities.empty() && - it->constraints.empty() && - it->function_calls.empty()) - continue; - -#ifdef DEBUG - std::cout << "add body node: " - << it->location->location_number << std::endl; -#endif - SSA.nodes.push_back(*it); //copy - local_SSAt::nodet &node = SSA.nodes.back(); - node.loophead = SSA.nodes.end(); - node.marked = false; - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) - { - SSA.rename(*e_it, node.location); - } - for (local_SSAt::nodet::constraintst::iterator c_it = - node.constraints.begin(); c_it != node.constraints.end(); c_it++) - { - SSA.rename(*c_it, node.location); - } - for (local_SSAt::nodet::function_callst::iterator f_it = - node.function_calls.begin(); - f_it != node.function_calls.end(); f_it++) - { - SSA.rename(*f_it, node.location); - } - //will do assertions separately - node.assertions.clear(); - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_assertions - * - * Input : - * - * Output : - * - * Purpose : adds the assertions - * - *****************************************************************************/ - -void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) -{ - for(local_SSAt::nodest::iterator it = loop.body_nodes.begin(); - it != loop.body_nodes.end(); ++it) - { - if(it->assertions.empty()) - continue; - - SSA.nodes.push_back(local_SSAt::nodet(it->location, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node = SSA.nodes.back(); - node.assertions = it->assertions; - SSA.current_location = node.location; //TODO: must go away - for (local_SSAt::nodet::assertionst::iterator a_it = - node.assertions.begin(); a_it != node.assertions.end(); a_it++) - { - SSA.rename(*a_it, node.location); - if(!is_last) //only add assumptions if we are not in %0 iteration - { - if(is_kinduction) - node.constraints.push_back(*a_it); - else if(is_bmc) - { - //only add in base case - exprt gs = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, loop.body_nodes.back().location); - node.constraints.push_back(implies_exprt(not_exprt(gs),*a_it)); - } - } - } - if(!is_last && (is_bmc || is_kinduction)) - { - node.assertions.clear(); - } - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_head - * - * Input : - * - * Output : - * - * Purpose : adds the new loop head - * - *****************************************************************************/ - -void ssa_local_unwindert::add_loop_head(loopt &loop) -{ - // new connecting loop head for the current instance - // (enabled for this iteration) - SSA.nodes.push_back(loop.body_nodes.front()); //copy loop head - local_SSAt::nodet &node=SSA.nodes.back(); - node.marked = false; - node.enabling_expr = loop.loop_enabling_exprs.back(); - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) - { - SSA.rename(*e_it, node.location); - } - assert(node.constraints.empty()); - assert(node.function_calls.empty()); - assert(node.assertions.empty()); -#ifdef DEBUG - std::cout << "add loop head: " << node.location->location_number << std::endl; -#endif -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_loop_connector - * - * Input : - * - * Output : - * - * Purpose : adds a connector to the previous iteration - * - *****************************************************************************/ - -void ssa_local_unwindert::add_loop_connector(loopt &loop) -{ - // connector to previous iteration (permanently added) - const local_SSAt::nodet &orig_node=loop.body_nodes.front(); - SSA.nodes.push_back(local_SSAt::nodet(orig_node.location, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node=SSA.nodes.back(); - SSA.current_location = node.location; //TODO: must go away - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - orig_node.equalities.begin(); - e_it != orig_node.equalities.end(); e_it++) - { - if(e_it->rhs().id()==ID_if) - { - node.equalities.push_back(*e_it); - node.equalities.back().rhs() = - loop.pre_post_map[to_symbol_expr(e_it->lhs())]; - SSA.rename(node.equalities.back().rhs(), node.location); - SSA.decrement_unwindings(0); - SSA.rename(node.equalities.back().lhs(), node.location); - SSA.increment_unwindings(0); - } - else if(e_it->lhs().id()==ID_symbol && - !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), - "ssa::$guard")) - { //this is needed for while loops - node.equalities.push_back(*e_it); - SSA.decrement_unwindings(0); - SSA.rename(node.equalities.back(), node.location); - SSA.increment_unwindings(0); - } - } - //continuation guard and condition - exprt g_rhs = and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); - SSA.decrement_unwindings(0); - exprt g_lhs = SSA.guard_symbol(loop.body_nodes.begin()->location); - SSA.increment_unwindings(0); - node.equalities.push_back(equal_exprt(g_lhs,g_rhs)); -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_exit_merges - * - * Input : - * - * Output : - * - * Purpose : adds the merge connector for the loop exits for the current instance - * - * - *****************************************************************************/ - -void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) -{ - SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node=SSA.nodes.back(); - node.enabling_expr = loop.loop_enabling_exprs.back(); - - for (loopt::exit_mapt::const_iterator x_it = loop.exit_map.begin(); - x_it != loop.exit_map.end(); x_it++) - { - node.equalities.push_back(build_exit_merge(x_it->first, - disjunction(x_it->second),k,node.location)); - } -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::build_exit_merge - * - * Input : - * - * Output : - * - * Purpose : generates exit merge expression for a given expression - * - *****************************************************************************/ - -equal_exprt ssa_local_unwindert::build_exit_merge( - exprt e, const exprt &exits, unsigned k, locationt loc) -{ - exprt re = e; - SSA.increment_unwindings(1); - SSA.rename(re, loc); //%0 - for (unsigned i = 1; i <= k; i++) - { - SSA.increment_unwindings(0); - exprt cond_expr = exits; - SSA.rename(cond_expr, loc); - exprt true_expr = e; - SSA.rename(true_expr, loc); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); - } - SSA.increment_unwindings(-1); - SSA.rename(e, loc); //lhs - return equal_exprt(e,re); -} - -/***************************************************************************** - * - * Function : ssa_local_unwindert::add_hoisted_assertions - * - * Input : - * - * Output : - * - * Purpose : adds the assumptions for hoisted assertions - for the current instance - * - *****************************************************************************/ - -void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) -{ - for(loopt::assertion_hoisting_mapt::const_iterator - it = loop.assertion_hoisting_map.begin(); - it != loop.assertion_hoisting_map.end(); ++it) - { - if(!is_last //only add assumptions if we are not in %0 iteration - && is_kinduction && !it->second.assertions.empty()) - { - exprt e = disjunction(it->second.exit_conditions); - SSA.rename(e, loop.body_nodes.begin()->location); - SSA.nodes.push_back(local_SSAt::nodet(it->first, - SSA.nodes.end())); //add new node - local_SSAt::nodet &node = SSA.nodes.back(); - node.constraints.push_back( - implies_exprt(e,conjunction(it->second.assertions))); -#ifdef DEBUG - std::cout << "adding hoisted assumption: " - << from_expr(SSA.ns, "", node.constraints.back()) - << std::endl; -#endif - } - } -} - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::compute_enable_expr - * - * Input : - * - * Output : - * - * Purpose : updates the enable_expr - * - *****************************************************************************/ - -void ssa_local_unwindert::compute_enable_expr() -{ - SSA.enabling_exprs.clear(); - for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) - { - for(exprt::operandst::const_iterator - e_it = ((it->second).loop_enabling_exprs).begin(); - e_it != ((it->second).loop_enabling_exprs).end(); e_it++) - { - exprt::operandst::const_iterator lh = e_it; lh++; - if(lh != ((it->second).loop_enabling_exprs).end()) - SSA.enabling_exprs.push_back(not_exprt(*e_it)); - else - SSA.enabling_exprs.push_back(*e_it); - } - } -} - - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::compute_loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : compute loop continuation conditions for all loops in this function - * - *****************************************************************************/ - -void ssa_local_unwindert::compute_loop_continuation_conditions() -{ - //clear old ones - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - it->second.current_continuation_conditions.clear(); - } - //compute new - SSA.current_unwindings.clear(); - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - if(!it->second.is_root) - continue; - compute_loop_continuation_conditions(it->second); //recursive - assert(SSA.current_unwindings.empty()); - } -} - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : return loop continuation conditions for all loops in this function - * - *****************************************************************************/ - -void ssa_local_unwindert::loop_continuation_conditions( - exprt::operandst &loop_cont) const -{ - for(loop_mapt::const_iterator it = loops.begin(); it != loops.end(); ++it) - { - loop_cont.insert(loop_cont.end(), - it->second.current_continuation_conditions.begin(), - it->second.current_continuation_conditions.end()); - } -} - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : return loop continuation conditions for all instances - * of the given loop in this function - * - *****************************************************************************/ - -void ssa_local_unwindert::loop_continuation_conditions( - const locationt& loop_id, - exprt::operandst &loop_cont) const -{ - if(loops.empty()) //ignore silently, TBD - return; - - const loopt &loop = loops.at(loop_id->location_number); - loop_cont.insert(loop_cont.end(), - loop.current_continuation_conditions.begin(), - loop.current_continuation_conditions.end()); -} - - -/***************************************************************************** - * - * Function : ssa_local_unwindert::get_continuation_condition - * - * Input : - * - * Output : - * - * Purpose : - * - * - *****************************************************************************/ - -exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const -{ - SSA.current_location = loop.body_nodes.back().location; //TODO: must go away - return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); -} - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::compute_loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : recursively construct loop continuation conditions - * - *****************************************************************************/ - -void ssa_local_unwindert::compute_loop_continuation_conditions( - loopt& loop) -{ - SSA.increment_unwindings(1); - loop.current_continuation_conditions.push_back( - get_continuation_condition(loop)); //%0 - for(long i=0; i<=loop.current_unwinding; ++i) - { - //recurse into child loops - for(std::vector::iterator l_it = loop.loop_nodes.begin(); - l_it != loop.loop_nodes.end(); ++l_it) - { - compute_loop_continuation_conditions(loops.at(*l_it)); - } - SSA.increment_unwindings(0); - } - SSA.increment_unwindings(-1); -} - - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::unwinder_rename - * - * Input : var, node - * - * Output : var is returned with a suffix that reflects the current unwinding - * with the context taken from the node - * - * E.g. - * - * node must look like - * - * cond"somesuffix" == TRUE - * - * e.g. cond%1%2%5%0 == TRUE - * - * and variable might be guard#ls25 - * - * if the current_unwinding is 6 - * - * the variable should be converted to guard#ls25%1%2%5%5 - * - * Note that if current_unwinding is X then suffixes can have at most - * X-1 in its parts - * - *****************************************************************************/ - -void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const -{ - //TODO: replace this by SSA.rename - // this requires odometer information in the nodes - - //only to be called for backedge nodes - //This is very dirty hack :-( - if(SSA.current_unwinding<0) return; - assert(node.equalities.size()>=1); - //copy suffix from equality lhs to var - std::string id = - id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); - size_t pos = id.find_first_of("%"); - if(pos==std::string::npos) return; - size_t pos1 = id.find_last_of("%"); - std::string suffix; - unsigned unwinding = pre ? SSA.current_unwinding : 0; - if(pos==pos1) - { - suffix = "%"+i2string(unwinding); - } - else - { - suffix = id.substr(pos,pos1-pos); - suffix += "%"+i2string(unwinding); - } - - var.set_identifier(id2string(var.get_identifier())+suffix); -#ifdef DEBUG - std::cout << "new id: " << var.get_identifier() << std::endl; -#endif -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind_loop_alone() - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned int k) -{ - assert(is_initialized); - unwinder_mapt::iterator it = unwinder_map.find(fname); - assert(it != unwinder_map.end()); - it->second.unwind_loop_at_location(loc, k); -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind_loop_once_more() - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -unsigned ssa_unwindert::unwind_loop_once_more(const irep_idt fname, unsigned loc) -{ - assert(is_initialized); - unwinder_mapt::iterator it = unwinder_map.find(fname); - assert(it != unwinder_map.end()); - return it->second.unwind_loop_at_location(loc); -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind - * - * Input : fname - name of the goto-function to be unwound, k - unwinding depth - * - * Output : false - if id does not correspond to any goto-function in the - * unwinder_map - * - * Purpose : incrementally unwind a function 'id' up to depth k. Initializer - * must have been invoked before calling this function - * - *****************************************************************************/ - -void ssa_unwindert::unwind(const irep_idt fname, unsigned int k) -{ - assert(is_initialized); - unwinder_mapt::iterator it = unwinder_map.find(fname); - assert(it != unwinder_map.end()); - it->second.unwind(k); -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::unwind_all - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::unwind_all(unsigned int k) -{ - assert(is_initialized); - - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - it->second.unwind(k); - } -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : Initialize unwinder_map by computing hierarchical tree_loopnodet - * for every goto-function - * Set is_initialized to true. Initializer must be called before - * unwind funcitions are called. - * - *****************************************************************************/ -void ssa_unwindert::init(bool is_kinduction, bool is_bmc) -{ - ssa_dbt::functionst& funcs = ssa_db.functions(); - for (ssa_dbt::functionst::iterator it = funcs.begin(); - it != funcs.end(); it++) - { - unwinder_map.insert(unwinder_pairt(it->first, - ssa_local_unwindert(it->first, (*(it->second)), - is_kinduction, is_bmc))); - } -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::init_localunwinders - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::init_localunwinders() -{ - for(unwinder_mapt::iterator it=unwinder_map.begin(); - it!=unwinder_map.end();it++) - { - it->second.init(); - } - is_initialized = true; -} +/*******************************************************************\ + +Module: SSA Unwinder + +Author: Peter Schrammel, Saurabh Joshi + +\*******************************************************************/ + +// #define DEBUG + +#include + +#include "ssa_unwinder.h" + +/*******************************************************************\ + +Function: ssa_local_unwindert::init + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_local_unwindert::init() +{ + build_loop_tree(); + build_pre_post_map(); + build_exit_conditions(); + unwind(0); +#ifdef DEBUG + SSA.output_verbose(std::cout); +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_loop_tree + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_local_unwindert::build_loop_tree() +{ + // build loop tree structure + // Assumes that initially the nodes are in the same order + // as in the goto program + std::list loopheads; + local_SSAt::nodest::iterator n_it=--SSA.nodes.end(); + while(n_it!=SSA.nodes.begin()) + { + // end of loop found + if(n_it->loophead!=SSA.nodes.end()) + { + loopt &loop=loops[n_it->loophead->location->location_number]; + if(loopheads.empty()) + { + loop.is_root=true; + } + else + { + loops[loopheads.back()->location->location_number]. + loop_nodes.push_back( + n_it->loophead->location->location_number); + } + loopheads.push_back(n_it->loophead); + loop.body_nodes.push_front(*n_it); +#ifdef DEBUG + std::cout << "pop " << n_it->location->location_number + << " for " << n_it->loophead->location->location_number + << std::endl; +#endif + // this test is ambiguous if the loop condition is true, + // but shouldn't have any consequences + assert(n_it->location->is_backwards_goto()); + loop.is_dowhile=!n_it->location->guard.is_true(); + SSA.nodes.erase(n_it--); + } + // beginning of loop found + else if(!loopheads.empty() && n_it==loopheads.back()) + { +#ifdef DEBUG + std::cout << "push " << n_it->location->location_number << std::endl; +#endif + loops[n_it->location->location_number].body_nodes.push_front(*n_it); + loopheads.pop_back(); + loops[n_it->location->location_number].body_nodes.back().loophead= + loops[n_it->location->location_number].body_nodes.begin(); + SSA.nodes.erase(n_it--); + } + // collect loop body nodes + else if(!loopheads.empty()) + { +#ifdef DEBUG + std::cout << "add " << n_it->location->location_number + << " for " << loopheads.back()->location->location_number + << std::endl; +#endif + loops[loopheads.back()->location->location_number]. + body_nodes.push_front(*n_it); + SSA.nodes.erase(n_it--); + } + else + --n_it; + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_pre_post_map + + Inputs: + + Outputs: + + Purpose: find variables at loop head and backedge + +\*******************************************************************/ + +void ssa_local_unwindert::build_pre_post_map() +{ + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + assert(!it->second.body_nodes.empty()); + const locationt &pre_loc=it->second.body_nodes.begin()->location; + const locationt &post_loc=(--it->second.body_nodes.end())->location; + SSA.current_location=pre_loc; // TODO: must go away + + // modified variables + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[pre_loc].phi_nodes; + for(local_SSAt::objectst::const_iterator o_it= + SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it=phi_nodes.find( + o_it->get_identifier()); + + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + it->second.modified_vars.push_back(o_it->get_expr()); + symbol_exprt pre=SSA.name(*o_it, local_SSAt::PHI, pre_loc); + it->second.pre_post_map[pre]=SSA.read_rhs(*o_it, post_loc); + } + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_conditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_local_unwindert::build_exit_conditions() +{ + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + unsigned location_number_end= + it->second.body_nodes.back().location->location_number; +#ifdef DEBUG + std::cout << "end: " << location_number_end << std::endl; +#endif + for(local_SSAt::nodest::iterator n_it=it->second.body_nodes.begin(); + n_it!=it->second.body_nodes.end(); n_it++) + { + if(!n_it->location->is_goto()) + continue; + local_SSAt::nodest::iterator next=n_it; ++next; + SSA.current_location=n_it->location; // TODO: must go away + if(n_it->location->get_target()->location_number>location_number_end) + { + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + // need disjunction of all exit conditions + // for each symbol name at exit location + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back(and_exprt(g, c)); + } + it->second.exit_map[g].push_back(and_exprt(g, c)); + it->second.exit_map[c].push_back(and_exprt(g, c)); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, c)); + } + } + else if(next==it->second.body_nodes.end() && + !n_it->location->guard.is_true()) + { // this happens in do-whiles + // ENHANCE: transform goto-program to make SSA uniform in this respect + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back( + and_exprt(g, not_exprt(c))); + } + it->second.exit_map[g].push_back(and_exprt(g, not_exprt(c))); + it->second.exit_map[c].push_back(and_exprt(g, not_exprt(c))); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, not_exprt(c))); + } + } + } + } + // collect assertions for hoisting + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + for(loopt::assertion_hoisting_mapt::iterator + h_it=it->second.assertion_hoisting_map.begin(); + h_it!=it->second.assertion_hoisting_map.end(); ++h_it) + { + local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + // find jump target in nodes + while(n_it->location!=h_it->first && + n_it!=SSA.nodes.end()) ++n_it; + local_SSAt::nodest::const_iterator prev=n_it; + for(; n_it!=SSA.nodes.end(); ++n_it, ++prev) + { + // we collect only on top level, but not beyond loops + // so, if there is a gap in the nodes, we would jump over a loop + if(n_it!=prev && // this would fail in the first iteration otherwise + prev->location->location_number+1!= + n_it->location->location_number) + break; + if(n_it==prev) + --prev; + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + h_it->second.assertions.push_back(*a_it); + } + } + } + } +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: + + Outputs: + + Purpose: unwind all loops up to k starting from previous unwindings + +\*******************************************************************/ + +void ssa_local_unwindert::unwind(unsigned k) +{ + if(SSA.current_unwinding>=(long)k) + return; + + current_enabling_expr= + symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), + bool_typet()); + SSA.enabling_exprs.push_back(current_enabling_expr); + + // TODO: just for exploratory integration, must go away + SSA.current_unwinding=k; + + // recursively unwind everything + SSA.current_unwindings.clear(); + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + unwind(it->second, k, false); // recursive + assert(SSA.current_unwindings.empty()); + } + // update current unwinding + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + it->second.current_unwinding=k; + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: + + Outputs: + + Purpose: unwind all instances of given loop up to k + starting from previous unwindings, and recur + +\*******************************************************************/ + +void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) +{ + odometert context=SSA.current_unwindings; +#ifdef DEBUG + std::cout << "unwind(k=" << k << ", is_new_parent=" << is_new_parent << "), "; + std::cout << "context=" << SSA.odometer_to_string(context, context.size()) + << std::endl; +#endif + SSA.increment_unwindings(1); + for(long i=0; i<=k; ++i) + { + // add new unwindings of this loop + if(i>loop.current_unwinding || is_new_parent) + { + add_loop_body(loop); + // set new loop end node + if(i==0) + { + assert(loop.end_nodes.find(context)==loop.end_nodes.end()); + loop.end_nodes[context]=--SSA.nodes.end(); + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); +#ifdef DEBUG + std::cout << "end node for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.end_nodes[context]->location->location_number << "==" + << loop.body_nodes.back().location->location_number + << std::endl; +#endif + } + if(i>0) + { + add_loop_connector(loop); + } + // in while loops we can only hoist in iterations %2 and higher + // otherwise we would block the loop exit that is only + // reachable via !guardls + bool is_last=(loop.is_dowhile && i==0) || + (!loop.is_dowhile && (i==0 || i==1)); + add_assertions(loop, is_last); + add_hoisted_assertions(loop, is_last); + } + if(i==k) + { + add_loop_head(loop); + // update loop head +#ifdef DEBUG + std::cout << "update loop head for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.body_nodes.begin()->location->location_number + << std::endl; +#endif + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); + loop.end_nodes[context]->loophead=--SSA.nodes.end(); + assert( + loop.end_nodes[context]->loophead->location->location_number== + loop.body_nodes.begin()->location->location_number); + } + // recurse into child loops + for(const auto &l : loop.loop_nodes) + { +#ifdef DEBUG + std::cout << i << ">" << loop.current_unwinding << std::endl; +#endif + unwind(loops[l], k, i>loop.current_unwinding || is_new_parent); + } + SSA.increment_unwindings(0); + } + SSA.increment_unwindings(-1); + add_exit_merges(loop, k); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_body + + Inputs: + + Outputs: + + Purpose: duplicates the loop body for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_body(loopt &loop) +{ + local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + ++it; // skip loop head, we'll do that separately + for(; it!=loop.body_nodes.end(); ++it) + { + if(it->equalities.empty() && + it->constraints.empty() && + it->function_calls.empty()) + continue; + +#ifdef DEBUG + std::cout << "add body node: " + << it->location->location_number << std::endl; +#endif + SSA.nodes.push_back(*it); // copy + local_SSAt::nodet &node=SSA.nodes.back(); + node.loophead=SSA.nodes.end(); + node.marked=false; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) + { + SSA.rename(*e_it, node.location); + } + for(local_SSAt::nodet::constraintst::iterator c_it= + node.constraints.begin(); c_it!=node.constraints.end(); c_it++) + { + SSA.rename(*c_it, node.location); + } + for(local_SSAt::nodet::function_callst::iterator f_it= + node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + SSA.rename(*f_it, node.location); + } + // will do assertions separately + node.assertions.clear(); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_assertions + + Inputs: + + Outputs: + + Purpose: adds the assertions and assumptions + +\*******************************************************************/ + +void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) +{ + for(local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + it!=loop.body_nodes.end(); ++it) + { + if(it->assertions.empty()) + continue; + + SSA.nodes.push_back( + local_SSAt::nodet(it->location, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.assertions=it->assertions; + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::assertionst::iterator a_it= + node.assertions.begin(); a_it!=node.assertions.end(); a_it++) + { + SSA.rename(*a_it, node.location); + if(!is_last) // only add assumptions if we are not in %0 iteration + { + if(is_kinduction) + node.constraints.push_back(*a_it); + else if(is_bmc) + { + // only add in base case + exprt gs= + SSA.name( + SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, + loop.body_nodes.back().location); + node.constraints.push_back(implies_exprt(not_exprt(gs), *a_it)); + } + } + } + if(!is_last && (is_bmc || is_kinduction)) + { + node.assertions.clear(); + } + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_head + + Inputs: + + Outputs: + + Purpose: adds the new loop head + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_head(loopt &loop) +{ + // new connecting loop head for the current instance + // (enabled for this iteration) + SSA.nodes.push_back(loop.body_nodes.front()); // copy loop head + local_SSAt::nodet &node=SSA.nodes.back(); + node.marked=false; + node.enabling_expr=current_enabling_expr; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) + { + SSA.rename(*e_it, node.location); + } + assert(node.constraints.empty()); + assert(node.function_calls.empty()); + assert(node.assertions.empty()); +#ifdef DEBUG + std::cout << "add loop head: " << node.location->location_number << std::endl; +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_connector + + Inputs: + + Outputs: + + Purpose: adds a connector to the previous iteration + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_connector(loopt &loop) +{ + // connector to previous iteration (permanently added) + const local_SSAt::nodet &orig_node=loop.body_nodes.front(); + SSA.nodes.push_back( + local_SSAt::nodet(orig_node.location, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + orig_node.equalities.begin(); + e_it!=orig_node.equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if) + { + node.equalities.push_back(*e_it); + node.equalities.back().rhs()= + loop.pre_post_map[to_symbol_expr(e_it->lhs())]; + SSA.rename(node.equalities.back().rhs(), node.location); + SSA.decrement_unwindings(0); + SSA.rename(node.equalities.back().lhs(), node.location); + SSA.increment_unwindings(0); + } + else if(e_it->lhs().id()==ID_symbol && + !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), + "ssa::$guard")) + { // this is needed for while loops + node.equalities.push_back(*e_it); + SSA.decrement_unwindings(0); + SSA.rename(node.equalities.back(), node.location); + SSA.increment_unwindings(0); + } + } + // continuation guard and condition + exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); + SSA.decrement_unwindings(0); + exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); + SSA.increment_unwindings(0); + node.equalities.push_back(equal_exprt(g_lhs, g_rhs)); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_exit_merges + + Inputs: + + Outputs: + + Purpose: adds the merge connector for the loop exits for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) +{ + SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, + SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.enabling_expr=current_enabling_expr; + + for(loopt::exit_mapt::const_iterator x_it=loop.exit_map.begin(); + x_it!=loop.exit_map.end(); x_it++) + { + node.equalities.push_back( + build_exit_merge( + x_it->first, disjunction(x_it->second), k, node.location)); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_merge + + Inputs: + + Outputs: + + Purpose: generates exit merge expression for a given expression + +\*******************************************************************/ + +equal_exprt ssa_local_unwindert::build_exit_merge( + exprt e, const exprt &exits, unsigned k, locationt loc) +{ + exprt re=e; + SSA.increment_unwindings(1); + SSA.rename(re, loc); // %0 + for(long i=1; i<=k; i++) + { + SSA.increment_unwindings(0); + exprt cond_expr=exits; + SSA.rename(cond_expr, loc); + exprt true_expr=e; + SSA.rename(true_expr, loc); + exprt false_expr=re; + re=if_exprt(cond_expr, true_expr, false_expr); + } + SSA.increment_unwindings(-1); + SSA.rename(e, loc); // lhs + return equal_exprt(e, re); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_hoisted_assertions + + Inputs: + + Outputs: + + Purpose: adds the assumptions for hoisted assertions + for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) +{ + for(loopt::assertion_hoisting_mapt::const_iterator + it=loop.assertion_hoisting_map.begin(); + it!=loop.assertion_hoisting_map.end(); ++it) + { + if(!is_last // only add assumptions if we are not in %0 iteration + && is_kinduction && !it->second.assertions.empty()) + { + exprt e=disjunction(it->second.exit_conditions); + SSA.rename(e, loop.body_nodes.begin()->location); + SSA.nodes.push_back( + local_SSAt::nodet(it->first, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.constraints.push_back( + implies_exprt(e, conjunction(it->second.assertions))); +#ifdef DEBUG + std::cout << "adding hoisted assumption: " + << from_expr(SSA.ns, "", node.constraints.back()) + << std::endl; +#endif + } + } +} + + +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::compute_loop_continuation_conditions + * + * Input : + * + * Output : + * + * Purpose : compute loop continuation conditions for all loops in this function + * + *****************************************************************************/ + +void ssa_local_unwindert::compute_loop_continuation_conditions() +{ + //clear old ones + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + { + it->second.current_continuation_conditions.clear(); + } + //compute new + SSA.current_unwindings.clear(); + for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) + { + if(!it->second.is_root) + continue; + compute_loop_continuation_conditions(it->second); //recursive + assert(SSA.current_unwindings.empty()); + } +} + +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::compute_loop_continuation_conditions + * + * Input : + * + * Output : + * + * Purpose : recursively construct loop continuation conditions + * + *****************************************************************************/ + +void ssa_local_unwindert::compute_loop_continuation_conditions( + loopt& loop) +{ + SSA.increment_unwindings(1); + loop.current_continuation_conditions.push_back( + get_continuation_condition(loop)); //%0 + for(long i=0; i<=loop.current_unwinding; ++i) + { + //recurse into child loops + for(std::vector::iterator l_it = loop.loop_nodes.begin(); + l_it != loop.loop_nodes.end(); ++l_it) + { + compute_loop_continuation_conditions(loops.at(*l_it)); + } + SSA.increment_unwindings(0); + } + SSA.increment_unwindings(-1); +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: return loop continuation conditions for all loops in this function + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + exprt::operandst& loop_cont) const +{ + SSA.current_unwindings.clear(); + for(loop_mapt::const_iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + loop_continuation_conditions(it->second, loop_cont); // recursive + assert(SSA.current_unwindings.empty()); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::get_continuation_condition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const +{ + SSA.current_location=loop.body_nodes.back().location; // TODO: must go away + return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: recursively construct loop continuation conditions + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + const loopt& loop, exprt::operandst& loop_cont) const +{ + SSA.increment_unwindings(1); + loop_cont.push_back(get_continuation_condition(loop)); // %0 + for(long i=0; i<=loop.current_unwinding; ++i) + { + // recurse into child loops + for(const auto &l : loop.loop_nodes) + { + loop_continuation_conditions(loops.at(l), loop_cont); + } + SSA.increment_unwindings(0); + } + SSA.increment_unwindings(-1); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwinder_rename + + Inputs: var, node + + Outputs: var is returned with a suffix that reflects the current unwinding + with the context taken from the node + + Purpose: E.g. node must look like + cond"somesuffix"==TRUE, e.g. cond%1%2%5%0==TRUE + and variable might be guard#ls25 + if the current_unwinding is 6 + the variable should be converted to guard#ls25%1%2%5%5 + + Note that if current_unwinding is X then suffixes can have at most + X-1 in its parts + +\*******************************************************************/ + +void ssa_local_unwindert::unwinder_rename( + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const +{ + // TODO: replace this by SSA.rename + // this requires odometer information in the nodes + + // only to be called for backedge nodes + // This is very dirty hack + if(SSA.current_unwinding<0) + return; + + assert(node.equalities.size()>=1); + // copy suffix from equality lhs to var + std::string id= + id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); + size_t pos=id.find_first_of("%"); + if(pos==std::string::npos) + return; + size_t pos1=id.find_last_of("%"); + std::string suffix; + unsigned unwinding=pre ? SSA.current_unwinding : 0; + if(pos==pos1) + { + suffix="%"+i2string(unwinding); + } + else + { + suffix=id.substr(pos, pos1-pos); + suffix+="%"+i2string(unwinding); + } + + var.set_identifier(id2string(var.get_identifier())+suffix); +#ifdef DEBUG + std::cout << "new id: " << var.get_identifier() << std::endl; +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::find_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_local_unwindert::find_loop( + unsigned location_number, const loopt *&loop) const +{ + loop_mapt::const_iterator it=loops.find(location_number); + if(it!=loops.end()) + { + loop=&it->second; + return true; + } + return false; +} + +/*******************************************************************\ + +Function: ssa_unwindert::unwind + + Inputs: fname-name of the goto-function to be unwound, k-unwinding depth + + Outputs: false-if id does not correspond to any goto-function in the + unwinder_map + + Purpose: incrementally unwind a function 'id' up to depth k. Initializer + must have been invoked before calling this function + +\*******************************************************************/ + +void ssa_unwindert::unwind(const irep_idt fname, unsigned int k) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + it->second.unwind(k); +} + +/*******************************************************************\ + +Function: ssa_unwindert::unwind_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::unwind_all(unsigned int k) +{ + assert(is_initialized); + + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.unwind(k); +} + +/*******************************************************************\ + +Function: ssa_unwindert::init + + Inputs: + + Outputs: + + Purpose: Initialize unwinder_map by computing hierarchical tree_loopnodet + for every goto-function + Set is_initialized to true. Initializer must be called before + unwind funcitions are called. + +\*******************************************************************/ + +void ssa_unwindert::init(bool is_kinduction, bool is_bmc) +{ + ssa_dbt::functionst& funcs=ssa_db.functions(); + for(auto &f : funcs) + { + unwinder_map.insert(unwinder_pairt( + f.first, + ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); + } +} + +/*******************************************************************\ + +Function: ssa_unwindert::init_localunwinders + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::init_localunwinders() +{ + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.init(); + + is_initialized=true; +} diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index eb4105e07..551bf1bf5 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -1,151 +1,165 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Peter Schrammel, Saurabh Joshi - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SSA_UNWINDER_H -#define CPROVER_DELTACHECK_SSA_UNWINDER_H - -#include "unwindable_local_ssa.h" -#include "../summarizer/ssa_db.h" - -class ssa_local_unwindert -{ -public: - typedef local_SSAt::locationt locationt; - typedef unwindable_local_SSAt::odometert odometert; - - ssa_local_unwindert(const irep_idt _fname, unwindable_local_SSAt& _SSA, - bool _is_kinduction, bool _is_bmc) - : - fname(_fname),SSA(_SSA), is_kinduction(_is_kinduction), is_bmc(_is_bmc) - {} - - void init(); - - void unwind_loop_at_location(unsigned loc, unsigned k); - unsigned unwind_loop_at_location(unsigned loc); - void unwind(unsigned k); - - //TODO: not yet sure how to do that -/* void unwind(locationt loop_head_loc, unsigned k) - { unwind(loops[loop_head_loc],k); } */ - - //TODO: this should be loop specific in future, maybe move to unwindable_local_ssa as it is not really unwinder related - void compute_loop_continuation_conditions(); - void compute_enable_expr(); - void loop_continuation_conditions(exprt::operandst &loop_cont) const; - void loop_continuation_conditions(const locationt& loop_id, - exprt::operandst &loop_cont) const; - - //TODO: these two should be possible with unwindable_local_ssa facilities - //exprt rename_invariant(const exprt& inv_in) const; - //void unwinder_rename(symbol_exprt &var,const local_SSAt::nodet &node, bool pre) const; - - //TODO: this must go away, should use SSA.rename instead - void unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const; -protected: - const irep_idt fname; - unwindable_local_SSAt& SSA; - bool is_kinduction,is_bmc; - - class loopt //loop tree - { - public: - loopt() - : - is_dowhile(false), - is_root(false), - current_unwinding(-1) - {} - - local_SSAt::nodest body_nodes; - //pointer to loop end nodes in SSA for updating current loop head - std::map end_nodes; - std::vector loop_nodes; //child loops - bool is_dowhile; - bool is_root; - long current_unwinding; - - // to have an enabling_expr and current_unwindings (odometert) - exprt::operandst loop_enabling_exprs; - - exprt::operandst current_continuation_conditions; - - typedef std::map exit_mapt; - exit_mapt exit_map; - std::map pre_post_map; - std::vector modified_vars; - - //for assertion hoisting - typedef struct { - exprt::operandst exit_conditions; - exprt::operandst assertions; - } assertions_after_loopt; - typedef std::map assertion_hoisting_mapt; - assertion_hoisting_mapt assertion_hoisting_map; - - }; - - //use location numbers as indices, as target addresses make - // things non-deterministic - typedef std::map loop_mapt; - loop_mapt loops; - - void build_loop_tree(); - void build_pre_post_map(); - void build_exit_conditions(); - - void unwind(loopt &loop, unsigned k, bool is_new_parent, - bool propagate = false, unsigned prop_unwind = 0, - unsigned prop_loc = 0, bool propagate_all = false); - - exprt get_continuation_condition(const loopt& loop) const; - void compute_loop_continuation_conditions(loopt& loop); - - void add_loop_body(loopt &loop); - void add_assertions(loopt &loop, bool is_last); - void add_loop_head(loopt &loop); - void add_loop_connector(loopt &loop); - void add_exit_merges(loopt &loop, unsigned k); - equal_exprt build_exit_merge(exprt e, const exprt &exits, - unsigned k, locationt loc); - void add_hoisted_assertions(loopt &loop, bool is_last); -}; - -class ssa_unwindert : public messaget -{ - -public: - typedef std::map unwinder_mapt; - typedef std::pair unwinder_pairt; - - ssa_unwindert(ssa_dbt& _db) - : - ssa_db(_db), is_initialized(false) - {} - - void init(bool is_kinduction, bool is_bmc); - void init_localunwinders(); - - void unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned k); - unsigned unwind_loop_once_more(const irep_idt fname, unsigned loc); - void unwind(const irep_idt fname, unsigned k); - void unwind_all(unsigned k); - - ssa_local_unwindert &get(const irep_idt& fname) - { return unwinder_map.at(fname); } - - -protected: - ssa_dbt& ssa_db; - bool is_initialized; - unwinder_mapt unwinder_map; - -}; - -#endif +/*******************************************************************\ + +Module: SSA Unwinder + +Author: Peter Schrammel, Saurabh Joshi + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_UNWINDER_H +#define CPROVER_2LS_SSA_SSA_UNWINDER_H + +#include "unwindable_local_ssa.h" +#include "ssa_db.h" + +class ssa_local_unwindert +{ +public: + typedef local_SSAt::locationt locationt; + typedef unwindable_local_SSAt::odometert odometert; + + ssa_local_unwindert( + const irep_idt _fname, + unwindable_local_SSAt& _SSA, + bool _is_kinduction, + bool _is_bmc): + fname(_fname), + SSA(_SSA), + is_kinduction(_is_kinduction), + is_bmc(_is_bmc) + { + } + + void init(); + + void unwind(unsigned k); + +#if 0 + // TODO: not yet sure how to do that + void unwind(locationt loop_head_loc, unsigned k) + { + unwind(loops[loop_head_loc], k); + } +#endif + + void compute_loop_continuation_conditions(); + + // TODO: this should be loop specific in future, + // maybe move to unwindable_local_ssa as it is not really unwinder related + void loop_continuation_conditions(exprt::operandst& loop_cont) const; + +#if 0 + // TODO: these two should be possible with unwindable_local_ssa facilities + exprt rename_invariant(const exprt& inv_in) const; + void unwinder_rename( + symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; +#endif + + // TODO: this must go away, should use SSA.rename instead + void unwinder_rename( + symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; + + class loopt // loop tree + { + public: + loopt(): + is_dowhile(false), + is_root(false), + current_unwinding(-1) + { + } + + local_SSAt::nodest body_nodes; + // pointer to loop end nodes in SSA for updating current loop head + std::map end_nodes; + std::vector loop_nodes; // child loops + bool is_dowhile; + bool is_root; + long current_unwinding; + + exprt::operandst current_continuation_conditions; + + typedef std::map exit_mapt; + exit_mapt exit_map; + std::map pre_post_map; + std::vector modified_vars; + + // for assertion hoisting + typedef struct + { + exprt::operandst exit_conditions; + exprt::operandst assertions; + } assertions_after_loopt; + typedef std::map assertion_hoisting_mapt; + assertion_hoisting_mapt assertion_hoisting_map; + }; + + bool find_loop(unsigned location_number, const loopt *&loop) const; + +protected: + const irep_idt fname; + unwindable_local_SSAt &SSA; + bool is_kinduction, is_bmc; + symbol_exprt current_enabling_expr; // TODO must become loop-specific + + // use location numbers as indices, as target addresses make + // things non-deterministic + typedef std::map loop_mapt; + loop_mapt loops; + + void build_loop_tree(); + void build_pre_post_map(); + void build_exit_conditions(); + + void unwind(loopt &loop, unsigned k, bool is_new_parent); + + exprt get_continuation_condition(const loopt& loop) const; + void compute_loop_continuation_conditions(loopt& loop); + void loop_continuation_conditions( + const loopt& loop, exprt::operandst &loop_cont) const; + + void add_loop_body(loopt &loop); + void add_assertions(loopt &loop, bool is_last); + void add_loop_head(loopt &loop); + void add_loop_connector(loopt &loop); + void add_exit_merges(loopt &loop, unsigned k); + equal_exprt build_exit_merge( + exprt e, const exprt &exits, unsigned k, locationt loc); + void add_hoisted_assertions(loopt &loop, bool is_last); +}; + +class ssa_unwindert:public messaget +{ +public: + typedef std::map unwinder_mapt; + typedef std::pair unwinder_pairt; + + explicit ssa_unwindert(ssa_dbt& _db): + ssa_db(_db), + is_initialized(false) + { + } + + void init(bool is_kinduction, bool is_bmc); + void init_localunwinders(); + + void unwind(const irep_idt fname, unsigned k); + void unwind_all(unsigned k); + + inline ssa_local_unwindert &get(const irep_idt& fname) + { + return unwinder_map.at(fname); + } + + inline const ssa_local_unwindert &get(const irep_idt& fname) const + { + return unwinder_map.at(fname); + } + +protected: + ssa_dbt &ssa_db; + bool is_initialized; + unwinder_mapt unwinder_map; +}; + +#endif diff --git a/src/ssa/ssa_unwinder_old.cpp b/src/ssa/ssa_unwinder_old.cpp deleted file mode 100644 index 25fe0d2ac..000000000 --- a/src/ssa/ssa_unwinder_old.cpp +++ /dev/null @@ -1,1556 +0,0 @@ -/******************************************************************* - Module: SSA Unwinder - - Author: Saurabh Joshi - - \*******************************************************************/ - -#include - -#include -#include -#include -#include - -#include "ssa_unwinder.h" - - -void ssa_local_unwindert::set_return_var(const irep_idt& id) -{ - return_var="c::"+id2string(id)+"#return_value"; -} -std::string ssa_local_unwindert::keep_first_two_hash(const std::string& str) const -{ - std::size_t pos = str.find('#'); - if(pos==std::string::npos) return str; - pos=str.find('#',pos+1); - if(pos==std::string::npos) return str; - return str.substr(0,pos); -} -void ssa_local_unwindert::dissect_loop_suffix(const irep_idt& id, - irep_idt& before_suffix,std::list& iterations,bool baseonly) const -{ - std::string s = id2string(id); - - std::size_t pos=s.find_first_of("%"); - if(pos==std::string::npos) { return; } - - before_suffix=s.substr(0,pos); - if(baseonly) return; - std::size_t pos1; - - do - { - pos1=s.find_first_of("%",pos+1); - if(pos1==std::string::npos) continue; - // if(pos1==pos+1) {assert(false&& "Ill formed loop suffix");} - //TODO use safe string to unsigned - - iterations.push_back(string2integer(s.substr(pos+1,pos1-(pos+1)),10).to_ulong()); - pos=pos1; - - }while(pos1!=std::string::npos); - - //if(pos==(s.length()-1)) {assert(false && "Ill-formed loop suffix");} - - iterations.push_back(string2integer(s.substr(pos+1)).to_ulong()); - -} -/***************************************************************************** - * - * Function : ssa_local_unwindert::get_base_name - * - * Input : id - symbol name - * - * Output : irep_id - a symbol name which is stripped off of all suffixes - * beginnign with the first # - * - *****************************************************************************/ -irep_idt ssa_local_unwindert::get_base_name(const irep_idt& id) -{ - std::string s = id2string(id); - std::size_t pos = s.find("#"); - if(pos==std::string::npos) {return id;} - std::string s1=s.substr(0,pos); - return irep_idt(s1); -} -void ssa_local_unwindert::is_void_func() -{ - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) { - - if(as_string(o_it->get_identifier()).find(RETVAR)){ isvoid=false; return;} - } - isvoid=true; - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::ssa_local_unwindert - * - * Input : _SSA - a reference to an object of type local_SSAt - * - * Output : - * - * Purpose : This just initialises the reference and does nothing else. A - * separate initialier init() is provided to populate the - * hierarchical loop - * data structure - * - * - *****************************************************************************/ -ssa_local_unwindert::ssa_local_unwindert(local_SSAt& _SSA,bool k_induct, - bool _ibmc):isvoid(true), SSA(_SSA), - current_unwinding(0),prev_unwinding(std::numeric_limits::max()), - is_initialized(false),is_kinduction(k_induct), - is_ibmc(_ibmc){ } -/******************************************************************* - Struct: compare_node_iterators - - Inputs: - - Outputs: - - Purpose: a function object to pass as a comparator for std::set - to enable us to order iterators. It takes the address of the - object being pointed to by iterators and compared them as - unsigned long - - \*******************************************************************/ -bool compare_node_iteratorst::operator()(const local_SSAt::nodest::iterator& a, - const local_SSAt::nodest::iterator& b) const { - return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); -} -bool compare_node_iteratorst::operator()(const local_SSAt::nodest::const_iterator& a, - const local_SSAt::nodest::const_iterator& b) const { - return ((unsigned long) (&(*a)) < (unsigned long) (&(*b))); -} -/*****************************************************************************\ - * Function : ssa_local_unwindert::init - * - * Inputs : None - * - * Outputs : None - * - * Purpose : from local_SSA, construct a hierarchical tree_loopnodet rooted at - * root_node. loophead of a loop is always stored as the first node in - * body_nodes. A backedge node is always the last node in body_nodes. - * A nested loop is stored as a tree_loopnodet in loop_nodes - * - *****************************************************************************/ - -void ssa_local_unwindert::init() { - std::set loopheads; - - loopheads.clear(); - for (local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) { - //continue until a back-edge is found - if (n_it->loophead == SSA.nodes.end()) - continue; - // all the loop-head nodes must be inserted - assert(loopheads.insert(n_it->loophead).second); - } - - if (loopheads.empty()) { - is_initialized = true; - loopless = true; - return; - } - - loopless = false; - tree_loopnodet* current_node = NULL; - - std::list current_stack; - current_stack.push_back(&root_node); - current_node = current_stack.back(); - for (local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) { - if (loopheads.find(n_it) != loopheads.end()) { - //we've found a loop-head, so create a - //tree_loopnodet for the nested loop - current_node = new tree_loopnodet(); - current_stack.push_back(current_node); - current_node = current_stack.back(); - // ASSUME : first node in body_nodes is always the - // loop-head of the loop except root_node -#if 0 - //get the guard for the loophead to be reachable - //get the loop condition - current_node->entry_guard=SSA.guard_symbol(n_it->location); - current_node->cond_expr=SSA.cond_symbol(n_it->location); -#endif - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked = false; - -#if 0 - { - //compute pre_post_exprs for the current loop - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[n_it->location].phi_nodes; - - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - ssa_domaint::phi_nodest::const_iterator p_it = - phi_nodes.find(o_it->get_identifier()); - - if(p_it==phi_nodes.end()) continue; // object not modified in this loop - - symbol_exprt pre = - SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - symbol_exprt post = SSA.read_rhs(*o_it, n_it->location); - - current_node->pre_post_exprs[pre] = post; - std::cout << pre << " ---" << post << std::endl; - } - } -#endif - } else if (n_it->loophead != SSA.nodes.end()) { - //we've found the end of the loop so move - // up the stack - - //ASSUME : the last node in the body_nodes - // of a loop is always the back-edge node - //copy assertions after the loop for assertion hoisting - current_node->assertions_after_loop = n_it->loophead->assertions_after_loop; - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked = false; - - //reset the back-edge - current_node->body_nodes.rbegin()->loophead=SSA.nodes.end(); - - { - local_SSAt::nodet loophead = current_node->body_nodes.front(); - loophead.marked = false; - //compute pre_post_exprs for the current loop - - const ssa_domaint::phi_nodest &phi_nodes = - SSA.ssa_analysis[loophead.location].phi_nodes; - - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) { - ssa_domaint::phi_nodest::const_iterator p_it = phi_nodes.find( - o_it->get_identifier()); - - if (p_it == phi_nodes.end()) - continue; // object not modified in this loop - //capture which variables are modified - //this will be used during renaming - current_node->vars_modified.insert(o_it->get_identifier()); - - - symbol_exprt pre = SSA.name(*o_it, local_SSAt::LOOP_BACK, - n_it->location); - symbol_exprt post = SSA.read_rhs(*o_it, n_it->location); - - current_node->pre_post_exprs[pre] = post; - - } - } - -#if 0 - //get the guard at the end of the loop body - //this guard needs to be fed to the next iteration - current_node->exit_guard=SSA.guard_symbol(n_it->location); -#endif - assert(!current_stack.empty()); - //assert would fail for unstructured program - current_stack.pop_back(); - - tree_loopnodet* new_current_node = current_stack.back(); - //hope push_back does a copy by value - yes it does - new_current_node->loop_nodes.push_back(*current_node); - - delete current_node; - - - current_node = new_current_node; - } else { - //a normal node belongs to body_nodes of the current loop - current_node->body_nodes.push_back(*n_it); - current_node->body_nodes.back().marked=false; - } - - } - //only the top level body_nodes are required - // all other required nodes will be inserted during unwinding - // so avoid duplicates by removing them from SSA.nodes - SSA.nodes.clear(); - SSA.nodes.insert(SSA.nodes.begin(), root_node.body_nodes.begin(), - root_node.body_nodes.end()); - - populate_parents(); - is_void_func(); - - if(!isvoid) - { - populate_return_val_mod(); - } - - //populate connector for every loop - for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); - lit!=root_node.loop_nodes.end();lit++) - { - populate_connectors(*lit); - } - - put_varmod_in_parent(); -#if 0 - //pointers of tree_loopnodet are stable now and they must not be - //modified beyond this point. Now populate "parent" field of - //every node - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - it->parent = current_node; -#if 1 - // if a variable is modified in child loop then consider it modified - //for the current loop for the purpose of renaming - //NOTE : this code only looks at the child. Not sure if you should look at - // all the descendents for the purpose of renaming - current_node->vars_modified.insert(it->vars_modified.begin(),it->vars_modified.end()); -#endif - worklist.push_back(&(*it)); - } - - } - -#endif - is_initialized=true; - -} -void ssa_local_unwindert::propagate_varmod_to_ancestors(const irep_idt& id,tree_loopnodet* current_loop) -{ - current_loop->vars_modified.insert(id); - if(current_loop->parent!=NULL && current_loop->parent!=&root_node) - { - propagate_varmod_to_ancestors(id,current_loop->parent); - } -} - -void ssa_local_unwindert::populate_parents() -{ - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - it->parent = current_node; - - worklist.push_back(&(*it)); - } - - } -} -void ssa_local_unwindert::put_varmod_in_parent() -{ - std::list worklist; - worklist.push_back(&root_node); - while(!worklist.empty()) - { - tree_loopnodet* current_node = worklist.back(); - worklist.pop_back(); - - if(current_node->loop_nodes.empty()) continue; - for(loop_nodest::iterator it=current_node->loop_nodes.begin(); - it!=current_node->loop_nodes.end();it++) - { - - - // if a variable is modified in child loop then consider it modified - //for the current loop for the purpose of renaming - //NOTE : this code only looks at the child. Not sure if you should look at - // all the descendents for the purpose of renaming - current_node->vars_modified.insert(it->vars_modified.begin(),it->vars_modified.end()); - worklist.push_back(&(*it)); - } - - } -} -void ssa_local_unwindert::populate_return_val_mod() -{ - std::list worklist; - for(loop_nodest::iterator lit=root_node.loop_nodes.begin(); - lit!=root_node.loop_nodes.end();lit++) - { - worklist.push_back(&(*lit)); - } - -while(!worklist.empty()) -{ - tree_loopnodet* current_loop=worklist.back(); - worklist.pop_back(); - for(local_SSAt::nodest::iterator nit=current_loop->body_nodes.begin(); - nit!=current_loop->body_nodes.end();nit++) - { - for(local_SSAt::nodet::equalitiest::iterator eit=nit->equalities.begin(); - eit!=nit->equalities.end();eit++) - { - - if(eit->lhs().id()==ID_symbol) - { - - symbol_exprt sym_e=to_symbol_expr(eit->lhs()); - irep_idt sym_id=sym_e.get_identifier(); - std::string s = as_string(sym_id); - - std::size_t pos=s.find(RETVAR); - if(pos!=std::string::npos) - { - - irep_idt id= keep_first_two_hash(s); - propagate_varmod_to_ancestors(id,current_loop); - current_loop->return_nodes.insert(nit); - } - } - } - } - for(loop_nodest::iterator lit=current_loop->loop_nodes.begin(); - lit!=current_loop->loop_nodes.end();lit++) - { - worklist.push_back(&(*lit)); - } -} - -} -void ssa_local_unwindert::populate_connectors(tree_loopnodet& current_loop) -{ - typedef std::map varobj_mapt; - varobj_mapt varobj_map; - body_nodest::const_iterator it=current_loop.body_nodes.begin(); - - body_nodest::const_reverse_iterator lit = current_loop.body_nodes.rbegin(); - - std::vector exit_conditions; - - unsigned int end_location = lit->location->location_number; - - - for(local_SSAt::nodet::equalitiest::const_iterator eqit=lit->equalities.begin(); - eqit!=lit->equalities.end();eqit++) - { - if(eqit->lhs()==SSA.cond_symbol(lit->location)) - { - if(!eqit->rhs().is_true()) - { - current_loop.is_dowhile=true; - - } - break; - } - } - exprt cond_e; - exprt guard_e; - exprt loop_continue_e; - if(!current_loop.is_dowhile) - { - cond_e = SSA.cond_symbol(it->location); - guard_e=SSA.guard_symbol(it->location); - exit_conditions.push_back(cond_e); - //either you reached head of the loop and exit - //or you have not reached the end of the loop - //this will happen only for while. What about dowhile? - loop_continue_e = and_exprt(cond_e,guard_e); - loop_continue_e=or_exprt(loop_continue_e,not_exprt(SSA.guard_symbol(lit->location))); - } - else - { - cond_e = SSA.cond_symbol(lit->location); - guard_e=SSA.guard_symbol(lit->location); - exprt not_cond_e=not_exprt(cond_e); - exit_conditions.push_back(not_cond_e); - loop_continue_e=and_exprt(not_cond_e,guard_e); - } - - for (local_SSAt::objectst::const_iterator o_it = - SSA.ssa_objects.objects.begin(); - o_it != SSA.ssa_objects.objects.end(); o_it++) - { - std::set::const_iterator fit = current_loop.vars_modified.find(o_it->get_identifier()); - if(fit==current_loop.vars_modified.end()) continue; - - varobj_map[*fit]=o_it; - //though #return_value is added into vars_modified - //we don't want PHI connectors for them - if(as_string(*fit).find(RETVAR)!=std::string::npos) continue; - - if(!current_loop.is_dowhile) - { - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.name(*o_it,local_SSAt::PHI,it->location),loop_continue_e)); - } - else - { - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*o_it,lit->location),loop_continue_e)); - } - } - -if(!current_loop.is_dowhile) -{ - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); -} -else -{ - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(lit->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(lit->location),loop_continue_e)); -} -//since loophead is processed we can probably go on? -it++; - - -for(;it!=current_loop.body_nodes.end();it++) -{ - body_nodest::const_iterator next_node = it; next_node++; - if(next_node==current_loop.body_nodes.end()) break; - - if(!is_break_node(*it,end_location)) continue; - //no separete treatment for return nodes required as break nodes - // are all nodes with jump out of the loop which include the return - //nodes - exprt break_cond_e=SSA.cond_symbol(it->location); - //NOTE : do we check if the end of the guard has reached in loop_continue_e? -loop_continue_e = and_exprt(break_cond_e,SSA.guard_symbol(it->location)); -if(!is_return_node(current_loop,it)) -{ -exit_conditions.push_back(break_cond_e); -} - - for(varobj_mapt::iterator vit=varobj_map.begin();vit!=varobj_map.end();vit++) - { - if(it==current_loop.body_nodes.begin() && - (as_string(vit->first).find(RETVAR)!=std::string::npos)) continue; - - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.read_rhs(*(vit->second),it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.guard_symbol(it->location),loop_continue_e)); - current_loop.connectors.insert(exp_guard_cond_pairt(SSA.cond_symbol(it->location),loop_continue_e)); - } - - -} - -current_loop.exit_condition=disjunction(exit_conditions); - -for(loop_nodest::iterator loopit=current_loop.loop_nodes.begin(); - loopit!=current_loop.loop_nodes.end();loopit++) -{ - populate_connectors(*loopit); -} - -} - -bool ssa_local_unwindert::is_break_node(const local_SSAt::nodet& node, - const unsigned int end_location) const -{ - local_SSAt::locationt instr = node.location; - if(!instr->is_goto()) return false; - - // a break should have only one target - if(instr->targets.size()>1) return false; - - if(instr->targets.front()->location_number <= end_location ) return false; - return true; - -} - -bool ssa_local_unwindert::is_return_node(const tree_loopnodet& current_loop, - const local_SSAt::nodest::const_iterator& node) const -{ - return_nodest::const_iterator it=current_loop.return_nodes.find(node); - return (it!=current_loop.return_nodes.end()); -} -/*****************************************************************************\ - * Function : ssa_local_unwindert::unwind - * - * Input : k - unwind_depth - * - * Output : new nodes added to reflect incremental unwinding - * - * Purpose : for all the loops at root-level call unwinder to do - * incremental unwinding - * Pre condition : k must be greater than current_unwinding - * - *****************************************************************************/ -void ssa_local_unwindert::unwind(const irep_idt& fname,unsigned int k) { - assert(is_initialized); - if (loopless) - return; - if (k <= current_unwinding) - assert(false && "unwind depth smaller than previous unwinding!!"); - -//watch out for border-line cases - //parameter to unwind must never be 0 - prev_unwinding=current_unwinding; - - local_SSAt::nodest new_nodes; - irep_idt func_name = "unwind:"+as_string(fname)+":enable_"+i2string(k); - /* if(return_var.empty()) - { - return_var=as_string(fname)+"#return_value"; - }*/ - symbol_exprt new_sym(func_name,bool_typet()); - SSA.enabling_exprs.push_back(new_sym); - for (loop_nodest::iterator it = root_node.loop_nodes.begin(); - it != root_node.loop_nodes.end(); it++) { - unwind(*it, "", false, k, new_sym,new_nodes); - } -//commit all the nodes - SSA.nodes.splice(SSA.nodes.begin(), new_nodes); - current_unwinding = k; - - SSA.current_unwinding = k-1; -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::need_renaming - * - * Input : current_loop - the current context of renaming, - * id - id to be renamed - * - * Output : bool - If id needs to be renamed within current context - * - * Purpose : If a symbol starts with "$cond" or "$guard" it will always have - * to be renamed in the current context. If the variable - * is modified in the current context (loop) then also it has to - * be renamed - * - *****************************************************************************/ -int ssa_local_unwindert::need_renaming(tree_loopnodet& current_loop, - const irep_idt& id) -{ - - //irep_idt base_id = get_base_name(id); -modvar_levelt::iterator mit = current_loop.modvar_level.find(id); - if(mit!=current_loop.modvar_level.end()) - { - return mit->second; - } - - //std::string s = id2string(base_id); - // if(s.find("$cond")!=std::string::npos) return true; - // if(s.find("$guard")!=std::string::npos) return true; - if(current_loop.vars_modified.find(id)!=current_loop.vars_modified.end()) - { - - current_loop.modvar_level[id]=0; - return 0; - - } - - if(current_loop.parent==NULL || current_loop.parent==&root_node) { current_loop.modvar_level[id]=-1; return -1;} - int mylevel = need_renaming(*current_loop.parent,id); - if(mylevel < 0) { current_loop.modvar_level[id]=-1; return -1;} - current_loop.modvar_level[id] = mylevel+1; - return mylevel+1; - - - -} - -unsigned int ssa_local_unwindert::get_last_iteration(std::string& suffix, bool& result) -{ - std::size_t pos = suffix.find_last_of("%"); - if(pos==std::string::npos) {result=false; return 0;} - unsigned int val = safe_string2unsigned(suffix.substr(pos+1)); - assert(val < std::numeric_limits::max()); - suffix=suffix.substr(0,pos); - result = true; - return val; - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::rename - * - * Input : expr - to be renamed, suffix - parent context, - * iteration - iteration in the current context(loop), current_loop - * - * Output : - * - * Purpose : expr is renamed with iteration of the current_loop appended - * if it is modified in the current_loop or if it starts - * with "$cond" or "$guard". For all other cases, - * only the parent context (suffix) is added to the expression - * - * A negative iteration indicates that it must only get parent - * suffix - * - * - * - * - *****************************************************************************/ -void ssa_local_unwindert::rename(exprt &expr, std::string suffix, - const int iteration,tree_loopnodet& current_loop) { - if (expr.id() == ID_symbol) { - symbol_exprt &sexpr = to_symbol_expr(expr); - irep_idt vid=sexpr.get_identifier(); - irep_idt base_id = get_base_name(vid); - bool isreturnvar=(as_string(vid).find(RETVAR)!=std::string::npos); - isreturnvar= isreturnvar||(as_string(vid).find(RETVAR1)!=std::string::npos); - int mylevel; - if(iteration<0) - { - - irep_idt id = id2string(vid) + suffix; - sexpr.set_identifier(id); - return; - } - - std::string s = id2string(base_id); - if(s.find("$guard")!=std::string::npos - || s.find("$cond")!=std::string::npos - || isreturnvar - || (mylevel = need_renaming(current_loop,base_id))==0) - { - - irep_idt id=id2string(vid) + suffix + "%" + i2string(iteration); - sexpr.set_identifier(id); - return; - } - if(mylevel<0) return; - - std::string fsuffix = suffix; - std::size_t pos; - for(unsigned int i=1;i L1_1_head - * uwind1=> L2_1_head - * L2_1_body - * uwind1=> L2_connector - * uwind1=> L1_connector - * and if we move to unwind_depth==2 - * - * uwind2=> L1_2_head - * L2_2 //full - * L2_1 - * uwind2=> L2_connector - * L1_1_head - * uwind2=> L2_2_head // partial - * L2_1 - * uwind2=> L2_connector - * uwind2=> L1_connector - * - * - * See that for L1_1 needs to be unwound partially and so is the case of - * loops nested inside it - * L1_2 on the other hand has to be unwound fully. Also, Li_connector are - * loop head nodes that connect the unwinding with the rest of the code. - * These connections needs to be broken for incremental unwinding so - * their equalities are transfered as constraint where new symbol - * new_sym => e (e is an equality) - * The topmost loop head also needs to change as it is the only one - * where phi nodes exist - * is introduced. To force the equality, set new_sym to true - * - * Backedge from the end of every Li_1 to Li_k_head (k being the unwind_depth) - * is maintained via an iterator. - * - * WARNING : Order of the nodes are completely arbitrary and should not - * be relied on at all. e.g. In the resulting SSA.nodes, a loop body - * may appear after loop head - * - *****************************************************************************/ -void ssa_local_unwindert::unwind(tree_loopnodet& current_loop, - std::string suffix, bool full, const unsigned int unwind_depth, symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes) { - - // a loop has to have at least one body_node, if not, it can not - // have a nested loop either so return - if (current_loop.body_nodes.empty()) - return; - - for (unsigned int i = 0; i < unwind_depth; i++) { - bool tmp = i < current_unwinding ? full : true; - for (loop_nodest::iterator it = current_loop.loop_nodes.begin(); - it != current_loop.loop_nodes.end(); it++) { - - unwind((*it), suffix + "%" + i2string(i), tmp, unwind_depth,new_sym, new_nodes); - - } - } - - unsigned int min_iter = full ? 0 : current_unwinding; - for (unsigned int i = min_iter; i < unwind_depth; i++) { - //process the loophead first - local_SSAt::nodest::iterator it = current_loop.body_nodes.begin(); - //unwinding is done from bottom to top, so topmost unwinding - // is special and is done after this loop - if (i > 0) { - - local_SSAt::nodet node = *it; //copy - node.marked = false; - assert(node.assertions.empty()); //loophead must not have assertions! - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - if (e_it->rhs().id() != ID_if) { - if (e_it->lhs() == SSA.guard_symbol(node.location)) { - //ASSUME : last node in body_nodes is - // always the back-edge node - //This back edge nodes gives us the reachability - //guard at the end of the loop, - //which should be used as reachability guard - //from previous to current iteration - rename(e_it->lhs(), suffix, i-1,current_loop); - exprt e = SSA.guard_symbol( - current_loop.body_nodes.rbegin()->location); - rename(e, suffix, i,current_loop); - e_it->rhs() = e; - - } else { - rename(*e_it, suffix, i-1,current_loop); - } - continue; - } - - if_exprt &e = to_if_expr(e_it->rhs()); -#if 0 - if(i==0) - { //for the first iteration, take the input - //coming from above - rename(e_it->lhs(),suffix, i,current_loop); - e_it->rhs() = e.false_case(); - } - else -#endif - { - //for other iterations, take the loopback - //value - e_it->rhs() = current_loop.pre_post_exprs[e.true_case()]; - rename(e_it->rhs(), suffix, i,current_loop); - rename(e_it->lhs(), suffix, i-1,current_loop); - } - } - new_nodes.push_back(node); - } -#ifdef ASSERTION_HOISTING - assertion_hoisting(current_loop,*it,suffix,is_kinduction, - unwind_depth,new_sym,new_nodes); -#endif - - it++; - //now process the rest of the nodes - for (; it != current_loop.body_nodes.end(); it++) { - //copy the body node, rename and store in new_nodes - local_SSAt::nodet new_node = (*it); - new_node.marked = false; - - rename(new_node, suffix, i,current_loop); - if(is_kinduction && !new_node.assertions.empty() &&( - (current_loop.is_dowhile && i>0) - || (!current_loop.is_dowhile && i>1))) - { //convert all assert to assumes for k-induction - //except the bottom most iteration - - //assertions should be converted to assume only if you are in the step case - //of k-induction and not the base case. that means - // you want guardls=> assume and \not guardls => assert - // as of now this conflicts with checking spurious examples - //so just removing the assertion if it is NOT the bottom most iteration. - // Unless you have checked it for all unwinding less than k, this will - // lead to unsoundness (a bug may not be found if the assertion can fail in iterations - //other than the last - -#if 1 - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); - rename(guard_select,suffix,i,current_loop); - - - for(local_SSAt::nodet::assertionst::iterator ait=new_node.assertions.begin(); - ait!=new_node.assertions.end();ait++) - { -// new_node.constraints.push_back(implies_exprt(not_exprt(guard_select),*ait)); - new_node.constraints.push_back(implies_exprt(guard_select,*ait)); -// new_node.constraints.push_back(*ait); - } -#else - //for linear k-induction (k increases only by 1, you do not need to check if - //you are in the step case or not, since for k-1 even base case must have hold - new_node.constraints.insert(new_node.constraints.end(),new_node.assertions.begin(),new_node.assertions.end()); -#endif - new_node.assertions.clear(); - } - else if(is_ibmc &&( - (current_loop.is_dowhile && i>0) - || (!current_loop.is_dowhile && i>1))) - { //convert all assert to assumes for incremental-bmc - //except the bottom most iteration - - //when you are in base case (guard_select is false) - //for k-1 you already have proved that there is no counter-example - //with k-1 bound. So converting assertion to assume just helps the solver - - - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.begin()->location); - rename(guard_select,suffix,i,current_loop); - for(local_SSAt::nodet::assertionst::iterator ait=new_node.assertions.begin(); - ait!=new_node.assertions.end();ait++) - { - new_node.constraints.push_back(implies_exprt(not_exprt(guard_select),*ait)); - } - - - new_node.assertions.clear(); - } - new_nodes.push_back(new_node); - } - if(i==0) - { - //this is a full unwinding, so end of the loop must be stored - local_SSAt::nodest::iterator le_it = new_nodes.end(); - le_it--; //now points to the last element of the bottom most iteration - - //store the end of this loop, its "loophead" field will be - //pointed to the topmost loophead node - current_loop.loopends_map[suffix] = le_it; - - { - //add all the loop continuation expressions for the bottom most iterations %0 - //this is requested by peter - //used for checking whether loops have been fully unwound - exprt loopend_guard = SSA.guard_symbol(current_loop.body_nodes.rbegin()->location); - exprt loopend_cond = SSA.cond_symbol(current_loop.body_nodes.rbegin()->location); - rename(loopend_guard,suffix,i,current_loop); - rename(loopend_cond,suffix,i,current_loop); - current_loop.loop_continuation_exprs.push_back(and_exprt(loopend_guard,loopend_cond)); - } - } - - } - - //symbol_exprt new_sym("unwind_" + i2string(unwind_depth), bool_typet()); - //SSA.enabling_exprs.push_back(new_sym); - - //only the last element in enabling_exprs needs to be - //set to true, all others should be set to false to enable constraint - // wrt current unwind_depth - { - //copy the original loop head as the first (topmost)iteration - local_SSAt::nodest::iterator it = current_loop.body_nodes.begin(); - local_SSAt::nodet node = *it; //copy - for (local_SSAt::nodet::equalitiest::iterator e_it = - node.equalities.begin(); e_it != node.equalities.end(); e_it++) { - - if (e_it->rhs().id() == ID_if) - { - rename(e_it->lhs(), suffix, unwind_depth-1,current_loop); - if_exprt &e = to_if_expr(e_it->rhs()); - rename(e.cond(),suffix, unwind_depth-1,current_loop); - rename(e.true_case(),suffix, unwind_depth-1,current_loop); - -#if 0 - //VERY DIRTY HACK, condition and true case at the topmost iteration - // are free variables, so suffixing "0" so that it always matches - //with the bottom most iteration. Later in the analysis these suffixes - //are needed to be matched :-( - rename(e.cond(),suffix + "%" + i2string(0)); - rename(e.true_case(),suffix + "%" + i2string(0)); -#endif -//for false_case the value comes from above so evaluate in the parent -// context - //if no enclosing loop then no need to rename the false_case - if(current_loop.parent!=NULL && current_loop.parent!= &root_node) - { - - bool result; - std::string parent_suffix = suffix; - unsigned int parent_iteration=get_last_iteration(parent_suffix,result); - - //if there is an enclosing loop, it must return with a valid iteration - if(!result) assert(false); - - rename(e.false_case(),parent_suffix,parent_iteration,*current_loop.parent); - - } - - } - else if (SSA.guard_symbol(node.location) == e_it->lhs()) { - - rename(e_it->lhs(), suffix, unwind_depth-1,current_loop); - rename(e_it->rhs(),suffix,-1,current_loop); - - } else { - rename(*e_it, suffix, unwind_depth-1,current_loop); - } - node.enabling_expr = new_sym; - //exprt e = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(e); - } - //node.equalities.clear(); - new_nodes.push_back(node); - - - local_SSAt::nodest::iterator le_it = new_nodes.end(); - le_it--; //points to the topmost loophead node - - //insert the backedge - current_loop.loopends_map[suffix]->loophead=le_it; - - //print for debugging -#if 0 - std::cout << "Loop end node------" << std::endl; - current_loop.loopends_map[suffix]->output(std::cout,SSA.ns); - std::cout << "Corresponding loop head node----" << std::endl; - current_loop.loopends_map[suffix]->loophead->output(std::cout,SSA.ns); -#endif - - - } - add_connector_node(current_loop,suffix,unwind_depth,new_sym,new_nodes); - -} - -void ssa_local_unwindert::add_connector_node(tree_loopnodet& current_loop, - std::string suffix, - const unsigned int unwind_depth,symbol_exprt& new_sym,local_SSAt::nodest& new_nodes) -{ - //copy the original loop head - - local_SSAt::nodet node=current_loop.body_nodes.front(); - node.marked = false; - node.equalities.clear(); - node.assertions.clear(); - node.constraints.clear(); - node.templates.clear(); - - - for(expr_break_mapt::iterator e_it=current_loop.connectors.begin(); - e_it!=current_loop.connectors.end();e_it++) - { - exprt e = e_it->first; - exprt re = e; - rename(re, suffix, 0,current_loop); - for (unsigned int i = 1; i < unwind_depth; i++) { - - - exprt cond_expr = e_it->second; - rename(cond_expr,suffix,i,current_loop); - exprt true_expr = e; - rename(true_expr, suffix,i,current_loop); - exprt false_expr = re; - re = if_exprt(cond_expr, true_expr, false_expr); - } - exprt rhs = re; - exprt lhs=e; - - rename(lhs,suffix,-1,current_loop); - node.equalities.push_back(equal_exprt(lhs,rhs)); - - node.enabling_expr = new_sym; - //exprt ie = implies_exprt(new_sym, *e_it); - //node.constraints.push_back(ie); - - } - //node.equalities.clear(); - new_nodes.push_back(node); - } - - -void ssa_local_unwindert::loop_continuation_conditions( - const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const -{ - loop_cont_e.insert(loop_cont_e.end(), - current_loop.loop_continuation_exprs.begin(), - current_loop.loop_continuation_exprs.end()); - for(loop_nodest::const_iterator it=current_loop.loop_nodes.begin(); - it!=current_loop.loop_nodes.end();it++) - { - loop_continuation_conditions(*it,loop_cont_e); - } -} - -void ssa_local_unwindert::loop_continuation_conditions( - exprt::operandst& loop_cont_e) const -{ - loop_continuation_conditions(root_node,loop_cont_e); -} - -void ssa_local_unwindert::assertion_hoisting(tree_loopnodet& current_loop, - const local_SSAt::nodet& tmp_node, - const std::string& suffix, const bool is_kinduction, - const unsigned int unwind_depth, - symbol_exprt& new_sym, local_SSAt::nodest& new_nodes) - -{ - - if(suffix=="" && is_kinduction) - { - unsigned lower_bound = current_loop.is_dowhile? 1 : 2; - exprt assertion_hoist_e = conjunction(current_loop.assertions_after_loop); - exprt guard_select = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, current_loop.body_nodes.rbegin()->location); - rename(guard_select,suffix,unwind_depth-1,current_loop); - for(unsigned int i=lower_bound;i0) - // || (!current_loop.is_dowhile && (i-1)>1)))) - // { - local_SSAt::nodet node= tmp_node; - node.marked=false; - node.assertions.clear(); - node.equalities.clear(); - node.constraints.clear(); - node.templates.clear(); - node.assertions_after_loop.clear(); - - // if(is_kinduction &&( - // (current_loop.is_dowhile && (i-1)>0) - // || (!current_loop.is_dowhile && (i-1)>1))) - // { //convert all assert to assumes for k-induction - //except the bottom most iteration - - //assertions should be converted to assume only if you are in the step case - //of k-induction and not the base case. that means - // you want guardls=> assume and \not guardls => assert - // as of now this conflicts with checking spurious examples - //so just removing the assertion if it is NOT the bottom most iteration. - // Unless you have checked it for all unwinding less than k, this will - // lead to unsoundness (a bug may not be found if the assertion can fail in iterations - //other than the last - - - - - //if outermost loop, do the assertion hoisting. - //for innerloop assertion hoisting is not necessary because assertions are - //assumed in the parent context anyway - - exprt exit_cond_e=current_loop.exit_condition; - if(!assertion_hoist_e.is_true()&& !exit_cond_e.is_false()) - { - //rename(assertion_hoist_e,suffix,-1,current_loop); - - rename(exit_cond_e,suffix,i,current_loop); - - exprt hoist_cond_e = and_exprt(guard_select,exit_cond_e); - - node.constraints.push_back(implies_exprt(hoist_cond_e,assertion_hoist_e)); - node.enabling_expr = new_sym; - new_nodes.push_back(node); - } - - // } - } - } - -} -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::unwinder_rename - * - * Input : var, node - * - * Output : var is returned with a suffix that reflects the current unwinding - * with the context taken from the node - * - * E.g. - * - * node must look like - * - * cond"somesuffix" == TRUE - * - * e.g. cond%1%2%5%0 == TRUE - * - * and variable might be guard#ls25 - * - * if the current_unwinding is 6 - * - * the variable should be converted to guard#ls25%1%2%5%5 - * - * Note that if current_unwinding is X then suffixes can have at most - * X-1 in its parts - * - *****************************************************************************/ - -void ssa_local_unwindert::unwinder_rename(symbol_exprt &var, - const local_SSAt::nodet &node, bool pre) const -{ - //only to be called for backedge nodes - //This is very dirty hack :-( - if(current_unwinding==0) return; - assert(node.equalities.size()>=1); - //copy suffix from equality lhs to var - std::string id = - id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); - size_t pos = id.find_first_of("%"); - if(pos==std::string::npos) return; - size_t pos1 = id.find_last_of("%"); - std::string suffix; - unsigned unwinding = pre ? current_unwinding-1 : 0; - if(pos==pos1) - { - suffix = "%"+i2string(unwinding); - } - else - { - suffix = id.substr(pos,pos1-pos); - suffix += "%"+i2string(unwinding); - } - - - var.set_identifier(id2string(var.get_identifier())+suffix); -#ifdef DEBUG - std::cout << "new id: " << var.get_identifier() << std::endl; -#endif -} - - -unsigned ssa_local_unwindert::rename_required(const exprt& e, - const unsigned prev_unwinding) const -{ - if(e.id()==ID_symbol) - { - const symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,false); - bool rename_required=true; - for(std::list::iterator it=iterations.begin(); - it!=iterations.end();it++) - { - if(*it!=(prev_unwinding-1)) rename_required=false; - } - //if(iterations.back()==(prev_unwinding-1)) return iterations.size(); - if(rename_required) return iterations.size(); - - - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::const_iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth) return depth; - } - } - } - - return 0; - -} - - -void ssa_local_unwindert::rename_invariant(exprt& e,const irep_idt& suffix) const -{ - if(e.id()==ID_symbol) - { - symbol_exprt& sym=to_symbol_expr(e); - irep_idt id = sym.get_identifier(); - - std::list iterations; - irep_idt basename; - dissect_loop_suffix(id,basename,iterations,true); - - - sym.set_identifier(id2string(basename)+id2string(suffix)); - } - else - { - if(!e.operands().empty()) - { - for(exprt::operandst::iterator e_it=e.operands().begin(); - e_it!=e.operands().end();e_it++) - { - rename_invariant(*e_it,suffix); - } - } - } - -} -/***************************************************************************** - * - * Function : ssa_local_unwindert::rename_invariant - * - * Input : inv_in - list of input invariants that is to be renamed for reuse - * - * Output : inv_out - list of renamed invariants - * - * Purpose : For the purpose of reuse of invariant, rename all - * - * - *****************************************************************************/ -void ssa_local_unwindert::rename_invariant(const exprt::operandst& inv_in, - std::vector& inv_out,const unsigned prev_unwinding) const -{ - - if(prev_unwinding==0 || prev_unwinding==std::numeric_limits::max()) - { - return; - } - for(std::vector::const_iterator e_it=inv_in.begin(); - e_it!=inv_in.end();e_it++) - { - unsigned depth=rename_required(*e_it,prev_unwinding); - if(depth==0) continue; - - std::vector iter_vector(depth-1,current_unwinding-1); - - do - { - - irep_idt suffix; - - for(std::vector::const_iterator vit=iter_vector.begin(); - vit!=iter_vector.end();vit++) - { - - suffix = id2string(suffix)+"%"+i2string(*vit); - } - suffix = id2string(suffix)+"%"+i2string(current_unwinding-1); - inv_out.push_back(*e_it); - exprt& e = inv_out.back(); - rename_invariant(e,suffix); - } while(odometer_increment(iter_vector,current_unwinding)); - } -} - -exprt ssa_local_unwindert::rename_invariant(const exprt& inv_in) const -{ - if(inv_in.is_true()) return inv_in; - - exprt::operandst inv_in_operands; - if(inv_in.id()!=ID_and) inv_in_operands.push_back(inv_in); - else inv_in_operands = inv_in.operands(); - - std::vector new_inv; - rename_invariant(inv_in_operands,new_inv,prev_unwinding); - - return conjunction(new_inv); -} - - -bool ssa_local_unwindert::odometer_increment(std::vector& odometer, - unsigned base) const -{ - if(odometer.empty()) return false; - unsigned i=odometer.size()-1; - while(true) - { - if(odometer[i] < base-1) {odometer[i]++; return true;} - odometer[i]=0; - if(i==0) return false; //overflow - i--; - - } -return false; -} -/*****************************************************************************\ - * - * Function : ssa_unwindert::ssa_unwindert - * - * Input : reference to ssa_db - * - * Output : - * - * Purpose : Constructor, set is_initialized to false. Initializer init() - * must be invoked before unwind functions are called - * - *****************************************************************************/ -ssa_unwindert::ssa_unwindert(ssa_dbt& _db) : - ssa_db(_db),is_initialized(false) { - - -} - -/*****************************************************************************\ - * - * Function : ssa_new_unwindert::unwind - * - * Input : id - name of the goto-function to be unwound, k - unwinding depth - * - * Output : false - if id does not correspond to any goto-function in the - * unwinder_map - * - * Purpose : incrementally unwind a function 'id' up to depth k. Initializer - * must have been invoked before calling this function - * - *****************************************************************************/ - -void ssa_unwindert::unwind(const irep_idt id, unsigned int k) { - assert(is_initialized); - unwinder_mapt::iterator it; - - it = unwinder_map.find(id); - if (it == unwinder_map.end()) - assert(false && "Function not found"); - it->second.unwind(it->first,k); - -} - -/*****************************************************************************\ - * - * Function : - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::unwind_all(unsigned int k) { - - assert(is_initialized); - - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - it->second.unwind(it->first,k); - } - -} - -/*****************************************************************************\ - * - * Function : - * - * Input : - * - * Output : - * - * Purpose : - * - *****************************************************************************/ - -void ssa_unwindert::output(std::ostream & out) { - if(!is_initialized) return; - for (unwinder_mapt::iterator it = unwinder_map.begin(); - it != unwinder_map.end(); it++) { - out << "Unwinding for function" << it->first << std::endl; - it->second.output(out); - } -} - -/*****************************************************************************\ - * - * Function : ssa_unwindert::init - * - * Input : - * - * Output : - * - * Purpose : Initialize unwinder_map by computing hierarchical tree_loopnodet - * for every goto-function - * Set is_initialized to true. Initializer must be called before - * unwind funcitions are called. - * - *****************************************************************************/ -void ssa_unwindert::init(bool kinduction, bool _ibmc) -{ - ssa_dbt::functionst& funcs = ssa_db.functions(); - for (ssa_dbt::functionst::iterator it = funcs.begin(); it != funcs.end(); - it++) { - unwinder_map.insert( - unwinder_pairt(it->first, ssa_local_unwindert((*(it->second)),kinduction,_ibmc))); - } - - -} - -void ssa_unwindert::init_localunwinders() -{ - for(unwinder_mapt::iterator it=unwinder_map.begin(); - it!=unwinder_map.end();it++) - { - it->second.set_return_var(it->first); - it->second.init(); - } - is_initialized=true; -} diff --git a/src/ssa/ssa_unwinder_old.h b/src/ssa/ssa_unwinder_old.h deleted file mode 100644 index df01fa8a5..000000000 --- a/src/ssa/ssa_unwinder_old.h +++ /dev/null @@ -1,186 +0,0 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Saurabh Joshi - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_SSA_UNWINDER_H -#define CPROVER_DELTACHECK_SSA_UNWINDER_H - -#include - -#include "../ssa/local_ssa.h" -#include "../summarizer/ssa_db.h" - -#define RETVAR "#return_value" -#define RETVAR1 "return_value___VERIFIER_nondet" - -struct compare_node_iteratorst { - bool operator()(const local_SSAt::nodest::iterator& a, - const local_SSAt::nodest::iterator& b) const; - bool operator()(const local_SSAt::nodest::const_iterator& a, - const local_SSAt::nodest::const_iterator& b) const; -}; - -class ssa_local_unwindert : public messaget -{ - irep_idt return_var; - bool isvoid; - local_SSAt& SSA; - unsigned int current_unwinding; - unsigned int prev_unwinding; - class tree_loopnodet; - typedef std::list loop_nodest; - typedef std::map loopends_mapt; - typedef std::map modvar_levelt; - typedef std::set exprst; - typedef exprt cond_et; - typedef exprt guard_et; - typedef std::map expr_break_mapt; - typedef std::pair exp_guard_cond_pairt; - typedef std::set return_nodest; - typedef local_SSAt::nodest body_nodest; - bool loopless; - - class tree_loopnodet - { - public: - return_nodest return_nodes; - //exprst connectors; - exprt::operandst assertions_after_loop; - exprt::operandst loop_continuation_exprs; - exprt exit_condition; - expr_break_mapt connectors; - tree_loopnodet* parent; - local_SSAt::nodest body_nodes; - std::map pre_post_exprs; - modvar_levelt modvar_level; - std::set vars_modified; -#if 0 - symbol_exprt entry_guard; - symbol_exprt exit_guard; - symbol_exprt cond_expr; -#endif - loop_nodest loop_nodes; - loopends_mapt loopends_map; - bool is_dowhile; - - tree_loopnodet(){parent=NULL;is_dowhile=false;} - - void output(std::ostream& out,const namespacet& ns) - { - - - out << "Body nodes" << std::endl; - for(local_SSAt::nodest::iterator it=body_nodes.begin(); - it!=body_nodes.end();it++) - { - it->output(out,ns); - } - out << "Nested loop nodes" << std::endl; - for(loop_nodest::iterator it=loop_nodes.begin(); - it!=loop_nodes.end();it++) - { - it->output(out,ns); - - } - - } - }; - tree_loopnodet root_node; - std::string keep_first_two_hash(const std::string& str) const; - void put_varmod_in_parent(); - void populate_parents(); - void propagate_varmod_to_ancestors(const irep_idt& id, - tree_loopnodet* current_loop); - void populate_return_val_mod(); - void is_void_func(); - bool is_break_node(const local_SSAt::nodet& node, - const unsigned int end_location) const; - bool is_return_node(const tree_loopnodet& current_loop, - const local_SSAt::nodest::const_iterator& node) const; - - void populate_connectors(tree_loopnodet& current_loop); - void unwind(tree_loopnodet& current_loop, - std::string suffix,bool full, - const unsigned int unwind_depth,symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes); - void rename(local_SSAt::nodet& node,std::string suffix, - const int iteration,tree_loopnodet& current_loop); - void rename(exprt &expr, std::string suffix, - const int iteration,tree_loopnodet& current_loop); - int need_renaming(tree_loopnodet& current_loop, - const irep_idt& id); - unsigned int get_last_iteration(std::string& suffix, bool& result); - irep_idt get_base_name(const irep_idt& id); - unsigned rename_required(const exprt& e, - const unsigned prev_unwinding) const; - void rename_invariant(exprt& e,const irep_idt& suffix) const; - void add_connector_node(tree_loopnodet& current_loop, - std::string suffix, - const unsigned int unwind_depth, - symbol_exprt& new_sym, - local_SSAt::nodest& new_nodes); - void loop_continuation_conditions( - const tree_loopnodet& current_loop, exprt::operandst& loop_cont_e) const; - void assertion_hoisting(tree_loopnodet& current_loop, - const local_SSAt::nodet& tmp_node, - const std::string& suffix, const bool is_kinduction, - const unsigned int unwind_depth, - symbol_exprt& new_sym, local_SSAt::nodest& new_nodes); - bool is_initialized; -public : - void set_return_var(const irep_idt& id); - void dissect_loop_suffix(const irep_idt& id, - irep_idt& before_suffix, - std::list& iterations, bool baseonly) const; - void rename_invariant(const std::vector& inv_in, - std::vector& inv_out,const unsigned prev_unwinding) const; - exprt rename_invariant(const exprt& inv_in) const; - void loop_continuation_conditions(exprt::operandst& loop_cont_e) const; - bool odometer_increment(std::vector& odometer, - unsigned base) const; - bool is_kinduction; - bool is_ibmc; - void init(); - void output(std::ostream& out) - { - SSA.output(out); - } -ssa_local_unwindert(local_SSAt& _SSA, bool k_induct, bool _ibmc); - void unwind(const irep_idt& fname,unsigned int k); - - void unwinder_rename(symbol_exprt &var,const local_SSAt::nodet &node, bool pre) const; -}; - -class ssa_unwindert : public messaget -{ - -public: - typedef std::map unwinder_mapt; - typedef std::pair unwinder_pairt; - - ssa_unwindert(ssa_dbt& _db); - - void init(bool kinduction, bool _ibmc); - - void init_localunwinders(); - - void unwind(const irep_idt id,unsigned int k); - - void unwind_all(unsigned int k); - - ssa_local_unwindert &get(const irep_idt& fname) { return unwinder_map.at(fname); } - - void output(std::ostream & out); - -protected: - ssa_dbt& ssa_db; - bool is_initialized; - unwinder_mapt unwinder_map; - -}; - -#endif diff --git a/src/ssa/ssa_value_set.cpp b/src/ssa/ssa_value_set.cpp index c3522207a..aa362160e 100644 --- a/src/ssa/ssa_value_set.cpp +++ b/src/ssa/ssa_value_set.cpp @@ -6,7 +6,7 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -19,7 +19,7 @@ Author: Daniel Kroening, kroening@kroening.com /*******************************************************************\ -Function: ssa_value_domaint::output +Function: ssa_value_domaint::transform Inputs: @@ -46,7 +46,7 @@ void ssa_value_domaint::transform( { // Perhaps look at condition, for stuff like // p!=0 or the like. - //exprt cond_deref=dereference(from->guard, *this, "", ns); + // exprt cond_deref=dereference(from->guard, *this, "", ns); } else if(from->is_decl()) { @@ -61,18 +61,18 @@ void ssa_value_domaint::transform( // functions may alter state almost arbitrarily: // * any global-scoped variables // * any dirty locals - - #if 0 + +#if 0 for(objectst::const_iterator - o_it=ssa_objects.dirty_locals.begin(); + o_it=ssa_objects.dirty_locals.begin(); o_it!=ssa_objects.dirty_locals.end(); o_it++) assign(*o_it, it, ns); for(objectst::const_iterator - o_it=ssa_objects.globals.begin(); + o_it=ssa_objects.globals.begin(); o_it!=ssa_objects.globals.end(); o_it++) assign(*o_it, it, ns); - #endif +#endif // the call might come with an assignment if(code_function_call.lhs().is_not_nil()) @@ -106,26 +106,26 @@ void ssa_value_domaint::assign_lhs_rec( const namespacet &ns, bool add) { - #ifdef DEBUG +#ifdef DEBUG std::cout << "assign_lhs_rec lhs: " << from_expr(ns, "", lhs) << '\n'; std::cout << "assign_lhs_rec rhs: " << from_expr(ns, "", rhs) << '\n'; - #endif - +#endif + // is the lhs an object? if(is_symbol_struct_member(lhs, ns)) { const typet &lhs_type=ns.follow(lhs.type()); - + if(lhs_type.id()==ID_struct) { // Are we assigning an entire struct? // If so, need to split into pieces, recursively. - + const struct_typet &struct_type=to_struct_type(lhs_type); const struct_typet::componentst &components=struct_type.components(); - + for(struct_typet::componentst::const_iterator - it=components.begin(); + it=components.begin(); it!=components.end(); it++) { @@ -133,13 +133,13 @@ void ssa_value_domaint::assign_lhs_rec( member_exprt new_rhs(rhs, it->get_name(), it->type()); assign_lhs_rec(new_lhs, new_rhs, ns, add); // recursive call } - + return; // done } // object? ssa_objectt ssa_object(lhs, ns); - + if(ssa_object) { valuest tmp_values; @@ -153,9 +153,11 @@ void ssa_value_domaint::assign_lhs_rec( lhs_values=tmp_values; #if 0 - std::cout << "value_set: "; lhs_values.output(std::cout,ns); std::cout << std::endl; + std::cout << "value_set: "; + lhs_values.output(std::cout, ns); + std::cout << std::endl; #endif - + if(lhs_values.empty()) value_map.erase(ssa_object); } @@ -177,25 +179,24 @@ void ssa_value_domaint::assign_lhs_rec( } else if(lhs.id()==ID_dereference) { -// assert(false); // should have been removed - - //not yet removed if there is an array inside a struct referenced by pointer + // not yet removed + // if there is an array inside a struct referenced by pointer assign_lhs_rec(to_dereference_expr(lhs).pointer(), rhs, ns, true); } else if(lhs.id()==ID_member) { - #if 0 +#if 0 // non-flattened struct or union member const member_exprt &member_expr=to_member_expr(lhs); assign(member_expr.struct_op(), loc, ns); - #endif +#endif } else if(lhs.id()==ID_complex_real || lhs.id()==ID_complex_imag) { - #if 0 +#if 0 assert(lhs.operands().size()==1); assign(lhs.op0(), loc, ns); - #endif +#endif } } @@ -221,7 +222,7 @@ void ssa_value_domaint::assign_rhs_rec( #ifdef DEBUG std::cout << "assign_rhs_rec: " << from_expr(ns, "", rhs) << '\n'; #endif - + if(rhs.id()==ID_address_of) { const exprt &op=to_address_of_expr(rhs).object(); @@ -250,7 +251,8 @@ void ssa_value_domaint::assign_rhs_rec( if(it->type().id()==ID_pointer) { mp_integer pointer_offset=pointer_offset_size(it->type().subtype(), ns); - if(pointer_offset<1) pointer_offset=1; + if(pointer_offset<1) + pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, *it, ns, true, a); } @@ -262,25 +264,24 @@ void ssa_value_domaint::assign_rhs_rec( if(rhs.type().id()==ID_pointer) { mp_integer pointer_offset=pointer_offset_size(rhs.type().subtype(), ns); - if(pointer_offset<1) pointer_offset=1; + if(pointer_offset<1) + pointer_offset=1; unsigned a=merge_alignment(integer2ulong(pointer_offset), alignment); assign_rhs_rec(dest, rhs.op0(), ns, true, a); } } else if(rhs.id()==ID_dereference) { - // std::cout << rhs.pretty() << std::endl; - // assert(false); // should have been removed - - //not yet removed if there is an array inside a struct referenced by pointer + // not yet removed + // if there is an array inside a struct referenced by pointer assign_rhs_rec(dest, rhs.op0(), ns, true, 1); } else { // object? - + ssa_objectt ssa_object(rhs, ns); - + if(ssa_object) { value_mapt::const_iterator m_it=value_map.find(ssa_object); @@ -288,7 +289,8 @@ void ssa_value_domaint::assign_rhs_rec( if(m_it!=value_map.end()) { valuest tmp_values=m_it->second; - if(offset) tmp_values.offset=true; + if(offset) + tmp_values.offset=true; tmp_values.alignment=merge_alignment(tmp_values.alignment, alignment); dest.merge(tmp_values); } @@ -325,12 +327,15 @@ void ssa_value_domaint::assign_rhs_rec_address_of( if(ssa_object) { dest.value_set.insert(ssa_object); - if(offset) dest.offset=true; + if(offset) + dest.offset=true; } else if(rhs.id()==ID_if) { - assign_rhs_rec_address_of(dest, to_if_expr(rhs).true_case(), ns, offset, alignment); - assign_rhs_rec_address_of(dest, to_if_expr(rhs).false_case(), ns, offset, alignment); + assign_rhs_rec_address_of( + dest, to_if_expr(rhs).true_case(), ns, offset, alignment); + assign_rhs_rec_address_of( + dest, to_if_expr(rhs).false_case(), ns, offset, alignment); } else if(rhs.id()==ID_index) { @@ -340,7 +345,8 @@ void ssa_value_domaint::assign_rhs_rec_address_of( { offset=true; mp_integer pointer_offset=pointer_offset_size(rhs.type(), ns); - if(pointer_offset<1) pointer_offset=1; + if(pointer_offset<1) + pointer_offset=1; a=merge_alignment(a, integer2ulong(pointer_offset)); } @@ -367,12 +373,16 @@ void ssa_value_domaint::valuest::output( if(offset) { out << " offset"; - if(alignment!=0) out << "*" << alignment; + if(alignment!=0) + out << "*" << alignment; } - if(null) out << " null"; - if(unknown) out << " unknown"; - if(integer_address) out << " integer_address"; + if(null) + out << " null"; + if(unknown) + out << " unknown"; + if(integer_address) + out << " integer_address"; for(value_sett::const_iterator it=value_set.begin(); it!=value_set.end(); @@ -398,7 +408,7 @@ void ssa_value_domaint::output( const namespacet &ns) const { for(value_mapt::const_iterator - v_it=value_map.begin(); + v_it=value_map.begin(); v_it!=value_map.end(); v_it++) { @@ -425,20 +435,37 @@ bool ssa_value_domaint::valuest::merge(const valuest &src) bool result=false; // bits - if(src.offset && !offset) { offset=true; result=true; } - if(src.null && !null) { null=true; result=true; } - if(src.unknown && !unknown) { unknown=true; result=true; } - if(src.integer_address && !integer_address) { integer_address=true; result=true; } + if(src.offset && !offset) + { + offset=true; + result=true; + } + if(src.null && !null) + { + null=true; + result=true; + } + if(src.unknown && !unknown) + { + unknown=true; + result=true; + } + if(src.integer_address && !integer_address) + { + integer_address=true; + result=true; + } // value set unsigned old_size=value_set.size(); value_set.insert(src.value_set.begin(), src.value_set.end()); - if(old_size!=value_set.size()) result=true; - + if(old_size!=value_set.size()) + result=true; + // alignment alignment=merge_alignment(alignment, src.alignment); - return result; + return result; } /*******************************************************************\ @@ -461,11 +488,11 @@ bool ssa_value_domaint::merge( value_mapt::iterator v_it=value_map.begin(); const value_mapt &new_value_map=other.value_map; bool result=false; - + for(value_mapt::const_iterator - it=new_value_map.begin(); + it=new_value_map.begin(); it!=new_value_map.end(); - ) // no it++ + ) // no it++ { if(v_it==value_map.end() || it->firstfirst) { @@ -479,15 +506,15 @@ bool ssa_value_domaint::merge( v_it++; continue; } - + assert(v_it->first==it->first); - + if(v_it->second.merge(it->second)) result=true; v_it++; it++; } - + return result; } diff --git a/src/ssa/ssa_value_set.h b/src/ssa/ssa_value_set.h index 719ba6308..4e1f8afc1 100644 --- a/src/ssa/ssa_value_set.h +++ b/src/ssa/ssa_value_set.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_SSA_VALUE_SET_H -#define CPROVER_SSA_VALUE_SET_H +#ifndef CPROVER_2LS_SSA_SSA_VALUE_SET_H +#define CPROVER_2LS_SSA_SSA_VALUE_SET_H #include @@ -17,7 +17,8 @@ class ssa_value_domaint:public ai_domain_baset { public: virtual void transform(locationt, locationt, ai_baset &, const namespacet &); - virtual void output(std::ostream &, const ai_baset &, const namespacet &) const; + virtual void output( + std::ostream &, const ai_baset &, const namespacet &) const; bool merge(const ssa_value_domaint &, locationt, locationt); struct valuest @@ -27,39 +28,42 @@ class ssa_value_domaint:public ai_domain_baset value_sett value_set; bool offset, null, unknown, integer_address; unsigned alignment; - + inline valuest(): - offset(false), null(false), unknown(false), integer_address(false), + offset(false), + null(false), + unknown(false), + integer_address(false), alignment(0) { } - + void output(std::ostream &, const namespacet &) const; - + bool merge(const valuest &src); - + inline void clear() { *this=valuest(); } - - bool empty() const + + inline bool empty() const { return value_set.empty() && !null && !unknown && !integer_address; } }; - + // maps objects to values typedef std::map value_mapt; value_mapt value_map; - + const valuest operator()(const exprt &src, const namespacet &ns) const { valuest tmp; assign_rhs_rec(tmp, src, ns, false, 0); return tmp; } - + protected: void assign_lhs_rec( const exprt &lhs, const exprt &rhs, @@ -81,9 +85,12 @@ class ssa_value_domaint:public ai_domain_baset static unsigned merge_alignment(unsigned a, unsigned b) { // could use lcm here - if(a==b) return a; - if(a==0) return b; - if(b==0) return a; + if(a==b) + return a; + if(a==0) + return b; + if(b==0) + return a; return 1; } }; diff --git a/src/ssa/translate_union_member.cpp b/src/ssa/translate_union_member.cpp index 593ab4dc0..a3e078ef5 100644 --- a/src/ssa/translate_union_member.cpp +++ b/src/ssa/translate_union_member.cpp @@ -25,14 +25,15 @@ Function: translate_union_member void translate_union_member(exprt &dest, const namespacet &ns) { +#if 0 if(dest.id()==ID_member) { + // TODO } - #if 0 address_of_exprt address_of_expr(member_expr.struct_op()); pointer_typet pointer_type(member_expr.type()); typecast_exprt typecast_expr(address_of_expr, pointer_type); return dereference_exprt(typecast_expr, member_expr.type()); - #endif +#endif } diff --git a/src/ssa/translate_union_member.h b/src/ssa/translate_union_member.h index 740d7df29..5b546cc0f 100644 --- a/src/ssa/translate_union_member.h +++ b/src/ssa/translate_union_member.h @@ -6,8 +6,8 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#ifndef CPROVER_TRANSLATE_UNION_MEMBER_H -#define CPROVER_TRANSLATE_UNION_MEMBER_H +#ifndef CPROVER_2LS_SSA_TRANSLATE_UNION_MEMBER_H +#define CPROVER_2LS_SSA_TRANSLATE_UNION_MEMBER_H #include #include diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 7b81e2dbd..8b49f8e76 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -31,10 +31,10 @@ Function: unwindable_local_SSAt::increment_unwindings void unwindable_local_SSAt::increment_unwindings(int mode) { - if(mode==0) + if(mode==0) { assert(current_unwindings.size()>=1); - unsigned index = current_unwindings.size()-1; + unsigned index=current_unwindings.size()-1; assert(current_unwindings[index]::max()); current_unwindings[index]++; } @@ -43,9 +43,9 @@ void unwindable_local_SSAt::increment_unwindings(int mode) assert(mode==1); current_unwindings.push_back(0); } - else //mode <=-1 + else // mode <=-1 { - for(int i=0;i>mode;i--) + for(int i=0; i>mode; --i) current_unwindings.pop_back(); } } @@ -64,10 +64,10 @@ Function: unwindable_local_SSAt::decrement_unwindings void unwindable_local_SSAt::decrement_unwindings(int mode) { - if(mode==0) + if(mode==0) { assert(current_unwindings.size()>=1); - unsigned index = current_unwindings.size()-1; + unsigned index=current_unwindings.size()-1; assert(current_unwindings[index]>=1); current_unwindings[index]--; } @@ -76,9 +76,9 @@ void unwindable_local_SSAt::decrement_unwindings(int mode) assert(mode==1); current_unwindings.push_back(current_unwinding); } - else //mode <=-1 + else // mode <=-1 { - for(int i=0;i>mode;i--) + for(int i=0; i>mode; --i) current_unwindings.pop_back(); } } @@ -98,13 +98,13 @@ Function: unwindable_local_SSAt::odometer_to_string std::string unwindable_local_SSAt::odometer_to_string( const odometert &odometer, unsigned level) const { - if(current_unwinding<0) //not yet unwind=0 + if(current_unwinding<0) // not yet unwind=0 return ""; if(level>odometer.size()) - level = odometer.size(); - std::string unwind_suffix = ""; - for(unsigned i=0;ilocation_number << std::endl; std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << def_level << std::endl; std::cout << "RENAME_SYMBOL: " - << object.get_identifier() << " --> " - << s.get_identifier() << std::endl; + << object.get_identifier() << " --> " + << s.get_identifier() << std::endl; #endif return s; @@ -147,7 +152,7 @@ Function: unwindable_local_SSAt::get_def_level Outputs: - Purpose: returns the definition level of a variable in the loop + Purpose: returns the definition level of a variable in the loop hierarchy \*******************************************************************/ @@ -155,33 +160,69 @@ Function: unwindable_local_SSAt::get_def_level unsigned unwindable_local_SSAt::get_def_level( locationt def_loc, locationt current_loc) const { - loop_hierarchy_levelt::const_iterator lhl_it = + loop_hierarchy_levelt::const_iterator lhl_it= loop_hierarchy_level.find(def_loc); - unsigned def_level = 0; - if(lhl_it != loop_hierarchy_level.end()) + unsigned def_level=0; + if(lhl_it!=loop_hierarchy_level.end()) { - def_level = lhl_it->second.level; - // If a variable is defined in an other loop (that is on - // the same level as we are [we should should check that] - // then we have to take the "merged version" + def_level=lhl_it->second.level; + // If a variable is defined in an other loop + // that is defined on the same or a higher level + // then we have to take the "merged version" // The reason for this is that the "exit mergers" actually // introduce a new SSA variable version on the context level of a loop. - loop_hierarchy_levelt::const_iterator current_lhl = + loop_hierarchy_levelt::const_iterator current_lhl= loop_hierarchy_level.find(current_loc); + #if 0 - std::cout << "def_level: " << def_level << std::endl; - std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; - std::cout << "current_location: " << current_loc->location_number << std::endl; - std::cout << "current_loop_number: " << current_lhl->second.loop_number << std::endl; + std::cout << "current_location: " + << current_loc->location_number << std::endl; + std::cout << "parent_location: " + << + (current_lhl->second.parent_loc!=goto_function.body.instructions.end()? + current_lhl->second.parent_loc->location_number:-1) + << std::endl; + std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; + std::cout << "current_number: " + << current_lhl->second.loop_number << std::endl; #endif - if(current_lhl != loop_hierarchy_level.end() && - lhl_it->second.loop_number != current_lhl->second.loop_number && - lhl_it->second.level == current_lhl->second.level) + + if(current_lhl!=loop_hierarchy_level.end() && + current_lhl->second.level==0) { - if(current_lhl->second.level>0) - def_level = current_lhl->second.level-1; - else - def_level = 0; + def_level=0; + } + else if(current_lhl!=loop_hierarchy_level.end() && + lhl_it->second.loop_number!=current_lhl->second.loop_number && + def_level>0) + { + bool is_parent=false; + while(current_lhl->second.parent_loc!= + goto_function.body.instructions.end()) + { +#if 0 + std::cout << "current-loc: " + << current_lhl->first->location_number << std::endl; + std::cout << "parent-loc: " + << current_lhl->second.parent_loc->location_number + << std::endl; + std::cout << "def_level: " << def_level << std::endl; + std::cout << "loop_number: " << lhl_it->second.loop_number << std::endl; + std::cout << "current_loop_number: " + << current_lhl->second.loop_number << std::endl; +#endif + + current_lhl=loop_hierarchy_level.find(current_lhl->second.parent_loc); + if(lhl_it->second.loop_number==current_lhl->second.loop_number) + { + is_parent=true; + break; + } + } + if(!is_parent) + { + --def_level; + } } } return def_level; @@ -199,23 +240,28 @@ Function: unwindable_local_SSAt::nondet_symbol \*******************************************************************/ -exprt unwindable_local_SSAt::nondet_symbol(std::string prefix, - const typet &type, locationt loc, unsigned counter) const +exprt unwindable_local_SSAt::nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const { - std::string unwind_suffix = odometer_to_string(current_unwindings, - current_unwindings.size()); + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); exprt s(ID_nondet_symbol, type); const irep_idt identifier= prefix+ i2string(loc->location_number)+ "."+i2string(counter)+unwind_suffix+suffix; s.set(ID_identifier, identifier); + #if 0 std::cout << "DEF_LOC: " << loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << current_unwindings.size() << std::endl; std::cout << "RENAME_SYMBOL: " - << s.get(ID_identifier) << std::endl; + << s.get(ID_identifier) << std::endl; #endif + return s; } @@ -233,44 +279,43 @@ Function: unwindable_local_SSAt::rename void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { - if (expr.id() == ID_function_application) - { - std::string unwind_suffix = odometer_to_string(current_unwindings, - current_unwindings.size()); - expr.set(ID_suffix,unwind_suffix); - } if(expr.id()==ID_symbol) { - symbol_exprt &s = to_symbol_expr(expr); + symbol_exprt &s=to_symbol_expr(expr); locationt def_loc; - //we could reuse name(), but then we would have to search in the ssa_objects - //ENHANCEMENT: maybe better to attach base name, ssa name, + // we could reuse name(), + // but then we would have to search in the ssa_objects + // ENHANCEMENT: maybe better to attach base name, ssa name, // and def_loc to the symbol_expr itself - irep_idt id = get_ssa_name(s,def_loc); - unsigned def_level = get_def_level(def_loc, current_loc); - std::string unwind_suffix = odometer_to_string(current_unwindings, - def_level); + irep_idt id=get_ssa_name(s, def_loc); + unsigned def_level=get_def_level(def_loc, current_loc); + std::string unwind_suffix= + odometer_to_string(current_unwindings, def_level); s.set_identifier(id2string(id)+unwind_suffix); - s.set(ID_suffix,unwind_suffix); + #if 0 - std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; - std::cout << "DEF_LEVEL: " << def_level << std::endl; - std::cout << "O.size: " << current_unwindings.size() << std::endl; - std::cout << "current: " << current_unwinding << std::endl; - std::cout << "RENAME_SYMBOL: " - << id << " --> " - << s.get_identifier() << std::endl; + std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; + std::cout << "DEF_LEVEL: " << def_level << std::endl; + std::cout << "O.size: " << current_unwindings.size() << std::endl; + std::cout << "current: " << current_unwinding << std::endl; + std::cout << "RENAME_SYMBOL: " + << id << " --> " + << s.get_identifier() << std::endl; #endif } if(expr.id()==ID_nondet_symbol) { - std::string unwind_suffix = odometer_to_string(current_unwindings, - current_unwindings.size()); - expr.set(ID_suffix,unwind_suffix); - expr.set(ID_identifier, - id2string(expr.get(ID_identifier))+unwind_suffix+suffix); + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); + std::string identifier=id2string(expr.get(ID_identifier)); + std::size_t pos=identifier.find("%"); + if(pos!=std::string::npos) + identifier=identifier.substr(0, pos); + expr.set( + ID_identifier, + identifier+unwind_suffix+suffix); } - Forall_operands(it,expr) + Forall_operands(it, expr) rename(*it, current_loc); } @@ -287,31 +332,69 @@ Function: unwindable_local_SSAt::get_ssa_name \*******************************************************************/ irep_idt unwindable_local_SSAt::get_ssa_name( - const symbol_exprt &symbol_expr, locationt &loc) + const symbol_exprt &symbol_expr, locationt &loc) const { - std::string s = id2string(symbol_expr.get_identifier()); + std::string s=id2string(symbol_expr.get_identifier()); #if 0 std::cout << "id: " << s << std::endl; #endif - std::size_t pos2 = s.find("%"); - std::size_t pos1 = s.find_last_of("#"); + std::size_t pos2=s.find("%"); + std::size_t pos1=s.find_last_of("#"); if(pos1==std::string::npos) return irep_idt(s); if(pos2==std::string::npos) - pos2 = s.size(); - if(s.substr(pos1+1,2) == "lb") pos1 += 2; - else if(s.substr(pos1+1,2) == "ls") pos1 += 2; - else if(s.substr(pos1+1,3) == "phi") pos1 += 3; - else if((pos2 == pos1+13) && (s.substr(pos1+1,12) == "return_value")) + pos2=s.size(); + if(s.substr(pos1+1, 2)=="lb") + pos1+=2; + else if(s.substr(pos1+1, 2)=="ls") + pos1+=2; + else if(s.substr(pos1+1, 3)=="phi") + pos1+=3; + else if((pos2==pos1+13) && (s.substr(pos1+1, 12)=="return_value")) return irep_idt(s); #if 0 - std::cout << s << ", " << s.substr(pos1+1,pos2-pos1-1) << ", " << s.substr(0,pos2) << std::endl; + std::cout << s << ", " << s.substr(pos1+1, pos2-pos1-1) + << ", " << s.substr(0, pos2) << std::endl; #endif - loc = get_location( - safe_string2unsigned(s.substr(pos1+1,pos2-pos1-1))); - return irep_idt(s.substr(0,pos2)); + loc=get_location( + safe_string2unsigned(s.substr(pos1+1, pos2-pos1-1))); + return irep_idt(s.substr(0, pos2)); } +/*******************************************************************\ + +Function: unwindable_local_SSAt::get_full_ssa_name + + Inputs: + + Outputs: + + Purpose: retrieve ssa name, location, and odometer + +\*******************************************************************/ + +irep_idt unwindable_local_SSAt::get_full_ssa_name( + const symbol_exprt &symbol_expr, + locationt &loc, + odometert &odometer) const +{ + const std::string s=id2string(symbol_expr.get_identifier()); + std::size_t pos1=s.find("%"); + if(pos1!=std::string::npos) + { + std::size_t pos2=0; + do + { + pos2=s.find("%", pos1+1); + if(pos2==std::string::npos) + pos2=s.size(); + odometer.push_back(safe_string2unsigned(s.substr(pos1+1, pos2-pos1-1))); + pos1=pos2; + } + while(pos2!=s.size()); + } + return get_ssa_name(symbol_expr, loc); +} /*******************************************************************\ @@ -329,7 +412,7 @@ void unwindable_local_SSAt::compute_loop_hierarchy() { loop_hierarchy_level.clear(); std::list loopheads; - goto_programt::const_targett i_it = goto_function.body.instructions.end(); + goto_programt::const_targett i_it=goto_function.body.instructions.end(); do { --i_it; @@ -338,34 +421,41 @@ void unwindable_local_SSAt::compute_loop_hierarchy() std::cout << "location: " << i_it->location_number << std::endl; if(i_it->is_goto()) std::cout << "- target: " << i_it->get_target()->location_number - << std::endl; + << std::endl; #endif if(i_it->is_backwards_goto()) { - loopheads.push_back(i_it->get_target()); - loop_hierarchy_level[loopheads.back()].loop_number = i_it->loop_number; + local_SSAt::locationt parent=goto_function.body.instructions.end(); + if(!loopheads.empty()) + parent=loopheads.back(); + local_SSAt::locationt loophead=i_it->get_target(); + loopheads.push_back(loophead); + loop_hierarchy_level[loophead].loop_number=i_it->loop_number; + loop_hierarchy_level[loophead].parent_loc=parent; } if(!loopheads.empty()) { - loop_hierarchy_level[i_it].loop_number = - loop_hierarchy_level[loopheads.back()].loop_number; - loop_hierarchy_level[i_it].level = loopheads.size(); + loop_hierarchy_level[i_it].loop_number= + loop_hierarchy_level[loopheads.back()].loop_number; + loop_hierarchy_level[i_it].level=loopheads.size(); + loop_hierarchy_level[i_it].parent_loc= + loop_hierarchy_level[loopheads.back()].parent_loc; #if 0 - std::cout << "- current: " << - loopheads.back()->location_number << std::endl; + std::cout << "- current: " << + loopheads.back()->location_number << std::endl; #endif - - if(i_it == loopheads.back()) + + if(i_it==loopheads.back()) { #if 0 - std::cout << "- is loop head" << std::endl; + std::cout << "- is loop head" << std::endl; #endif - loopheads.pop_back(); + loopheads.pop_back(); } } } - while(i_it != goto_function.body.instructions.begin()); + while(i_it!=goto_function.body.instructions.begin()); } diff --git a/src/ssa/unwindable_local_ssa.h b/src/ssa/unwindable_local_ssa.h index 5cc1abc58..462e89169 100644 --- a/src/ssa/unwindable_local_ssa.h +++ b/src/ssa/unwindable_local_ssa.h @@ -6,22 +6,21 @@ Author: Peter Schrammel, Saurabh Joshi \*******************************************************************/ -#ifndef CPROVER_DELTACHECK_SSA_UNWINDABLE_LOCAL_SSA_H -#define CPROVER_DELTACHECK_SSA_UNWINDABLE_LOCAL_SSA_H +#ifndef CPROVER_2LS_SSA_UNWINDABLE_LOCAL_SSA_H +#define CPROVER_2LS_SSA_UNWINDABLE_LOCAL_SSA_H #include #include "local_ssa.h" -class unwindable_local_SSAt : public local_SSAt +class unwindable_local_SSAt:public local_SSAt { public: unwindable_local_SSAt( const goto_functiont &_goto_function, const namespacet &_ns, - const std::string &_suffix="") - : - local_SSAt(_goto_function,_ns,_suffix), + const std::string &_suffix=""): + local_SSAt(_goto_function, _ns, _suffix), current_unwinding(-1) { compute_loop_hierarchy(); @@ -29,43 +28,59 @@ class unwindable_local_SSAt : public local_SSAt virtual ~unwindable_local_SSAt() {} - virtual symbol_exprt name(const ssa_objectt &obj, - kindt kind, locationt loc) const - { return name(obj,kind,loc,loc); } - symbol_exprt name(const ssa_objectt &, kindt, - locationt def_loc, locationt current_loc) const; - virtual exprt nondet_symbol(std::string prefix, const typet &type, - locationt loc, unsigned counter) const; - - //control renaming during unwindings + virtual symbol_exprt name( + const ssa_objectt &obj, + kindt kind, + locationt loc) const + { + return name(obj, kind, loc, loc); + } + symbol_exprt name( + const ssa_objectt &, kindt, locationt def_loc, locationt current_loc) const; + virtual exprt nondet_symbol( + std::string prefix, + const typet &type, + locationt loc, + unsigned counter) const; + + // control renaming during unwindings typedef std::vector odometert; odometert current_unwindings; - long current_unwinding; //TODO: must go away - locationt current_location; //TOOD: must go away, not sure how; + long current_unwinding; // TODO: must go away + locationt current_location; // TOOD: must go away, not sure how; // mode==0: current, mode>0 push, mode<0 pop void increment_unwindings(int mode); // mode==0: current, mode>0 push, mode<0 pop void decrement_unwindings(int mode); - std::string odometer_to_string(const odometert &odometer, - unsigned level) const; + std::string odometer_to_string( + const odometert &odometer, + unsigned level) const; void rename(exprt &expr, locationt loc); - typedef struct { + typedef struct + { unsigned level; unsigned loop_number; - } loop_hierarchy_infot; + local_SSAt::locationt parent_loc; + } + loop_hierarchy_infot; - typedef std::map + typedef std::map loop_hierarchy_levelt; loop_hierarchy_levelt loop_hierarchy_level; + irep_idt get_full_ssa_name( + const symbol_exprt &symbol_expr, + locationt &loc, + odometert &odometer) const; + protected: - irep_idt get_ssa_name(const symbol_exprt &, locationt &loc); + irep_idt get_ssa_name(const symbol_exprt &, locationt &loc) const; + unsigned get_def_level(locationt def_loc, locationt current_loc) const; void compute_loop_hierarchy(); - }; #endif diff --git a/src/storefront/Makefile b/src/storefront/Makefile deleted file mode 100644 index 55c7aa7ce..000000000 --- a/src/storefront/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -include ../config.inc - -SRC = storefront_main.cpp storefront_parse_options.cpp data.cpp \ - property_view.cpp file_view.cpp trace_view.cpp - -OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - ../html/html_escape$(OBJEXT) \ - ../html/syntax_highlighting$(OBJEXT) \ - ../html/logo$(OBJEXT) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -LIBS = - -CLEANFILES = storefront$(EXEEXT) - -all: storefront$(EXEEXT) - -############################################################################### - -storefront$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/storefront/data.cpp b/src/storefront/data.cpp deleted file mode 100644 index 391a40991..000000000 --- a/src/storefront/data.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include "data.h" - -/*******************************************************************\ - -Function: datat::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void datat::read(const std::string &file) -{ - xmlt xml; - - console_message_handlert message_handler; - parse_xml(file, message_handler, xml); - - read(xml); -} - -/*******************************************************************\ - -Function: datat::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void datat::read(const xmlt &xml) -{ - for(xmlt::elementst::const_iterator - it=xml.elements.begin(); - it!=xml.elements.end(); - it++) - { - if(it->name=="property") - { - propertyt property; - - for(xmlt::elementst::const_iterator - e_it=it->elements.begin(); - e_it!=it->elements.end(); - e_it++) - { - if(e_it->name=="file") - property.file=e_it->data; - else if(e_it->name=="line") - property.line=unsafe_string2unsigned(e_it->data); - else if(e_it->name=="category") - property.category=e_it->data; - else if(e_it->name=="message") - property.message=e_it->data; - } - - properties.push_back(property); - } - else if(it->name=="description") - { - description=it->data; - } - } -} - diff --git a/src/storefront/data.h b/src/storefront/data.h deleted file mode 100644 index 046c58ceb..000000000 --- a/src/storefront/data.h +++ /dev/null @@ -1,40 +0,0 @@ -/*******************************************************************\ - -Module: Data - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_DATA_H -#define CPROVER_DELTACHECK_DATA_H - -#include - -class datat -{ -public: - class propertyt - { - public: - irep_idt file; - unsigned line; - irep_idt category; - std::string message; - }; - - typedef std::vector propertiest; - propertiest properties; - - std::string description; - - inline void add(const propertyt &e) - { - properties.push_back(e); - } - - void read(const std::string &file); - void read(const class xmlt &); -}; - -#endif diff --git a/src/storefront/file_view.cpp b/src/storefront/file_view.cpp deleted file mode 100644 index a20041d77..000000000 --- a/src/storefront/file_view.cpp +++ /dev/null @@ -1,147 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include - -#include "../html/html_escape.h" -#include "../html/syntax_highlighting.h" - -#include "data.h" - -/*******************************************************************\ - -Function: file_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void print_file(const datat &data, irep_idt file, std::ostream &out) -{ - out << "
\n"; - out << "
" << html_escape(file) << "
\n"; - out << "
\n"; - - std::ifstream in(file.c_str()); - if(!in) - { - } - else - { - // line to property number - std::map > line_map; - - for(datat::propertiest::const_iterator - e_it=data.properties.begin(); - e_it!=data.properties.end(); - e_it++) - if(e_it->file==file) - { - line_map[e_it->line].push_back(e_it-data.properties.begin()); - } - - syntax_highlightingt syntax_highlighting(out); - - unsigned line_no=1; - - std::string line; - while(std::getline(in, line)) - { - syntax_highlighting.strong_class=""; - syntax_highlighting.line_no=line_no; - - std::vector &properties=line_map[line_no]; - - if(!properties.empty()) - { - syntax_highlighting.strong_class="alarm"; - } - - syntax_highlighting(line); - - line_no++; - } - } - - out << "
\n\n"; -} - -/*******************************************************************\ - -Function: file_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void file_view(const datat &data) -{ - std::ofstream out("file_view.html"); - - out << "\n\n"; - out << "" << html_escape(data.description) << "\n"; - - out << "\n"; - out << "\n"; - - out << "\n"; - - out << "
\n"; - - out << "
\n"; - out << html_escape(data.description) << "\n"; - out << "
\n"; - - std::set files; - - for(datat::propertiest::const_iterator - e_it=data.properties.begin(); - e_it!=data.properties.end(); - e_it++) - files.insert(e_it->file); - - for(std::set::const_iterator - f_it=files.begin(); - f_it!=files.end(); - f_it++) - { - if(has_prefix(id2string(*f_it), "/usr/include/")) - continue; - - if(has_prefix(id2string(*f_it), "")) - continue; - - print_file(data, *f_it, out); - } - - out << "\n\n"; -} diff --git a/src/storefront/file_view.h b/src/storefront/file_view.h deleted file mode 100644 index e0c5ca8ee..000000000 --- a/src/storefront/file_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: File View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_FILE_VIEW_H -#define CPROVER_DELTACHECK_FILE_VIEW_H - -void file_view(const class datat &); - -#endif diff --git a/src/storefront/property_view.cpp b/src/storefront/property_view.cpp deleted file mode 100644 index 21c5b31e8..000000000 --- a/src/storefront/property_view.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/*******************************************************************\ - -Module: Property View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "data.h" - -/*******************************************************************\ - -Function: property_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void property_view(const class datat &) -{ - -} - - - - diff --git a/src/storefront/property_view.h b/src/storefront/property_view.h deleted file mode 100644 index 83f1eae61..000000000 --- a/src/storefront/property_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Property View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_PROPERTY_VIEW_H -#define CPROVER_DELTACHECK_PROPERTY_VIEW_H - -void property_view(const class datat &); - -#endif diff --git a/src/storefront/storefront_main.cpp b/src/storefront/storefront_main.cpp deleted file mode 100644 index 3914e4dbd..000000000 --- a/src/storefront/storefront_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "storefront_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - storefront_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - storefront_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/storefront/storefront_parse_options.cpp b/src/storefront/storefront_parse_options.cpp deleted file mode 100644 index 2cf35655c..000000000 --- a/src/storefront/storefront_parse_options.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "../deltacheck/version.h" - -#include "storefront_parse_options.h" -#include "data.h" -#include "file_view.h" -#include "trace_view.h" -#include "property_view.h" - -/*******************************************************************\ - -Function: storefront_parse_optionst::storefront_parse_optionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -storefront_parse_optionst::storefront_parse_optionst( - int argc, const char **argv): - parse_options_baset(STOREFRONT_OPTIONS, argc, argv) -{ -} - -/*******************************************************************\ - -Function: storefront_parse_optionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int storefront_parse_optionst::doit() -{ - if(cmdline.isset("version")) - { - std::cout << DELTACHECK_VERSION << std::endl; - return 0; - } - - try - { - if(cmdline.args.empty()) - { - usage_error(); - return 10; - } - - // read config - datat data; - - for(unsigned i=0; i - -#define STOREFRONT_OPTIONS \ - "(verbosity):(version)" - -class storefront_parse_optionst:public parse_options_baset -{ -public: - virtual int doit(); - virtual void help(); - - storefront_parse_optionst( - int argc, const char **argv); - -protected: -}; - -#endif diff --git a/src/storefront/trace_view.cpp b/src/storefront/trace_view.cpp deleted file mode 100644 index 73c2a30b3..000000000 --- a/src/storefront/trace_view.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "data.h" - -/*******************************************************************\ - -Function: trace_view - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void trace_view(const class datat &) -{ - -} - - - diff --git a/src/storefront/trace_view.h b/src/storefront/trace_view.h deleted file mode 100644 index fcaaf7a4f..000000000 --- a/src/storefront/trace_view.h +++ /dev/null @@ -1,14 +0,0 @@ -/*******************************************************************\ - -Module: Trace View - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_TRACE_VIEW_H -#define CPROVER_DELTACHECK_TRACE_VIEW_H - -void trace_view(const class datat &); - -#endif diff --git a/src/summarizer/2ls-wrapper.sh b/src/summarizer/2ls-wrapper.sh deleted file mode 100644 index ab4dadd9c..000000000 --- a/src/summarizer/2ls-wrapper.sh +++ /dev/null @@ -1,81 +0,0 @@ -#!/bin/bash - -parse_property_file() -{ - local fn=$1 - - cat $fn | sed 's/[[:space:]]//g' | perl -n -e ' -if(/^CHECK\(init\((\S+)\(\)\),LTL\(G(\S+)\)\)$/) { - print "ENTRY=$1\n"; - print "PROPERTY=\"--error-label $1\"\n" if($2 =~ /^!label\((\S+)\)$/); - print "PROPERTY=\" \"\n" if($2 =~ /^!call\(__VERIFIER_error\(\)\)$/); - print "PROPERTY=\"--pointer-check --memory-leak-check --bounds-check\"\n" if($2 =~ /^valid-(free|deref|memtrack)$/); - print "PROPERTY=\"--signed-overflow-check\"\n" if($2 =~ /^!SignedIntegerOverflow$/); -}' -} - -parse_result() -{ - if tail -n 50 $LOG.ok | grep -q "^[[:space:]]*__CPROVER_memory_leak == NULL$" ; then - echo 'FALSE(valid-memtrack)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*dereference failure:" ; then - echo 'FALSE(valid-deref)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*double free$" ; then - echo 'FALSE(valid-free)' - elif tail -n 50 $LOG.ok | grep -q "^[[:space:]]*free argument has offset zero$" ; then - echo 'FALSE(valid-free)' - else - echo FALSE - fi -} - -BIT_WIDTH="--64" -BM="" -PROP_FILE="" - -while [ -n "$1" ] ; do - case "$1" in - --32|--64) BIT_WIDTH="$1" ; shift 1 ;; - --propertyfile) PROP_FILE="$2" ; shift 2 ;; - *) BM="$1" ; shift 1 ;; - esac -done - -if [ -z "$BM" ] || [ -z "$PROP_FILE" ] ; then - echo "Missing benchmark or property file" - exit 1 -fi - -if [ ! -s "$BM" ] || [ ! -s "$PROP_FILE" ] ; then - echo "Empty benchmark or property file" - exit 1 -fi - -eval `parse_property_file $PROP_FILE` -export ENTRY -export PROPERTY -export BIT_WIDTH -export BM - -export LOG=`mktemp -t 2ls-log.XXXXXX` -trap "rm -f $LOG.ok $LOG.cex" EXIT - -./2ls --k-induction --competition-mode --graphml-cex $LOG.cex $BIT_WIDTH $PROPERTY --function $ENTRY $BM >> $LOG.ok 2>&1 -ec=$? -cat $LOG.ok -cat $LOG.cex -if [ $ec -eq 0 ] -then - echo "TRUE" -fi -if [ $ec -eq 5 ] -then - echo "UNKNOWN" -fi -if [ $ec -eq 10 ] -then - parse_result - cp $LOG.cex witness.graphml -fi -exit $ec - diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile index 600dc3a7c..bcc3b454b 100644 --- a/src/summarizer/Makefile +++ b/src/summarizer/Makefile @@ -1,8 +1,7 @@ include ../config.inc -SRC = summarizer_base.cpp summarizer_languages.cpp \ +SRC = summarizer_base.cpp \ summarizer_fw.cpp summarizer_bw.cpp \ - summarizer_fw_term.cpp summarizer_bw_term.cpp \ summarizer_bw_cex.cpp summarizer_bw_cex_complete.cpp \ summarizer_bw_cex_concrete.cpp \ summarizer_bw_cex_wp.cpp \ @@ -12,11 +11,10 @@ SRC = summarizer_base.cpp summarizer_languages.cpp \ show.cpp summary_checker_base.cpp \ summary_checker_ai.cpp summary_checker_bmc.cpp \ summary_checker_kind.cpp \ - cover_goals_ext.cpp horn_encoding.cpp \ + cover_goals_ext.cpp \ summary_db.cpp summary.cpp ssa_db.cpp \ array_abstraction.cpp preprocessing_util.cpp \ - instrument_goto.cpp -#function_signature.cpp + instrument_goto.cpp function_signature.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ @@ -32,10 +30,10 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/solvers/solvers$(LIBEXT) \ $(CBMC)/src/util/util$(LIBEXT) \ + DELTA_OBJ+=\ $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ - $(CBMC)/src/goto-instrument/loop_utils$(OBJEXT) \ - ../ssa/local_ssa$(OBJEXT) \ + ../ssa/ssa_slicer$(OBJEXT) \ ../ssa/malloc_ssa$(OBJEXT) \ ../ssa/ssa_domain$(OBJEXT) \ ../ssa/assignments$(OBJEXT) \ @@ -48,14 +46,16 @@ DELTA_OBJ+=\ ../ssa/split_loopheads$(OBJEXT)\ ../ssa/ssa_inliner$(OBJEXT)\ ../ssa/ssa_unwinder$(OBJEXT)\ - ../ssa/unwindable_local_ssa$(OBJEXT)\ ../ssa/ssa_value_set$(OBJEXT) \ ../ssa/const_propagator$(OBJEXT) \ ../ssa/replace_symbol_ext$(OBJEXT) \ ../ssa/ssa_const_propagator$(OBJEXT) \ ../ssa/ssa_dependency_graph$(OBJEXT) \ - ../ssa/ssa_refiner_monolithic$(OBJEXT) \ - ../ssa/ssa_refiner_selective$(OBJEXT) \ + ../functions/summary$(OBJEXT) \ + ../functions/get_function$(OBJEXT) \ + ../functions/path_util$(OBJEXT) \ + ../functions/index$(OBJEXT) \ + ../functions/index$(OBJEXT) \ ../domains/fixed_point$(OBJEXT) \ ../domains/ssa_fixed_point$(OBJEXT) \ ../domains/tpolyhedra_domain$(OBJEXT) \ @@ -76,15 +76,9 @@ DELTA_OBJ+=\ ../domains/template_generator_callingcontext$(OBJEXT) \ ../domains/ssa_analyzer$(OBJEXT) \ ../domains/disjunctive_analyzer$(OBJEXT) \ - ../domains/predicate$(OBJEXT) \ - ../domains/template_generator_ranking$(OBJEXT) \ - ../domains/ranking_solver_enumeration$(OBJEXT) \ - ../domains/linrank_domain$(OBJEXT) \ - ../domains/lexlinrank_solver_enumeration$(OBJEXT) \ - ../domains/lexlinrank_domain$(OBJEXT) \ - ../domains/simplify_bounds$(OBJEXT) + ../domains/predicate$(OBJEXT) # ../domains/solver_enumeration$(OBJEXT) -# ../domains/strategy_solver_binsearch2$(OBJEXT) + OBJ+=$(DELTA_OBJ) @@ -105,9 +99,9 @@ LIBS = # $(CUDD)/obj/libobj.a -CLEANFILES = 2ls$(EXEEXT) $(DELTA_OBJ) +CLEANFILES = summarizer$(EXEEXT) $(DELTA_OBJ) -all: 2ls$(EXEEXT) +all: summarizer$(EXEEXT) ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) @@ -121,6 +115,6 @@ endif ############################################################################### -2ls$(EXEEXT): $(OBJ) +summarizer$(EXEEXT): $(OBJ) $(LINKBIN) diff --git a/src/summarizer/function_signature.cpp b/src/summarizer/function_signature.cpp deleted file mode 100644 index df464a847..000000000 --- a/src/summarizer/function_signature.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*******************************************************************\ - -Module: Signature of a Function - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "../ssa/local_ssa.h" -#include "function_signature.h" - -/*******************************************************************\ - -Function: update_function_signature - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void update_function_signature( - const local_SSAt &SSA, - class jsont &dest) -{ - jsont &j_signature=dest["signature"]; - jsont &j_reads=j_signature["reads"]; - jsont &j_modifies=j_signature["modifies"]; - - j_signature.kind=jsont::J_OBJECT; - j_reads=jsont::json_array(); - j_modifies=jsont::json_array(); - - std::set modifies; - std::set reads; - - for(assignmentst::assignment_mapt::const_iterator - a_it=SSA.assignments.assignment_map.begin(); - a_it!=SSA.assignments.assignment_map.end(); - a_it++) - { - for(assignmentst::objectst::const_iterator - o_it=a_it->second.begin(); - o_it!=a_it->second.end(); - o_it++) - { - modifies.insert(o_it->get_identifier()); - } - } - - for(ssa_objectst::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - reads.insert(o_it->get_identifier()); - } - - for(std::set::const_iterator it=reads.begin(); - it!=reads.end(); - it++) - { - j_reads.array.push_back(jsont::json_string(id2string(*it))); - } - - for(std::set::const_iterator it=modifies.begin(); - it!=modifies.end(); - it++) - { - j_modifies.array.push_back(jsont::json_string(id2string(*it))); - } - -} - diff --git a/src/summarizer/function_signature.h b/src/summarizer/function_signature.h deleted file mode 100644 index 16a7eaf09..000000000 --- a/src/summarizer/function_signature.h +++ /dev/null @@ -1,17 +0,0 @@ -/*******************************************************************\ - -Module: Signature of a Function - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H -#define CPROVER_SUMMARIZER_FUNCTION_SIGNATURE_H - -#include - -void update_function_signature(const class local_SSAt &, jsont &); - -#endif - diff --git a/src/summarizer/preprocessing_util.cpp b/src/summarizer/preprocessing_util.cpp deleted file mode 100644 index 0be64ff76..000000000 --- a/src/summarizer/preprocessing_util.cpp +++ /dev/null @@ -1,265 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "summarizer_parse_options.h" - - -/*******************************************************************\ - -Function: summarizer_parse_optionst::inline_main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parse_optionst::inline_main(goto_modelt &goto_model) -{ - goto_programt &main = goto_model.goto_functions.function_map[ID__start].body; - goto_programt::targett target = main.instructions.begin(); - while(target!=main.instructions.end()) - { - if(target->is_function_call()) - { - const code_function_callt &code_function_call= - to_code_function_call(target->code); - irep_idt fname = code_function_call.function().get(ID_identifier); - - debug() << "Inlining " << fname << eom; - - goto_programt tmp; - tmp.copy_from(goto_model.goto_functions.function_map[fname].body); - (--tmp.instructions.end())->make_skip(); - goto_model.goto_functions.function_map.erase(fname); - - goto_programt::targett next_target(target); - target->make_skip(); - next_target++; - main.instructions.splice(next_target, tmp.instructions); - target=next_target; - } - else target++; - } - - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: summarizer_parse_optionst::propagate_constants - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parse_optionst::propagate_constants(goto_modelt &goto_model) -{ - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - constant_propagator_ait(f_it->second,ns); - } -} - -/*******************************************************************\ - -Function: summarizer_parse_optionst::nondet_locals - - Inputs: - - Outputs: - - Purpose: explicitly assign a nondet_symbol to local variables - this is required by the unwinder, which would be unable - to recognise in which scope variables have been declared - -\*******************************************************************/ - -void summarizer_parse_optionst::nondet_locals(goto_modelt &goto_model) -{ - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(i_it->is_decl()) - { - const code_declt& decl = to_code_decl(i_it->code); - side_effect_expr_nondett nondet(decl.symbol().type()); - goto_programt::targett t = f_it->second.body.insert_after(i_it); - t->make_assignment(); - code_assignt c(decl.symbol(),nondet); - t->code.swap(c); - t->source_location = i_it->source_location; - } - } - } - goto_model.goto_functions.update(); -} - -/*******************************************************************\ - -Function: goto_unwind - - Inputs: - - Outputs: - - Purpose: unwind all loops - -\*******************************************************************/ - -void summarizer_parse_optionst::unwind_goto_into_loop(goto_modelt &goto_model, unsigned k) -{ - typedef std::vector > loopst; - - Forall_goto_functions(f_it, goto_model.goto_functions) - { - goto_programt &body=f_it->second.body; - - loopst loops; - Forall_goto_program_instructions(i_it, body) - { - if(i_it->is_backwards_goto()) - { - goto_programt::targett loop_head = i_it->get_target(); - goto_programt::targett loop_exit = i_it; - bool has_goto_into_loop = false; - - goto_programt::targett it = loop_head; - if(it!=loop_exit) it++; - for(; it!=loop_exit; it++) - { - for( std::set::iterator - s_it = it->incoming_edges.begin(); - s_it!=it->incoming_edges.end(); ++s_it) - { - if((*s_it)->is_goto() && - (*s_it)->location_number < loop_head->location_number) - { - has_goto_into_loop = true; - break; - } - } - if(has_goto_into_loop) break; - } - if(has_goto_into_loop) - { - status() << "Unwinding jump into loop" << eom; - loops.push_back(loopst::value_type(++loop_exit,loop_head)); - } - } - } - - for(loopst::iterator l_it = loops.begin(); l_it != loops.end(); ++l_it) - { - std::vector iteration_points; - unwind(body,l_it->second,l_it->first,k,iteration_points); - assert(iteration_points.size()==2); - goto_programt::targett t=body.insert_before(l_it->first); - t->make_goto(); - t->targets.push_back(iteration_points.front()); - } - } - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: remove_multiple_dereferences - - Inputs: - - Outputs: - - Purpose: temporary fix to circumvent ssa_dereference problem - -\*******************************************************************/ - -void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model, goto_programt &body, goto_programt::targett t, exprt &expr, unsigned &var_counter, bool deref_seen) -{ - if(expr.id()==ID_member) - { - member_exprt &member_expr = to_member_expr(expr); - if(member_expr.compound().id()==ID_dereference) - { - dereference_exprt &deref_expr = to_dereference_expr(member_expr.compound()); - remove_multiple_dereferences(goto_model,body,t,deref_expr.pointer(),var_counter,true); - if(deref_seen) - { - symbolt new_symbol; - new_symbol.type=member_expr.type(); - new_symbol.name="$deref"+i2string(var_counter++); - new_symbol.base_name=new_symbol.name; - new_symbol.pretty_name=new_symbol.name; - goto_model.symbol_table.add(new_symbol); - goto_programt::targett t_new = body.insert_before(t); - t_new->make_assignment(); - t_new->code = code_assignt(new_symbol.symbol_expr(),member_expr); - expr = new_symbol.symbol_expr(); - for(std::set::iterator t_it = - t->incoming_edges.begin(); - t_it != t->incoming_edges.end(); ++t_it) - { - (*t_it)->targets.clear(); - (*t_it)->targets.push_back(t_new); - } - body.compute_location_numbers(); - body.compute_target_numbers(); - body.compute_incoming_edges(); - } - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); - } - else - Forall_operands(o_it,expr) - remove_multiple_dereferences(goto_model,body,t,*o_it,var_counter,deref_seen); -} - -void summarizer_parse_optionst::remove_multiple_dereferences(goto_modelt &goto_model) -{ - unsigned var_counter = 0; - namespacet ns(goto_model.symbol_table); - Forall_goto_functions(f_it, goto_model.goto_functions) - { - Forall_goto_program_instructions(i_it, f_it->second.body) - { - if(i_it->is_goto()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - i_it->guard, - var_counter, false); - } - else if(i_it->is_assign()) - { - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).lhs(), - var_counter, false); - remove_multiple_dereferences(goto_model, - f_it->second.body, - i_it, - to_code_assign(i_it->code).rhs(), - var_counter, false); - } - } - } -} diff --git a/src/summarizer/show.cpp b/src/summarizer/show.cpp index deef5d0a9..88751bfd0 100644 --- a/src/summarizer/show.cpp +++ b/src/summarizer/show.cpp @@ -11,9 +11,6 @@ Author: Daniel Kroening, kroening@kroening.com #include #include #include -#include -#include -#include #include #include @@ -392,7 +389,7 @@ void print_symbol_values(const local_SSAt &SSA, } } -void show_raw_countermodel(const irep_idt &property_id, +void show_error_trace(const irep_idt &property_id, const local_SSAt &SSA, prop_convt &solver, std::ostream &out, @@ -443,7 +440,7 @@ local_SSAt::locationt find_loc_by_guard(const local_SSAt &SSA, unsigned pos1 = gstr.find("#")+1; unsigned pos2 = gstr.find("%",pos1); unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); - return SSA.get_location(n); + return SSA.find_location_by_number(n); } void purify_identifiers(exprt &expr) diff --git a/src/summarizer/ssa_db.h b/src/summarizer/ssa_db.h index 6c3823b2b..22642b260 100644 --- a/src/summarizer/ssa_db.h +++ b/src/summarizer/ssa_db.h @@ -12,7 +12,6 @@ Author: Daniel Kroening, kroening@kroening.com #include #include "../ssa/local_ssa.h" -#include "../ssa/unwindable_local_ssa.h" #include "../ssa/ssa_inliner.h" #include "../ssa/ssa_dependency_graph.h" #include "../domains/incremental_solver.h" @@ -25,7 +24,7 @@ class ssa_dbt { public: typedef irep_idt function_namet; - typedef std::map functionst; + typedef std::map functionst; typedef std::map depgrapht; typedef std::map solverst; @@ -69,7 +68,7 @@ class ssa_dbt const goto_functionst::goto_functiont &goto_function, const namespacet &ns) { - store[function_name] = new unwindable_local_SSAt(goto_function,ns); + store[function_name] = new local_SSAt(goto_function,ns); } void depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner, bool entry); diff --git a/src/summarizer/summarizer.cpp b/src/summarizer/summarizer.cpp deleted file mode 100644 index 04c170e0f..000000000 --- a/src/summarizer/summarizer.cpp +++ /dev/null @@ -1,125 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -#include "function_signature.h" -#include "summary_db.h" -#include "summarizer.h" - -/*******************************************************************\ - -Function: summarizert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::operator()(const goto_modelt &goto_model) -{ - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - summarize(goto_model, f_it); -} - -/*******************************************************************\ - -Function: summarizert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::operator()( - const goto_modelt &goto_model, - const irep_idt &id) -{ - // analyze the given function only - - goto_functionst::function_mapt::const_iterator f_it= - goto_model.goto_functions.function_map.find(id); - - if(f_it==goto_model.goto_functions.function_map.end()) - throw "function not found"; - - summarize(goto_model, f_it); -} - -/*******************************************************************\ - -Function: summarizert::summarize - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::summarize( - const goto_modelt &goto_model, - const goto_functionst::function_mapt::const_iterator f_it) -{ - status() << "** Analyzing " << f_it->first << messaget::eom; - - summary_dbt summary_db; - - summary_db.set_message_handler(get_message_handler()); - summary_db.read(id2string(f_it->first)); - - const namespacet ns(goto_model.symbol_table); - - // build SSA - progress() << "Building SSA" << messaget::eom; - local_SSAt SSA(f_it->second, ns); - - // simplify, if requested - if(simplify) - { - progress() << "Simplifying" << messaget::eom; - ::simplify(SSA, ns); - } - - - // fixed-point for loops - #if 0 - progress() << "Fixed-point" << messaget::eom; - ssa_fixed_point(SSA); - #endif - - update_function_signature(SSA, summary_db.summary); - - summary_db.write(); -} - -/*******************************************************************\ - -Function: summarizert::report_statistics() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizert::report_statistics() -{ -} - diff --git a/src/summarizer/summarizer.h b/src/summarizer/summarizer.h deleted file mode 100644 index f4cc72785..000000000 --- a/src/summarizer/summarizer.h +++ /dev/null @@ -1,45 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_H -#define CPROVER_SUMMARIZER_H - -#include -#include - -#include - -#include "../ssa/local_ssa.h" - -class summarizert:public messaget -{ -public: - inline summarizert(): - simplify(false), - fixed_point(false) - { - } - - bool simplify, fixed_point; - - void operator()(const goto_modelt &); - void operator()(const goto_modelt &, const irep_idt &); - - // statistics - absolute_timet start_time; - time_periodt sat_time; - -protected: - void report_statistics(); - - void summarize( - const goto_modelt &, - const goto_functionst::function_mapt::const_iterator); -}; - -#endif diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h index 517153549..be8d80ae3 100644 --- a/src/summarizer/summarizer_base.h +++ b/src/summarizer/summarizer_base.h @@ -34,8 +34,7 @@ class summarizer_baset : public messaget nonpassed_assertions(false), solver_instances(0), solver_calls(0), - summaries_used(0), - termargs_computed(0) + summaries_used(0) {} typedef summaryt::predicatet preconditiont; @@ -48,28 +47,15 @@ class summarizer_baset : public messaget virtual void summarize(); virtual void summarize(const function_namet &entry_function); - static void get_loop_continues( - const local_SSAt &SSA, - ssa_local_unwindert &ssa_local_unwinder, - exprt::operandst &loop_continues); + unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + unsigned get_number_of_summaries_used() { return summaries_used; } - static void get_loop_continues( - const local_SSAt &SSA, + static exprt::operandst get_loophead_selects( + const local_SSAt &SSA, const ssa_local_unwindert &ssa_local_unwinder, - const local_SSAt::locationt &loop_id, - exprt::operandst &loop_continues); - - static void get_loophead_selects( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, - exprt::operandst &loophead_selects); - - inline unsigned get_number_of_solver_instances() { return solver_instances; } - inline unsigned get_number_of_solver_calls() { return solver_calls; } - inline unsigned get_number_of_summaries_used() { return summaries_used; } - inline unsigned get_number_of_termargs_computed() { return termargs_computed; } - + prop_convt &solver); + protected: optionst &options; summary_dbt &summary_db; @@ -112,16 +98,10 @@ class summarizer_baset : public messaget local_SSAt &SSA, const exprt &cond); - bool is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver); - //statistics unsigned solver_instances; unsigned solver_calls; unsigned summaries_used; - unsigned termargs_computed; }; diff --git a/src/summarizer/summarizer_bw_term.cpp b/src/summarizer/summarizer_bw_term.cpp deleted file mode 100644 index ac2878a34..000000000 --- a/src/summarizer/summarizer_bw_term.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis with Termination - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw_term.h" -#include "summarizer_fw_term.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -#define MAX_PRECONDITION_DISJUNCTS 5 -#define MAX_BOOTSTRAP_ATTEMPTS 20 - -/*******************************************************************\ - -Function: summarizer_bw_termt::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::compute_summary_rec( - const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - const summaryt &old_summary = summary_db.get(function_name); - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,old_summary, - postcondition,context_sensitive, - false); - - status() << "Analyzing function " << function_name << eom; - - bool has_loops = false; - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - if(n_it->loophead != SSA.nodes.end()) { has_loops = true; break; } - } - - debug() << "function " << - (has_loops ? "has loops" : "does not have loops") << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.bw_postcondition = postcondition; - - do_nontermination(function_name,SSA,old_summary,summary); - if(!options.get_bool_option("havoc") && - summary.terminates!=NO) - { - if(!has_loops) - { - do_summary(function_name,SSA,old_summary,summary,context_sensitive); - } - else - { - do_summary_term(function_name,SSA,old_summary,summary,context_sensitive); - } - } - - // store summary in db - summary_db.put(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_bw_termt::do_nontermination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::do_nontermination( - const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - cond.push_back(old_summary.fw_invariant); - cond.push_back(old_summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - ssa_inliner.get_summaries(SSA,false,cond,cond); //backward summaries - - if(!check_end_reachable(function_name,SSA,conjunction(cond))) - { - status() << "Function never terminates" << eom; - summary.bw_transformer = false_exprt(); - summary.bw_precondition = false_exprt(); - summary.terminates = NO; - } -} - -/*******************************************************************\ - -Function: summarizer_bw_termt::do_summary_term() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_termt::do_summary_term(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive) -{ - status() << "Computing preconditions for termination" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //templates for ranking functions - template_generator_rankingt template_generator1( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator1.set_message_handler(get_message_handler()); - template_generator1(solver.next_domain_number(),SSA,true); - - //templates for backward summary - template_generator_summaryt template_generator2( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator2.set_message_handler(get_message_handler()); - template_generator2(solver.next_domain_number(),SSA,false); - - exprt::operandst bindings; - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,bindings); //backward summaries - collect_postconditions(function_name, SSA, summary, postcond,true); - - //prepare solver - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << old_summary.fw_precondition; - solver << old_summary.fw_invariant; - solver << ssa_inliner.get_summaries(SSA); //forward summaries - solver << conjunction(bindings); //bindings for backward summaries - -#if 0 - for(unsigned i=0; iget(*it))); - } - precondition = conjunction(c); - debug() << "bootstrap model for precondition: " - << from_expr(SSA.ns,"",precondition) << eom; - solver.pop_context(); - } - else // whole precondition space covered - { - solver.pop_context(); - break; - } - - termination_argument = - compute_termination_argument(SSA,precondition, - solver,template_generator1); - - if(summarizer_fw_termt::check_termination_argument( - termination_argument)==YES) - { - return true; - } - - solver.new_context(); - checked_candidates.push_back(precondition); - solver << not_exprt(disjunction(checked_candidates)); //next one, please! - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_bw_termt:::compute_termination_argument() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_termt::compute_termination_argument( - local_SSAt &SSA, - const exprt &precondition, - incremental_solvert &solver, - template_generator_rankingt &template_generator) -{ - // compute ranking functions - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver, SSA, precondition, template_generator); - exprt termination_argument; - analyzer.get_result(termination_argument, - template_generator.all_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - termargs_computed++; - - return termination_argument; -} - -/*******************************************************************\ - -Function: summarizer_bw_termt:::compute_precondition() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_termt::compute_precondition(local_SSAt &SSA, - summaryt &summary, - const exprt::operandst &postconditions, - incremental_solvert &solver, - template_generator_summaryt &template_generator, - bool context_sensitive) -{ - exprt postcond = not_exprt(conjunction(postconditions)); - - //compute backward summary - exprt bw_transformer, bw_invariant, bw_precondition; - if(!template_generator.out_vars().empty()) - { - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver,SSA,postcond,template_generator); - analyzer.get_result(bw_transformer,template_generator.inout_vars()); - analyzer.get_result(bw_invariant,template_generator.loop_vars()); - analyzer.get_result(bw_precondition,template_generator.out_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - } - else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - { - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << postcond; - exprt result = true_exprt(); - if(solver()==decision_proceduret::D_UNSATISFIABLE) result = false_exprt(); - solver.pop_context(); - bw_transformer = result; - bw_invariant = result; - bw_precondition = result; - } - - bw_transformer = not_exprt(bw_transformer); - bw_invariant = not_exprt(bw_invariant); - bw_precondition = not_exprt(bw_precondition); - - if(context_sensitive && !summary.bw_postcondition.is_true()) - { - bw_transformer = implies_exprt(summary.bw_postcondition,bw_transformer); - bw_invariant = implies_exprt(summary.bw_postcondition,bw_invariant); - bw_precondition = implies_exprt(summary.bw_postcondition,bw_precondition); - } - - //join // TODO: should go into summaryt - if(summary.bw_transformer.is_nil()) - { - summary.bw_transformer = bw_transformer; - summary.bw_invariant = bw_invariant; - summary.bw_precondition = bw_precondition; - } - else - { - summary.bw_transformer = or_exprt(summary.bw_transformer,bw_transformer); - summary.bw_invariant = or_exprt(summary.bw_invariant,bw_invariant); - summary.bw_precondition = or_exprt(summary.bw_precondition,bw_precondition); - } - - return bw_precondition; -} - - diff --git a/src/summarizer/summarizer_bw_term.h b/src/summarizer/summarizer_bw_term.h deleted file mode 100644 index d8097338d..000000000 --- a/src/summarizer/summarizer_bw_term.h +++ /dev/null @@ -1,77 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis with Termination - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_TERM_H -#define CPROVER_SUMMARIZER_BW_TERM_H - -#include -#include -#include - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_ranking.h" - -#include "ssa_db.h" -#include "summarizer_bw.h" - -class summarizer_bw_termt : public summarizer_bwt -{ - public: - explicit summarizer_bw_termt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_bwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive); - - void do_summary_term(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive); - - void do_nontermination(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary); - - bool bootstrap_preconditions( - local_SSAt &SSA, - summaryt &summary, - incremental_solvert &solver, - template_generator_rankingt &template_generator1, - template_generator_summaryt &template_generator2, - exprt &termination_argument); - - exprt compute_termination_argument( - local_SSAt &SSA, - const exprt &precondition, - incremental_solvert &solver, - template_generator_rankingt &template_generator); - - exprt compute_precondition(local_SSAt &SSA, - summaryt &summary, - const exprt::operandst &postconditions, - incremental_solvert &solver, - template_generator_summaryt &template_generator, - bool context_sensitive); - -}; - - -#endif diff --git a/src/summarizer/summarizer_fw.cpp b/src/summarizer/summarizer_fw.cpp index 70529ded7..dcf437019 100644 --- a/src/summarizer/summarizer_fw.cpp +++ b/src/summarizer/summarizer_fw.cpp @@ -77,8 +77,6 @@ void summarizer_fwt::compute_summary_rec(const function_namet &function_name, do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); } - -#if 0 if(!options.get_bool_option("competition-mode")) { std::ostringstream out; @@ -86,18 +84,9 @@ void summarizer_fwt::compute_summary_rec(const function_namet &function_name, summary.output(out,SSA.ns); status() << out.str() << eom; } -#endif // store summary in db summary_db.put(function_name,summary); - - if(!options.get_bool_option("competition-mode")) - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } } /*******************************************************************\ @@ -146,13 +135,6 @@ void summarizer_fwt::do_summary(const function_namet &function_name, exprt inv = ssa_unwinder.get(function_name).rename_invariant(old_inv); conds.push_back(inv); -#ifdef SHOW_WHOLE_RESULT - // to see all the custom template values - exprt whole_result; - analyzer.get_result(whole_result,template_generator.all_vars()); - debug() << "whole result: " << from_expr(SSA.ns,"",whole_result) << eom; -#endif - #if 0 std::ostringstream out; out << "(original inv)" << from_expr(SSA.ns,"",old_inv) << "\n"; diff --git a/src/summarizer/summarizer_fw_contexts.cpp b/src/summarizer/summarizer_fw_contexts.cpp deleted file mode 100644 index 0a78d403e..000000000 --- a/src/summarizer/summarizer_fw_contexts.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis with Calling Context output - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "summarizer_fw_contexts.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -/*******************************************************************\ - -Function: summarizer_fw_contextst::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_contextst::summarize() -{ - exprt precondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); - it!=ssa_db.functions().end(); it++) - { - if(excluded_functions.find(it->first)!=excluded_functions.end()) - continue; - status() << "\nSummarizing function " << it->first << eom; - if(!summary_db.exists(it->first) || - summary_db.get(it->first).mark_recompute) - compute_summary_rec(it->first,precondition,false); - else status() << "Summary for function " << it->first << - " exists already" << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_fw_contextst::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_contextst::inline_summaries( - const function_namet &function_name, - local_SSAt &SSA, const exprt &precondition, - bool context_sensitive) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - { - continue; - } - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(excluded_functions.find(fname)!=excluded_functions.end()) - { - exprt precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - // output calling context - switch(ui) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml_cc("calling-context"); - xml_cc.set_attribute("function",id2string(fname)); - xml_cc.set_attribute("goto_location", - i2string(n_it->location->location_number)); - - //location - const source_locationt &source_location = - n_it->location->source_location; - xmlt xml_location; - if(source_location.is_not_nil() && source_location.get_file()!="") - xml_location=xml(source_location); - if(xml_location.name!="") - xml_cc.new_element().swap(xml_location); - - //argument ranges - xmlt xml_args("argument-ranges"); - assert(precondition_call.operands().size()%2==0); - for(unsigned i=0;i -#include -#include - -#include "summarizer_fw.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fw_contextst : public summarizer_fwt -{ - public: - explicit summarizer_fw_contextst(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_fwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner), - ui(ui_message_handlert::PLAIN) - { - if(_options.get_bool_option("xml-ui")) - ui = ui_message_handlert::XML_UI; - - optionst::value_listt _excluded_functions = - _options.get_list_option("do-not-analyze-functions"); - excluded_functions.insert(_excluded_functions.begin(), - _excluded_functions.end()); - - } - - virtual void summarize(); - - protected: - language_uit::uit ui; // use gui format - std::set excluded_functions; - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const exprt &precondition, - bool context_sensitive); -}; - - -#endif diff --git a/src/summarizer/summarizer_fw_term.cpp b/src/summarizer/summarizer_fw_term.cpp deleted file mode 100644 index cb9481308..000000000 --- a/src/summarizer/summarizer_fw_term.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_fw_term.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" -#include "../domains/template_generator_ranking.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizert::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::compute_summary_rec( - const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) -{ - if(options.get_bool_option("competition-mode") && - summary_db.exists(ID__start) && - summary_db.get(ID__start).terminates==NO) - { - return; - } - - local_SSAt &SSA = ssa_db.get(function_name); - - // recursively compute summaries for function calls - threevalt calls_terminate = YES; - bool has_function_calls = false; - inline_summaries(function_name,SSA,precondition,context_sensitive, - calls_terminate,has_function_calls); - - status() << "Analyzing function " << function_name << eom; - - { - std::ostringstream out; - out << "Function body for " << function_name << - " to be analyzed: " << std::endl; - for(local_SSAt::nodest::iterator n = SSA.nodes.begin(); - n!=SSA.nodes.end(); n++) - { - if(!n->empty()) n->output(out,SSA.ns); - } - out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) - << "\n"; - debug() << out.str() << eom; - } - - bool has_loops = false; - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - if(n_it->loophead != SSA.nodes.end()) { has_loops = true; break; } - } - - debug() << "function " << - (has_function_calls ? "has function calls" : - "does not have function calls") << eom; - debug() << "function calls terminate: " << - threeval2string(calls_terminate) << eom; - debug() << "function " << - (has_loops ? "has loops" : "does not have loops") << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.fw_precondition = precondition; - summary.terminates = UNKNOWN; - - //compute summary - if(!options.get_bool_option("havoc")) - { - //We are not allowed to assume the assertions here, - // otherwise we might cut off all terminating executions - // and classify the program as non-terminating. - do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); - } - - //check termination - status() << "Computing termination argument for " << function_name << eom; - // check non-termination if we haven't analyzed this function yet, - // otherwise the termination status is UNKNOWN anyways - if(!summary_db.exists(function_name)) - { - do_nontermination(function_name,SSA,summary); - } - if(summary.terminates==UNKNOWN) - { - if(!has_loops && !has_function_calls) - { - status() << "Function trivially terminates" << eom; - summary.terminates = YES; - } - else if(!has_loops && has_function_calls && calls_terminate==YES) - { - status() << "Function terminates" << eom; - summary.terminates = YES; - } - else if(has_function_calls && calls_terminate!=YES) - { - summary.terminates = calls_terminate; - } - else if(has_loops && - (!has_function_calls || - (has_function_calls && calls_terminate==YES))) - { - do_termination(function_name,SSA,summary); - } - } - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary.output(out,SSA.ns); - status() << out.str() << eom; - } - - // store summary in db - summary_db.put(function_name,summary); -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, exprt precondition, - bool context_sensitive, - threevalt &calls_terminate, - bool &has_function_calls) -{ - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - - exprt::operandst c; - c.push_back(precondition); - get_assertions(SSA,c); //assertions as assumptions - precondition = conjunction(c); - - if(!options.get_bool_option("competition-mode") && - !check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - continue; - - has_function_calls = true; - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - if(!check_precondition(function_name,SSA,n_it,f_it, - precondition,context_sensitive)) - { - exprt precondition_call = true_exprt(); - if(context_sensitive) - precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,precondition_call,context_sensitive); - summaries_used++; - } - - //get information about callee termination - if(summary_db.exists(fname) && summary_db.get(fname).terminates!=YES) - { - calls_terminate = UNKNOWN; //cannot propagate NO because call reachability might be over-approximating - break; - } - } - } -} - -/*******************************************************************\ - -Function: summarizer_baset::do_nontermination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::do_nontermination( - const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - if(!summary.fw_invariant.is_nil()) cond.push_back(summary.fw_invariant); - if(!summary.fw_precondition.is_nil()) cond.push_back(summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - - if(!check_end_reachable(function_name,SSA,conjunction(cond))) - { - status() << "Function never terminates normally" << eom; - if(summary.fw_precondition.is_true()) summary.fw_transformer = false_exprt(); - else summary.fw_transformer = implies_exprt(summary.fw_precondition,false_exprt()); - summary.terminates = NO; - } -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::do_termination() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fw_termt::do_termination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary) -{ - // calling context, invariant, function call summaries - exprt::operandst cond; - if(!summary.fw_invariant.is_nil()) cond.push_back(summary.fw_invariant); - if(!summary.fw_precondition.is_nil()) cond.push_back(summary.fw_precondition); - cond.push_back(ssa_inliner.get_summaries(SSA)); - - status() << "Synthesizing ranking function to prove termination" << eom; - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - template_generator_rankingt template_generator1( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator1.set_message_handler(get_message_handler()); - template_generator1(solver.next_domain_number(),SSA,true); - - if(template_generator1.all_vars().empty()) return; //nothing to do - - get_assertions(SSA,cond); //add assertions as assumptions - - // compute ranking functions - ssa_analyzert analyzer1; - analyzer1.set_message_handler(get_message_handler()); - analyzer1(solver,SSA,conjunction(cond),template_generator1); - analyzer1.get_result(summary.termination_argument, - template_generator1.all_vars()); - - //extract information whether a ranking function was found for all loops - summary.terminates = check_termination_argument(summary.termination_argument); - if(!summary.fw_precondition.is_true()) - summary.termination_argument = implies_exprt(summary.fw_precondition, - summary.termination_argument); - - //statistics - solver_instances += analyzer1.get_number_of_solver_instances(); - solver_calls += analyzer1.get_number_of_solver_calls(); - termargs_computed++; -} - -/*******************************************************************\ - -Function: summarizer_fw_termt::check_termination_argument() - - Inputs: - - Outputs: - - Purpose: checks whether a termination argument implies termination - -\******************************************************************/ - -threevalt summarizer_fw_termt::check_termination_argument(exprt expr) -{ - if(expr.is_false()) return YES; - // should be of the form /\_i g_i => R_i - if(expr.id()==ID_and) - { - threevalt result = YES; - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) - { - if(it->is_true()) result = UNKNOWN; - if(it->id()==ID_implies) - { - if(it->op1().is_true()) result = UNKNOWN; - } - } - return result; - } - else - { - if(expr.id()==ID_implies) - { - if(expr.op1().is_true()) return UNKNOWN; - } - else return !expr.is_true() ? YES : UNKNOWN; - } - return YES; -} - diff --git a/src/summarizer/summarizer_fw_term.h b/src/summarizer/summarizer_fw_term.h deleted file mode 100644 index c5dc7f611..000000000 --- a/src/summarizer/summarizer_fw_term.h +++ /dev/null @@ -1,61 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FW_TERM_H -#define CPROVER_SUMMARIZER_FW_TERM_H - -#include -#include - -#include "summarizer_fw.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fw_termt : public summarizer_fwt -{ - public: - explicit summarizer_fw_termt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_fwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - static threevalt check_termination_argument(exprt expr); - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive); - - void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - exprt precondition, - bool context_sensitive, - threevalt &calls_terminate, - bool &has_function_calls); - - void do_termination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary); - - void do_nontermination(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary); - - }; - - -#endif diff --git a/src/summarizer/summarizer_parseoptions.cpp b/src/summarizer/summarizer_parseoptions.cpp deleted file mode 100644 index 62d3050d3..000000000 --- a/src/summarizer/summarizer_parseoptions.cpp +++ /dev/null @@ -1,1372 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Command Line Options Processing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "array_abstraction.h" - -#include - -#include - -#include -#include "version.h" - -#include "summarizer_parseoptions.h" -#include "summary_db.h" -#include "summary_checker.h" -#include "../ssa/split_loopheads.h" -#include "show.h" - -//#define ANONYMOUS - -/*******************************************************************\ - -Function: summarizer_parseoptionst::summarizer_parseoptionst - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -summarizer_parseoptionst::summarizer_parseoptionst(int argc, const char **argv): - parseoptions_baset(SUMMARIZER_OPTIONS, argc, argv), - language_uit("2LS " SUMMARIZER_VERSION, cmdline) -{ -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::eval_verbosity - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::eval_verbosity() -{ - // this is our default verbosity - int v=messaget::M_STATISTICS; - - if(cmdline.isset("verbosity")) - { - v=unsafe_string2int(cmdline.get_value("verbosity")); - if(v<0) - v=0; - else if(v>10) - v=10; - } - - ui_message_handler.set_verbosity(v); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::get_command_line_options - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::get_command_line_options(optionst &options) -{ - if(config.set(cmdline)) - { - usage_error(); - exit(1); - } - - if(cmdline.isset("debug-level")) - options.set_option("debug-level", cmdline.get_value("debug-level")); - - if(cmdline.isset("unwindset")) - options.set_option("unwindset", cmdline.get_value("unwindset")); - - if(cmdline.isset("unwind")) - options.set_option("unwind", cmdline.get_value("unwind")); - - if(cmdline.isset("inline-partial")) - options.set_option("inline-partial", cmdline.get_value("inline-partial")); - - // check array bounds - if(cmdline.isset("bounds-check")) - options.set_option("bounds-check", true); - else - options.set_option("bounds-check", false); - - // check division by zero - if(cmdline.isset("div-by-zero-check")) - options.set_option("div-by-zero-check", true); - else - options.set_option("div-by-zero-check", false); - - // check overflow/underflow - if(cmdline.isset("signed-overflow-check")) - options.set_option("signed-overflow-check", true); - else - options.set_option("signed-overflow-check", false); - - // check overflow/underflow - if(cmdline.isset("unsigned-overflow-check")) - options.set_option("unsigned-overflow-check", true); - else - options.set_option("unsigned-overflow-check", false); - - // check overflow on floats - if(cmdline.isset("float-overflow-check")) - options.set_option("float-overflow-check", true); - else - options.set_option("float-overflow-check", false); - - // check for NaN (not a number) - if(cmdline.isset("nan-check")) - options.set_option("nan-check", true); - else - options.set_option("nan-check", false); - - // check pointers - if(cmdline.isset("pointer-check")) - options.set_option("pointer-check", true); - else - options.set_option("pointer-check", false); - - // check for memory leaks - if(cmdline.isset("memory-leak-check")) - options.set_option("memory-leak-check", true); - else - options.set_option("memory-leak-check", false); - - // check assertions - if(cmdline.isset("no-assertions")) - options.set_option("assertions", false); - else - options.set_option("assertions", true); - - // use assumptions - if(cmdline.isset("no-assumptions")) - options.set_option("assumptions", false); - else - options.set_option("assumptions", true); - - // magic error label - if(cmdline.isset("error-label")) - options.set_option("error-label", cmdline.get_value("error-label")); - - // use incremental assertion checks - if(cmdline.isset("non-incremental")) - options.set_option("incremental", false); - else - options.set_option("incremental", true); - - // compute preconditions - if(cmdline.isset("preconditions")) - options.set_option("preconditions", true); - - // compute necessary/sufficient preconditions - if(cmdline.isset("sufficient")) - options.set_option("sufficient", true); - - // compute ranking functions - if(cmdline.isset("termination")) - { - options.set_option("termination", true); - options.set_option("sufficient", true); - } - - if(cmdline.isset("monolithic-ranking-function")) - { - options.set_option("monolithic-ranking-function", true); - } - else options.set_option("monolithic-ranking-function", false); - - if(cmdline.isset("lexicographic-ranking-function")) - { - options.set_option("lexicographic-ranking-function", - cmdline.get_value("lexicographic-ranking-function")); - } - else options.set_option("lexicographic-ranking-function",3); - - if(cmdline.isset("max-inner-ranking-iterations")) - { - options.set_option("max-inner-ranking-iterations", - cmdline.get_value("max-inner-ranking-iterations")); - } - else options.set_option("max-inner-ranking-iterations",20); - - // do k-induction refinement - if(cmdline.isset("k-induction")) - options.set_option("k-induction", true); - - // do incremental bmc - if(cmdline.isset("incremental-bmc")) - { - options.set_option("incremental-bmc", true); - options.set_option("inline", true); - options.set_option("havoc", true); - if(options.get_unsigned_int_option("unwind")==0) - options.set_option("unwind",UINT_MAX); - } - - // check for spuriousness of assertion failures - if(cmdline.isset("no-spurious-check")) - options.set_option("spurious-check", false); - else - options.set_option("spurious-check", true); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::doit - - Inputs: - - Outputs: - - Purpose: invoke main modules - -\*******************************************************************/ - -int summarizer_parseoptionst::doit() -{ - if(cmdline.isset("version")) - { -#ifndef ANONYMOUS - std::cout << SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << std::endl; -#else - std::cout << SUMMARIZER_VERSION << std::endl; -#endif - return 0; - } - - // - // command line options - // - - optionst options; - get_command_line_options(options); - - eval_verbosity(); - - // - // Print a banner - // -#ifndef ANONYMOUS - status() << "2LS version " SUMMARIZER_VERSION " (based on CBMC " CBMC_VERSION ")" << eom; -#else - status() << "2LS version " SUMMARIZER_VERSION << eom; -#endif - - register_language(new_ansi_c_language); - register_language(new_cpp_language); - - goto_modelt goto_model; - - if(get_goto_program(options, goto_model)) - return 6; - - // options for various debug outputs - - if(cmdline.isset("show-ssa")) - { - bool simplify=!cmdline.isset("no-simplify"); - show_ssa(goto_model, simplify, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-fixed-points")) - { - bool simplify=!cmdline.isset("no-simplify"); - show_fixed_points(goto_model, simplify, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-stats")) - { - show_stats(goto_model, std::cout); - return 7; - } - - if(cmdline.isset("show-defs")) - { - show_defs(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-assignments")) - { - show_assignments(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-guards")) - { - show_guards(goto_model, std::cout, ui_message_handler); - return 7; - } - - if(cmdline.isset("show-invariants")) - { - options.set_option("show-invariants", true); - } - - if(cmdline.isset("context-sensitive")) - { - options.set_option("context-sensitive", true); - status() << "Context-sensitive analysis from " << - goto_model.goto_functions.entry_point() << eom; - } - - if(cmdline.isset("arrays")) - { - options.set_option("arrays", true); - status() << "Do not ignore array contents" << eom; - } - - if(cmdline.isset("havoc")) - { - options.set_option("havoc", true); - status() << "Havocking loops and function calls"; - } - else if(cmdline.isset("equalities")) - { - options.set_option("equalities", true); - status() << "Using equalities domain "; - } - else - { - if(cmdline.isset("zones")) - { - options.set_option("zones", true); - status() << "Using zones domain "; - } - else if(cmdline.isset("octagons")) - { - options.set_option("octagons", true); - status() << "Using octagons domain "; - } - else //if(cmdline.isset("intervals")) //default - { - options.set_option("intervals", true); - status() << "Using intervals domain "; - } - - if(cmdline.isset("enum-solver")) - { - options.set_option("enum-solver", true); - status() << "with enumeration solver"; - } - else //if(cmdline.isset("binsearch-solver")) //default - { - options.set_option("binsearch-solver", true); - status() << "with binary search solver"; - } - } - status() << eom; - - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("enum-solver")) - { - warning() << "Option --enum-solver ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("binsearch-solver")) - { - warning() << "Option --binsearch-solver ignored" << eom; - } - if(cmdline.isset("havoc") && - cmdline.isset("equalities")) - { - warning() << "Option --equalities ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("intervals")) - { - warning() << "Option --intervals ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("zones")) - { - warning() << "Option --zones ignored" << eom; - } - if((cmdline.isset("equalities") || cmdline.isset("havoc")) && - cmdline.isset("octagons")) - { - warning() << "Option --octagons ignored" << eom; - } - - try - { - summary_checkert summary_checker(options); - - summary_checker.set_message_handler(get_message_handler()); - summary_checker.simplify=!cmdline.isset("no-simplify"); - summary_checker.fixed_point=!cmdline.isset("no-fixed-point"); - - if(cmdline.isset("show-vcc")) - { - std::cout << "VERIFICATION CONDITIONS:\n\n"; - summary_checker.show_vcc=true; - summary_checker(goto_model); - return 0; - } - - // do actual analysis - int retval; - switch(summary_checker(goto_model)) - { - case property_checkert::PASS: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - report_success(); - retval = 0; - break; - - case property_checkert::FAIL: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - report_failure(); - retval = 10; - break; - - case property_checkert::UNKNOWN: - if(!options.get_bool_option("termination")) - report_properties(goto_model, summary_checker.property_map); - if(options.get_bool_option("preconditions")) return 0; - report_unknown(); - retval = 5; - break; - - default: - return 8; - } - - return retval; - } - - catch(const std::string error_msg) - { - error() << error_msg << messaget::eom; - return 8; - } - - catch(const char *error_msg) - { - error() << error_msg << messaget::eom; - return 8; - } - - #if 0 - // let's log some more statistics - debug() << "Memory consumption:" << messaget::endl; - memory_info(debug()); - debug() << eom; - #endif -} - - - -void summarizer_parseoptionst::type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns) -{ - - if(type.id()==ID_symbol) - type_stats_rec(ns.follow(type), stats, ns); - - if(type.id()==ID_pointer || type.id()==ID_array) - { - stats.has_array=true; - - const typet &subtype=ns.follow(type.subtype()); - - if(subtype.id()==ID_signedbv || - subtype.id()==ID_unsignedbv) - { - stats.has_string=(to_bitvector_type(subtype).get_width()==config.ansi_c.char_width); - } - } - - if(type.has_subtypes()) - { - forall_subtypes(it, type) - { - type_stats_rec(*it, stats, ns); - } - } -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::expr_stats_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - - -void summarizer_parseoptionst::expr_stats_rec( - const exprt &expr, - expr_statst &stats) -{ - - if(expr.id()==ID_side_effect) - { - const side_effect_exprt &side_effect_expr=to_side_effect_expr(expr); - const irep_idt &statement=side_effect_expr.get_statement(); - - if(statement==ID_malloc) - { - stats.has_malloc=true; - } - else if(statement==ID_nondet) - { - // done in statet:instantiate_rec - } - } - - if(expr.id()==ID_symbol ) - { - - } - - if(expr.has_operands()) - { - forall_operands(it, expr) - { - expr_stats_rec(*it, stats); - } - } -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::show_stats - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::show_stats(const goto_modelt &goto_model, - std::ostream &out) -{ - - const namespacet ns(goto_model.symbol_table); - - expr_statst stats; - - unsigned nr_instructions=0; - unsigned nr_functions=0; - unsigned nr_loops=0; - - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body_available) continue; - - ++nr_functions; - - const goto_programt &goto_program=f_it->second.body; - -#if 0 - statistics() << "function size of " << f_it->first << ": " - << goto_program.instructions.size() << eom; -#endif - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) - { - nr_instructions++; - const goto_programt::instructiont &instruction=*i_it; - - if(i_it->is_backwards_goto()) nr_loops++; - - switch(instruction.type) - { - case ASSIGN: - { - const code_assignt &assign=to_code_assign(instruction.code); - expr_stats_rec(assign.lhs(), stats); - expr_stats_rec(assign.rhs(), stats); - } - break; - case ASSUME: - expr_stats_rec(instruction.guard, stats); - break; - case ASSERT: - expr_stats_rec(instruction.guard, stats); - break; - case GOTO: - expr_stats_rec(instruction.guard, stats); - break; - - case DECL: - // someone declaring an array - type_stats_rec(to_code_decl(instruction.code).symbol().type(), stats, ns); - - break; - - default: - // skip - break; - } // switch - } // forall instructions - } // forall functions - - out << " =============== STATS =============== " << std::endl; - out << " nr of instructions: " << nr_instructions << std::endl; - out << " nr of functions: " << nr_functions << std::endl; - out << " nr of loops: " << nr_loops << std::endl; - out << " malloc: " << (stats.has_malloc ? "YES" : "NO") << std::endl; - out << " arrays: " << (stats.has_array ? "YES" : "NO") << std::endl; - out << " strings: " << (stats.has_string ? "YES" : "NO") << std::endl; -} - - -/*******************************************************************\ - -Function: summarizer_parseoptionst::set_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::set_properties(goto_modelt &goto_model) -{ - try - { - if(cmdline.isset("property")) - ::set_properties(goto_model, cmdline.get_values("property")); - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::get_goto_program - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::get_goto_program( - const optionst &options, - goto_modelt &goto_model) -{ - if(cmdline.args.size()==0) - { - error() << "Please provide a program to verify" << eom; - return true; - } - - try - { - if(cmdline.args.size()==1 && - is_goto_binary(cmdline.args[0])) - { - status() << "Reading GOTO program from file" << eom; - - if(read_goto_binary(cmdline.args[0], - goto_model, get_message_handler())) - return true; - - config.ansi_c.set_from_symbol_table(goto_model.symbol_table); - - if(cmdline.isset("show-symbol-table")) - { - show_symbol_table(); - return true; - } - - irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(goto_model.symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) - { - error() << "The goto binary has no entry point; please complete linking" << eom; - return true; - } - } - else if(cmdline.isset("show-parse-tree")) - { - if(cmdline.args.size()!=1) - { - error() << "Please give one source file only" << eom; - return true; - } - - std::string filename=cmdline.args[0]; - - #ifdef _MSC_VER - std::ifstream infile(widen(filename).c_str()); - #else - std::ifstream infile(filename.c_str()); - #endif - - if(!infile) - { - error() << "failed to open input file `" << filename << "'" << eom; - return true; - } - - languaget *language=get_language_from_filename(filename); - - if(language==NULL) - { - error() << "failed to figure out type of file `" << filename << "'" << eom; - return true; - } - - language->set_message_handler(get_message_handler()); - - status("Parsing", filename); - - if(language->parse(infile, filename)) - { - error() << "PARSING ERROR" << eom; - return true; - } - - language->show_parse(std::cout); - return true; - } - else - { - - if(parse()) return true; - if(typecheck()) return true; - if(final()) return true; - - // we no longer need any parse trees or language files - clear_parse(); - - if(cmdline.isset("show-symbol-table")) - { - show_symbol_table(); - return true; - } - - irep_idt entry_point=goto_model.goto_functions.entry_point(); - - if(symbol_table.symbols.find(entry_point)==symbol_table.symbols.end()) - { - error() << "No entry point; please provide a main function" << eom; - return true; - } - - status() << "Generating GOTO Program" << eom; - - goto_convert(symbol_table, goto_model, ui_message_handler); - } - - // finally add the library - status() << "Adding CPROVER library" << eom; - link_to_library(goto_model, ui_message_handler); - - if(process_goto_program(options, goto_model)) - return true; - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::process_goto_program - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool summarizer_parseoptionst::process_goto_program( - const optionst &options, - goto_modelt &goto_model) -{ - try - { - // do partial inlining - if(cmdline.isset("inline-partial")) - { - unsigned limit = options.get_unsigned_int_option("inline-partial"); - status() << "Performing partial inlining (" << limit << ")" << eom; - goto_partial_inline(goto_model, ui_message_handler, limit/2); - //TODO: where is limit multiplied by 2??? - - //remove inlined functions - Forall_goto_functions(f_it, goto_model.goto_functions) - if(f_it->first!=ID_main && - f_it->second.body.instructions.size()<=2*(limit/2)) - { - f_it->second.body_available=false; - f_it->second.body.clear(); - } - } - - // add generic checks - status() << "Generic Property Instrumentation" << eom; - goto_check(options, goto_model); - - status() << "Function Pointer Removal" << eom; - remove_function_pointers( - goto_model, cmdline.isset("pointer-check")); - - // remove returns (must be done after function pointer removal) - remove_returns(goto_model); - - split_loopheads(goto_model); - - // recalculate numbers, etc. - goto_model.goto_functions.update(); - - // add loop ids - goto_model.goto_functions.compute_loop_numbers(); - - // if we aim to cover, replace - // all assertions by false to prevent simplification - if(cmdline.isset("cover-assertions")) - make_assertions_false(goto_model); - - // show it? - if(cmdline.isset("show-loops")) - { - show_loop_ids(get_ui(), goto_model); - return true; - } - - // now do full inlining, if requested - if(cmdline.isset("inline")) - { - status() << "Performing full inlining" << eom; - goto_inline(goto_model, ui_message_handler); - } - - //inline __CPROVER_initialize and main - if(cmdline.isset("inline-main")) - { - inline_main(goto_model); - } - - // do array abstraction - if(cmdline.isset("array-abstraction")) - { - status() << "Performing array abstraction" << eom; - array_abstraction(goto_model.symbol_table, ui_message_handler, - goto_model.goto_functions); - } - - label_properties(goto_model); - - if(cmdline.isset("show-properties")) - { - show_properties(goto_model, get_ui()); - return true; - } - - if(set_properties(goto_model)) - return true; - - // show it? - if(cmdline.isset("show-goto-functions")) - { - const namespacet ns(goto_model.symbol_table); - goto_model.goto_functions.output(ns, std::cout); - return true; - } - } - - catch(const char *e) - { - error() << e << eom; - return true; - } - - catch(const std::string e) - { - error() << e << eom; - return true; - } - - catch(int) - { - return true; - } - - catch(std::bad_alloc) - { - error() << "Out of memory" << eom; - return true; - } - - return false; -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::inline_main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::inline_main(goto_modelt &goto_model) -{ - goto_programt &main = goto_model.goto_functions.function_map[ID_main].body; - goto_programt::targett target = main.instructions.begin(); - while(target!=main.instructions.end()) - { - if(target->is_function_call()) - { - const code_function_callt &code_function_call= - to_code_function_call(target->code); - irep_idt fname = code_function_call.function().get(ID_identifier); - - debug() << "Inlining " << fname << eom; - - goto_programt tmp; - tmp.copy_from(goto_model.goto_functions.function_map[fname].body); - (--tmp.instructions.end())->make_skip(); - goto_model.goto_functions.function_map.erase(fname); - - goto_programt::targett next_target(target); - target->make_skip(); - next_target++; - main.instructions.splice(next_target, tmp.instructions); - target=next_target; - } - else target++; - } - - goto_model.goto_functions.update(); - goto_model.goto_functions.compute_loop_numbers(); -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_properties( - const goto_modelt &goto_model, - const property_checkert::property_mapt &property_map) -{ - for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); - it!=property_map.end(); - it++) - { - /* if(it->first=="") //TODO: some properties do not show up in initialize_property_map - continue; */ - - if(get_ui()==ui_message_handlert::XML_UI) - { - xmlt xml_result("result"); - xml_result.set_attribute("property", id2string(it->first)); - xml_result.set_attribute("status", property_checkert::as_string(it->second.result)); - std::cout << xml_result << "\n"; - } - else - { - status() << "[" << it->first << "] " - << it->second.location->source_location.get_comment() - << ": " - << property_checkert::as_string(it->second.result) - << eom; - } - - if(cmdline.isset("show-trace") && - it->second.result==property_checkert::FAIL) - show_counterexample(goto_model, it->second.error_trace); - } - - if(!cmdline.isset("property")) - { - status() << eom; - - unsigned failed=0; - unsigned unknown=0; - - for(property_checkert::property_mapt::const_iterator - it=property_map.begin(); - it!=property_map.end(); - it++) - { - if(it->second.result==property_checkert::UNKNOWN) - unknown++; - if(it->second.result==property_checkert::FAIL) - failed++; - } - - status() << "** " << unknown - << " of " << property_map.size() << " unknown" - << eom; - status() << "** " << failed - << " of " << property_map.size() << " failed" - << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_success - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_success() -{ - result() << "VERIFICATION SUCCESSFUL" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="SUCCESS"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::show_counterexample - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::show_counterexample( - const goto_modelt &goto_model, - const goto_tracet &error_trace) -{ - const namespacet ns(goto_model.symbol_table); - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - std::cout << std::endl << "Counterexample:" << std::endl; - show_goto_trace(std::cout, ns, error_trace); - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml; - convert(ns, error_trace, xml); - std::cout << xml << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_failure - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_failure() -{ - result() << "VERIFICATION FAILED" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="FAILURE"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::report_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_parseoptionst::report_unknown() -{ - result() << "VERIFICATION INCONCLUSIVE" << eom; - - switch(get_ui()) - { - case ui_message_handlert::PLAIN: - break; - - case ui_message_handlert::XML_UI: - { - xmlt xml("cprover-status"); - xml.data="UNKNOWN"; - std::cout << xml; - std::cout << std::endl; - } - break; - - default: - assert(false); - } -} - -/*******************************************************************\ - -Function: summarizer_parseoptionst::help - - Inputs: - - Outputs: - - Purpose: display command line help - -\*******************************************************************/ - -void summarizer_parseoptionst::help() -{ -#ifdef ANONYMOUS - std::cout << - "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2014 "; -#else - std::cout << - "\n" - "* * 2LS " SUMMARIZER_VERSION " - Copyright (C) 2014 * *\n" - "* * (based on CBMC " CBMC_VERSION " "; -#endif - std::cout << "(" << (sizeof(void *)*8) << "-bit version))"; - - std::cout << " * *\n"; - - std::cout << -#ifndef ANONYMOUS - "* * Daniel Kroening * *\n" - "* * University of Oxford * *\n" - "* * kroening@kroening.com * *\n" - "\n" -#endif - "Usage: Purpose:\n" - "\n" - " summarizer [-?] [-h] [--help] show help\n" - " summarizer file.c ... source file names\n" - "\n" - "Frontend options:\n" - " -I path set include path (C/C++)\n" - " -D macro define preprocessor macro (C/C++)\n" - " --preprocess stop after preprocessing\n" - " --16, --32, --64 set width of int\n" - " --LP64, --ILP64, --LLP64,\n" - " --ILP32, --LP32 set width of int, long and pointers\n" - " --little-endian allow little-endian word-byte conversions\n" - " --big-endian allow big-endian word-byte conversions\n" - " --unsigned-char make \"char\" unsigned by default\n" - " --show-stats show statistics about program\n" - " --show-parse-tree show parse tree\n" - " --show-symbol-table show symbol table\n" - " --show-goto-functions show goto program\n" - " --arch set architecture (default: " - << configt::this_architecture() << ")\n" - " --os set operating system (default: " - << configt::this_operating_system() << ")\n" - #ifdef _WIN32 - " --gcc use GCC as preprocessor\n" - #endif - " --no-arch don't set up an architecture\n" - " --no-library disable built-in abstract C library\n" - " --round-to-nearest IEEE floating point rounding mode (default)\n" - " --round-to-plus-inf IEEE floating point rounding mode\n" - " --round-to-minus-inf IEEE floating point rounding mode\n" - " --round-to-zero IEEE floating point rounding mode\n" - "\n" - "Program instrumentation options:\n" - " --bounds-check enable array bounds checks\n" - " --pointer-check enable pointer checks\n" - " --array-abstraction use array and string abstraction for bounds checks\n" - " --div-by-zero-check enable division by zero checks\n" - " --memory-leak-check enable memory leak checks\n" - " --signed-overflow-check enable arithmetic over- and underflow checks\n" - " --unsigned-overflow-check enable arithmetic over- and underflow checks\n" - " --nan-check check floating-point for NaN\n" - " --error-label label check that label is unreachable\n" - " --show-properties show the properties\n" - " --no-assertions ignore user assertions\n" - " --no-assumptions ignore user assumptions\n" - " --inline inline all functions into main\n" - " --inline-partial nr inline functions smaller than the given nr of instructions\n" - "\n" - "Backend options:\n" - " --termination compute ranking functions to prove termination\n" - " --k-induction use k-induction\n" - " --incremental-bmc use incremental-bmc\n" - " --preconditions compute preconditions\n" - " --sufficient sufficient preconditions (default: necessary)\n" - " --context-sensitive context-sensitive analysis from entry point\n" - " --havoc havoc loops and function calls\n" - " --intervals use interval domain\n" - " --equalities use equalities and disequalities domain\n" - " --zones use zone domain\n" - " --octagons use octagon domain\n" - " --enum-solver use solver based on model enumeration\n" - " --binsearch-solver use solver based on binary search\n" - " --arrays do not ignore array contents\n" - " --lexicographic-ranking-function n (default n=3)\n" - " --monolithic-ranking-function\n" - " --max-inner-ranking-iterations n (default n=20)\n" - "\n" - "Other options:\n" - " --version show version and exit\n" - " --xml-ui use XML-formatted output\n" - "\n"; -} diff --git a/src/summarizer/summarizer_parseoptions.h b/src/summarizer/summarizer_parseoptions.h deleted file mode 100644 index 031717956..000000000 --- a/src/summarizer/summarizer_parseoptions.h +++ /dev/null @@ -1,128 +0,0 @@ -/*******************************************************************\ - -Module: Command Line Parsing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_PARSEOPTIONS_H -#define CPROVER_SUMMARIZER_PARSEOPTIONS_H - -#include -#include - -#include - -class goto_modelt; -class optionst; - -#include "summary_checker.h" - -#define SUMMARIZER_OPTIONS \ - "(xml-ui)" \ - "(function):" \ - "D:I:" \ - "(depth):(context-bound):(unwind):" \ - "(bounds-check)(pointer-check)(div-by-zero-check)(memory-leak-check)" \ - "(signed-overflow-check)(unsigned-overflow-check)" \ - "(float-overflow-check)(nan-check)" \ - "(array-abstraction)" \ - "(non-incremental)" \ - "(no-assertions)(no-assumptions)" \ - "(16)(32)(64)(LP64)(ILP64)(LLP64)(ILP32)(LP32)" \ - "(little-endian)(big-endian)" \ - "(error-label):(verbosity):(no-library)" \ - "(version)" \ - "(i386-linux)(i386-macos)(i386-win32)(win32)(winx64)(gcc)" \ - "(ppc-macos)(unsigned-char)" \ - "(havoc)(intervals)(zones)(octagons)(equalities)"\ - "(enum-solver)(binsearch-solver)(arrays)"\ - "(string-abstraction)(no-arch)(arch):(floatbv)(fixedbv)" \ - "(round-to-nearest)(round-to-plus-inf)(round-to-minus-inf)(round-to-zero)" \ - "(inline)(inline-main)(inline-partial):" \ - "(context-sensitive)(termination)" \ - "(lexicographic-ranking-function):(monolithic-ranking-function)" \ - "(max-inner-ranking-iterations):" \ - "(preconditions)(sufficient)" \ - "(show-locs)(show-vcc)(show-properties)(show-trace)(show-fixed-points)(show-stats)" \ - "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ - "(show-invariants)" \ - "(property):(all-properties)(k-induction)(incremental-bmc)" \ - "(no-spurious-check)" \ - "(no-simplify)(no-fixed-point)" \ - "(no-unwinding-assertions)(no-propagation)" - // the last line is for CBMC-regression testing only - -class summarizer_parseoptionst: - public parseoptions_baset, - public language_uit -{ -public: - virtual int doit(); - virtual void help(); - - summarizer_parseoptionst(int argc, const char **argv); - summarizer_parseoptionst( - int argc, - const char **argv, - const std::string &extra_options); - -protected: - void get_command_line_options(optionst &options); - - bool get_goto_program( - const optionst &options, - goto_modelt &goto_model); - - bool process_goto_program( - const optionst &options, - goto_modelt &goto_model); - - bool set_properties(goto_modelt &); - - void report_success(); - void report_failure(); - void report_unknown(); - - void report_properties( - const goto_modelt &, - const summary_checkert::property_mapt &); - - void show_counterexample( - const goto_modelt &, - const class goto_tracet &); - - struct expr_statst { - bool has_malloc; - bool has_string; - bool has_array; - bool has_pointer; - - expr_statst() - : has_malloc(false) - , has_string(false) - , has_array(false) - , has_pointer(false) - {} - }; - - void type_stats_rec( - const typet &type, - expr_statst &stats, - const namespacet &ns); - - void expr_stats_rec( - const exprt &expr, - expr_statst &stats); - - void show_stats( - const goto_modelt &, - std::ostream &); - - void eval_verbosity(); - - void inline_main(goto_modelt &goto_model); -}; - -#endif diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp index c4300a613..6904fa878 100644 --- a/src/summarizer/summary.cpp +++ b/src/summarizer/summary.cpp @@ -42,11 +42,6 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const it != globals_out.end(); it++) out << from_expr(ns,"",*it) << " "; out << std::endl; - out << "nondets: "; - for(summaryt::expr_sett::const_iterator it = nondets.begin(); - it != nondets.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; out << "forward precondition: " << (fw_precondition.is_nil() ? "not computed" : from_expr(ns,"",fw_precondition)) << std::endl; @@ -68,16 +63,6 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const out << "backward invariant: " << (bw_invariant.is_nil() ? "not computed" : from_expr(ns,"",bw_invariant)) << std::endl; - out << "termination argument: "; - if(termination_argument.is_nil()) out << "not computed"; - else -#if PRETTY_PRINT - pretty_print_termination_argument(out,ns,termination_argument); -#else - out << from_expr(ns,"",termination_argument) << std::endl; -#endif - out << std::endl; - out << "terminates: " << threeval2string(terminates) << std::endl; for(error_summariest::const_iterator it = error_summaries.begin(); it != error_summaries.end(); it++) @@ -142,39 +127,5 @@ void summaryt::join(const summaryt &new_summary) combine_or(bw_postcondition,new_summary.bw_postcondition); combine_and(bw_transformer,new_summary.bw_transformer); combine_and(bw_invariant,new_summary.bw_invariant); - combine_and(termination_argument,new_summary.termination_argument); - switch(new_summary.terminates) - { - case YES: - break; - case NO: terminates=NO; - break; - case UNKNOWN: - if(terminates!=NO) terminates=UNKNOWN; - break; - default: assert(false); - } } -/*******************************************************************\ - -Function: threeval2string - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string threeval2string(threevalt v) -{ - switch(v) - { - case YES: return "yes"; - case NO: return "no"; - case UNKNOWN: return "unknown"; - } - assert(false); -} diff --git a/src/summarizer/summary_checker.cpp b/src/summarizer/summary_checker.cpp deleted file mode 100644 index c44c05f57..000000000 --- a/src/summarizer/summary_checker.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include -#include - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_build_goto_trace.h" -#include "../domains/ssa_fixed_point.h" - -#include "summary_checker.h" - -/*******************************************************************\ - -Function: summary_checkert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checkert::operator()( - const goto_modelt &goto_model) -{ - return check_properties(goto_model); -} - -/*******************************************************************\ - -Function: summary_checkert::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -summary_checkert::resultt summary_checkert::check_properties( - const goto_modelt &goto_model) -{ - // properties - initialize_property_map(goto_model.goto_functions); - - const namespacet ns(goto_model.symbol_table); - - // analyze all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body.has_assertion()) continue; - check_properties(f_it, ns); - } - - for(property_mapt::const_iterator - p_it=property_map.begin(); p_it!=property_map.end(); p_it++) - if(p_it->second.result==FAIL) - return property_checkert::FAIL; - - return property_checkert::PASS; -} - -/*******************************************************************\ - -Function: summary_checkert::check_properties - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::check_properties( - const goto_functionst::function_mapt::const_iterator f_it, - const namespacet &ns) -{ - status() << "Analyzing " << f_it->first << messaget::eom; - - // build SSA - local_SSAt SSA(f_it->second, ns); - - // simplify, if requested - if(simplify) - { - status() << "Simplifying" << messaget::eom; - ::simplify(SSA, ns); - } - - // fixed-point for loops - status() << "Fixed-point" << messaget::eom; - ssa_fixed_point(SSA); - - status() << "Checking properties" << messaget::eom; - - const goto_programt &goto_program=f_it->second.body; - - for(goto_programt::instructionst::const_iterator - i_it=goto_program.instructions.begin(); - i_it!=goto_program.instructions.end(); - i_it++) - { - if(!i_it->is_assert()) - continue; - - const source_locationt &source_location=i_it->source_location; - irep_idt property_id=source_location.get_property_id(); - - if(show_vcc) - { - do_show_vcc(SSA, i_it); - continue; - } - - // solver - satcheckt satcheck; - bv_pointerst solver(SSA.ns, satcheck); - solver.unbounded_array=bv_pointerst::U_AUTO; - - satcheck.set_message_handler(get_message_handler()); - solver.set_message_handler(get_message_handler()); - - // give SSA to solver - solver << SSA; - - // give negation of property to solver - exprt negated_property=not_exprt(SSA.assertion(i_it)); - - solver << negated_property; - - property_statust &property_status=property_map[property_id]; - - // solve - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - property_status.result=FAIL; - build_goto_trace(SSA, solver, property_status.error_trace); - break; - - case decision_proceduret::D_UNSATISFIABLE: - property_status.result=PASS; - break; - - case decision_proceduret::D_ERROR: - default: - property_status.result=ERROR; - throw "error from decision procedure"; - } - } -} - -/*******************************************************************\ - -Function: summary_checkert::report_statistics() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::report_statistics() -{ -} - -/*******************************************************************\ - -Function: summary_checkert::do_show_vcc - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checkert::do_show_vcc( - const local_SSAt &SSA, - const goto_programt::const_targett i_it) -{ - std::cout << i_it->source_location << "\n"; - std::cout << i_it->source_location.get_comment() << "\n"; - - std::list ssa_constraints; - ssa_constraints << SSA; - - unsigned i=1; - for(std::list::const_iterator c_it=ssa_constraints.begin(); - c_it!=ssa_constraints.end(); - c_it++, i++) - std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; - - std::cout << "|--------------------------\n"; - - exprt property=SSA.assertion(i_it); - - std::cout << "{1} " << from_expr(SSA.ns, "", property) << "\n"; - - std::cout << "\n"; -} - diff --git a/src/summarizer/summary_checker.h b/src/summarizer/summary_checker.h deleted file mode 100644 index 4c8d32da7..000000000 --- a/src/summarizer/summary_checker.h +++ /dev/null @@ -1,51 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARY_CHECKER_H -#define CPROVER_SUMMARY_CHECKER_H - -#include - -#include - -#include "../ssa/local_ssa.h" - -class summary_checkert:public property_checkert -{ -public: - inline summary_checkert(): - show_vcc(false), - simplify(false), - fixed_point(false) - { - } - - bool show_vcc, simplify, fixed_point; - irep_idt function_to_check; - - virtual resultt operator()(const goto_modelt &); - - // statistics - absolute_timet start_time; - time_periodt sat_time; - -protected: - void report_statistics(); - - void do_show_vcc( - const local_SSAt &, - const goto_programt::const_targett); - - resultt check_properties(const goto_modelt &); - - void check_properties( - const goto_functionst::function_mapt::const_iterator f_it, - const namespacet &); -}; - -#endif diff --git a/src/summarizer/summary_checker_ai.cpp b/src/summarizer/summary_checker_ai.cpp deleted file mode 100644 index 9afbbbb6e..000000000 --- a/src/summarizer/summary_checker_ai.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker for AI - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include "summary_checker_ai.h" - -/*******************************************************************\ - -Function: summary_checker_ait::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_ait::operator()( - const goto_modelt &goto_model) -{ - const namespacet ns(goto_model.symbol_table); - - SSA_functions(goto_model,ns); - - ssa_unwinder.init(false,false); - - unsigned unwind = options.get_unsigned_int_option("unwind"); - if(unwind>0) - { - status() << "Unwinding" << messaget::eom; - - ssa_unwinder.init_localunwinders(); - - ssa_unwinder.unwind_all(unwind); - } - -#if 0 - /*********************************************************************************/ - /**************** code to test the loop-specific unwind function *****************/ - /**/ - if(unwind > 0){ - forall_goto_functions(f_it, goto_model.goto_functions){ - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - local_SSAt &SSA = ssa_db.get(f_it->first); - std::cout << "==>> Output SSA for function: " << f_it->first << "\n"; - SSA.output(std::cout); - } - } - else{ - ssa_unwinder.init_localunwinders(); // possibly required; - - // iterate over the SSA to unwind loops - forall_goto_functions(f_it, goto_model.goto_functions){ - - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - - local_SSAt &SSA = ssa_db.get(f_it->first); - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++){ - if(n_it->loophead!=SSA.nodes.end()){ - std::cout << "==>> unwinding loop with location number: " - << n_it->loophead->location->location_number << "\n"; - ssa_unwinder.unwind_loop_alone(f_it->first, - n_it->loophead->location->location_number, 1); - } - } - } - - forall_goto_functions(f_it, goto_model.goto_functions){ - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - local_SSAt &SSA = ssa_db.get(f_it->first); - std::cout << "==>> Output SSA for function: " << f_it->first << "\n"; - SSA.output(std::cout); - } - - } - /**/ - /*********************************************************************************/ -#endif - - irep_idt entry_function = goto_model.goto_functions.entry_point(); - if(options.get_bool_option("unit-check")) - entry_function = ""; - - if(!(options.get_bool_option("inline"))) - { - if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")) - { - SSA_dependency_graphs(goto_model, ns); - } - } - - // properties - initialize_property_map(goto_model.goto_functions); - - bool preconditions = options.get_bool_option("preconditions"); - bool termination = options.get_bool_option("termination"); - bool trivial_domain = options.get_bool_option("havoc"); - if(!trivial_domain || termination) - { - //forward analysis - summarize(goto_model,true,termination); - } - if(!trivial_domain && preconditions) - { - //backward analysis - summarize(goto_model,false,termination); - } - - if(preconditions) - { - report_statistics(); - report_preconditions(); - return property_checkert::UNKNOWN; - } - - if(termination) - { - report_statistics(); - return report_termination(); - } - -#ifdef SHOW_CALLINGCONTEXTS - if(options.get_bool_option("show-calling-contexts")) - return property_checkert::UNKNOWN; -#endif - -/* - irep_idt entry_function = goto_model.goto_functions.entry_point(); - if(options.get_bool_option("unit-check")) - entry_function = ""; -*/ - - std::set seen_function_calls; - property_checkert::resultt result = check_properties(entry_function, entry_function, seen_function_calls, false); - report_statistics(); - return result; -} - - -/*******************************************************************\ - -Function: summary_checker_ait::report_preconditions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_ait::report_preconditions() -{ - result() << eom; - result() << "** " << (options.get_bool_option("sufficient") ? - "Sufficient" : "Necessary") - << " preconditions: " << eom; - ssa_dbt::functionst &functions = ssa_db.functions(); - for(ssa_dbt::functionst::iterator it = functions.begin(); - it != functions.end(); it++) - { - exprt precondition; - bool computed = summary_db.exists(it->first); - if(computed) precondition = summary_db.get(it->first).bw_precondition; - if(precondition.is_nil()) computed = false; - result() << eom << "[" << it->first << "]: " - << (!computed ? "not computed" : - from_expr(it->second->ns, "", precondition)) << eom; - } -} - -/*******************************************************************\ - -Function: summary_checker_ait::report_termination - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_ait::report_termination() -{ - result() << eom; - result() << "** Termination: " << eom; - bool not_computed = true; - bool all_terminate = true; - bool one_nonterminate = false; - ssa_dbt::functionst &functions = ssa_db.functions(); - for(ssa_dbt::functionst::iterator it = functions.begin(); - it != functions.end(); it++) - { - threevalt terminates = YES; - bool computed = summary_db.exists(it->first); - if(computed) - { - terminates = summary_db.get(it->first).terminates; - not_computed = false; - } - all_terminate = all_terminate && (terminates==YES); - one_nonterminate = one_nonterminate || (terminates==NO); - result() << "[" << it->first << "]: " - << (!computed ? "not computed" : threeval2string(terminates)) << eom; - } - if(not_computed) return property_checkert::UNKNOWN; - if(all_terminate) return property_checkert::PASS; - if(one_nonterminate) return property_checkert::FAIL; - return property_checkert::UNKNOWN; -} diff --git a/src/summarizer/summary_checker_bmc.cpp b/src/summarizer/summary_checker_bmc.cpp deleted file mode 100644 index a4edfb589..000000000 --- a/src/summarizer/summary_checker_bmc.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Checker for BMC - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary_checker_bmc.h" - -#include "../ssa/ssa_refiner.h" -#include "../ssa/ssa_refiner_monolithic.h" -#include "../ssa/ssa_refiner_selective.h" - - -/*******************************************************************\ - -Function: summary_checker_bmct::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_bmct::operator()( - const goto_modelt &goto_model) -{ - const namespacet ns(goto_model.symbol_table); - irep_idt entry_function = goto_model.goto_functions.entry_point(); - if(options.get_bool_option("unit-check")) - entry_function = ""; - - SSA_functions(goto_model,ns); - - ssa_unwinder.init(false,true); - - property_checkert::resultt result = property_checkert::UNKNOWN; - unsigned max_unwind = options.get_unsigned_int_option("unwind"); - status() << "Max-unwind is " << max_unwind << eom; - ssa_unwinder.init_localunwinders(); - - ssa_refinert *ssa_refiner; - if((options.get_bool_option("inline"))) - ssa_refiner = new ssa_refiner_monolithict(summary_db, ssa_unwinder, - max_unwind); - else - ssa_refiner = new ssa_refiner_selectivet(ssa_db, ssa_unwinder, - max_unwind, ssa_inliner, reason); - ssa_refiner->set_message_handler(get_message_handler()); - - //while can refine - while((*ssa_refiner)()) - { - unsigned unwind = ssa_refiner->get_unwind(); - - //dependency graphs - if(!(options.get_bool_option("inline"))) - { - if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")) - { - SSA_dependency_graphs(goto_model, ns); - } - } - - //check - std::set seen_function_calls; - result = check_properties(entry_function, entry_function, - seen_function_calls, false); - - //result - if(result == property_checkert::PASS) - { - status() << "incremental BMC proof found after " - << unwind << " unwinding(s)" << messaget::eom; - break; - } - else if(result == property_checkert::FAIL) - { - status() << "incremental BMC counterexample found after " - << unwind << " unwinding(s)" << messaget::eom; - break; - } - } - report_statistics(); - return result; -} diff --git a/src/summarizer/summary_checker_kind.cpp b/src/summarizer/summary_checker_kind.cpp deleted file mode 100644 index 2500ecce8..000000000 --- a/src/summarizer/summary_checker_kind.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/*******************************************************************\ - -Module: Summary Checker for k-induction - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary_checker_kind.h" -#include "../ssa/ssa_refiner.h" -#include "../ssa/ssa_refiner_monolithic.h" -#include "../ssa/ssa_refiner_selective.h" - -#define GIVE_UP_INVARIANTS 7 - -/*******************************************************************\ - -Function: summary_checker_kindt::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summary_checker_kindt::operator()( - const goto_modelt &goto_model) -{ - const namespacet ns(goto_model.symbol_table); - irep_idt entry_function = goto_model.goto_functions.entry_point(); - if(options.get_bool_option("unit-check")) - entry_function = ""; - - SSA_functions(goto_model,ns); - - ssa_unwinder.init(true,false); - - property_checkert::resultt result = property_checkert::UNKNOWN; - unsigned max_unwind = options.get_unsigned_int_option("unwind"); - status() << "Max-unwind is " << max_unwind << eom; - ssa_unwinder.init_localunwinders(); - - ssa_refinert *ssa_refiner; - if((options.get_bool_option("inline"))) - ssa_refiner = new ssa_refiner_monolithict(summary_db, ssa_unwinder, - max_unwind); - else - ssa_refiner = new ssa_refiner_selectivet(ssa_db, ssa_unwinder, - max_unwind, ssa_inliner, reason); - ssa_refiner->set_message_handler(get_message_handler()); - - //while can refine - while((*ssa_refiner)()) - { - unsigned unwind = ssa_refiner->get_unwind(); - - //dependency graphs - if(!(options.get_bool_option("inline"))) - { - if((options.get_option("spurious-check") != "concrete") && - (options.get_option("spurious-check") != "abstract")) - { - //TODO: update only those that were refined by unwinding - SSA_dependency_graphs(goto_model, ns); - } - } - - //check - std::set seen_function_calls; - result = check_properties(entry_function, entry_function, - seen_function_calls, false); - - //do static analysis and check again - if(result == property_checkert::UNKNOWN && - !options.get_bool_option("havoc") && - (unwind seen_function_calls; - result = check_properties(entry_function, entry_function, - seen_function_calls, false); - } - - //result - if(result == property_checkert::PASS) - { - status() << "k-induction proof found after " - << unwind << " unwinding(s)" << eom; - break; - } - else if(result == property_checkert::FAIL) - { - status() << "k-induction counterexample found after " - << unwind << " unwinding(s)" << eom; - break; - } - } - delete ssa_refiner; - report_statistics(); - return result; -} - diff --git a/src/summarizer/summary_db.cpp b/src/summarizer/summary_db.cpp index 7517780a7..4e7dde39e 100644 --- a/src/summarizer/summary_db.cpp +++ b/src/summarizer/summary_db.cpp @@ -62,7 +62,7 @@ void summary_dbt::read(const std::string &id) { current=id; - summary.make_object(); + summary=jsont::json_object(); parse_json(file_name(id), get_message_handler(), summary); } diff --git a/src/summarizer/version.h b/src/summarizer/version.h index 8853bd115..401faf272 100644 --- a/src/summarizer/version.h +++ b/src/summarizer/version.h @@ -1 +1 @@ -#define SUMMARIZER_VERSION "0.4.0" +#define SUMMARIZER_VERSION "0.1.0" From c45492993ea88d4a4e73de83d6eaca9cf936d0ce Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Mon, 17 Apr 2017 14:45:04 +0530 Subject: [PATCH 85/90] added spurious check option --- src/2ls/2ls_parse_options.cpp | 5 +++++ src/2ls/2ls_parse_options.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/2ls/2ls_parse_options.cpp b/src/2ls/2ls_parse_options.cpp index b1363d104..e2debca7c 100644 --- a/src/2ls/2ls_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -149,6 +149,11 @@ void twols_parse_optionst::get_command_line_options(optionst &options) if(cmdline.isset("inline")) options.set_option("inline", true); + if(cmdline.isset("spurious-check")) + options.set_option("spurious-check", cmdline.get_value("spurious-check")); + else + options.set_option("spurious-check", "all"); + if(cmdline.isset("slice") && cmdline.isset("inline")) options.set_option("slice", true); else diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index d564b1279..01a99d5e4 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -52,6 +52,7 @@ class optionst; "(no-spurious-check)(all-functions)" \ "(no-simplify)(no-fixed-point)" \ "(graphml-witness):(json-cex):" \ + "(spurious-check):(no-all-properties)" \ "(no-spurious-check)(stop-on-fail)" \ "(competition-mode)(slice)(no-propagation)(independent-properties)" \ "(no-unwinding-assertions)" From c098fb6288ea03b0019314a8b9d3ffd0b441ece6 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 3 May 2017 08:44:57 +0100 Subject: [PATCH 86/90] Remove duplicate option --- src/2ls/2ls_parse_options.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index 01a99d5e4..642b37082 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -49,7 +49,7 @@ class optionst; "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ - "(no-spurious-check)(all-functions)" \ + "(all-functions)" \ "(no-simplify)(no-fixed-point)" \ "(graphml-witness):(json-cex):" \ "(spurious-check):(no-all-properties)" \ From cecd69000a348b77d73345ea34787843c8276e08 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 3 May 2017 08:54:45 +0100 Subject: [PATCH 87/90] Synchronise install script --- COMPILING | 4 ++-- install.sh | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/COMPILING b/COMPILING index a7535aef3..308d1b9a3 100644 --- a/COMPILING +++ b/COMPILING @@ -1,6 +1,6 @@ -2LS 0.4 +2LS 0.5 ./install.sh -The executable is src/summarizer/2ls +The executable is src/2ls/2ls diff --git a/install.sh b/install.sh index 9c2717e29..c72d8988e 100755 --- a/install.sh +++ b/install.sh @@ -1,13 +1,33 @@ -git clone https://github.com/peterschrammel/cbmc +#!/bin/bash + +CBMC_REPO=https://github.com/peterschrammel/cbmc +CBMC_VERSION=d95e7da28018fd315b04a1201d5b7cfe8195cbc6 + +if [ "$1" != "" ] +then + COMPILER="$1" +fi + +git clone $CBMC_REPO cd cbmc CBMC=`pwd` -git checkout e347c69bb1071f174f611278f5e001fd6070884a -cd src -make minisat2-download -make -cd ../../src +git checkout $CBMC_VERSION +make -C src minisat2-download +if [ "$COMPILER" != "" ] +then + make -C src CXX=$COMPILER +else + make -C src +fi + +cd ../src cp config.inc.template config.inc sed -i.bak "s#CBMC = ~/my-cbmc#CBMC = $CBMC#g" config.inc -make +if [ "$COMPILER" != "" ] +then + make CXX=$COMPILER +else + make +fi cd .. -echo "The executable is src/summarizer/2ls" +echo "The executable is src/2ls/2ls" From 0ec33ec57acbb76966bfb673061ecd0ce3862129 Mon Sep 17 00:00:00 2001 From: Peter Schrammel Date: Wed, 3 May 2017 10:05:29 +0100 Subject: [PATCH 88/90] Fix regression test makefiles --- regression/Makefile | 2 +- regression/interprocedural/Makefile | 4 +- regression/invariants/Makefile | 4 +- regression/kiki-modular/Makefile | 4 +- regression/kiki/Makefile | 6 +-- regression/one-version/Makefile | 14 ------- regression/one-version/array1/main.c | 12 ------ regression/one-version/array1/test.desc | 7 ---- regression/one-version/array2/main.c | 21 ---------- regression/one-version/array2/test.desc | 7 ---- regression/one-version/assumption1/main.c | 11 ------ regression/one-version/assumption1/test.desc | 7 ---- regression/one-version/context1/main.c | 12 ------ regression/one-version/context1/test.desc | 7 ---- regression/one-version/context2/main.c | 18 --------- regression/one-version/context2/test.desc | 7 ---- regression/one-version/context3/main.c | 26 ------------ regression/one-version/context3/test.desc | 7 ---- regression/one-version/context4/main.c | 18 --------- regression/one-version/context4/test.desc | 7 ---- regression/one-version/function1/main.c | 24 ------------ regression/one-version/function1/test.desc | 7 ---- regression/one-version/goto1/main.c | 23 ----------- regression/one-version/goto1/test.desc | 7 ---- regression/one-version/goto2/main.c | 22 ----------- regression/one-version/goto2/test.desc | 7 ---- regression/one-version/guards1/main.c | 12 ------ regression/one-version/guards1/test.desc | 7 ---- regression/one-version/guards2/main.c | 15 ------- regression/one-version/guards2/test.desc | 7 ---- regression/one-version/interval1/main.c | 13 ------ regression/one-version/interval1/test.desc | 7 ---- regression/one-version/interval2/main.c | 11 ------ regression/one-version/interval2/test.desc | 7 ---- regression/one-version/loop1/main.c | 17 -------- regression/one-version/loop1/test.desc | 7 ---- regression/one-version/loop2/main.c | 20 ---------- regression/one-version/loop2/test.desc | 7 ---- regression/one-version/loop3/main.c | 21 ---------- regression/one-version/loop3/test.desc | 7 ---- regression/one-version/loop4/main.c | 14 ------- regression/one-version/loop4/test.desc | 7 ---- regression/one-version/pointer1/main.c | 10 ----- regression/one-version/pointer1/test.desc | 7 ---- regression/one-version/pointer2/main.c | 21 ---------- regression/one-version/pointer2/test.desc | 7 ---- regression/one-version/pointer3/main.c | 23 ----------- regression/one-version/pointer3/test.desc | 7 ---- regression/one-version/pointer4/main.c | 26 ------------ regression/one-version/pointer4/test.desc | 7 ---- regression/one-version/pointer5/main.c | 25 ------------ regression/one-version/pointer5/test.desc | 7 ---- regression/one-version/simple1/main.c | 11 ------ regression/one-version/simple1/test.desc | 7 ---- regression/one-version/simple2/main.c | 11 ------ regression/one-version/simple2/test.desc | 7 ---- regression/one-version/struct1/main.c | 27 ------------- regression/one-version/struct1/test.desc | 7 ---- regression/one-version/struct2/main.c | 18 --------- regression/one-version/struct2/test.desc | 7 ---- regression/one-version/struct3/main.c | 18 --------- regression/one-version/struct3/test.desc | 7 ---- regression/one-version/struct4/main.c | 37 ------------------ regression/one-version/struct4/test.desc | 7 ---- regression/one-version/union1/main.c | 34 ---------------- regression/one-version/union1/test.desc | 6 --- regression/preconditions/Makefile | 4 +- regression/spurious-check-abstract/Makefile | 4 +- regression/spurious-check-complete/Makefile | 4 +- regression/spurious-check-concrete/Makefile | 4 +- regression/termination/Makefile | 4 +- regression/two-versions/Makefile | 14 ------- regression/two-versions/ofer-example1/new.c | 18 --------- regression/two-versions/ofer-example1/new.o | Bin 4931 -> 0 bytes regression/two-versions/ofer-example1/old.c | 18 --------- regression/two-versions/ofer-example1/old.o | Bin 4931 -> 0 bytes .../two-versions/ofer-example1/test.desc | 7 ---- regression/two-versions/ofer-example2/new.c | 11 ------ regression/two-versions/ofer-example2/new.o | Bin 4272 -> 0 bytes regression/two-versions/ofer-example2/old.c | 14 ------- regression/two-versions/ofer-example2/old.o | Bin 4383 -> 0 bytes .../two-versions/ofer-example2/test.desc | 7 ---- regression/two-versions/ofer-example3/new.c | 10 ----- regression/two-versions/ofer-example3/new.o | Bin 4271 -> 0 bytes regression/two-versions/ofer-example3/old.c | 11 ------ regression/two-versions/ofer-example3/old.o | Bin 4271 -> 0 bytes .../two-versions/ofer-example3/test.desc | 7 ---- regression/two-versions/ofer-example4/new.c | 10 ----- regression/two-versions/ofer-example4/new.o | Bin 4271 -> 0 bytes regression/two-versions/ofer-example4/old.c | 11 ------ regression/two-versions/ofer-example4/old.o | Bin 4271 -> 0 bytes .../two-versions/ofer-example4/test.desc | 7 ---- regression/two-versions/ofer-example5/new.c | 11 ------ regression/two-versions/ofer-example5/new.o | Bin 4400 -> 0 bytes regression/two-versions/ofer-example5/old.c | 7 ---- regression/two-versions/ofer-example5/old.o | Bin 4013 -> 0 bytes .../two-versions/ofer-example5/test.desc | 7 ---- regression/two-versions/pointer1/new.c | 18 --------- regression/two-versions/pointer1/new.o | Bin 4575 -> 0 bytes regression/two-versions/pointer1/old.c | 15 ------- regression/two-versions/pointer1/old.o | Bin 4453 -> 0 bytes regression/two-versions/pointer1/test.desc | 7 ---- regression/two-versions/simple1/new.c | 10 ----- regression/two-versions/simple1/new.o | Bin 4103 -> 0 bytes regression/two-versions/simple1/old.c | 7 ---- regression/two-versions/simple1/old.o | Bin 4013 -> 0 bytes regression/two-versions/simple1/test.desc | 7 ---- regression/two-versions/simple2/new.c | 6 --- regression/two-versions/simple2/new.o | Bin 3865 -> 0 bytes regression/two-versions/simple2/old.c | 8 ---- regression/two-versions/simple2/old.o | Bin 4151 -> 0 bytes regression/two-versions/simple2/test.desc | 7 ---- regression/two-versions/slam-example1/new.c | 23 ----------- regression/two-versions/slam-example1/old.c | 23 ----------- .../two-versions/slam-example1/test.desc | 7 ---- regression/two-versions/struct1/new.c | 9 ----- regression/two-versions/struct1/new.o | Bin 4028 -> 0 bytes regression/two-versions/struct1/old.c | 9 ----- regression/two-versions/struct1/old.o | Bin 4016 -> 0 bytes regression/two-versions/struct1/test.desc | 7 ---- 120 files changed, 20 insertions(+), 1147 deletions(-) delete mode 100644 regression/one-version/Makefile delete mode 100644 regression/one-version/array1/main.c delete mode 100644 regression/one-version/array1/test.desc delete mode 100644 regression/one-version/array2/main.c delete mode 100644 regression/one-version/array2/test.desc delete mode 100644 regression/one-version/assumption1/main.c delete mode 100644 regression/one-version/assumption1/test.desc delete mode 100644 regression/one-version/context1/main.c delete mode 100644 regression/one-version/context1/test.desc delete mode 100644 regression/one-version/context2/main.c delete mode 100644 regression/one-version/context2/test.desc delete mode 100644 regression/one-version/context3/main.c delete mode 100644 regression/one-version/context3/test.desc delete mode 100644 regression/one-version/context4/main.c delete mode 100644 regression/one-version/context4/test.desc delete mode 100644 regression/one-version/function1/main.c delete mode 100644 regression/one-version/function1/test.desc delete mode 100644 regression/one-version/goto1/main.c delete mode 100644 regression/one-version/goto1/test.desc delete mode 100644 regression/one-version/goto2/main.c delete mode 100644 regression/one-version/goto2/test.desc delete mode 100644 regression/one-version/guards1/main.c delete mode 100644 regression/one-version/guards1/test.desc delete mode 100644 regression/one-version/guards2/main.c delete mode 100644 regression/one-version/guards2/test.desc delete mode 100644 regression/one-version/interval1/main.c delete mode 100644 regression/one-version/interval1/test.desc delete mode 100644 regression/one-version/interval2/main.c delete mode 100644 regression/one-version/interval2/test.desc delete mode 100644 regression/one-version/loop1/main.c delete mode 100644 regression/one-version/loop1/test.desc delete mode 100644 regression/one-version/loop2/main.c delete mode 100644 regression/one-version/loop2/test.desc delete mode 100644 regression/one-version/loop3/main.c delete mode 100644 regression/one-version/loop3/test.desc delete mode 100644 regression/one-version/loop4/main.c delete mode 100644 regression/one-version/loop4/test.desc delete mode 100644 regression/one-version/pointer1/main.c delete mode 100644 regression/one-version/pointer1/test.desc delete mode 100644 regression/one-version/pointer2/main.c delete mode 100644 regression/one-version/pointer2/test.desc delete mode 100644 regression/one-version/pointer3/main.c delete mode 100644 regression/one-version/pointer3/test.desc delete mode 100644 regression/one-version/pointer4/main.c delete mode 100644 regression/one-version/pointer4/test.desc delete mode 100644 regression/one-version/pointer5/main.c delete mode 100644 regression/one-version/pointer5/test.desc delete mode 100644 regression/one-version/simple1/main.c delete mode 100644 regression/one-version/simple1/test.desc delete mode 100644 regression/one-version/simple2/main.c delete mode 100644 regression/one-version/simple2/test.desc delete mode 100644 regression/one-version/struct1/main.c delete mode 100644 regression/one-version/struct1/test.desc delete mode 100644 regression/one-version/struct2/main.c delete mode 100644 regression/one-version/struct2/test.desc delete mode 100644 regression/one-version/struct3/main.c delete mode 100644 regression/one-version/struct3/test.desc delete mode 100644 regression/one-version/struct4/main.c delete mode 100644 regression/one-version/struct4/test.desc delete mode 100644 regression/one-version/union1/main.c delete mode 100644 regression/one-version/union1/test.desc delete mode 100644 regression/two-versions/Makefile delete mode 100644 regression/two-versions/ofer-example1/new.c delete mode 100644 regression/two-versions/ofer-example1/new.o delete mode 100644 regression/two-versions/ofer-example1/old.c delete mode 100644 regression/two-versions/ofer-example1/old.o delete mode 100644 regression/two-versions/ofer-example1/test.desc delete mode 100644 regression/two-versions/ofer-example2/new.c delete mode 100644 regression/two-versions/ofer-example2/new.o delete mode 100644 regression/two-versions/ofer-example2/old.c delete mode 100644 regression/two-versions/ofer-example2/old.o delete mode 100644 regression/two-versions/ofer-example2/test.desc delete mode 100644 regression/two-versions/ofer-example3/new.c delete mode 100644 regression/two-versions/ofer-example3/new.o delete mode 100644 regression/two-versions/ofer-example3/old.c delete mode 100644 regression/two-versions/ofer-example3/old.o delete mode 100644 regression/two-versions/ofer-example3/test.desc delete mode 100644 regression/two-versions/ofer-example4/new.c delete mode 100644 regression/two-versions/ofer-example4/new.o delete mode 100644 regression/two-versions/ofer-example4/old.c delete mode 100644 regression/two-versions/ofer-example4/old.o delete mode 100644 regression/two-versions/ofer-example4/test.desc delete mode 100644 regression/two-versions/ofer-example5/new.c delete mode 100644 regression/two-versions/ofer-example5/new.o delete mode 100644 regression/two-versions/ofer-example5/old.c delete mode 100644 regression/two-versions/ofer-example5/old.o delete mode 100644 regression/two-versions/ofer-example5/test.desc delete mode 100644 regression/two-versions/pointer1/new.c delete mode 100644 regression/two-versions/pointer1/new.o delete mode 100644 regression/two-versions/pointer1/old.c delete mode 100644 regression/two-versions/pointer1/old.o delete mode 100644 regression/two-versions/pointer1/test.desc delete mode 100644 regression/two-versions/simple1/new.c delete mode 100644 regression/two-versions/simple1/new.o delete mode 100644 regression/two-versions/simple1/old.c delete mode 100644 regression/two-versions/simple1/old.o delete mode 100644 regression/two-versions/simple1/test.desc delete mode 100644 regression/two-versions/simple2/new.c delete mode 100644 regression/two-versions/simple2/new.o delete mode 100644 regression/two-versions/simple2/old.c delete mode 100644 regression/two-versions/simple2/old.o delete mode 100644 regression/two-versions/simple2/test.desc delete mode 100644 regression/two-versions/slam-example1/new.c delete mode 100644 regression/two-versions/slam-example1/old.c delete mode 100644 regression/two-versions/slam-example1/test.desc delete mode 100644 regression/two-versions/struct1/new.c delete mode 100644 regression/two-versions/struct1/new.o delete mode 100644 regression/two-versions/struct1/old.c delete mode 100644 regression/two-versions/struct1/old.o delete mode 100644 regression/two-versions/struct1/test.desc diff --git a/regression/Makefile b/regression/Makefile index 22aec995c..655b3a4b2 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -1,4 +1,4 @@ -DIRS = termination kiki preconditions interprocedural invariants +DIRS = spurious-check-complete spurious-check-abstract spurious-check-concrete kiki-modular termination kiki preconditions interprocedural invariants test: $(foreach var,$(DIRS), make -C $(var) test;) diff --git a/regression/interprocedural/Makefile b/regression/interprocedural/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/interprocedural/Makefile +++ b/regression/interprocedural/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/invariants/Makefile b/regression/invariants/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/invariants/Makefile +++ b/regression/invariants/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/kiki-modular/Makefile b/regression/kiki-modular/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/kiki-modular/Makefile +++ b/regression/kiki-modular/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/kiki/Makefile b/regression/kiki/Makefile index 1b86b08f2..b5f2484a2 100644 --- a/regression/kiki/Makefile +++ b/regression/kiki/Makefile @@ -1,12 +1,12 @@ default: tests.log -FLAGS = --verbosity 10 --inline +FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/one-version/Makefile b/regression/one-version/Makefile deleted file mode 100644 index 9c3114939..000000000 --- a/regression/one-version/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -default: tests.log - -test: - @../test.pl -c ../../../src/summarizer/summarizer - -tests.log: ../test.pl - @../test.pl -c ../../../src/summarizer/summarizer - -show: - @for dir in *; do \ - if [ -d "$$dir" ]; then \ - vim -o "$$dir/*.c" "$$dir/*.out"; \ - fi; \ - done; diff --git a/regression/one-version/array1/main.c b/regression/one-version/array1/main.c deleted file mode 100644 index 348f79b70..000000000 --- a/regression/one-version/array1/main.c +++ /dev/null @@ -1,12 +0,0 @@ -int main() -{ - long int i; - char my_buffer[10]; - - if(i>=0 && i<10) - { - my_buffer[i]=0; // should pass - } - else - my_buffer[i]=1; // should fail -} diff --git a/regression/one-version/array1/test.desc b/regression/one-version/array1/test.desc deleted file mode 100644 index 25b56b8ec..000000000 --- a/regression/one-version/array1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---bounds-check -^EXIT=10$ -^SIGNAL=0$ -^** 2 of 4 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/array2/main.c b/regression/one-version/array2/main.c deleted file mode 100644 index 764abe8c8..000000000 --- a/regression/one-version/array2/main.c +++ /dev/null @@ -1,21 +0,0 @@ -int main() -{ - long int i, j; - char my_buffer[10]; - - // constant index - my_buffer[1]=1; - my_buffer[2]=2; - assert(my_buffer[1]==1); - assert(my_buffer[2]==2); - - // variable index - if(i>=0 && i<10 && j>=0 && j<10) - { - my_buffer[i]=1; - assert(my_buffer[i]==1); - my_buffer[j]=2; - assert(my_buffer[j]==2); - if(i!=j) assert(my_buffer[i]==1); - } -} diff --git a/regression/one-version/array2/test.desc b/regression/one-version/array2/test.desc deleted file mode 100644 index 3cffe7b65..000000000 --- a/regression/one-version/array2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 5 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/assumption1/main.c b/regression/one-version/assumption1/main.c deleted file mode 100644 index 63ac134ec..000000000 --- a/regression/one-version/assumption1/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - int x, i, y; - - __CPROVER_assume(x==y); - - x++; - y++; - - assert(x==y); -} diff --git a/regression/one-version/assumption1/test.desc b/regression/one-version/assumption1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/assumption1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/context1/main.c b/regression/one-version/context1/main.c deleted file mode 100644 index 8e4a9bfd3..000000000 --- a/regression/one-version/context1/main.c +++ /dev/null @@ -1,12 +0,0 @@ -void f(int i) -{ - if(i == 1) - assert(0); // this should not fail, due to calling context -} - -void main() -{ - int i = 2; - f(i); -} - diff --git a/regression/one-version/context1/test.desc b/regression/one-version/context1/test.desc deleted file mode 100644 index d19906887..000000000 --- a/regression/one-version/context1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/context2/main.c b/regression/one-version/context2/main.c deleted file mode 100644 index 36eb553b0..000000000 --- a/regression/one-version/context2/main.c +++ /dev/null @@ -1,18 +0,0 @@ -int z; - -int f(int i) -{ - z=1; - return i; -} - -void main() -{ - int i, j; - j=f(i); - - // should pass, due to postcondition of f - assert(j==i); - assert(z==1); -} - diff --git a/regression/one-version/context2/test.desc b/regression/one-version/context2/test.desc deleted file mode 100644 index 81e6e1c1a..000000000 --- a/regression/one-version/context2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/context3/main.c b/regression/one-version/context3/main.c deleted file mode 100644 index 5b87d158a..000000000 --- a/regression/one-version/context3/main.c +++ /dev/null @@ -1,26 +0,0 @@ -#include - -void main() -{ - int i; - - if(i==1) - { - // should pass, due to postcondition of exit() - exit(0); - assert(0); - } - else if(i==2) - { - // should pass, due to postcondition of _Exit() - _Exit(0); - assert(0); - } - else if(i==3) - { - // should pass, due to postcondition of abort() - abort(); - assert(0); - } -} - diff --git a/regression/one-version/context3/test.desc b/regression/one-version/context3/test.desc deleted file mode 100644 index 28d612c3f..000000000 --- a/regression/one-version/context3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 3 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/context4/main.c b/regression/one-version/context4/main.c deleted file mode 100644 index 6ac09c6e6..000000000 --- a/regression/one-version/context4/main.c +++ /dev/null @@ -1,18 +0,0 @@ -#include - -void f(int i, int j) -{ - if(i!=j) - exit(0); -} - -void main() -{ - int i, j; - - f(i, j); - - // should pass, due to postcondition of f - assert(i==j); -} - diff --git a/regression/one-version/context4/test.desc b/regression/one-version/context4/test.desc deleted file mode 100644 index d19906887..000000000 --- a/regression/one-version/context4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/function1/main.c b/regression/one-version/function1/main.c deleted file mode 100644 index bc9cd7dc2..000000000 --- a/regression/one-version/function1/main.c +++ /dev/null @@ -1,24 +0,0 @@ -int my_global; - -int f(void) -{ - my_global=4; - return 2; -} - -int main() -{ - int i=1; - my_global=3; - - i=f(); - - // check that 'i' is assigned, i.e., should fail - assert(i==1); - - // check that 'my_global' is assigned, i.e., should fail - assert(my_global==3); - - return 0; -} - diff --git a/regression/one-version/function1/test.desc b/regression/one-version/function1/test.desc deleted file mode 100644 index 4a219b909..000000000 --- a/regression/one-version/function1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 2 of 2 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/goto1/main.c b/regression/one-version/goto1/main.c deleted file mode 100644 index e90e2e2db..000000000 --- a/regression/one-version/goto1/main.c +++ /dev/null @@ -1,23 +0,0 @@ -int main() -{ - int x, i, y; - - y=1; - - switch(i) - { - case 1: goto l1; - case 2: goto l2; - case 3: goto l3; - case 4: goto l4; - default: return 0; - } - - l1: x=1; goto check; - l2: x=y; goto check; - l3: x=1; goto check; - l4: x=1; goto check; - - check: - assert(x==1); -} diff --git a/regression/one-version/goto1/test.desc b/regression/one-version/goto1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/goto1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/goto2/main.c b/regression/one-version/goto2/main.c deleted file mode 100644 index 01272964c..000000000 --- a/regression/one-version/goto2/main.c +++ /dev/null @@ -1,22 +0,0 @@ -int main() -{ - int x, i; - - switch(i) - { - case 1: goto l1; - case 2: goto l2; - case 3: goto l3; - case 4: goto l4; - default: return 0; - } - - l1: x=1; goto check; - l2: x=2; goto check; - l3: x=1; goto check; - l4: x=1; goto check; - - check: - // should fail - assert(x==1); -} diff --git a/regression/one-version/goto2/test.desc b/regression/one-version/goto2/test.desc deleted file mode 100644 index 6511ad6c7..000000000 --- a/regression/one-version/goto2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 1 of 1 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/guards1/main.c b/regression/one-version/guards1/main.c deleted file mode 100644 index 9808a9254..000000000 --- a/regression/one-version/guards1/main.c +++ /dev/null @@ -1,12 +0,0 @@ -int main() -{ - int x; - x=1; - - while(x==1) - { - } - - // the above never exits - assert(0); -} diff --git a/regression/one-version/guards1/test.desc b/regression/one-version/guards1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/guards1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/guards2/main.c b/regression/one-version/guards2/main.c deleted file mode 100644 index 0871f3447..000000000 --- a/regression/one-version/guards2/main.c +++ /dev/null @@ -1,15 +0,0 @@ -int main() -{ - int i, j, k; - - __CPROVER_assume(i==10); - - k=100; - - while(j!=10) - { - } - - __CPROVER_assert(i==10, "i"); - __CPROVER_assert(j==10, "j"); -} diff --git a/regression/one-version/guards2/test.desc b/regression/one-version/guards2/test.desc deleted file mode 100644 index 81e6e1c1a..000000000 --- a/regression/one-version/guards2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/interval1/main.c b/regression/one-version/interval1/main.c deleted file mode 100644 index 5c84d5f76..000000000 --- a/regression/one-version/interval1/main.c +++ /dev/null @@ -1,13 +0,0 @@ -int main() -{ - unsigned int x, y; - - __CPROVER_assume(x>=1 && x<=3); - __CPROVER_assume(y<2); - - // should be both UNSAT - assert(x!=4); - assert(y<=1); - - return 0; -} diff --git a/regression/one-version/interval1/test.desc b/regression/one-version/interval1/test.desc deleted file mode 100644 index 5cc4b55db..000000000 --- a/regression/one-version/interval1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/interval2/main.c b/regression/one-version/interval2/main.c deleted file mode 100644 index 9e5697e10..000000000 --- a/regression/one-version/interval2/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - unsigned int x, y; - - __CPROVER_assume(x>=1 && x<=3); - __CPROVER_assume(y<=2); - - // should be both SAT - assert(x!=2); - assert(y!=x); -} diff --git a/regression/one-version/interval2/test.desc b/regression/one-version/interval2/test.desc deleted file mode 100644 index 4a219b909..000000000 --- a/regression/one-version/interval2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 2 of 2 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/loop1/main.c b/regression/one-version/loop1/main.c deleted file mode 100644 index a1e45faac..000000000 --- a/regression/one-version/loop1/main.c +++ /dev/null @@ -1,17 +0,0 @@ -int some_function(); - -int main() -{ - unsigned int x, y; - - x=0; - y=0; - - while(some_function()) - { - x++; - y++; - } - - assert(x==y); -} diff --git a/regression/one-version/loop1/test.desc b/regression/one-version/loop1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/loop1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop2/main.c b/regression/one-version/loop2/main.c deleted file mode 100644 index 1acf2c01f..000000000 --- a/regression/one-version/loop2/main.c +++ /dev/null @@ -1,20 +0,0 @@ -int some_function(); - -int main() -{ - unsigned int x, y, c; - - x=0; - y=0; - - while(some_function()) - { - if(c) - { - x++; - y++; - } - } - - assert(x==y); -} diff --git a/regression/one-version/loop2/test.desc b/regression/one-version/loop2/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/loop2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop3/main.c b/regression/one-version/loop3/main.c deleted file mode 100644 index ce5792d6c..000000000 --- a/regression/one-version/loop3/main.c +++ /dev/null @@ -1,21 +0,0 @@ -int x, y, z; - -int main() -{ - // does not require invariant, just exit condition - for(x=0; x!=100; x++); - assert(x==100); - - // same again with do-while - y=0; - do y++; while(y!=100); - assert(y==100); - - // Same again with a 'break', but this is basically - // the same CFG as the first one. - z=0; - while(1) { if(z==100) break; z++; } - assert(z==100); - - return 0; -} diff --git a/regression/one-version/loop3/test.desc b/regression/one-version/loop3/test.desc deleted file mode 100644 index 28d612c3f..000000000 --- a/regression/one-version/loop3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 3 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/loop4/main.c b/regression/one-version/loop4/main.c deleted file mode 100644 index 5d2168ad1..000000000 --- a/regression/one-version/loop4/main.c +++ /dev/null @@ -1,14 +0,0 @@ -int main() -{ - int i; - - // jump into the loop - goto label; - - while(i!=100) - { - label:; - } - - assert(0); // unsafe -} diff --git a/regression/one-version/loop4/test.desc b/regression/one-version/loop4/test.desc deleted file mode 100644 index 6511ad6c7..000000000 --- a/regression/one-version/loop4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 1 of 1 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/pointer1/main.c b/regression/one-version/pointer1/main.c deleted file mode 100644 index 725733dc8..000000000 --- a/regression/one-version/pointer1/main.c +++ /dev/null @@ -1,10 +0,0 @@ -unsigned int x, y; - -int main() -{ - unsigned int *p; - - p=&x; - - y=*p; -} diff --git a/regression/one-version/pointer1/test.desc b/regression/one-version/pointer1/test.desc deleted file mode 100644 index f6cb77be0..000000000 --- a/regression/one-version/pointer1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---pointer-check -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 0 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer2/main.c b/regression/one-version/pointer2/main.c deleted file mode 100644 index 12a0d0e6c..000000000 --- a/regression/one-version/pointer2/main.c +++ /dev/null @@ -1,21 +0,0 @@ -#include - -int global; - -int main() -{ - int *p; - - p=&global; - assert(p==&global); - global=1; - assert(global==1); - - // writing to pointer - *p=2; - assert(global==2); - - // reading from pointer - global=3; - assert(*p==3); -} diff --git a/regression/one-version/pointer2/test.desc b/regression/one-version/pointer2/test.desc deleted file mode 100644 index 8c2cd97d3..000000000 --- a/regression/one-version/pointer2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---pointer-check -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer3/main.c b/regression/one-version/pointer3/main.c deleted file mode 100644 index 376867895..000000000 --- a/regression/one-version/pointer3/main.c +++ /dev/null @@ -1,23 +0,0 @@ -struct S -{ - unsigned int x, y; -} my_s; - -int main() -{ - unsigned int *p; - - my_s.x=0; - my_s.y=0; - - // read - p=&my_s.x; - *p=2; - my_s.x=1; - assert(*p==1); - - // write - p=&my_s.y; - *p=3; - assert(my_s.y==3); -} diff --git a/regression/one-version/pointer3/test.desc b/regression/one-version/pointer3/test.desc deleted file mode 100644 index 5cc4b55db..000000000 --- a/regression/one-version/pointer3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 2 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer4/main.c b/regression/one-version/pointer4/main.c deleted file mode 100644 index 6fdf021c4..000000000 --- a/regression/one-version/pointer4/main.c +++ /dev/null @@ -1,26 +0,0 @@ -int array[10000]; - -int main() -{ - int *p; - int index; - - // read - p=array; - assert(p==array); - array[1]=10; - p++; - assert(p[0]==10); - - // read with index - if(index>=0 && index<10000) - { - p=array+index; - assert(*p==array[index]); - } - - // write - p=array; - *(p+3)=3; - assert(array[3]==3); -} diff --git a/regression/one-version/pointer4/test.desc b/regression/one-version/pointer4/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/pointer4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/pointer5/main.c b/regression/one-version/pointer5/main.c deleted file mode 100644 index 223b6d6de..000000000 --- a/regression/one-version/pointer5/main.c +++ /dev/null @@ -1,25 +0,0 @@ -int i; - -int main() -{ - // 'open' pointers - int *p, *q; - - *p=1; - assert(*p==1); - - if(p==q) - assert(*q==1); - - i=100; - *q=2; - - if(p==q) - assert(*p==2); - - // aliasing with other stuff - if(q==&i) - assert(i==2); - else - assert(i==100); -} diff --git a/regression/one-version/pointer5/test.desc b/regression/one-version/pointer5/test.desc deleted file mode 100644 index 7282e4951..000000000 --- a/regression/one-version/pointer5/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 5 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/simple1/main.c b/regression/one-version/simple1/main.c deleted file mode 100644 index 44598aa83..000000000 --- a/regression/one-version/simple1/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int main() -{ - unsigned int x, y; - - x=y; - - x++; - y++; - - assert(x==y); -} diff --git a/regression/one-version/simple1/test.desc b/regression/one-version/simple1/test.desc deleted file mode 100644 index 880397d5d..000000000 --- a/regression/one-version/simple1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/simple2/main.c b/regression/one-version/simple2/main.c deleted file mode 100644 index 8d66a2ce8..000000000 --- a/regression/one-version/simple2/main.c +++ /dev/null @@ -1,11 +0,0 @@ -int g; - -int main() -{ - int input; - - if(input) - g=1; - - assert(g!=1); -} diff --git a/regression/one-version/simple2/test.desc b/regression/one-version/simple2/test.desc deleted file mode 100644 index 6511ad6c7..000000000 --- a/regression/one-version/simple2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=10$ -^SIGNAL=0$ -^** 1 of 1 failed$ -^VERIFICATION FAILED$ diff --git a/regression/one-version/struct1/main.c b/regression/one-version/struct1/main.c deleted file mode 100644 index 1c43f3853..000000000 --- a/regression/one-version/struct1/main.c +++ /dev/null @@ -1,27 +0,0 @@ -struct outer -{ - struct inner - { - int x, y; - } a; - - int b, c, d; -}; - -int main() -{ - struct outer my_s1; - struct inner my_s2; - - my_s1.b=1; - assert(my_s1.b==1); - - my_s1.b=my_s1.c; - assert(my_s1.b==my_s1.c); - - my_s2.x=10; - my_s1.a=my_s2; - assert(my_s1.a.x==10); - - assert(my_s1.a.y==my_s2.y); -} diff --git a/regression/one-version/struct1/test.desc b/regression/one-version/struct1/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct2/main.c b/regression/one-version/struct2/main.c deleted file mode 100644 index 75ec068d1..000000000 --- a/regression/one-version/struct2/main.c +++ /dev/null @@ -1,18 +0,0 @@ -struct S -{ - int a, b, c; - int array[4]; -}; - -int main() -{ - struct S s1, s2; - - // copy full struct - s1=s2; - - assert(s1.a==s2.a && s1.b==s2.b); - assert(s1.array[0]==s2.array[0]); - assert(s1.array[1]==s2.array[1]); - assert(s1==s2); -} diff --git a/regression/one-version/struct2/test.desc b/regression/one-version/struct2/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct3/main.c b/regression/one-version/struct3/main.c deleted file mode 100644 index 9a7b80f1d..000000000 --- a/regression/one-version/struct3/main.c +++ /dev/null @@ -1,18 +0,0 @@ -struct some_struct -{ - int n; -}; - -void func(struct some_struct *my_ptr) -{ - assert(my_ptr->n==42); -} - -int main() -{ - struct some_struct local_struct = { .n = 42 }; - - func(&local_struct); - - return 0; -} diff --git a/regression/one-version/struct3/test.desc b/regression/one-version/struct3/test.desc deleted file mode 100644 index a67495a20..000000000 --- a/regression/one-version/struct3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c ---inline -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 1 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/struct4/main.c b/regression/one-version/struct4/main.c deleted file mode 100644 index 4a7a6beea..000000000 --- a/regression/one-version/struct4/main.c +++ /dev/null @@ -1,37 +0,0 @@ -struct some_struct -{ - int a, b, c, d; - - struct nested - { - int e, f; - } sub; -}; - -int main() -{ - struct some_struct my_s, *p; - int *q; - char *char_p=0; - - // read through pointer - p=&my_s; - my_s.b=1; - __CPROVER_assert(p->b==1, "p->b"); - - // write through pointer - p->c=2; - __CPROVER_assert(my_s.c==2, "my_s.c"); - - // pointer into struct - q=&p->d; - my_s.d=3; - __CPROVER_assert(*q==3, "q 3"); - - // pointer into sub-struct - q=&p->sub.e; - my_s.sub.e=4; - __CPROVER_assert(*q==4, "q 4"); - - return 0; -} diff --git a/regression/one-version/struct4/test.desc b/regression/one-version/struct4/test.desc deleted file mode 100644 index eb7f2a21c..000000000 --- a/regression/one-version/struct4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 4 failed$ -^VERIFICATION SUCCESSFUL$ diff --git a/regression/one-version/union1/main.c b/regression/one-version/union1/main.c deleted file mode 100644 index 3c034ae00..000000000 --- a/regression/one-version/union1/main.c +++ /dev/null @@ -1,34 +0,0 @@ -union my_U -{ - char array[sizeof(int)]; - - int i; - - struct S - { - char ch0, ch1, ch2, ch3; - } s; -}; - -int main() -{ - union my_U u; - - assert(u.array[0]==u.s.ch0); - assert(u.array[1]==u.s.ch1); - assert(u.array[2]==u.s.ch2); - assert(u.array[3]==u.s.ch3); - - u.i=0x04030201; - - if(u.s.ch0==0x01) - { - // little endian - assert(u.s.ch1==0x02 && u.s.ch2==0x03 && u.s.ch3==0x04); - } - else - { - // big endian - assert(u.s.ch1==0x03 && u.s.ch2==0x02 && u.s.ch3==0x01); - } -} diff --git a/regression/one-version/union1/test.desc b/regression/one-version/union1/test.desc deleted file mode 100644 index 72624a0c9..000000000 --- a/regression/one-version/union1/test.desc +++ /dev/null @@ -1,6 +0,0 @@ -CORE -main.c - -^EXIT=0$ -^SIGNAL=0$ -^** 0 of 6 failed$ diff --git a/regression/preconditions/Makefile b/regression/preconditions/Makefile index d2ff74910..b5f2484a2 100644 --- a/regression/preconditions/Makefile +++ b/regression/preconditions/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/spurious-check-abstract/Makefile b/regression/spurious-check-abstract/Makefile index 3a92d5017..d53a81eae 100644 --- a/regression/spurious-check-abstract/Makefile +++ b/regression/spurious-check-abstract/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check abstract test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/spurious-check-complete/Makefile b/regression/spurious-check-complete/Makefile index 2a3b5cb15..8de4c2425 100644 --- a/regression/spurious-check-complete/Makefile +++ b/regression/spurious-check-complete/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check complete test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/spurious-check-concrete/Makefile b/regression/spurious-check-concrete/Makefile index c26b769b2..b3ed1298c 100644 --- a/regression/spurious-check-concrete/Makefile +++ b/regression/spurious-check-concrete/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check concrete test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/termination/Makefile b/regression/termination/Makefile index 47bfd1905..f92c8b2be 100644 --- a/regression/termination/Makefile +++ b/regression/termination/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --termination test: - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" + @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/two-versions/Makefile b/regression/two-versions/Makefile deleted file mode 100644 index bbbf50d3a..000000000 --- a/regression/two-versions/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -default: tests.log - -test: - @../test.pl -c ../../../src/deltacheck/deltacheck - -tests.log: ../test.pl - @../test.pl -c ../../../src/deltacheck/deltacheck - -show: - @for dir in *; do \ - if [ -d "$$dir" ]; then \ - vim -o "$$dir/*.c" "$$dir/*.out"; \ - fi; \ - done; diff --git a/regression/two-versions/ofer-example1/new.c b/regression/two-versions/ofer-example1/new.c deleted file mode 100644 index 8a49e92dc..000000000 --- a/regression/two-versions/ofer-example1/new.c +++ /dev/null @@ -1,18 +0,0 @@ -_Bool some_function(); - -void f(int x, int y) -{ - while(some_function()) - { - x--; - y--; - } - - assert(x==y); -} - -void main() -{ - int x,y; - f(x, y); -} diff --git a/regression/two-versions/ofer-example1/new.o b/regression/two-versions/ofer-example1/new.o deleted file mode 100644 index ae6401fb76539e2e90321b0e88968909ed5ffd86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4931 zcmaJ^Yj_mZ89ry$S%JESpcJsJrVP?*iLi+am=Z9IpsjR37N|wrVRm;m8Qq;-_W}vf zqOG-8)KsgM(uSL&f>g8@OVz4P6as|%H9%>&B>b~K`opL6vF|r$c6O0x*dP1so;m0J z-uFA-ZC2bpeWr9P8W1T5P0>CJSyEGQs~7rbjRmQu3K% zE^g|nOx!3~8Jk_rm^L(NrA#(xhcYErv{D7jt~GM;7OP;!3&osSn=j-nyE#7e!!J@GLuUi1vX@l6sEPS_b9rq&Y3%V!Hl_jC5oPJ$>a*8NEfacP;7Gv z+oN;^RD81u>~qt#fUGrLv&~e^RB)dG81k2KlEX4U)rP_r4JnK@6@KH%*myz2zs^;`Y*Ob~+TVulNw zpMOcdL)heO__FHSMwpy;_RB zM8cxb6an|;PPq>0K?+9il1PgdxPjf&C6~jj(2_Hagl;9+fYK#*1-fK5nJI$Am_v#m z2{)LWw76NgYwW9X*fsW*{ZhD&uK(tPeqM7z=`*f72-h8i>*TMxPEbR)kU*{a*7d=N z8>*w(EgOPm{<%!iPT)rMbS7a!!-im2U_%fZz9ulPS3~0jzuL`m80CYU!P&UOe#Dl< zTa27uPPD@ybr_^>TV&*NMjMNcmt&bs3a&Kr)>4yA_?TOE(z2~W8@wB);zCzl_jIWa zm6C=`hw6o%+dq=)h>Nolk!&l4vs41=w_kQQdEIP9IiU;Mjtkl@BUUGQ4w@{)-LXXu z*ApIYnoU09C(hAk<@Hh&0`my;9f7`vk#X{%_HUjuQMqhUPQTu0rn69{hNi$!<7uip zx5cTLO?_Psqhx3!l0`f2R73FBdIDJ^XQWLeb)HRQ+DXYwrL1h;$}`CUx%1I6@k~04 znwem1N+(YnmJJr2yerVjSq*FQluZ}PrV35dkNRmUUD&0vjzZQ^N<(#2^ezu`hH(GP z9de!LElPqnNZ>&4i9>uP0L( z6#N0@80Z}XJ@!ojd2g?8s<|mhZ7wuRm>ohsk=U%`eg=&cx@X!%2OLn2!_;vy^(|pa zI}!BDYG)m(O{QiGQ*+*u!*1r?w@RiSG5ds{)wG$;XC&>O> zSE?F{3ha4r%VD=kNGy8N57tD28@6rc^H6gVXeSA6zK6CzpndyYIqa5yi7hzgho;S+ zHA_dVEH!f(;GF{ADd62d5|RI%Mm4k$XpLI+1JBD8Ar#RMvgmi)xfj>SR2kdU7q}8L z@}_Ph+}Zaw23z5^i;`j`;C3r%nK`DPQIZV}SZC2i?c6(NoK@PKy|Huj9KXrm3)!6u zJ9rY5I*%39!D*wi0udE z{vexR3k#n}5(=utA{l`+mTjuY*3x)~xkOZ9y+|YmlugLmO~~5pqUa+8Gz=iQm1U|_ z3+aN5{KvSPuo@}|_~LQd>0P*!0*e%SD{P4X2X-&8du3L)n679EqNY`xr7^E3Fp>U8 zTDMl5r2b$bE733pl&v_uRc1A9F?ar9(WSsxCNP%&Q?7T$4(JAlW$gQKWt-OYgM*x6 z7nGNsu0T$hM#>pToepst(5E4KMJ)O#2j*j3fc}vGORjgivp~iAk2r8w0GB<+`z{4c z=$Y6<7+}G*4}g6De4NLkKPJQ{TtrTTI1$9cPYCL9PFPRyGZk2Ahg%u#4A9O1?Wde> zq)H9_%tZqb#v>N_x%>YY{H$D033%>uG`Fst1J*fU{gTrq$m%l1vZ=MC(0tHrA}a|B zX);zD@gzq+v3W*5Z{|qUJjww!{wW@@GX?XJqLE_Jr%3}wXhdwgOX64jyj)Ku&*`es zXG%K|zT=%;?Y#eTdZ;aEl9=CVT0k&^Wz(Ey{D}1z$ilOZ zAD`nFDxpPXO_|2SfV=?63xH(5aT#IASw*Or>ETG4Z&vYczXDh}zjq^-yQ!M~wv4pe zMfx3oM)u~V)qJNP-jYnttMxm9x0CRG@8VsfhTwXK*#pF^igoZ^en_QB$(zl00d5!I z5^K=y))Efxa~NlhGsxc{$m#pg~b!@rs4g$U^*khW`0+~ zjANozL-272U(2t3jfjocnzrzFcp-;3WPFf8e{k@luk%S-Ag(Rn%F$=&korDXQsPw_ zjr-j-pUuZ0Kv8eTq*NV{4G7YdQZTuSH(6=%085a-x4G zG6}8eFZ@5=kOV$X5>ikE#xntcAbAT6|4Q^^ZMm+7wz)BLtrIPv33`9yXQf((UD3B) z_umT)v~=1G=V;U3;WJSxFhLPZtak8iJXB`+E^PeWwGmT8@3}UHRoHkRHn0a49Aw(! zB0Kv#_!cs56q zSD{Uhr|_6{@%i2^JVv@`|5J*nfVJ4T7;%w6l?;5y$279&M`f3-q}zm>wwD#UOOC@) zRdI~%a3Vp7>gF@hgfPylp`9+Hv3}Gu6;G diff --git a/regression/two-versions/ofer-example1/old.c b/regression/two-versions/ofer-example1/old.c deleted file mode 100644 index ec76ef8c0..000000000 --- a/regression/two-versions/ofer-example1/old.c +++ /dev/null @@ -1,18 +0,0 @@ -_Bool some_function(); - -void f(int x, int y) -{ - while(some_function()) - { - x++; - y++; - } - - assert(x==y); -} - -void main() -{ - int x,y; - f(x, y); -} diff --git a/regression/two-versions/ofer-example1/old.o b/regression/two-versions/ofer-example1/old.o deleted file mode 100644 index c2d19b0ba02c870c4edd9481350507536ffcb581..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4931 zcmaJ^Yj_mZ89ry$S%Er)pcJsJrVP@mMA*ayObHl9&{jGi3)G_Ru$S3nba!^$3nV~` zT5GSUsa7qe4L3yvsc0{js#TjP1PJ$QfYJm=_-B9ghfnEa-*3+BWRYjsAN%Z{Ip_V} z_q&~MR^B~hmUIgnl(!mfGZ9IvB<}<}y z+%!^|c(h<;Y<4wc`mm&xGTES=>eN`#N);@-I+}|&Sp_p*DCW%Sd?9DqjrnQVS(&nI zlMP-cPXaIPL$1b?nOr(rU_+&GK3HXv(&x0`DO3 zt{+^q%X7E_8TX}DskG?%5(d6Jni4Y}%@L<;-ZpuoA3a?T|YH9WtB3R6%0wVbza> zn@mnw+$7vJ?ln2&8vEJ-DO5w@zv+;l*PK-Poa+w3b%)?O`RlF|H05Rzs8`;yAsBX3 zbqu?CV{m|fE>pASO887%%Z;?Z2ALI6rddl&<+{7I@xp36d~@8t#YWA z@DOP><*1)HN1K&5N?8cZqtJI0`szl-$%Fd8dCo-VvPC=nM!lKNLYbyag`xU0)OT)+ zQ!|_Ph8#l6&_^bVcHHTP;IDTDve8^LZKA02Y&z3VNoFc#W%E{^Ne;-JkB*IJ(pmJ( z1Y=X%c{*y@VA0My0_~htu?A1s454hA&@|(ipQcj4E|ql*vW`(1YNI1}d6+YW`)BQx zYdmjJNzCjlbGvOe7mV_{=!Dxl$?bQ0K=%l8-+WUJg-dR~=eQrmq}6OD40L-VnaZHy z_p8T2?>OkOZwbhIyM0T|EkU~Gl-a`U5b}w{W}omg7){~!Oq;j?`_&ULb%IQNTbR;M z2K}nqSx0G;sX4;b+_&YBTY2}bmg$a|bJEXh+DvD1Ek-75=Aw4OaLS`!Jqi0K$^JZ7 zs-{E)_WXC`kb6l;EON>Z)gm)EFN8QZiMxf+Y+ zO~XdIv+r*THp6QdCDls6?N-t)3{+`3%vv?~0CI!k6ePkmlkDXVk6zl3j#8@0Vud?xSIN3MwqyoEFRBd)~fw|=5+-o(f@Gs zwz89SKUkp+z=CTJ0DA!V1dm02Oo&gqh@1v-B8Y{b5Y!W#u%6_-Wmsv4+W=ZG(0YOPQ%(`7 z(3GFKXaK@^#KJ#!|NnxYm20U0&s~n@_LXzMItQ#@atea1Emtj@u9g%oA6z!!RRo1H z8K;kYild&`{AfOJ=1A0hssT3PX&$yS1@n<&G{qv%kOqv<$kl}HehsjEe(yvrcTzX~?Eumm z7wLEWS=pPH*6>|^cuO-muh;Ja-Y&xXy^D8^roiP-1#9EG{g6tNk~f?02HbAI zCD!7$TSqvE=X%~I!)+8*c99zB*g`s6SLaSG&I_G!NlzS$y#KFy#pg~>l-UNC^+x{c z72Z20I&U7-vtM$h-AB@fvzPcDKWWs$Jz0AoYY%zpWzU|bY;v*Iqp%2U6Yuf^bEde` zi(nVPx&S66+2B|BsK~2)a=s-U!-T2_68eg=>*zIpQH9?En>k)^rsMs&U^*kh7JgU4 zjANqJ6!^G}uj4~sCt?%yhOPWvUdZ7M86Q;89~}J18+@`Ji0do1aojU>Nc(`RDe)?e zi~LQFDQvUIq_=epQ)mi;YF{ixvIXt3NwU0BAcVh ztI%S^Q+Uie_yTVi9;00J|EWcEz-nwf|$V31OVqlwB^{YR4C#zl&FtWHGi|sM1%Ua_Q0Ffk)13KyVLGyIi|_ zImQo7!Ia{7ZVRX1?L3LxETzjnY~Sy0Kfp1QTzUIJY+uVGhv>xpL_$*zyL{(j2x3C_ z5pG0|dK_qrSU=_&^445xZwa7wewdf#@^M%@;hk}}c4BL{YjZx?gre?p7oEhZQ=aup RPknCd>5?}a9gs3-{vT>Ap;Q0> diff --git a/regression/two-versions/ofer-example1/test.desc b/regression/two-versions/ofer-example1/test.desc deleted file mode 100644 index 27a3f37cc..000000000 --- a/regression/two-versions/ofer-example1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 1$ -^Properties failed: 0$ diff --git a/regression/two-versions/ofer-example2/new.c b/regression/two-versions/ofer-example2/new.c deleted file mode 100644 index 257d4ed68..000000000 --- a/regression/two-versions/ofer-example2/new.c +++ /dev/null @@ -1,11 +0,0 @@ -int g(); - -void f(unsigned l, int x, int y) { - while (l) { - l--; - x++; - y--; // note the --. It should fail it. - } - - assert(x == y); // this should fail. -} diff --git a/regression/two-versions/ofer-example2/new.o b/regression/two-versions/ofer-example2/new.o deleted file mode 100644 index 98317978c7a9348348737b149ee5f134d611cbd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4272 zcmaKvdvp}l9mnsTbrwP$V^9jQt)`69T8(5A7Z8aQreG~NQYO%1ZHL+2Su(mi6K8iJ z0jl-Ap3{DRb7wbeIn(_kCv)fC z&+q-azj^r1*>j~yY;?g{=GvLma;C56E1XQRlkvS$TPtJQMM+J2ZlP$oMJDZ*5?Ffq zGBs@$d%A2ETO)~+ri0gvT{gSASC0idS1t!NPkM&sk{JQp`f9=NHK$Q@CPSb?p^w zkJ=a0@Rzl)kImL&vOeqbx1$XS%n*hsPs!*kVpETuG{HyGZ(je!#%V0SE z)M~L88tf(cSBCO6r9rUA4oK5NHfz`yMeXuHv;C@7`@q{rysHH7>fUI3T7*Fqm~lep z$6t|e6EZm)w?9f|0rAM$RgwE4azBY2Uo~NZz%_1>6No#7s-8_a5XH-QrBn^?0C)$$ zyQZ$>*5+_MGVT-Wq{?E|mk{vDMoP@IRVXs!Q$mYt|0+i(qj0D{aD_TmL4vk}knYQgWRf&Qc3#+i^*33Z~tSc0vK#fdK80k*iay4w@#6yKTFi zXd@mX&88iWGA^*qDVUWk1oJTLJ52dMy~?~@FlW3jC#Wd|%-fGdF)I%HDE0`jN2mR}1cHKWWGV#knABsTY0lt3$o+beJ51{zk6 zLDVr4b*B*Z#S`(UstVRoS|sW&A?izS$%!!Y=e;0PIPN+TB{gs7J-^5Fx@_NaGiH#U z!|DmhKSA>6ho)-E-GZ&ZBPYTpEwQ^#MqzCfnB}^5p#W=6f_9Q<_f%;M1ntZ3$%(N2 zN^HTYD4M=#{(KX;veNcF@J@ku3cQ6A(p13uzqoJX;&jDrZnWF^E)J;&9QRj^GL z1opK-6?LtBD5*{cv2(JH?X&yNs@dt&vCfRIuj9C7#j$T}j&}!p&2#h~|1)E0&3o&- z+7rA_4bgl2PeJWq=?^iyiqhPQ(!77E@c@Cw0+M@T#M9Jp zV7YGPKY;HNn&Jw)cT5g02>8ThN%we-ERn@v9|!xm%v!r9AS)hh*A!ozoe@NR3Oy(N z-kOutgdLO~y7I8vi_^U_YbvC|A%hu3FiL{4{2y{#aJR#5=ul!S;L3tNtNS3Q1cl0_ z(>3IbZRLWgFKEN3fj$kRds4VL{@!Tl$YtJpVz>;aOkI`dIf#`IG6V?KnI zXMj2b)I%xbVW1x2g2iddCB?AtEe_5@#Ce1d)`0SXN+YO2pay~ZHm5+;Ysz;*7&sU` zSn|8!^Y{2!xs8hF+@-XzXPg7;99ZAyb}2Mtyv?uk<8F{2>Pz!DdR~FN3IR&>-ezT7TgfRpIHAfNm#?lp=GsYq06+Xe(%%=vB`!r=sD6z}~k-*^itNg6g z%&^UPEd1^0c`wTNN?1y zLXQbt@izarDjEsCWtzI1+S{9OgX-hDd>U?wJ!U$G*X;LvQFRwyBi8ywwS@lDjE&0? z+6>Yl6J2T!L@zkRfWrXLw5evhmpy!~u;TZdaP*7iu3@Y3 diff --git a/regression/two-versions/ofer-example2/old.c b/regression/two-versions/ofer-example2/old.c deleted file mode 100644 index a379b1e23..000000000 --- a/regression/two-versions/ofer-example2/old.c +++ /dev/null @@ -1,14 +0,0 @@ -int z; - -void f(unsigned l, int x, int y) -{ - z++; - while (l) { - l--; - x++; - y++; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example2/old.o b/regression/two-versions/ofer-example2/old.o deleted file mode 100644 index 468b00b4973a61c7e0f3caba0834c68475a7d1ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4383 zcmaKvYj_mZ8OP6=bs|v57!(29YRY)2)krpRxk;oj1#88TGJzIrJIwA*$ms4&oZW>4 z)1tjF1W5R1Kl0S)Y5(t>*~wa->3+zQnRCwX zy}#$36?e>-i0 zwwd!XR?+cXb}?i6Wo=H*W+OgSr>9F!uIRW8mY->Nigu=0^6iE~(RbV>h1;>SF6X#5 z8~LI<33_SoaW&KC`FX3zM(vi8^zP!_s%dKT=HIh$_I$INViwvxzepCD!WBcRm&>x< zYHvcrU)IPzHA_#(`piq-j5j1OLl~k!?Gbw2rEBELLY3OR(xhAQud*jjgPi9sf#LX5 zt;HT_u!rPd7RuL@I>8>jOPUh0S>4_^YKI4!?MGJa1#d6$E*HEjHpko3C=8;&j1e-g zd|93k^3NWABh|rnJ`Y^#;=!?h&zRBS- zYGlc+&f!{Q+^2h_%FD=?5b&Atl$aT-P-Mnug%($TAjc=9SRuo&fu{96vPiuK%DrsX zq0|%!_qq4wCX@$RFky#8w&;ng*fqWKaGDj{ecQ^KPL>U+y>f4&S7uX~DwMeP?`oV9 zVKN0}ah-72^{>jwP}mLoq+}C?|GEQlUJFv`e_VF}t~&tN$)69MpeZ+!fqLB+*3_iJ zRGq+XTw61&zwedYEFx;=y{ruz*4FeU*4Ds=n*`$uO}Sa<*RWAeqJ5AvIJ@~^oMOu| z?Urv27urE6br4G3ve@!{tBV;E<+SJJ;7Y6DEVo(XN8EAS9M>s!!MjOnE)?pfrz(A@ zQZyu5X%_a}`Zu|WbP3+1B-hE|EVY2|tOJQyH|3 zH*Sx}GlcnH{7`O+w561s(N|@*Yj+mS>b&fO*?na8%m_3~sHnXzCsP#_XB~-CvCZkU zvnIN&*_QLraEH_*Q11v?{Uw2%eJnQB!xErtMwuhTjv}8(Y|ha*fmRN;SKh`AG^8Gd zsG}t64k7Bx$7|xMDtM35B2jk=QD502C&SFYYqd<_xbt|N)V!Vd{4Ue$uzkzTnn8LF zsmCGzILV(InyM*x3%0&hPKHfdVt1d2!`dh?%XRHS0oI%V?F7;8iD>f#?W^y|$*}!O zY~IN@n!aG}Tobvn-1a^2PJ(w5y!qoYRKWVbxNqa)bj59MwAlF$6qcs6Ldb$sbnV@e zp}Tb9Dw#5f>AMmgmT%>4Tq6bcHD-Lhhet0^REtK2xBa~|U0-Uy!b68>(uxGF)LQ5IxW=k#mRC4XD`- zsM!Y=8#w}v1| zVK;PG$~xi7qCT_h0H@4_%1cjGk+Zgy3&z2qKc52n6pUV;HXZ`9TOhyvPq{g`7zMJM zeFwm%bl^Bed)XsvoEoY2#rI)+1cNIL*JL8D4ys1GIubkMMyiQa;dW zm^A>_09fDS6p30*`F;ok2SW)<{UCh)AwMlQQ(2w4kQR26Ghm$o>qnfNFKZ8}j!Oxh zLjf|0r5+^|Qn*1M+g5Tj!I=CQM|QJTt5C3gvaOZ!mQDOIPq|*vez0WanDG;`1rsb% z_VG}gC-_;pIl6$1Co4OUzY~L9kMqF`^+aoN?PVCB>9R&PL*GH@yOO7kr^t~#A^oR( zNNx`94&hI{f0`t$ll~sIu9B-NDN3DK4c-x8hi;#DTrmPNm3rVwDwwmvV zBUJ{KXf)md+z!J1GQ>T{U->`tcw;b;Q=5A}lzA7)9L=8RJL6>1C>6=u33)q7-mfBg znu62`Jo^GK%i&c&nw9ykIASp3RKAjSfw&7qwAm3a@^QvXd~%^HpT_t`$<|%f=GSmE zT1{C?(vZF{aDD7!sI_;wniJoM5;K0y2c(-AAjZp~g0qPuP9@fHd^gO%XKB$6ZvORr z4l*8d0C@!5GGEJ;SLonGedeqDZC>>8>51Qe@T+BP115?x-xw+|GLX-I!y8Z|%-F+$*F-0Mpg`A?7uFf B>=Xb1 diff --git a/regression/two-versions/ofer-example2/test.desc b/regression/two-versions/ofer-example2/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/ofer-example2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/ofer-example3/new.c b/regression/two-versions/ofer-example3/new.c deleted file mode 100644 index 92fcf17ee..000000000 --- a/regression/two-versions/ofer-example3/new.c +++ /dev/null @@ -1,10 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; // note the --. It should fail it. - } - - assert(x == y); // this should fail. -} diff --git a/regression/two-versions/ofer-example3/new.o b/regression/two-versions/ofer-example3/new.o deleted file mode 100644 index f36e642535cd0e4a3244f3af2c63a32260ed38cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4271 zcmaKveVi0^9mjvaS?74j={%GtE4>({Oiym_TtMU`n7|Aii8&0j9A4J$GgEtbb%n?CLp(#oacul&|7G=h)#p3E*}XMhm^DY5$c7i3C9a)KFJt-!zTC+Mos937T3Z;?-j~#j=N1CX4Vbh?O5)Xv zm#G;u=;^Xq;z>!IG#xB6cG;}zUOf@+kcWfFaU{X?naiwP(Pni!MmEQPlC%UNidyiB ze#SO)Ud9R>&t=0H)34}oayA?Ot~xzkbaH{?Hd%hA(+TWMQ1tDlLf|{@lEUrS*^qNw zn~m5gPljIFAGn(7@cg_Lu#x+uB)uE9Pc=<#{(`&jnZ3ZQrI>|I&kx8VQ@En6y7qFm zPwh)+_-4)QBeV2`tk1ms&3HpXGlU@u)E=%kd~}mMMW|ALK$?6Dek%LpG{|}G5*UuJ zS}pcNgZ(7`ib%euGzj*nz0%Z(%^D8GQM)|QY`<#N0q_nG?@GbDsyE)AW?>Lr%xEEV z%uDjELMCUU55~zX;68G8Rpdd4JV+wPR!tZuaO1biN!&YKRXrPbD2|u)is>5OA@B}? zclC&(TN}gmh`5ifl`1c*zJ!2}kEg)QScQNYpAcGH^CvkzAjJw9el0X@Tq}#vYogf8 zW*rJm5pbXUquhe@APXk!mdF-8aW%WPPaX=hpwqXltm$M~S?!bi5`8k8%2c7mb$?Og zl!$^UOpEJIw&Pu==!fe9Otz#l>X0khvB-zaGm_=$O)Qq0~x3{d}dQ! zDhkyJ?1s&CL;Cw((aqvU&AgYjVZ-LSzQpD_*l?p@T%{>D3H_S3%Sn_Eat3EN9f?zH zd8X6y&7nj)0;P^XshbyDzHjv~W0IWqyc}F<6`U0|YkrSAZinMKK@Yr}q~bzX-SSkW z4powdL@TYro?HGbw~#L3o0Q}_Ih>^u(7N-IyD6A<B7g34GF``=r{bjM?Y!srm|mCdTW;12 z!?UcOg8Wk?e_mv&rragi`de}`D$)|W>vSB}MuJ(cYZnTz<}_%hiFS9DHeb-b_>P>6 z%CE%cpNXUC3+K%<5i2Wf-vjRqcxS*{FfKz0tp9`iHX5fZy1CJ2=ev+tn$iv-3(r#9 zyE#L>RKnNZM3?1Tc^e(1!0utjm)7#?3*^(H5mBSRcg;}Q6;lCt+Ip_0t%7a3Ah0i= zQ<2ize#X?V|!udL;0u;SQPx72lqd+$F_>-fzKvu1tqpxP6z zTNh{@zv({PS^D3YTk>5P7qX1Cmv{%vFY&%aiL=HJ1*2si7s;j8PDu;O_}U+x@>JV2mPfIPT*$kSA7V7YGP z-h=OwnvxfI_kaT;WLqRU9y*S+~vqnE1%@@olfDs5r@vm}g*w$e;bO_ioxYE~WEk&?lm$ipf$EBFfA;*}+QjZd9q24e?AKOuMGvN^Y7)JoJ zcB@dZeX^^aVwO#MoTpqbu$LCC95cQ{reHorxIPhS^Id*HZmqT#<9n4Ih}}uyt|xf; zQaw>PPy#R|{>6$3LoO2flC-XB-pYKP-~q-#Hw${!4oo{mUAF!D6t z6~|ld`PD+c3%p&#dnV#tt|{vy-n0A}IqInOY(3u{$Eyq@)$w;Xpt}kBTm=0QfB8dZ z?&fgnrh2v^l027uGKy{Bd*Wo$xKx$52lDoiydPKPY0C2vtHeuk)Xqn-=lR|^VmQWB z{t4{`aW9Cdr^8-AS1$35ghP}*%k{C3amHWdO$bG1Y~m?nGoMoE$)_>Ep+@Q}YpcDc zY~CrfiKw&J87kg3~Ya^HLMT zF5{JmGn<5p&53Ose=}y_Uu$SiaP$M>SUXg$tdIJ>(2e9$i5q*q) zB}z@`i#PedR8ddxSEjMMv8}BUT~r_4<OV diff --git a/regression/two-versions/ofer-example3/old.c b/regression/two-versions/ofer-example3/old.c deleted file mode 100644 index 005e7b70d..000000000 --- a/regression/two-versions/ofer-example3/old.c +++ /dev/null @@ -1,11 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y++; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example3/old.o b/regression/two-versions/ofer-example3/old.o deleted file mode 100644 index ffa8fda70417e4a9c3d7b81e0e85a7cf15a6abf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4271 zcmaKveVi0^9mjvaS?74j={%GtE4>({Oiym_TtMU`n7|Aii8&0j9A4J$GgEtbb%n?CLp(#oacul&|7G=h)#p3E*}XMhm^DY5$c7i3C9a)KFJt-!zTC+Mos937T3Z;?-j~#j=N1CX4Vbh?O5)Xv zm#G;u=;^Xq;z>!IG#xB6cG;}zUOf@+kcWfFaU{X?naiwP(Pni!MmEQPlC%UNidyiB ze#SO)Ud9R>&t=0H)34}oayA?Ot~xzkbaH{?Hd%hA(+TWMQ1tDlLf|{@lEUrS*^qNw zn~m5gPljIFAGn(7@cg_Lu#x+uB)uE9Pc=<#{(`&jnZ3ZQrI>|I&kx8VQ@En6dbupy zr}iZ@e6wcuky&~|)@NS+X1pPx8Nv_+Y7f^NKDtSsB2=kAAWgmnKb8G)8st282@JKS8U%aPUTJE?W(^18s9hdtwqLdC0C)$8cctK6)f;b5voMG*X0(tw z<|X-7A(OMw2jgTGa349lD)Jyi9wd=tt0s&Sxba)%B<`KAs-BHI6vxYY#dHnt5O{~c zyLv>?t&QP&MBK;LN|l#YUqZmg$5UWttU|zyPY5lp`I8(UkYa@lzZRM{u9ZdTHBszk zvkrx(2)IxFQEowckOdQVOJs|lxSCztCl7^L(COP&)^xJ0toF%$i9VT4WvWo(y1%G# zN<_gFrp5KbT@zoClaa7b9h8zSbp6*Kj`La=O8@7&!*JbUxK93bIXp zJkx3U=1`&?fl^1H)Xj@6-?w_0F-cB)UJkCb3eF0fHNVFlx5IIrpa+XQp^t8$WxLdd-BSRAwBun%I70eg(n zpl!TydzCyxnE%;#<(8_p6p}OgYs_}-?!c^#%YK;MPiD`of@TR7wb$fis-oho<8dl> zINf&EM71?Lavln9Sv?N*j+51&6Uf;oV?#Yk0ctbK93gfj`9xxKPQ(eca_C-p8y%>u zo`9$mBhq`S;<74ykJKVjcM4HoctcJ`kw5o2nXco`Q*l!BcHZ-QOs~uKEjMe1 z;aOHsLH;R{KQA&>Q|=OM{Vh2e6={jxbvh1fBf%`!wF?DUa~ibMM7z66n=fczd`C`3 z3x15ZIT` zsYq$I#98B_RQrHhR?op3jBT%zQ|;&RxCO;5 zdc1(g!Dw|+X1tLV?!QJ;;4~_c9T)1RM3*^%jAwSarc8D@UACD)t)z%716!_R%}a!X z-j^g4tb19iQ;;UIZ8h0g8K2PiMU^v)7^gQXb3IO0d)?j zhttN_fLbL`oaS57YZd!C2jyX+tQx4HN4oY?srFJ2{L%vR~4kJ(V zU2(kSo?k8GyTIE;yk{cb<(jfS;yufsk)w`U&(`zZalFbfQXPMH1G<}_&qdH5@s~ej z=57wBZmMS+BFS^fC!^Q~z9&v5jZ0N|dmwKQ$@_6to~Aq>u}ZuoN9}wRd!Fx&BZgy4 z<)6@A5ch(JdOGX{bmbD?NH|34vs@qh7-#%N-h@zO#wMOJHuEWko_rbu9BQP#vbNf5 z%0{mJo~t?Wr%+g z>@r@7II~Hp*qqqL@i$`z{>27%I4ErAbC5z98_2o9mH1Y!{FDw((r51AZ}Y&%KUIAG zi%&gcKZ63Vk^-{6@_tR}jT9&en}r6i@d4>ZhHb{rBX5%iXk)Y+8t5~&^YEd>dr4S@ z{R^;v8L=%*c|Br}s4?zWKw}TUA&|CIfp{qK*K2_P1v{$ zSDQu6O1#CdYh%XSL#}$1ZW;IU&X9%CtFZK-#^D(Itx!;3i3lq3-$hD}4_z>#VhCgZ E1A`jO2mk;8 diff --git a/regression/two-versions/ofer-example3/test.desc b/regression/two-versions/ofer-example3/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/ofer-example3/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/ofer-example4/new.c b/regression/two-versions/ofer-example4/new.c deleted file mode 100644 index 8694ef3e6..000000000 --- a/regression/two-versions/ofer-example4/new.c +++ /dev/null @@ -1,10 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; - } - - assert(x == y); // this should pass. -} diff --git a/regression/two-versions/ofer-example4/new.o b/regression/two-versions/ofer-example4/new.o deleted file mode 100644 index f36e642535cd0e4a3244f3af2c63a32260ed38cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4271 zcmaKveVi0^9mjvaS?74j={%GtE4>({Oiym_TtMU`n7|Aii8&0j9A4J$GgEtbb%n?CLp(#oacul&|7G=h)#p3E*}XMhm^DY5$c7i3C9a)KFJt-!zTC+Mos937T3Z;?-j~#j=N1CX4Vbh?O5)Xv zm#G;u=;^Xq;z>!IG#xB6cG;}zUOf@+kcWfFaU{X?naiwP(Pni!MmEQPlC%UNidyiB ze#SO)Ud9R>&t=0H)34}oayA?Ot~xzkbaH{?Hd%hA(+TWMQ1tDlLf|{@lEUrS*^qNw zn~m5gPljIFAGn(7@cg_Lu#x+uB)uE9Pc=<#{(`&jnZ3ZQrI>|I&kx8VQ@En6y7qFm zPwh)+_-4)QBeV2`tk1ms&3HpXGlU@u)E=%kd~}mMMW|ALK$?6Dek%LpG{|}G5*UuJ zS}pcNgZ(7`ib%euGzj*nz0%Z(%^D8GQM)|QY`<#N0q_nG?@GbDsyE)AW?>Lr%xEEV z%uDjELMCUU55~zX;68G8Rpdd4JV+wPR!tZuaO1biN!&YKRXrPbD2|u)is>5OA@B}? zclC&(TN}gmh`5ifl`1c*zJ!2}kEg)QScQNYpAcGH^CvkzAjJw9el0X@Tq}#vYogf8 zW*rJm5pbXUquhe@APXk!mdF-8aW%WPPaX=hpwqXltm$M~S?!bi5`8k8%2c7mb$?Og zl!$^UOpEJIw&Pu==!fe9Otz#l>X0khvB-zaGm_=$O)Qq0~x3{d}dQ! zDhkyJ?1s&CL;Cw((aqvU&AgYjVZ-LSzQpD_*l?p@T%{>D3H_S3%Sn_Eat3EN9f?zH zd8X6y&7nj)0;P^XshbyDzHjv~W0IWqyc}F<6`U0|YkrSAZinMKK@Yr}q~bzX-SSkW z4powdL@TYro?HGbw~#L3o0Q}_Ih>^u(7N-IyD6A<B7g34GF``=r{bjM?Y!srm|mCdTW;12 z!?UcOg8Wk?e_mv&rragi`de}`D$)|W>vSB}MuJ(cYZnTz<}_%hiFS9DHeb-b_>P>6 z%CE%cpNXUC3+K%<5i2Wf-vjRqcxS*{FfKz0tp9`iHX5fZy1CJ2=ev+tn$iv-3(r#9 zyE#L>RKnNZM3?1Tc^e(1!0utjm)7#?3*^(H5mBSRcg;}Q6;lCt+Ip_0t%7a3Ah0i= zQ<2ize#X?V|!udL;0u;SQPx72lqd+$F_>-fzKvu1tqpxP6z zTNh{@zv({PS^D3YTk>5P7qX1Cmv{%vFY&%aiL=HJ1*2si7s;j8PDu;O_}U+x@>JV2mPfIPT*$kSA7V7YGP z-h=OwnvxfI_kaT;WLqRU9y*S+~vqnE1%@@olfDs5r@vm}g*w$e;bO_ioxYE~WEk&?lm$ipf$EBFfA;*}+QjZd9q24e?AKOuMGvN^Y7)JoJ zcB@dZeX^^aVwO#MoTpqbu$LCC95cQ{reHorxIPhS^Id*HZmqT#<9n4Ih}}uyt|xf; zQaw>PPy#R|{>6$3LoO2flC-XB-pYKP-~q-#Hw${!4oo{mUAF!D6t z6~|ld`PD+c3%p&#dnV#tt|{vy-n0A}IqInOY(3u{$Eyq@)$w;Xpt}kBTm=0QfB8dZ z?&fgnrh2v^l027uGKy{Bd*Wo$xKx$52lDoiydPKPY0C2vtHeuk)Xqn-=lR|^VmQWB z{t4{`aW9Cdr^8-AS1$35ghP}*%k{C3amHWdO$bG1Y~m?nGoMoE$)_>Ep+@Q}YpcDc zY~CrfiKw&J87kg3~Ya^HLMT zF5{JmGn<5p&53Ose=}y_Uu$SiaP$M>SUXg$tdIJ>(2e9$i5q*q) zB}z@`i#PedR8ddxSEjMMv8}BUT~r_4<OV diff --git a/regression/two-versions/ofer-example4/old.c b/regression/two-versions/ofer-example4/old.c deleted file mode 100644 index 53027a24f..000000000 --- a/regression/two-versions/ofer-example4/old.c +++ /dev/null @@ -1,11 +0,0 @@ -void f(unsigned l, int x, int y) -{ - while (l) { - l--; - x++; - y--; - } - - assert(x == y); -} - diff --git a/regression/two-versions/ofer-example4/old.o b/regression/two-versions/ofer-example4/old.o deleted file mode 100644 index 01c8f20704f6ae7d884fad5072a82e4a4868e2c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4271 zcmaKveVi0^9mjvaS?74j={%GtE4>({Oiym_TtMU`n7|Aii8&0j9A4J$GgEtbb%n?CLp(#oacul&|7G=h)#p3E*}XMhm^DY5$c7i3C9a)KFJt-!zTC+Mos937T3Z;?-j~#j=N1CX4Vbh?O5)Xv zm#G;u=;^Xq;z>!IG#xB6cG;}zUOf@+kcWfFaU{X?naiwP(Pni!MmEQPlC%UNidyiB ze#SO)Ud9R>&t=0H)34}oayA?Ot~xzkbaH{?Hd%hA(+TWMQ1tDlLf|{@lEUrS*^qNw zn~m5gPljIFAGn(7@cg_Lu#x+uB)uE9Pc=<#{(`&jnZ3ZQrI>|I&kx8VQ@En6dbupy zr}iZ@e6wcuky&~|)@NS+X1pPx8Nv_+Y7f^NKDtSsB2=kAAWgmnKb8G)8st282@JKS8U%aPUTJE?W(^18s9hdtwqLdC0C)$8cctK6)f;b5voMG*X0(tw z<|X-7A(OMw2jgTGa349lD)Jyi9wd=tt0s&Sxba)%B<`KAs-BHI6vxYY#dHnt5O{~c zyLv>?t&QP&MBK;LN|l#YUqZmg$5UWttU|zyPY5lp`I8(UkYa@lzZRM{u9ZdTHBszk zvkrx(2)IxFQEowckOdQVOJs|lxSCztCl7^L(COP&)^xJ0toF%$i9VT4WvWo(y1%G# zN<_gFrp5KbT@zoClaa7b9h8zSbp6*Kj`La=O8@7&!*JbUxK93bIXp zJkx3U=1`&?fl^1H)Xj@6-?w_0F-cB)UJkCb3eF0fHNVFlx5IIrpa+XQp^t8$WxLdd-BSRAwBun%I70eg(n zpl!TydzCyxnE%;#<(8_p6p}OgYs_}-?!c^#%YK;MPiD`of@TR7wb$fis-oho<8dl> zINf&EM71?Lavln9Sv?N*j+51&6Uf;oV?#Yk0ctbK93gfj`9xxKPQ(eca_C-p8y%>u zo`9$mBhq`S;<74ykJKVjcM4HoctcJ`kw5o2nXco`Q*l!BcHZ-QOs~uKEjMe1 z;aOHsLH;R{KQA&>Q|=OM{Vh2e6={jxbvh1fBf%`!wF?DUa~ibMM7z66n=fczd`C`3 z3x15ZIT` zsYq$I#98B_RQrHhR?op3jBT%zQ|;&RxCO;5 zdc1(g!Dw|+X1tLV?!QJ;;4~_c9T)1RM3*^%jAwSarc8D@UACD)t)z%716!_R%}a!X z-j^g4tb19iQ;;UIZ8h0g8K2PiMU^v)7^gQXb3IO0d)?j zhttN_fLbL`oaS57YZd!C2jyX+tQx4HN4oY?srFJ2{L%vR~4kJ(V zU2(kSo?k8GyTIE;yk{cb<(jfS;yufsk)w`U&(`zZalFbfQXPMH1G<}_&qdH5@s~ej z=57wBZmMS+BFS^fC!^Q~z9&v5jZ0N|dmwKQ$@_6to~Aq>u}ZuoN9}wRd!Fx&BZgy4 z<)6@A5ch(JdOGX{bmbD?NH|34vs@qh7-#%N-h@zO#wMOJHuEWko_rbu9BQP#vbNf5 z%0{mJo~t?Wr%+g z>@r@7II~Hp*qqqL@i$`z{>27%I4ErAbC5z98_2o9mH1Y!{FDw((r51AZ}Y&%KUIAG zi%&gcKZ63Vk^-{6@_tR}jT9&en}r6i@d4>ZhHb{rBX5%iXk)Y+8t5~&^YEd>dr4S@ z{R^;v8L=%*c|Br}s4?zWFb;bF4uQ0#3dBQ+zg`P`0X1SH=M@Rap*K)rZ~z;B9nr_= zSEAH}zIc=WOBMA5e`OlG8{66%(M9#qT|Uh=L64cqVVV7gFRbpuGVWUcr&>fAYQn~4 zxY{ggR^lywT^lpr9&**Abj!G(cZMvCUWKIxH4ewvZ-s*TN<>hJ|1MH;eCUD^6+;;N EAA}&x2><{9 diff --git a/regression/two-versions/ofer-example4/test.desc b/regression/two-versions/ofer-example4/test.desc deleted file mode 100644 index 27a3f37cc..000000000 --- a/regression/two-versions/ofer-example4/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 1$ -^Properties failed: 0$ diff --git a/regression/two-versions/ofer-example5/new.c b/regression/two-versions/ofer-example5/new.c deleted file mode 100644 index 831e18f7a..000000000 --- a/regression/two-versions/ofer-example5/new.c +++ /dev/null @@ -1,11 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - int x = 0; - while (x == 0) { - x = 1; - } - assert(parameter==1); - assert(glob==2); -} diff --git a/regression/two-versions/ofer-example5/new.o b/regression/two-versions/ofer-example5/new.o deleted file mode 100644 index 1023eb15589eb2786774e05ca5f44bbc2fd8a816..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4400 zcmai%dwdkt702(Lbt0h-F(5D7YRVw3Es<>E0wR&Z2-b=tWdYk-nau7^$ms4&n4N_L zvGi$ct7v=_OKGV7mpVjmRjBl}l}_BEwmu!rxFriN@*zdw%J>4Ii?kyrb{+fTd^f_KgKcz>FNLv%1B zh0Ref$hQcaoQ*sXXS0a&$Xk)g12B1jOpcCR7$b0FUzC$LcRH#%Hs)X)FXxsrRlI}X z9R%;%+LBY9!*$5GkM>BFhfyqH;A3McF|%gTXX%d%71#Y)j!#JOh8({hN*j7)k$R1k zd%2uVsVNff6MvGMQ6A*MxLp$Yq9?9p*Z0bUY36r$mYFl`94o86a&MwnW>c9eM2!Ed z8Yd!5rl2ft5aF8evYZT!ee!^mY^LMC;ZR)Ef>ipS&>ceP4k2{%r@|m;%8lfpUjONh zHK{OF$FUnX)eP$Q+>(>Si5dkrXTgU}HNA;VHSpmxf^m(eOceSWx5`Ph4+;in6A#A; zTb}JOJ!7!Y4nx#oh`OoG^gOehr61)Xh&; z`cS24$h6WT{JHrrax-ZOo}?tl&SNjNfR-JXolViOy3kJOfOg=3cF4%pDN%r?3g>Rw zCMR2nhm&SgkHk3__+}T4N*0271pXbN{GS#vZxzhxugFPi3IX%hqjAhiz%Ioe1@M_VWMqb}8kh4$3rg~Tcbj>JpgxR4K6N$|^9%sojGtkZG)!k-Pfn_L7qg#)&p;6V~IZ(JnQmAn?v{Ik+(p*#b)yPE^?vX$|%XuusUW*&Txn z=xo)LrDE^&;HW3l_tetuRXeFG+o(mf>9V>VySK}%!OMhG2TK>h@CBpvH@PLa?BO>w z_-q+M>FG03gvx%P`hj{llm04Ds{{&JDIlxZ*ElE-6J^yv6{Qg98RQIrGXT!l zIh{hCrhJ176uiSA!cyN1zkiFLm0PG>&RtFmJIFb(&Vlu9PJx%Thg934%+I5+n8Z?# z5DIlSN*~?E7MR7N<&i53DDl~(@9>o4`qt8tnP=(ml6nlPsDejB0pH{2<(BB4Nq@hx z0_i#_SoJ6`UoH(D1f2p?GTq5YUPvrM;$u9Mew-t4t3&z|{2y{la7Bm!;`x(g;W17j zT+J_3qecJBTjqm6UI6j}kgO+Egi+!tqGEDJ5NV2diuc8}x#}so4;`V8dgjxEq-R3X zv;1i}no6GGJL7oET`%hBJHgvYydQ+Tt2AXz$h()6)v+~vR~)G_k3^3(P#XO&q2oHx{vlM8+c{u z3z!lH)Xn4@>Z6wGgTKfdQ4cKr5+C2n(l7HVfz0(>Q#Nz$9j@lZZ&Hb+w}kOQvEUU= zc0oxHE-{HQ4)|7R`FRy$TWD$+nF0-zUxbv|nzD^3QV|7pruB%#jqk*{%K7nXhtOK*ghe#bFjXv&)rBZ}W!q2agr`45>9>DMA5pzc|4 zsC|J~$%WtJsQwTg6<$;Jyu)YXD#84qDes1+C(zL$+$iJ9F);Q=aSHm%w%|?e;G6#f D`UdtQ diff --git a/regression/two-versions/ofer-example5/old.c b/regression/two-versions/ofer-example5/old.c deleted file mode 100644 index db9246507..000000000 --- a/regression/two-versions/ofer-example5/old.c +++ /dev/null @@ -1,7 +0,0 @@ -int glob; - -void my_f(int parameter) -{ - assert(parameter==1); - assert(glob==2); -} diff --git a/regression/two-versions/ofer-example5/old.o b/regression/two-versions/ofer-example5/old.o deleted file mode 100644 index f5f5632c5561f759d35be10f6bf337d97b22ddbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4013 zcmai%ZFC$}8OQIP<%XuRgtSyrREV7xMG4I&Y*R`DCS!|88EUsJzQJ^McajXu&MdRL zNz+zAKvCqSp(3@Q6R~?(Af-XEYyj zlG!`I=l?wSdE3YC?p~_QW7A4@uWRMA{Y-zA57_x~AL9p=_BO_}_Y^hfxuvq{mYK3& zN#W7!SExCoJXo|?;(0~BY1sIfu`9Zxvw9*pA*O-I@k)Z}UHzt0u~^-%>8iWX-*PqAbQ-w?M;o1D!$9A~qH`*5tt8#z?wzixfi)e8e6AV&S5uiuba~Edlfg28#M}E-hvO?>xL8C>)^vDCF5Exd5hH7vQwncJ}4NR-Eu5W z*g&q&^o^-PI|fn5AnMjNrth1BEHhtZJtwlSb3)+JV+9Qyw9Z`T5O6P9h zB~tCg!%ee=$K#v}e6veNH4DK!4*!l*{x6D{cSz>qpNbSUg@Ad-i8y98VDHDC0QLlx zLFequoe}vi>HeqQ5p9uLO3AxMYTS0MwPm9=FGt|^2)W%Afx4wc?PnsDu1f4a87HyF zUTftIbX%jx@z8K5)RT~RlDz)3L@qfUo9bZ+(3nXsm1d_?Ocb{CRGdN6!SpIvm_QTi zDVRD%rtX%eK69onuBw9PC@nH|k2Lk!pNmwO`S)%SbRGAciL+X;3Z6e`cty)M-MkT` z=Y)C&_Ro<0WudED@;=Gde<@O7lUCS$XXCII3e0p}t5kwFXF)qlwEH94a!LE#uSF_s zzY1G^E{>+JTDHtUuB@|s54>~WodZv#R{)baPlN5&98F7wjj6qkm&Ktm92&!+l}$OS za{aH|w=i8@Ir3L^T7@F2PfM;Qgx>hT%Yw3t>7$9F>6--$Bc{YY&oW;a;?V;XoWL8} zX0B$VwRjtm%41yJ#6Xv_49(NwmDlI@X$#7T!}7^q+@VIxk5#u^lzVRo#X##%0qo zDkNe;-GL(8fg-ckWO@iR1CZY5rleBqpqQF{?{f4xEon>q!>2^h>Jbu~rR~!-wq!nn zeH!f3g0(%;gsXEpwPZoQyEwR`1@wQqu)F3>YNs}e2>o?J-Ho?*3)bvr!*zjWJTQuq z@#vpKdoZQpH#8KPk5IaLSLrCHRDb{r$*)QdT*Lan(vQi1ALkcDJLUMr%TZz3Uj*nPKwsk&bfK+RZI{y3 zK{YUirJvwHp;(^eTLoIEeqB$+%>`TSQyl5bR+^=f<&$SCDS_Giuk*C)m90lBro%Gd z;8DUpO&~<=n|wmFNAn`{t?CJ6?|dzZQ&02B%hjS2xYhu+U7D@PWvHHn>SuU1^KFhe zJ{!`X<9`wDL93VX#PjFL!ZVyg`YgXxiBW$=iEk3ocJU(L8^;^){HQbU1#d6$z7z7U(vmNQyaz~G z1AB?@iz8L{ifEJF2i!iweK*9t%-{MS>vLY|dbt`TjVYwE0B#_QT$O zviH5no|fDavYtfd6Ko3~je`ZtNcH>7C}5+2acZ_K@d~mw@hWd(Y+U_5A%jfSXCi}x zvHAo4cU2}Rmfhy4H8*#5HluR&Sw)|wV0qBUIXK3)@>S7k%JrwX{x`LP{BFU)esudh zN+R(?KDU!)UgI65!9o@*9(|?SP&*2=ooH0HTrvuqtwg47a`k@~ diff --git a/regression/two-versions/ofer-example5/test.desc b/regression/two-versions/ofer-example5/test.desc deleted file mode 100644 index c17a01622..000000000 --- a/regression/two-versions/ofer-example5/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/pointer1/new.c b/regression/two-versions/pointer1/new.c deleted file mode 100644 index 3d4684abb..000000000 --- a/regression/two-versions/pointer1/new.c +++ /dev/null @@ -1,18 +0,0 @@ -void my_f(int *parameter) -{ - parameter[10]=1; -} - -struct S -{ - struct S *n; - int payload; -}; - -int i; - -void my_g(struct S *p) -{ - i++; - p->n->n->payload=1; -} diff --git a/regression/two-versions/pointer1/new.o b/regression/two-versions/pointer1/new.o deleted file mode 100644 index 8b75edbffeb0570e10989ef1884bb549cbc9e93b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4575 zcmaJ^e{>Yp6@G8l34uCIQmr9M+t8uXN}<`r5TpVPqhKoxl&Q2-+hKNhl8o-oF8c!# z43%ot+7>ZLtF>5);4h#QY>QUFiZu!W0tuAgP#T1Uoc0|1SC8j#PW#Gnq4~vn5l5^r&#~p8_qb*L>j%IUddweFFcHHL7 zRP3yXJFd-!uahT&m-ZG{qfNUP!NN7 z(rt$0_*cy05EvYSeYsq6ojB7&6#)4R(62d13@1d%?Q1v7VZ=?ZY|6P&PqzxTqts!8 z>WC|NCyL-ucADRTA3^} zz95KJzat0dpLjwbBe&6x>XKS3Gpze91p_GtMp;m8->r) z_sX>ewv^G0T}5uYc5BuwPU|kX-9>I|1)$pm#oJ$z!}WQJx19*0*yOa@F%z}fY>FpQ zo(I(vpm&12o*^J-o(jxtKjWz>DR&67BS;g8-ElI=pcO~2OW5dFgX&3`I!UI!DNKFq zbV<-9^44*a$kd&})U4O!u%GC&pOGmXcb*Qiny?eebeow>*=fs-nO;r~s;6Q9G}+gE zS2bmhz@EEP4*NA(Vsp*}!P>Y_mh0M?47@o5v@?YE?E>07fo8lRhy7|Rv3X~M(Db`z z%`%ZIOYL+LcxQok7I@zo8KwKG|A(h-v{_g5{=25wi4<;=rp$+tYxD{Um#sULveH(< zMxV&A?=s_i9lWrBd$%CsHxXvYbPgZwX|6`BjBUC&#=hUdnRZ!Hofrb-G&y#f-F;q7 zw3$tlCl3KpR^jwLmob_oQkV#0jLq-!wt;pZX!ohCdO`X8^Xj15!8=2YZ7%u2LmcBl zE}CWfYM!;4*$*$M1}Hzv2_)&lV1}8@SO}h0#E3Lx+7b~|5WT&KO!T6PqvLT-Q)0sH zO1%<&*!#K6^TB?2msR9i}3dX-2bmCT&)Jt<;$gn zhlO(It{m!-Vq!~H87Yx1)ta&hXei+6izN+y?u`MH1Y6W!RK!zG$~L2@~2W3M*eG=#FEXiYioo0LA1a%DtH*PY2aas8pL!sanf( zmmpIweo|WeXrb`{;Z_23IbSiP57lFE(XAiz_u)XjraXvnkUkIbld{(_K@@A{5Ast* z1jJ2;!BensN@le`;Uvz(9O^vcBhY}_9jfpa${*p|ijGr@a!{ktvj){|fY>Ip>PLCR z_$dKA<^%kU|3j`rO3-{Rfd!Ym~CZZc~x%2{z{{D!z-l&{v5H9pp}{Gwb}=&ibzZRc>k4F za4|80<2{0g58Mk_>xZ?q5aM?v#5y1SIX)oQd2LbXf%WxdVXe?(9lunJEt*E$w(bMw zC173xCfnds!npK2!Q$ltg3!G5Jnsss?Zqz=UARzP)F@sULVD3hdWk}kqYA8Q+5EBnJtCELoogAlzT zF8|ip4a9CBO4PrY^a>wo{E@%OV|LnZ!pDu;%=i;OFO6fypLqkiquzeErfesZsOg)y zUim6VBV*cbuExc?v&4+oc$M)NpW^HMy-%Et!VrZd+0G%wSY&WLJ`3z+2ju!zKiEKQpIPXw;;>k$bDIFf1Be4RZ}|s6C?D>cQ`6PDFCML zLMv=F_T(80M`$C<*{?@@ylD;3;ZrV8f7_xX~Nit2pIeMXyX8V>Bg6X^yLsS zXw}>A*OVUL`Yf_8OzWXGTK8yRI?z`Q%?yS1UU(e&$X2xek9{Ts2Cd(JX7uL0@|I|Z g_2NKIfzg24Q|#1Xp|Rc$h6CympYdFeagoCR2M%5)`2YX_ diff --git a/regression/two-versions/pointer1/old.c b/regression/two-versions/pointer1/old.c deleted file mode 100644 index 6a6f09d46..000000000 --- a/regression/two-versions/pointer1/old.c +++ /dev/null @@ -1,15 +0,0 @@ -void my_f(int *parameter) -{ - parameter[10]=1; -} - -struct S -{ - struct S *n; - int payload; -}; - -void my_g(struct S *p) -{ - p->n->n->payload=1; -} diff --git a/regression/two-versions/pointer1/old.o b/regression/two-versions/pointer1/old.o deleted file mode 100644 index 55298d542d39817a36f927eeb84b9ad99c5de45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4453 zcmaKvdvp}l9mnsTb;6@gW2hRawt%CwRwLQOC5WJfDOd}RltD|i?J&DLn~dzvtotB@ zhKjXV+bSBd)%sQ_m4_&{SgUBo8ifD>0_6>*i4e$X&#`~?cn;^Z-{0KX%_<#p&hFXV zx%cyX-rw&x_uMpXhBS^1%{$Gmos2JJ`qRACNfufd-z_yZFs6MXsR_@`7c95Hq@7X} zOD~?MCd@)x)@G6Ak~nEPc+A*&)5?4Gh`&P~3L?jm2-Bx7w9-YJRc#nvAO07mM-ZZ@ zd9RpD*k;;GSOv#(*$~F`3sO$nW<$5BljB7vU2xnwE0<_-3U;DU%-MDMLe6oU^EY5; zP1G_Z)ikx)b8fwT`W&;8V&+@CT!Abyg)0VB zFP&t&)Xs>8Z&uH~Finrh`qcB^3^&9#Ll~k!?V)V9GC8}bCroA@@krZckv$OE1Ch1Eif(1jRU<=2c1Wegvh5HsY7`|>!pawz z@nxatB_GJ)2_&A7qL)I^uXM;FRq7~Ll1YbBNhHWu-Yduo5fx>^qzA({#Dt$)&KFHq(`SRBTr)tWTx~K3P@O8Cg{YpIjvv7i!AYLchB8aunr*e8Sn) z`@&SUN9%WDo3d(_{{4MhA~SH`!x0- zum^#yoifU}p-jF}nE%afazj~LO7R=JD$I86MFq1m&%0oD7nwb^44NiX)LxULv670@ z4uz?hau(T16V=vCr9Bkf0re2nJ49B0OCYBo2~G9j3Q(I-W(cvv$tMzS2gFOrmZQqP~5!D(qJI>$qAZ>SiJ8J8#I*AoFKFDN{IZJ{l%9V`scvo9Sik zoaH7>KRpN3qmX};gVrj}; z2)XS9wY_T+)Jr9Nbw{#R&dS*6AbEB>Grrfs%L}+q^Ws5!VTMcqe6%OH8n^Pc>Eams zeg|jTSxI%0h?J9Z>>RtJPtCNMsY#OtL6lxNedk$B9hoeeeUh=cz5X`n-wXYFRW@c` zY;K=Apmy-i2xIH3esCX0=PxD-On;0QtY-GZld1ugpWy`YTnNmwA2Gv(p7Y~|)s%0` z#8FlB_6lXi}Sd!{jvsN1}01Uvsl%_Nb=*4<1SVjxBIm2i;V)~N1{-s`?wUU&I)hfb;-&AH9 z{H`er#JOSqx!8hJF#II^@E=v!JO&ysT`Hx07HVPU;lVa4rZRVw;fegYL{qY$p~l~R zs;Vi-#?jCu&9eO!bw2B4Z8L#lPgklRMZUj^)#pYcU-DBl1#Twq)cFb_FN(}q*unKk zYus4Wq_@XuNAN)kwDrhYm~mEGe1^iPjnsP6qLGJk}KP>w_6?$SVb}v6tAzxg5$U6c7M`YG;A1B}ZjKew00>Wf) zyCW0)h1fE_vEn$jF9$^n{cJ$p2*gI2jk%x4jh_?XfdKFe{&%?%89^hs1U0b!OO7z! z&-JOl;`=yVZ9IK|pQu15Z7b~$%zg(u0rm;F<3X?=BKGot{V@NB+~|)Ff{pb@i2b0? zX3Kf+r3MZm=E7BDP4MlhrV6lE)TUj;1V>o-W@c$G1Be{{<*vWHgLG3*W)=99&|CYyGuVCL@w9?FYl61ke zj@*ALU_H%G$&KY^Ydllhfr~KS-}MylKUWIwrE9n1Re=T*Tnk9+hqPzmi08-=&j<7u z_-VP(?~}qGSbvcuJS+V1JU>&(7F{H5TMNKE1Lhep*~&mA3{~yQJzpW0XS3COdl;|P%a!}ucJQ_n@1=luzNV}Rcx(CNGDQaZ zRkJmGM;Na(o0Z=Nb^yAApz8wYdcNj!UP0>pmk=r)F9(umlFSk8Wxg{^CQXlJc{?F* zC&}ATmZvGN1guy2b8;}X%}@fv@ZC+(b_!pBY6 z%=iQEldfXMA9)kHq~3mqrfedSsGw`PUi&&nLu1+&uBOH7vBZo&@lnPbfr@|TAARny z6opgBC7U>0aTzi=iqAZJ(1o$K1(TEUX26;rY?J7*ej{G$3${Nuy~Sr(d3qlcB-X2L z;>u<^HC~_kHh-I=yV{5_`!D{g8G8ryF7g*X5*_q)j@|DD2F)aU*s#sKw2=xvleWMu z@A0uHn{jk~Q8(H6)*r1T=0) - { - assert(parameter==1); - assert(glob==2); - } -} diff --git a/regression/two-versions/simple1/new.o b/regression/two-versions/simple1/new.o deleted file mode 100644 index db3fcd32b3d4f66a44d3bbbce3ac7eea66fbc89f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4103 zcmai%ZFCe@8OQIP<)(mL(u5+RY8x4-R-2Mdx`a|fg%N5kogxcVt?e+oJ0a8V&TMCA zfdE=R~?(AeMXEYyj zlG!`I=l?wSdE19?U$|JA$;OxM)sB_Vu3`FCzShqBy^J4FIy)HC-c!_^>y&-d@tLwu zN#W7U#gN6}#ZuPMhiFdTrmz`4!Jk{5 z>R4;peswUR;Wuk%A6Td-gudX)U&cEUxFH=$LiTvQY0@^)C8abTRA%3dKa&UI6ck)% zH5|vUdKCwt-~idbDzvX9n4ugzAA2!HaVMeD9&aX_ffDSlZRmP5Sg4BxzHkU(_RxP+&f)WBWpPv$IH8wY#r|~ zc!$BeW5|*AmS5JenO642c@n3LZ)6D zipi&>cbOjv#d6<6#iAuW z4e2mdr?VTjH;n1`+=`ROjT%KaZ^4J{4TFj84e;TUl5w?`yiw|F+bL3L9~2DEZaf+% zY;CUB^o+4WI|@-pA?l`8rstV`EHg`FUAKTxnq_;v#oFKHw$o!fw%>>7rl`5lRd+mH z>qE7oA=AlD>Cer76dj}`c#>8eyMTA81$6Gd>~6}2wGQosE@(F{XtzMFc0~c2Bb~ct zmq>LI4>!%`9E)=<@Xan8wJZel82md%`9C*e-YS{%-V`Zn3IX%h<8jPdz}}BN4(xF% zgYIdW`4Rax>HeqQ79EjVO3B-X>fCm$b-q!bmqT!Sh}>Qfffh=M+FK%(u1Q>YB2HqD zz0S%T=(a{r!9~NZswW`t1bO{wiClClHr2xtpfQtNEX_`&m?&)V$vA^%0n@8!VFFdv zlQ4CXOx-R`edcsSTvY|nQCei`4r%JMzYwV~^Y7du=sNB=9cQ&@6&ZWf0|aA*XFmbc`n z%Jsi;&%$(d>k_l*uCe~VxQ47ckbAmlyI>1rGH`%DV8w)^Ng(= z4UR$VD8!DctW~5}o>!}CKOammwzJ{B3E|pt|E94aAa@jFT4t4u=CxYVgb(O=*=j>q zcsJ7_^*&}^tm}FuyJQ(Tv_49(i)eWl8(4czT6j-U(0}e{>AWOe&vw)mS8W@@7?(}U zsE~-Nx&uYF14U-7%JdMZ8IaXaj!C7~K`}LZ-{t6YTGE#I2TqEh)gvS}OWUXFY{`5C z`xMxx1nYRP1y@(-){;f}?!4fRX4C)a;y~S-)J|;_5&COY9l+ZIg0(u?a9v;-7mSi* zJoJv}45l>vhK3UJ5K2d1P(H#b)gZZau8y3y%tElg1r7Ba(C6T^pUqT&#Ii}TU;Ag# z8B8*XT*KA@=>y5ujJzL}H{9$4*z}-s1gH_9`m&kzKy8pHq?3efU>gDHBglr)I!G~) zGlm%jW)zq&(;YNw$*)KbT*Lan(htjjzsk>xPRj8Mm!rb6zW~q$fWF2l=tA3|+76{_ z0oA}1mVSf-g<_ecPwlBVxnQ4tlp}H3amP}4)y^`0Ihp;ZK=XZW)s+AI3`o;coG*Nb}c9`N=M@7a)drIvgyC>1RhAJLi=WGC53 z%wp`{_-RB!yo7Q|yv$n|>~9P0Ptqr&a)NQbo&Q~x<$)!;H5v)6-QBI|82XfwN7L8u zGjatSW3TWP(P?}PFZ92u6;xLn4z7XVJW4U~DxU#we$2Z9H}2JvkC1j~el66zD0oCG zN1z#{EA=wS9aUcsWj5=R-{AkKlSk8&lp#CyDX#=Ugn1a;!DnQ4hUR|4FR~?(Af-XEYyj zlG!`I=l?wSdE3YC?p~_QW7A4@uWRMA{Y-zA57_x~AL9p=_BO_}_Y^hfxuvq{mYK3& zN#W7!SExCoJXo|?;(0~BY1sIfu`9Zxvw9*pA*O-I@k)Z}UHzt0u~^-%>8iWX-*PqAbQ-w?M;o1D!$9A~qH`*5tt8#z?wzixfi)e8e6AV&S5uiuba~Edlfg28#M}E-hvO?>xL8C>)^vDCF5Exd5hH7vQwncJ}4NR-Eu5W z*g&q&^o^-PI|fn5AnMjNrth1BEHhtZJtwlSb3)+JV+9Qyw9Z`T5O6P9h zB~tCg!%ee=$K#v}e6veNH4DK!4*!l*{x6D{cSz>qpNbSUg@Ad-i8y98VDHDC0QLlx zLFequoe}vi>HeqQ5p9uLO3AxMYTS0MwPm9=FGt|^2)W%Afx4wc?PnsDu1f4a87HyF zUTftIbX%jx@z8K5)RT~RlDz)3L@qfUo9bZ+(3nXsm1d_?Ocb{CRGdN6!SpIvm_QTi zDVRD%rtX%eK69onuBw9PC@nH|k2Lk!pNmwO`S)%SbRGAciL+X;3Z6e`cty)M-MkT` z=Y)C&_Ro<0WudED@;=Gde<@O7lUCS$XXCII3e0p}t5kwFXF)qlwEH94a!LE#uSF_s zzY1G^E{>+JTDHtUuB@|s54>~WodZv#R{)baPlN5&98F7wjj6qkm&Ktm92&!+l}$OS za{aH|w=i8@Ir3L^T7@F2PfM;Qgx>hT%Yw3t>7$9F>6--$Bc{YY&oW;a;?V;XoWL8} zX0B$VwRjtm%41yJ#6Xv_49(NwmDlI@X$#7T!}7^q+@VIxk5#u^lzVRo#X##%0qo zDkNe;-GL(8fg-ckWO@iR1CZY5rleBqpqQF{?{f4xEon>q!>2^h>Jbu~rR~!-wq!nn zeH!f3g0(%;gsXEpwPZoQyEwR`1@wQqu)F3>YNs}e2>o?J-Ho?*3)bvr!*zjWJTQuq z@#vpKdoZQpH#8KPk5IaLSLrCHRDb{r$*)QdT*Lan(vQi1ALkcDJLUMr%TZz3Uj*nPKwsk&bfK+RZI{y3 zK{YUirJvwHp;(^eTLoIEeqB$+%>`TSQyl5bR+^=f<&$SCDS_Giuk*C)m90lBro%Gd z;8DUpO&~<=n|wmFNAn`{t?CJ6?|dzZQ&02B%hjS2xYhu+U7D@PWvHHn>SuU1^KFhe zJ{!`X<9`wDL93VX#PjFL!ZVyg`YgXxiBW$=iEk3ocJU(L8^;^){HQbU1#d6$z7z7U(vmNQyaz~G z1AB?@iz8L{ifEJF2i!iweK*9t%-{MS>vLY|dbt`TjVYwE0B#_QT$O zviH5no|fDavYtfd6Ko3~je`ZtNcH>7C}5+2acZ_K@d~mw@hWd(Y+U_5A%jfSXCi}x zvHAo4cU2}Rmfhy4H8*#5HluR&Sw)|wV0qBUIXK3)@>S7k%JrwX{x`LP{BFU)esudh zN+R(?KDU!)UgI65!9o@*9(|?SP&*2=ooH0HTrvuqtwg47a`k@~ diff --git a/regression/two-versions/simple1/test.desc b/regression/two-versions/simple1/test.desc deleted file mode 100644 index c17a01622..000000000 --- a/regression/two-versions/simple1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/simple2/new.c b/regression/two-versions/simple2/new.c deleted file mode 100644 index 52748f287..000000000 --- a/regression/two-versions/simple2/new.c +++ /dev/null @@ -1,6 +0,0 @@ -void my_f(int parameter) -{ - // should fail, stronger! - assert(parameter==100); -} - diff --git a/regression/two-versions/simple2/new.o b/regression/two-versions/simple2/new.o deleted file mode 100644 index 4616339a009044c4973fc79db94cd819a2915e45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3865 zcmaKveQ+FQ9mjvq9#3d0XGozoMTOWcC`xE9;o4FfAXzD5%TjwOzJPA`?l#HNyWL}V zuW3pZiYTI%5>P0JBDCU58OHjT(+gS2B3? z+GT3N2u3OvrJh#gO~b~|M3?o&XZ2Kgg3khx;Yy11-VJ7{YEjdhvpbVt8SxW@q*dRo zdIifUxdk(@U573w(ytt_OBT&~TV0;7+NHpDI!v!HWCvCusCrh1A9%Ji=--Z$ttH#B zsQFdC7#)iAWxYwo-Mt~Ev@#qft*FAx?P(iJt;v4-h@ zI+oJ#PM!3DK0U?t-Yb5SY)EK^G$akRXX&jU+`*SfRay=yi*Lcl^uZ(zO0F{q!|~Rr z#X)FrP{_YBlCPy(C40_!BoS#RbjsVyg~;cS!KAlDw;TC)?904HAKwD`kG@ zb$+Xq$!PB3B$+P#(g91^X0Xi=MiMt{dYsX%-B5mRU6HBGuF}K9(Bew3Jk-#KOO*NlHY? z6qd#H(p@+Ff@dOOA3v;Qx=yKV`tG<|!MMV;?4+ZnJOI~aj?GorbOP^Dk zc!)GDJ)UG-Xq)XD^(+MQIP5zv@_$*(yiGEf|B`1!QwW*2ok(KV9rk|g31Cl%GU%C~ zyFDh~AeJt*IxW-brEgrzP^P(}}4bm4FyC=@nA!Y~d4yR-8%_XqGU&$`&S2O+5us zr-Z1xrKr!GX-cZ9@Ht9Lh`L9L`s}ZHCd&MkFL4o$d(I?DEn8*R8!_C9<(W>=2-CBs zo`L)`LjJ1AR4sk4Wb41>nW#xCbnn?Dtc3zI9mn#0SaTM%vx0VCOj|8!pZh(}MD16h z)#s9E`r1{i4CKlt%X7gy2i`gG*0dEw0qg%@o`u2b$Z4MIvC0(`mX_{^khSN<*t@wP zW~qX=iB!e(%(8_E;?w<<`}`=2AE2H%ZfV<@nm2vRaBz;kFv>{#x1!obgvK7QEswr9 zrItsGfo02PZi;+@yf0nEno+J`x)+K1C&Ob=K~o?Wc6>nI~-IJ8PEnGJs>HVd_SsaxcP^$$U)0VpeBJD z&gV7)g7zxEFA z7X7hyDJd%I8aOp@9u+R<+9Rs%h-59H7D%D&V+^3Rdh1Gk-ayqUgv;s{hP0)A)Aub; zSkx~Pmli$FvW^>A4_D0+<-X41M14XaVB$B}ySzId1G#V3Pata-X=$8#f=yqlCJG#- ziX~QzOyn@sorbzkvV87a4E}p6qJNwHop*;FT>25uzau0($%HGPViy|Gvb~iob3Kq3 zfV==CeK*nwlYXn9VzGrg#gO01#*?~G;}ScLiXIm&?dciPwutl$dydCT!#1`zi8t(e zal_pU-d@2Qjd)jR>1QL}bL<5kO}-X-mhDU8)t82NP2C6RJ^}q+1pPky`TyCCJHzc* zG_vO-$t#6V=FszOf09hG*2MDmL*9NN?}b>Nmfjw*USzNEXzb3R?Q9~67_J-je=HLq zPJqZnza`}*)~2U^z}ko=)t3b~OxLTB#o^riA^VpqQx)56d)%DcdwSZ#0jbZecw*26 zBSxWwgY+Y|Ha?HOGj9vi|EX4y>m4|_0Uf`H!brWs7WPo?$83o|QqE(G6aShvH;w}> z{TS2!z|@lbXQ)u_)yM&_v3HVpgbb951~=}%#dfgAo5Sz2Oi+*XAG}(Kc1pYT&082O f|4C%|i%e)t&94h#i}c=~vfr`5!(UUpqKp3pMWkP* diff --git a/regression/two-versions/simple2/old.c b/regression/two-versions/simple2/old.c deleted file mode 100644 index 11f74f395..000000000 --- a/regression/two-versions/simple2/old.c +++ /dev/null @@ -1,8 +0,0 @@ -void my_f(int parameter) -{ - int bound; - - if(bound<10) - assert(parameter==100); -} - diff --git a/regression/two-versions/simple2/old.o b/regression/two-versions/simple2/old.o deleted file mode 100644 index beb1b8a48f3b5573f98937ed59f074102b9253fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4151 zcmaKvdvp}l9mnsT^+rM+Vo*x3t)`6F+7gpZTtFmJGJ>_@NZDX(6^Gf~2^rj-3A4L| zAg!&ow$>`9+M-nJ18P+$HMaKj^t3&voTkM9;aT2L8ia)Z?4O)^PW%1Mot>=ZjQhu) z-PwCTzxQJwxuau|GJ{PhIxAf}ms!J%7kRIfD|IuzPibpqOuwjTS!3i+|M2;&7W-M4^<;ym!**>u){*|PkAVk%Q zUfIvuX5PzMCC78w<%}6ub~$;QO?Xe6mnl2>lH)d6ezw~w+1XOrx0{M3-*HzKZ^Oy@ zyyM!e_Ej+pdg(j3mhJNVf>mM@_bN$xcKKe-H1#D*@40v3QnQ+37P~#aL>8IS6=Ry0 z&#}GQU_!?`Yhj=0FcQL8aK#_u4GGMUhNw_`f>A$dvzRSasoSs2x)~qUeQ_G(J$EGx z$6K`)`=G%-l7D3=UsvlTd-AX{CuFnw{c+SD4>a44tlAIWe&Ssvc~=j_+tVTqqQFd% zGC%pcxJAn3Y|4Q+nMK4SZ$}~zK;!`uIW;n&LE@&pA(DtYg{qD<9E{`TymF?BcM!aT z;9XN&cB^x^4jK2Uex>p-@+Aa(dKx8W)+&}*`ZH3CYyT$V6H@*{hF=Fw8~cS!y(Y@N zT+X4?lnM9Qzlv6r2U#$Ew?eiUiEG$(gJL|*O5MI~3jn4%4I(*Z)I{ zQzA^Jpe$xccir%oNQS~bcR)$DQut>ait}2KO8@7&LvY<8xK4aNbb_wlNCq19U)Wre z3R87DyKzg+xc1}>#|!N+lsXKhZdzgazSYOl%_8G@dAQOlI_qrK@&R|;F2{9BeeiCQnhS-x^_fZ^ zsuT^0R@N{%z1B%BsGPAdF#CNOYe!+NLxzD`9oD^yY||WS)G?dFnfs1UJ!vgq>B1GBAKeF*l{dQ#V%*9 zoiovG&91zMhC8MmgL=ov>f0r9;fdH(4@-c?jJimQok%`W*rMZc0v8q}*uLfF%pg6- zw3Cp3lH@NAP1V)ACENI;NQO;XVRxU3!`dh?%XRHy5!Rdn?G(}OiD*kC?W=zl$*}z@ zY{}_3nz3y0ViUQt&h|a0=M+%xt6htw&~&=`+7fT`iF|<QOsaz5Z_gSxs( z;vYILf=K|Eu41WGC#q!0bOrka*e3*Q%{L(Yg?3$a<=uHfjAzmJlsiy$le(>g8bkXX z(*|&RK(IzH6K)eM-2=vI$?*Rv+JYetyP?BsR)i}(V?pT?un0oS5uiqZT9-*b4Agpw zLOM#wdiG5~))8d=*(yjOP-h%-7MQcZe2ZdGr>l=h4ot&#!BXGm^80uA`=X6H?72%> zVOux{(m9YG<>YmtKdd<}<$E6G#1xi#j8MquT4U;R)@c=swoi6+Qo6I|@A8!EmF(4J zE6>v3<57A(9xCvCeqOXi!z2BJ$_eCZv##RQ<9zf|)zB7PyBDi34O(O_R2+qhPw-6o zha6seGNk{AkBPRR?@RCD`BNm}2~G}tl3%Ds%chvOt%rcT0OSQA+0&s$*bg=k73(mZ zNUOmHJ`~rxs#o73^noF2gwKqV&|U)4kNLAAS~dFlt~g$==SOXN7kImf_iV_!LRX&) zc@L0gb?iC5JC0OYGNQG1H*mWN_mdF!Q~u`1?9wg4HcgFhV<_`3k~x`e|(i#-W z+XH!fNZ#|2JY9VuWNqRvi*QU&W-svJIAXA#RQ}-%gE$N#r~b^u&v--nMLxUOSIA)d zqJKNHWs|f|HMl^ z{{G?hU%cuVdks^4MB7YiBlS0N%6-0tHzC(q`WJjena9$v^Vxy)m$Jj+gE6JYr#+z5XJ+#_$hPri(RmtA%Z-a#88zL6b8Fzp9BMxCD}H@D zOK%SiyiZpjBl}?2uS2`u=A-d;B_TOnUT|**AK;JH2H&N9Xebpozk^u*CURpCGh_a5 o`9gGG%vxQ2H#B$>`5z8=m)E_*8Sej1`ovhjJa}4*r(xv&2R}=#cK`qY diff --git a/regression/two-versions/simple2/test.desc b/regression/two-versions/simple2/test.desc deleted file mode 100644 index b5c767f39..000000000 --- a/regression/two-versions/simple2/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -CORE -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 0$ -^Properties failed: 1$ diff --git a/regression/two-versions/slam-example1/new.c b/regression/two-versions/slam-example1/new.c deleted file mode 100644 index 9c114dff4..000000000 --- a/regression/two-versions/slam-example1/new.c +++ /dev/null @@ -1,23 +0,0 @@ -int nondet_int(); - -int main() -{ - int Old = 0, New = 0; - char lock; - - do - { - lock = 1; - Old = New; - - if(nondet_int()) - { - lock = 0; - New++; - } - } - while (New != Old); - - assert(lock == 1); -} - diff --git a/regression/two-versions/slam-example1/old.c b/regression/two-versions/slam-example1/old.c deleted file mode 100644 index 9c114dff4..000000000 --- a/regression/two-versions/slam-example1/old.c +++ /dev/null @@ -1,23 +0,0 @@ -int nondet_int(); - -int main() -{ - int Old = 0, New = 0; - char lock; - - do - { - lock = 1; - Old = New; - - if(nondet_int()) - { - lock = 0; - New++; - } - } - while (New != Old); - - assert(lock == 1); -} - diff --git a/regression/two-versions/slam-example1/test.desc b/regression/two-versions/slam-example1/test.desc deleted file mode 100644 index 362e2770c..000000000 --- a/regression/two-versions/slam-example1/test.desc +++ /dev/null @@ -1,7 +0,0 @@ -KNOWNBUG -new.o -old.o -^EXIT=0$ -^SIGNAL=0$ -^Properties passed: 2$ -^Properties failed: 0$ diff --git a/regression/two-versions/struct1/new.c b/regression/two-versions/struct1/new.c deleted file mode 100644 index d40416971..000000000 --- a/regression/two-versions/struct1/new.c +++ /dev/null @@ -1,9 +0,0 @@ -struct my_S_new -{ - int a, b, c; -} x; - -void f() -{ - assert(x.a==x.b); -} diff --git a/regression/two-versions/struct1/new.o b/regression/two-versions/struct1/new.o deleted file mode 100644 index 6f058eaa7160f035e909c2d46b9746f72a194c05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4028 zcmaKvYjhn|701t+%cN=L8WJc?Q6cS66eVqL!fgsR)Z|(~tyAo6QG8A3-aAQ#&Yii; z%xxYNs8)PZ=mSKav7}OvgMWu$n!yTB5%a^bw52 z70)TUSwpj&tnQnR&1NvBUej$_2CIEXS(+)DmT%h4x|{7aeIx4^U8C9aUDNLI?!wM` z%d`zPbBDYbx>A4QO19f^^SaMw9h4HZHshe8X{q~Gt-OEvDy^ELdA*M7lSi5e#gt+j zee9qz8cX3jYhfShQe(2(dG&9i9SPhJj>I9mR;~Z&u-q=B)E$-<-;Q77hoTf%j@<*t z@vU0LAt*RR_OA);r{eX3-7qdK3E8ava1^!RfM&R5uMUHEn0VI;-gP6<{yU9D+bWe7%dv!kkI$vV%<7)c(w`72Zv2ZJosi-aa{MMJZQ3e})N7{P z%jHZ;O_6Z({w%kmJjjFj`z7*4jorX*8kH+)=J&dWp3}@6n^H#Q(b%ZWmM}$#xcP5N zl!!2yg0fg3!gb3Vaw0VL$s`v=h3Zy||#gGIF)O9H1q_xjXjA zi8kWlrrDAcQO*UvnVwe4LNHIjzZ3B9&USAgpMhiQ1~X^azS(UWF8l1H;_E%l`2j7h zQOkkgrLfp>ff~@lEOj-BvE*H}rq(ZeUZyUxtEL~|UAT!|H4yu`ve-@`w(Bi9LHh!+ zohPHjmTuz1dQU>{Nvf~Z-1O(mNHH9CR1M)rqr`FQRNz}qh`s}N@420U(1QGq?WOjXQK?} zjlAOyXimX!bvvg8DL4LruSm=M0! zVdM)a)>Qlf7(v#~L>7<73c9Q34Ln93`#Mw4Nrq*a1<&-D6zu%QWegqp0^a%@V-F1R zZrB;(qp=~*n!dS-8v=D8?Ma++jF!f4c~~={f8M;iSJ3>v8c)FFbjs3MI)xHoJQGCqwMp-!evQ9H|%9eTaa#HyQ?a)G@D_l5G_C zD$`k;E)b{zkcT!`q*5b8TvXkCpQG!h;-0|!r{&<`K@dEa^v+b-5}6G48L-dDthLyP z%vj%%iuZ}LUEqz!7SrF!zLBbv)Z9%}9=iRMGJ?}1GHdG3gu@F<4}dW!7!UtlZVP4x z_zew%Yy(2suXa9ij8l?A^5BIka?a4LV44it`vssc!0AUb>BoTF$OZE|{2y{#@UjXh z*1yYveH2)>kxzV>m(!fggBU&`bppHz@HX*G`gcme^+iRzc%S@r5#A)MZvBueELeAXeM~zFoe^SkEDj)Y3SXKNIp%Gd?uv- zl>bw13mys)P^>>o7PbcgeTHAEMvGQz8TvXPF9CT8NcOW(5hj)Ah>F1+5v3vJIX)KE z@v4XLF;w{&wa@1(q!&Wci+sCW9(rEj`=WS#j$3Z-`@q{ryq7}W)v5T)AumJ9>e$PC ze-x=S?vw}Y{lM)f+)#-7Ie-2CO!Y0nTu)tWM`&{y*=%4t_<<;!G?10;9e}+9Wbc)- zy;OWSWbNcba`>({uwgzPMGOX|(*IiHAdZ8GrZ4>h85?_*H!`@hi=aW4UW?=nUgg*L zzZH?87{Z(S7wSzN9r!^_dv?L4R}@V?Yhep}ov$hH#K&_sbM;?J5y{<*jq5RkV{u(3|Fc66Fl`tu2|y#SSff9wpHGn@_Q0&lURBW zp9ccf$Tp5QBwP+k0X4_|gReTqekGh#H*Dr`^R3X$>q#SawI{9ozGD+h@`N zDS@R|u2Qm^-(NIX>`6(S)J%NL*i~JXy=pAjA=iS)aU{mn&h@%gGFZ)?SuN3D3Hk^| z;)>^#+^nHlPFD9#$7VAaQ?JRJmceS@QI=*(rsbPN$o(FrL&A;)im(x$DlNWEst zy)YuK|rV)8M&HNtM&~uuZW0T5=JQ5p`*%GD*5jX!$ zi4qYeQ&1KQM7VBwLr#RoK6yk+w9@r2I2IMPAeH_nbjJ|7V+fu6sW1qs_^sriTL0;x znq-)&^VzLCYo_(PPRY*UMzw;IGvLF{nvvMf8u;)T!MH9JzfI_Cep61MeNZqsyX|Yh1Zu$K3^Y3EJb^!pLr1T`1B>eemg zK2$CmG97Od{@ngoxs|j8D@n;VEu5tm(6;xAyYV!m5AB35XfG~kuZ&!6uLNj`aPE#h za-xlRxM{ZJM3i%ZZ>Fb}vk=S^@b3ityR+TvFJ$1Dy1~pDwr}Q5!)2eHP<*|+xzMkr zN$gTs>$pJ8XJM8)n#5T0E?QITmpw022iaBA2k0)`!mb(!{ai(8rx4opmYkq{fzZyA zQ9{eN@L{bdq4gxyR%&kg^A+-PkdY&IzazI+LO^-Ge6%W-w$bNn)yY1JSdLOG@2P<9 z6%xPjwwy?oCEj}~N@CvZGjbYQxR$pZbn!{$6y%+PJoZI_{L-1o`U&fb-mdrxVRjb9 zL}DvWM;X*DJc$Kd4NRSesncZY%fgg;wkE1ngLTv^nOZ4K-S=xb5th?3w(@M0!Gck6 z+@Flm9sE?mP~&o^fDD+C9vt=%89V)OKjD-D6D~k)ot7GJUDU=v~xsT zU7@WJwEKTAC&G3vu{GzTXzH33D>S6ZfZ;meod@qccwcSIQbSNb;I4s(*cQ{l*E)N&}e2MQr2hc(;TJ3zBR6hYEfU}bjq=DV;=i9OKYfcS$)KYDYkY;tq#-g zNkz!>`!%tcit9pF=2@9inWeMhOm3()6Kv1XmNfh#QvMG!s0!bc^0W%6OIdR1gfgj& zBM2WTLRXVevvH%;Ju@79dY+nohQyR)&P_ik;(uc*z785tPu9CN?cr_ChrMpLZmO#9 z7R{oeWzl>o8>dk2Qz-X>uy9$D&;ff`(iWs!*zT%IEYD*YBt+ZPN+e=Z*^P?ajf!;E zri%n>0OX;~(^9E5m8`wZA;WY$`0L`JOd zNX2`_*)H(LVvFhTWbbg*NowdODh?ffQW?hSVVO1cWx|1lrTf7c5R8ZaF1H190{n)C z0k#35>{B}*ImRhDA$j0J6**_>$tMVfl59}td!FHvcdJKYu(9Y#p0pj`c&MaXEd3NYgV`6E zyCqb!mA@;uRbH9&w(<_7@uFbY7Cv>QPBanQ=*8eo&peVEdZ(awJ0kfsMe>=D{!{)> zxh;4gL_o3rELqqd1oRnxsTwU>sAcHufV>3cB_P?)LPeNPo+B!TaYU2`ljrzoRHv&R zyhl;tqtrH^pC-K!l3wK7<;tM*0^b+K>vh~pQ{M;PKH|L;@~%$BUk-U0QdY-a=KG^a zgd1^YS^=jF1?;;@L3C6*z0^vWhXwKvze>^Qc6hfW^7!KxhscCh`qsY z?qKQNd>-0Y218i-P2R4??o>DYl4Gb!#h>7*KXSzq|GP@TTd-~Vt*N{v!8VDd_wacj zP>pQkct67BpcGJZ>_7ObW9(PLNp-_!4maNl-MpSOvYEpa) Date: Wed, 28 Jun 2017 20:22:52 +0530 Subject: [PATCH 89/90] same as ps comp-kind branch --- README.md | 21 + doc/deltacheck_logo.ai | 4776 ------------ doc/deltacheck_logo_large.png | Bin 15178 -> 0 bytes doc/deltacheck_logo_small.png | Bin 5396 -> 0 bytes doc/deltacheck_logo_small_html.html | 9 - regression/Makefile | 2 +- .../interprocedural/contextsensitive1/main.c | 34 +- .../interprocedural/contextsensitive2/main.c | 56 +- .../interprocedural/contextsensitive3/main.c | 56 +- .../interprocedural/contextsensitive4/main.c | 58 +- .../interprocedural/contextsensitive5/main.c | 30 +- .../interprocedural/contextsensitive6/main.c | 24 +- regression/interprocedural/global1/main.c | 42 +- regression/interprocedural/global2/main.c | 44 +- regression/interprocedural/global5/main.c | 19 + regression/interprocedural/global5/test.desc | 6 + regression/interprocedural/sum1/main.c | 54 +- regression/invariants/ite1/main.c | 26 +- regression/invariants/unwind20/test.desc | 2 +- regression/kiki-modular/Makefile | 4 +- regression/kiki-modular/induction4/main.c | 32 +- regression/kiki-modular/induction5/main.c | 32 +- regression/kiki-modular/induction7/main.c | 46 +- regression/kiki-modular/induction8/main.c | 38 +- regression/kiki-modular/nested12/main.c | 36 +- regression/kiki/Makefile | 2 +- regression/kiki/induction4/main.c | 32 +- regression/kiki/induction5/main.c | 32 +- regression/kiki/induction6/main.c | 114 +- regression/kiki/induction7/main.c | 46 +- regression/kiki/induction8/main.c | 38 +- regression/kiki/nested12/main.c | 36 +- .../precond_contextsensitive1/main.c | 30 +- .../precond_contextsensitive2/main.c | 22 +- regression/spurious-check-abstract/Makefile | 4 +- regression/spurious-check-complete/Makefile | 4 +- regression/spurious-check-concrete/Makefile | 4 +- regression/termination/bubble_sort2/main.c | 20 + regression/termination/bubble_sort2/test.desc | 6 + .../termination/contextsensitive1/main.c | 34 +- .../termination/contextsensitive2/main.c | 56 +- .../termination/contextsensitive3/main.c | 56 +- .../termination/contextsensitive4/main.c | 58 +- .../termination/contextsensitive5/main.c | 30 +- .../termination/contextsensitive6/main.c | 24 +- regression/termination/float4/test.desc | 2 +- regression/termination/global1/main.c | 42 +- regression/termination/global2/main.c | 44 +- regression/termination/ite1/main.c | 26 +- regression/termination/pointer5/test.desc | 2 +- .../termination/precond_term4/test.desc | 2 +- regression/termination/running3/test.desc | 2 +- regression/termination/sum1/main.c | 54 +- scripts/cpplint.py | 6619 +++++++++++++++++ scripts/run_lint.sh | 97 + src/2ls/2ls_parse_options.cpp | 11 +- src/2ls/2ls_parse_options.h | 3 +- src/2ls/Makefile | 6 +- src/2ls/cover_goals_ext.cpp | 120 +- src/2ls/cover_goals_ext.h | 48 +- src/2ls/dynamic_cfg.cpp | 7 +- src/2ls/graphml_witness_ext.h | 4 +- src/2ls/instrument_goto.cpp | 3 +- src/2ls/show.h | 1 - src/2ls/summarizer_bw_cex.cpp | 100 - src/2ls/summarizer_bw_cex.h | 56 - src/2ls/summarizer_bw_cex_ai.cpp | 549 -- src/2ls/summarizer_bw_cex_ai.h | 76 - src/2ls/summarizer_bw_cex_all.h | 73 - src/2ls/summarizer_bw_cex_complete.cpp | 680 -- src/2ls/summarizer_bw_cex_complete.h | 81 - src/2ls/summarizer_bw_cex_concrete.cpp | 637 -- src/2ls/summarizer_bw_cex_concrete.h | 77 - src/2ls/summarizer_bw_cex_wp.cpp | 643 -- src/2ls/summarizer_bw_cex_wp.h | 76 - src/2ls/summary_checker_ai.cpp | 5 +- src/2ls/summary_checker_base.cpp | 547 +- src/2ls/summary_checker_base.h | 52 +- src/deltacheck/Makefile | 88 - src/deltacheck/analyzer.cpp | 423 -- src/deltacheck/analyzer.h | 26 - src/deltacheck/change_impact.cpp | 390 - src/deltacheck/change_impact.h | 73 - src/deltacheck/html_report.cpp | 106 - src/deltacheck/html_report.h | 27 - src/deltacheck/report_header.html | 183 - src/deltacheck/report_source_code.cpp | 297 - src/deltagit/Makefile | 36 - src/deltagit/deltagit_main.cpp | 38 - src/deltagit/revisions_report.cpp | 298 - src/domains/Makefile | 5 +- src/domains/disjunctive_analyzer.cpp | 331 +- src/domains/disjunctive_analyzer.h | 47 +- src/domains/domain_refinement.h | 39 + src/domains/domain_refinement_variables.cpp | 20 + src/domains/incremental_solver.cpp | 42 +- src/domains/incremental_solver.h | 42 +- src/domains/lexlinrank_solver_enumeration.cpp | 16 +- src/domains/lexlinrank_solver_enumeration.h | 6 +- src/domains/predabs_domain.h | 4 +- src/domains/ranking_solver_enumeration.cpp | 11 +- src/domains/ranking_solver_enumeration.h | 4 +- src/domains/simplify_bounds.cpp | 454 ++ src/domains/simplify_bounds.h | 32 + src/domains/simplify_bounds_class.h | 69 + src/domains/simplify_transformer.cpp | 259 + src/domains/simplify_transformer.h | 35 + src/domains/simplify_transformer_class.h | 61 + src/domains/ssa_analyzer.cpp | 422 +- src/domains/ssa_analyzer.h | 137 +- src/domains/strategy_solver_base.h | 7 +- src/domains/strategy_solver_binsearch.cpp | 13 +- src/domains/strategy_solver_binsearch.h | 2 +- src/domains/strategy_solver_binsearch2.cpp | 18 +- src/domains/strategy_solver_binsearch2.h | 13 +- src/domains/strategy_solver_binsearch3.cpp | 17 +- src/domains/strategy_solver_binsearch3.h | 9 +- src/domains/strategy_solver_enumeration.cpp | 18 +- src/domains/strategy_solver_enumeration.h | 8 +- src/domains/strategy_solver_equality.cpp | 7 +- src/domains/strategy_solver_equality.h | 2 +- src/domains/strategy_solver_predabs.cpp | 7 +- src/domains/strategy_solver_predabs.h | 5 +- src/domains/template_generator_base.cpp | 232 +- src/domains/template_generator_base.h | 18 +- src/domains/template_generator_summary.cpp | 6 + src/domains/tpolyhedra_domain.cpp | 101 +- src/domains/tpolyhedra_domain.h | 12 +- src/domains/util.cpp | 15 +- src/functions/Makefile | 19 - src/functions/get_function.cpp | 70 - src/functions/index.cpp | 245 - src/html/Makefile | 16 - src/solver/Makefile | 21 +- src/solver/summarizer_base.cpp | 156 - src/solver/summarizer_base.h | 24 - src/solver/summarizer_bw_cex.cpp | 274 + src/solver/summarizer_bw_cex.h | 80 + src/solver/summarizer_bw_cex_ai.cpp | 529 ++ src/solver/summarizer_bw_cex_ai.h | 84 + src/{2ls => solver}/summarizer_bw_cex_all.cpp | 16 +- src/solver/summarizer_bw_cex_all.h | 93 + src/solver/summarizer_bw_cex_complete.cpp | 735 ++ src/solver/summarizer_bw_cex_complete.h | 88 + src/solver/summarizer_bw_cex_concrete.cpp | 657 ++ src/solver/summarizer_bw_cex_concrete.h | 84 + src/solver/summarizer_bw_cex_wp.cpp | 768 ++ src/solver/summarizer_bw_cex_wp.h | 86 + src/solver/summary.cpp | 16 +- src/solver/summary.h | 42 +- src/solver/summary_db.h | 4 +- src/ssa/Makefile | 24 +- src/ssa/local_ssa.cpp | 297 +- src/ssa/local_ssa.h | 30 +- src/ssa/malloc_ssa.cpp | 2 - src/ssa/ssa_const_propagator.cpp | 344 +- src/ssa/ssa_const_propagator.h | 43 +- src/ssa/ssa_db.cpp | 11 +- src/ssa/ssa_db.h | 39 +- src/ssa/ssa_dependency_graph.cpp | 727 +- src/ssa/ssa_dependency_graph.h | 60 +- src/ssa/ssa_inliner.cpp | 932 +-- src/ssa/ssa_inliner.h | 241 +- src/ssa/ssa_refiner.h | 8 +- src/ssa/ssa_refiner_monolithic.cpp | 30 + src/ssa/ssa_refiner_monolithic.h | 44 + src/ssa/ssa_refiner_selective.cpp | 30 +- src/ssa/ssa_refiner_selective.h | 59 +- src/ssa/ssa_unwinder.cpp | 2128 +++--- src/ssa/ssa_unwinder.h | 347 +- src/ssa/unwindable_local_ssa.cpp | 18 +- src/summarizer/Makefile | 120 - src/summarizer/array_abstraction.cpp | 2053 ----- src/summarizer/array_abstraction.h | 205 - src/summarizer/show.cpp | 618 -- src/summarizer/ssa_db.h | 83 - src/summarizer/summarizer_base.h | 108 - src/summarizer/summarizer_bw.cpp | 509 -- src/summarizer/summarizer_bw.h | 81 - src/summarizer/summarizer_fw.cpp | 229 - src/summarizer/summarizer_fw.h | 54 - src/summarizer/summarizer_main.cpp | 38 - src/summarizer/summary.cpp | 131 - src/summarizer/summary_db.cpp | 86 - src/summarizer/version.h | 1 - 185 files changed, 16027 insertions(+), 19181 deletions(-) create mode 100644 README.md delete mode 100644 doc/deltacheck_logo.ai delete mode 100644 doc/deltacheck_logo_large.png delete mode 100644 doc/deltacheck_logo_small.png delete mode 100644 doc/deltacheck_logo_small_html.html create mode 100644 regression/interprocedural/global5/main.c create mode 100644 regression/interprocedural/global5/test.desc create mode 100644 regression/termination/bubble_sort2/main.c create mode 100644 regression/termination/bubble_sort2/test.desc create mode 100644 scripts/cpplint.py create mode 100755 scripts/run_lint.sh delete mode 100644 src/2ls/summarizer_bw_cex.cpp delete mode 100644 src/2ls/summarizer_bw_cex.h delete mode 100644 src/2ls/summarizer_bw_cex_ai.cpp delete mode 100644 src/2ls/summarizer_bw_cex_ai.h delete mode 100644 src/2ls/summarizer_bw_cex_all.h delete mode 100644 src/2ls/summarizer_bw_cex_complete.cpp delete mode 100644 src/2ls/summarizer_bw_cex_complete.h delete mode 100644 src/2ls/summarizer_bw_cex_concrete.cpp delete mode 100644 src/2ls/summarizer_bw_cex_concrete.h delete mode 100644 src/2ls/summarizer_bw_cex_wp.cpp delete mode 100644 src/2ls/summarizer_bw_cex_wp.h delete mode 100644 src/deltacheck/Makefile delete mode 100644 src/deltacheck/analyzer.cpp delete mode 100644 src/deltacheck/analyzer.h delete mode 100644 src/deltacheck/change_impact.cpp delete mode 100644 src/deltacheck/change_impact.h delete mode 100644 src/deltacheck/html_report.cpp delete mode 100644 src/deltacheck/html_report.h delete mode 100644 src/deltacheck/report_header.html delete mode 100644 src/deltacheck/report_source_code.cpp delete mode 100644 src/deltagit/Makefile delete mode 100644 src/deltagit/deltagit_main.cpp delete mode 100644 src/deltagit/revisions_report.cpp create mode 100644 src/domains/domain_refinement.h create mode 100644 src/domains/domain_refinement_variables.cpp create mode 100644 src/domains/simplify_bounds.cpp create mode 100644 src/domains/simplify_bounds.h create mode 100644 src/domains/simplify_bounds_class.h create mode 100644 src/domains/simplify_transformer.cpp create mode 100644 src/domains/simplify_transformer.h create mode 100644 src/domains/simplify_transformer_class.h delete mode 100644 src/functions/Makefile delete mode 100644 src/functions/get_function.cpp delete mode 100644 src/functions/index.cpp delete mode 100644 src/html/Makefile create mode 100644 src/solver/summarizer_bw_cex.cpp create mode 100644 src/solver/summarizer_bw_cex.h create mode 100644 src/solver/summarizer_bw_cex_ai.cpp create mode 100644 src/solver/summarizer_bw_cex_ai.h rename src/{2ls => solver}/summarizer_bw_cex_all.cpp (82%) create mode 100644 src/solver/summarizer_bw_cex_all.h create mode 100644 src/solver/summarizer_bw_cex_complete.cpp create mode 100644 src/solver/summarizer_bw_cex_complete.h create mode 100644 src/solver/summarizer_bw_cex_concrete.cpp create mode 100644 src/solver/summarizer_bw_cex_concrete.h create mode 100644 src/solver/summarizer_bw_cex_wp.cpp create mode 100644 src/solver/summarizer_bw_cex_wp.h create mode 100644 src/ssa/ssa_refiner_monolithic.cpp create mode 100644 src/ssa/ssa_refiner_monolithic.h delete mode 100644 src/summarizer/Makefile delete mode 100644 src/summarizer/array_abstraction.cpp delete mode 100644 src/summarizer/array_abstraction.h delete mode 100644 src/summarizer/show.cpp delete mode 100644 src/summarizer/ssa_db.h delete mode 100644 src/summarizer/summarizer_base.h delete mode 100644 src/summarizer/summarizer_bw.cpp delete mode 100644 src/summarizer/summarizer_bw.h delete mode 100644 src/summarizer/summarizer_fw.cpp delete mode 100644 src/summarizer/summarizer_fw.h delete mode 100644 src/summarizer/summarizer_main.cpp delete mode 100644 src/summarizer/summary.cpp delete mode 100644 src/summarizer/summary_db.cpp delete mode 100644 src/summarizer/version.h diff --git a/README.md b/README.md new file mode 100644 index 000000000..aa46ece57 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +[![Build Status][build_img]][travis] + +About +===== + +2LS is a verification tool for C programs. It is built upon the +CPROVER framework ([cprover.org](http://www.cprover.org)), which +supports C89, C99, most of C11 and most compiler extensions provided +by gcc and Visual Studio. It allows verifying array bounds (buffer +overflows), pointer safety, exceptions, user-specified assertions, and +termination properties. The analysis is performed by template-based +predicate synthesis and abstraction refinements techniques. + +For more information see [cprover.org](http://www.cprover.org/2LS). + +License +======= +4-clause BSD license, see `LICENSE` file. + +[build_img]: https://travis-ci.org/diffblue/2ls.svg?branch=master +[travis]: https://travis-ci.org/diffblue/2ls diff --git a/doc/deltacheck_logo.ai b/doc/deltacheck_logo.ai deleted file mode 100644 index 58140b302..000000000 --- a/doc/deltacheck_logo.ai +++ /dev/null @@ -1,4776 +0,0 @@ -%PDF-1.5 %âãÏÓ -1 0 obj <>/OCGs[5 0 R]>>/Pages 3 0 R/Type/Catalog>> endobj 2 0 obj <>stream - - - - - application/pdf - - - deltacheck - - - - - 2012-07-11T12:42:11+01:00 - 2012-07-11T12:42:11+01:00 - 2012-07-11T12:42:11+01:00 - Adobe Illustrator CS5 - - - - 256 - 60 - JPEG - /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgAPAEAAwER AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq85s/wA8PLFz 5/k8nrE4kWVoI9Q5KYXmUboANxvUVPhmadDMY+P7GoZhxcLLpdVvry8mstHRP9GPC61CcM0KSdfT RFKGVx+18ShfGvw5hNqX+YtVvvL+ntf3+tW8SVokT2jO0j9o4UWdGJPuTTqTSuKonyf5kuNc01bm 4txby/tIpLD23IHb/M9cVT/FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY q7FXYq7FXYq8E/N7/nJAaPdz6D5O9Oe/hJju9WcCSKJxsUhQ/C7KerN8I6UPaqWTucfJmrYPEdN1 rzD5o1lr/wAxarc3lpZA3N01xI0iAKCwRY2PCmxPAUqoIGSwx4pXL6RuWmMjI7lC394UvbPzDpgN t67+sgBLeldwsDInJqk7lZP9VxnTaLOM2LfnyLTPY2H1Zp/5reVNJ/Kex81zssNsIViW0ViWa7Ao 0QYjkWL1LNQ+O+aLUY/DmQXYxmDG0q8qaBqXnW7i80a7MtxBMOVmiE+kkTCqrEB+z+J775SzerWt pb2sQigQIi9AMVQ+t6xbaPp0l/cxXE0MVOaWkEt1LQmlRFCruQO5ptirz+2/5yM/LG8mFvYXF7fX Z+za21jcySmnWi8O2Ko25/PDydY0bVrXV9JgNCbi90y7ijAPSp4H9WKss8veavLfmO0N5oWpQajb g0doHDFSezr9pT7MMVTXFXYql+vax+h9MkvvqV3qJjoFtLCIzTuT/KlR95OKsM/KT82G/MJ9ckXT Tpttpc0UMEcj85mEgYsZBQBTVOg6eOKvQ8VdirsVdiqA1zX9F0LT5NR1m9hsLKP7U87hFqeiiv2m PYDc4qxKP84tEukabStE1/VbMAlbyz0u4aFwP5DIIy30DFUd5c/NfyNr+oHS7W/NrrCtxOl30clp dcqVoI5gnI07LXFW/wAw/wAxrDyVorarc6fe6jEE51s4S8SgsFUyzmkcYZmHevtiqN/L/wA1N5s8 naZ5ia2Fm2oRtIbYP6gTjIyU58Ur9nwxVkGKuxV2KuxV2KsD/NL81ovIOni6l0S+1FWChbiFVS0V 2JCrLOS3Amn8hxVkfkvXpfMPlPSNcliWCXUrWK5eFCWVDIobiCetK4qnOKvNvz988XXlbyLKmnOU 1bV3+pWbJ9tFZS00q03qqCgI6Eg5CZoNeWRA25vi91ZWKuCGHUHY5Q4BFMp1Vf0R5ettEhB/SOok S6hTrQN8MX0OOJU7qyGmzZlZBwQEestz+gNh2FdXseqfkZ9Q/JFHEZPmO2P6VuRQlqMgDwAca/DH Tb+YfLM3s3L4c6PIt2TD6K6vDWjk1PQpNPLktpzvf2sddirKq3KgePBFkHgFbxzM7WwcUeMdHGxy 2Ie5/wDOKXnmZzfeTLuQtHEhvdL5H7I5ATxD5lw4H+tmhxno5Onn0fRmWuS7FXz3+Ttnax/85C/m AY4lX0vrPp0AHHndoWp4VOKvoOSNJEaORQ6OCrowqCDsQQcVfO/5y+Rbv8u9Rg/MryCTpqxSoms6 dD8NuVkYBW9IUX03aiunSpBFOuKXtvkjzXZebPKuneYLMcIb+Lm0RIJjkUlJIyR/I6kYoTzFXYq8 J/5xi/46Hn3/ALai/wDEpsVe1S6zpcWrwaO9wo1O5hkuYLXcs0UTKrvsKAAuBv17YqjMVdiqje3l tY2c97dOIra1jeaeU9FjjUszH5AYq8W/LG0k/NLzBefmH5lQy6TY3LWvlbRZfighEYBadkPwvIeQ 3P7VfBaKvb8VYT+af5W6H590GW1uYki1eFGOmalx/eRSdQrEbtGx2Zfp64qxDUrbUrX/AJxiurHV IHtr+x0uSznglUqym1lMI6gbERgqehFCKjfFWTfkJ/5KHy3/AMYJP+T8mKs/xV2KuxV2KuxV5r/z keB/ypjzD7fU6f8ASdBikJ5+UX/kr/K3/bMtv+TYxQy7FXl3n/zvpvlX8z/Lf6YgSTTtUtpLdbuU BvqsqyjjLHy2SvOkhWhIpWvEZEy3pgZUQEV+a/5U6L5n0o6lZWUSeYbA/WbSeNFBmKfEYpVApIGp tXv3pXJgCxaZQBeGfkL5Tl85fmLJrWoRH9H6MRPJG/xD1E+C3hJPXjxqa9QuVkmcjIuPihciS9L/ AOcnvzMuPLug2vlzSZjFq+rkSyyoaNFbRt1Hu7inXsfHDKVN2SVB873My2epWuq2carb3SrdwQkV QBiVlgIPVFdXj36r886fTZBnxb9di6+XplYZr+SNqth+eGipalhYXa3UtqWNS1u9nM6KzUALIV4v T9pTnMZMJx5DE9G/F9Yp9hZJzXYq8B/KH/1oX8w/+e//AFFJir37FUg/MDS4NV8ja/p8wBS4sLlR Xs4iYo3+xYA4q8a/IPztaeVfyK1bXNWqbTTNRnS2iXZpS8UDJGldqtLIRXoPvxSXoMOh/mdregRa u3mh9E1u5jFxbaZb21tJYweoOSQzetFJPI1CA78xQ1ouKG/yd/Mu585aZf2mrwx2vmXQ5zaatbxH 4CwJVZUFTQMyMCK9R4UxVhP/ADjP6n1z8wPTAMn6THAMSF5cpqVIrtirEtKtvzYl/wCcg9as4tZs D5pgsv3l1NHI9mltIsMwhhjpyUKJVA28TuTXFL0vzN5u8+/lz5Iv9b81aha61q97dRWekxW8Jis7 YujnnKVVZGX4WZq+AUUrihS8/wBl+bPlfy1dea9O85nUZdOjE95ps9jarbSRAjn6fBea8ev2q0/a xVrzr54l8yf843ah5nhiMMuo2KxzQxHkFZrlbacCv7IPL6MVTP8A5xtEX/Km9C4U5crz1afzfXJu v+xpikvTcUOxVhP52f8AkqPM/wDzBP8ArGKvPfyls/zI8xflbo1toerx+U9Ms0khivWtVvbm7YSs XdVkdEiiDMVB3YkHoMVTLyN+YXnnRfzHP5c+f54dQurqIzaPrMKLF6qhWYB1RUWjLGwBoCGWnxVr irMvP3ni50nUtG8saIscvmjzDK0dkJgWit4YwWmuZVUgsEUEqtRyp7Yqx7z+v5leS9Fl816V5jk1 yGwCy6tpGo29qsUkNaO0D28ULxcQfs1PjU0oVU31P84NCtvy60/zjaxPdPrAjh0nTFI9Wa8kJT6v UbAo6sHPseuwxVCav5S/OS/00Xtr50j03W+PqLpsNlAbBHO/pepIsszAdObcv9XfFWLfmJqXmzUf +catYn82WwtddSSKC7QIYwxg1SKISBelHCcqjY9RtikMl8ueYLPQvyI0O+uNR/Rcn6Jt0tLgIs0h naIemkULf3rsdgnf264oRP5bW/5wXttban511G3s1b4ho0FrGJmUrt68pJ4Gu/FBX3HTFWLf85Ve V59S8m2GuW6l20S4YTqB0gugqs/jtJHGPp9sryDZozxsX3Mh/InzXdal5WTQ9WlDa3oyJHKpI5tC aqnIbHlGytE237I8ctMTGr502QNhlmleXvLnk+21vUbZBbW95PLqd+9BRfgq9KD7K8SQO1cDN8O+ evN135y85aj5huSeFxIVtIzv6cCbRoNh0XMeZsuFmnZXaS/1zSbnTus9qWvbQU3KhQLlPE/Aiyb9 AjeObXsnUcM+A8pfe0kWHqv/ADjfJpNz54tFv5GTUdNguDpPSjrOpEkbMT+xyZkA7u2Zfamnusg+ LdpiLp9V5p3NdirwH8of/WhfzD/57/8AUUmKvfsVYZ+cPma38vfl1rV070ubq3eysIx9t7m5UxRh ANyRy5bdhirwr8z/ACXqXlL/AJx28taZKjJO2pJd6undZriGZlVv9QEIfcYpD2jRvyv/ACs1bSLL U7PSg9rewR3EDLc3JHCRQw6S++KE98rfl35M8qXF1c+X9MSxnvQBcyK8jlwu4H7xnpvvtiryz/nG L/joeff+2ov/ABKbFVvl7/1rfzP/ANsyP/qFs8UvU/zD81+V/K/le41TzKiz6eCI1tDGsrTytUpE iP8ACWPEnfYUrihg/nrU/wAwdZ/KzXtQuNPsfL+ly6bPJ9RnMt3fGAxEgNwNvFC7Dt8fHuMVQf5X +XG8y/8AOMlvoSECa/tdQjgJ6CUXs7RE17cwMUlJ/wDnFTzcsVjqnkTUq2+q6dcSXFtbyfC5QkLP GAf2opFqR/le2Kl9A4oaZlVSzEBQKknYADFXmP5heaNP8zfkp5q1TTQzWHpXVvbzmnGYW83pGVKf sMyGmKph+Qn/AJKHy3/xgk/5PyYqwj8zwP8AoYz8vj39Ef8AJybFUs/Ma30q4/5yZ0C08yxeto97 YJb24Z5I1DSCdY6MpU7z7bHvil6nL+S35ZSxmOXRVkjb7SNcXJB+YMuKHlH516donkrzF+V9nYW4 0/yvpupNdPEGZkBjubZ5GdpCxY8amrHucUvo0EEAg1B3BGKHmv8Azkf/AOSY8w/9Gf8A1HQYpDxX Sda81eS7z8vvNnm6OLVfJ8lhHb6ckaFksAyijopH+9IRBJy/aHJV6bKvq6zvLW9tIby0lWe1uEWW CeMhkdHFVZSOoIOKED5nsNR1DQL6w054Y7u6heGOS5XnEvMUqyUbkPbJ4zESHFyQeTwC28s+cPym 816Xr+qStqOkylLXUr6zt1ZFhakfG4kqk1UADKWUqSBvXMnU6jFPlEiR6tEIygd92Rf85QfmBb6X 5atPLVtcUutcIknMarKRaIQd1YqKSPtWvY5iRnGJ9W4bMkqD5yt9R0iKJU9Q7DvptmfxL5ljVab/ AFNwifP7AyzyD5Z1nzdqotvLqOGTae/bTrSKCBWBB9SVWNKgn4RVj4HLIavTXYx7soRMuX3PSfLn /ONvnTQtdsdYtNcsluLKVZVosoqBsy/Z/aWozJydpQnExIO7ZHTGJu30SvLiOVOVN6dK5pnLQ2qT ahDp9xLp1ul3fIhNtbSSeikjgbK0nF+IPjxxV4DoP5V/nvo3nnUfPFlPosd9qUsr3mmSTXBhkSd+ ZjosXRDTieddu/dS9LTVvzrciE+X9Ejeg/0ptQnMQP8AxjW354oX6T+XN7da9b+ZfOmpLresWdTp tnDF6OnWRbq0MLF2eT/ix2r9wxVkHnHynpXmzy5e6Dqik2t4lOa05xup5JIhP7SsAcVeP+UPJ/8A zkD+XxfQ9EbTNf8AL3NmtGvJGj9EMa1A5JIlTuUHJa1p1rir0bQ9H/MGys77WNY1CDV/Ms8PC00q F3tdKt968E+F3Yk9ZHUtTYUxVg/5T/lv+bHkXV9Subk6NqNhrk8c1/ClzcRyxNzYtJETbcTQSGqn rQUIxVF+afIHnvSfzYf8w/J1va6ob+2W21LTLqX0G+FEirG5+EArEhr4g7GuKtfmX+XP5h/mH5Ke 21Q6dp+q29zFd6Zp1u8rxgKkiSJPcMPidxIOPGMAFeprsqmUuk/mz5x8pXPl3zNbaf5fF3CYL7Ub aZrqWYEbiO3UBIw9PiJlOx2XwVVfyW8s/mD5S0RPLGvQae2j6eZvqN7bTSNO/qytL8UZTjx5SMa8 gR0p3xVD/mN+R1p5h1uLzV5b1BvL3m63YSLexCsUzqKKZVFCGptyHUbMGxVX03Wfz606JbbVfLel a7Kg4fX7PUPqatSnxsksTdf8lR8hiqH1fyh+bHneJrDzHqdp5Z8uy1W607SGe4vJ0PVJLmRURVP+ SCD3BxVGeePIurRflovkXyRp9uLSeE2jy3c7RiCInm0myu0jyMTXpua4qr/k/onnfyz5bs/LHmCx tBb6fHILfUrS5MnPlKXCPC0aEEcz8QY9MVYt+YHkH80Nb/NHSfNukWumx2vl/hHZx3FzJzuEV2d2 cLEeHIOVAFadd8VTr80fypuPzG8u2FzOE0PzZp/J7WRZTPEhLbxPKqozKeIZWC1U9uoKqW+Xh/zk zDaJpF9Hop9MCMa9cu8snACnIxxMPUen8yCp64qnvm/8nrbzR5DXy7quq3F7q0UjXcOuXFDILtq1 PprRVhNePpLQBadxXFWNeTbH/nI3yzYp5fltNI1iytV9Kx1K6uZFKIuyhioErqo6Ax1p3xVMPP35 ffmb5h8jT+XF1Oyv7/WZkn1e+unktra3SBkkit7O3jjmIXkgq7HkaGv2vhVTDyt5A12fyFH5D89W On3elW9oLeK+sbmV3Yo37r91LBF6bxjcOHO46Yqk/wCXvlD84vIIn0O2Om+YPLEcrNp31m5ltbiJ GNdisM4Ub1K0O/Q4q9hxVTuII54JIZFDRyKVZSKggihBxV5Re/kL5e1zWDceYVlvY4EWCycSvGYr ePaKJQhC0RKLUip69cBiCxlAHmmWn/8AOO35S2cvqnR2uW2Ki4uJ3UU/yQ6qa/5QOR8MMRhj3PQN O03TtNtEs9OtYrO0i2jt4EWONa+CqAMmA2AInFXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq 7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7 FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX/9k= - - - - - - uuid:73daaeb6-1136-654a-971b-5e977a1fafc2 - xmp.did:01801174072068118A6D8685B6A1D455 - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - uuid:a3439645-3ef4-5b4b-a8cd-d96103a2c5f4 - xmp.did:370310640E2068118A6DC38C6951E8DC - uuid:5D20892493BFDB11914A8590D31508C8 - proof:pdf - - - - - saved - xmp.iid:360310640E2068118A6DC38C6951E8DC - 2012-07-11T10:44:56+01:00 - Adobe Illustrator CS5 - / - - - saved - xmp.iid:370310640E2068118A6DC38C6951E8DC - 2012-07-11T10:45:15+01:00 - Adobe Illustrator CS5 - / - - - saved - xmp.iid:01801174072068118A6D8685B6A1D455 - 2012-07-11T12:42:08+01:00 - Adobe Illustrator CS5 - / - - - - - - Print - Document - - - False - True - 1 - - 297.000132 - 210.000102 - Millimeters - - - - Cyan - Magenta - Yellow - Black - - - - - - Default Swatch Group - 0 - - - - White - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 0.000000 - - - Black - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 100.000000 - - - CMYK Red - CMYK - PROCESS - 0.000000 - 100.000000 - 100.000000 - 0.000000 - - - CMYK Yellow - CMYK - PROCESS - 0.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Green - CMYK - PROCESS - 100.000000 - 0.000000 - 100.000000 - 0.000000 - - - CMYK Cyan - CMYK - PROCESS - 100.000000 - 0.000000 - 0.000000 - 0.000000 - - - CMYK Blue - CMYK - PROCESS - 100.000000 - 100.000000 - 0.000000 - 0.000000 - - - CMYK Magenta - CMYK - PROCESS - 0.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=15 M=100 Y=90 K=10 - CMYK - PROCESS - 14.999998 - 100.000000 - 90.000000 - 10.000002 - - - C=0 M=90 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 90.000000 - 85.000000 - 0.000000 - - - C=0 M=80 Y=95 K=0 - CMYK - PROCESS - 0.000000 - 80.000000 - 95.000000 - 0.000000 - - - C=0 M=50 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 50.000000 - 100.000000 - 0.000000 - - - C=0 M=35 Y=85 K=0 - CMYK - PROCESS - 0.000000 - 35.000004 - 85.000000 - 0.000000 - - - C=5 M=0 Y=90 K=0 - CMYK - PROCESS - 5.000001 - 0.000000 - 90.000000 - 0.000000 - - - C=20 M=0 Y=100 K=0 - CMYK - PROCESS - 19.999998 - 0.000000 - 100.000000 - 0.000000 - - - C=50 M=0 Y=100 K=0 - CMYK - PROCESS - 50.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=75 M=0 Y=100 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 100.000000 - 0.000000 - - - C=85 M=10 Y=100 K=10 - CMYK - PROCESS - 85.000000 - 10.000002 - 100.000000 - 10.000002 - - - C=90 M=30 Y=95 K=30 - CMYK - PROCESS - 90.000000 - 30.000002 - 95.000000 - 30.000002 - - - C=75 M=0 Y=75 K=0 - CMYK - PROCESS - 75.000000 - 0.000000 - 75.000000 - 0.000000 - - - C=80 M=10 Y=45 K=0 - CMYK - PROCESS - 80.000000 - 10.000002 - 45.000000 - 0.000000 - - - C=70 M=15 Y=0 K=0 - CMYK - PROCESS - 70.000000 - 14.999998 - 0.000000 - 0.000000 - - - C=85 M=50 Y=0 K=0 - CMYK - PROCESS - 85.000000 - 50.000000 - 0.000000 - 0.000000 - - - C=100 M=95 Y=5 K=0 - CMYK - PROCESS - 100.000000 - 95.000000 - 5.000001 - 0.000000 - - - C=100 M=100 Y=25 K=25 - CMYK - PROCESS - 100.000000 - 100.000000 - 25.000000 - 25.000000 - - - C=75 M=100 Y=0 K=0 - CMYK - PROCESS - 75.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=50 M=100 Y=0 K=0 - CMYK - PROCESS - 50.000000 - 100.000000 - 0.000000 - 0.000000 - - - C=35 M=100 Y=35 K=10 - CMYK - PROCESS - 35.000004 - 100.000000 - 35.000004 - 10.000002 - - - C=10 M=100 Y=50 K=0 - CMYK - PROCESS - 10.000002 - 100.000000 - 50.000000 - 0.000000 - - - C=0 M=95 Y=20 K=0 - CMYK - PROCESS - 0.000000 - 95.000000 - 19.999998 - 0.000000 - - - C=25 M=25 Y=40 K=0 - CMYK - PROCESS - 25.000000 - 25.000000 - 39.999996 - 0.000000 - - - C=40 M=45 Y=50 K=5 - CMYK - PROCESS - 39.999996 - 45.000000 - 50.000000 - 5.000001 - - - C=50 M=50 Y=60 K=25 - CMYK - PROCESS - 50.000000 - 50.000000 - 60.000004 - 25.000000 - - - C=55 M=60 Y=65 K=40 - CMYK - PROCESS - 55.000000 - 60.000004 - 65.000000 - 39.999996 - - - C=25 M=40 Y=65 K=0 - CMYK - PROCESS - 25.000000 - 39.999996 - 65.000000 - 0.000000 - - - C=30 M=50 Y=75 K=10 - CMYK - PROCESS - 30.000002 - 50.000000 - 75.000000 - 10.000002 - - - C=35 M=60 Y=80 K=25 - CMYK - PROCESS - 35.000004 - 60.000004 - 80.000000 - 25.000000 - - - C=40 M=65 Y=90 K=35 - CMYK - PROCESS - 39.999996 - 65.000000 - 90.000000 - 35.000004 - - - C=40 M=70 Y=100 K=50 - CMYK - PROCESS - 39.999996 - 70.000000 - 100.000000 - 50.000000 - - - C=50 M=70 Y=80 K=70 - CMYK - PROCESS - 50.000000 - 70.000000 - 80.000000 - 70.000000 - - - - - - Grays - 1 - - - - C=0 M=0 Y=0 K=100 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 100.000000 - - - C=0 M=0 Y=0 K=90 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 89.999405 - - - C=0 M=0 Y=0 K=80 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 79.998795 - - - C=0 M=0 Y=0 K=70 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 69.999702 - - - C=0 M=0 Y=0 K=60 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 59.999104 - - - C=0 M=0 Y=0 K=50 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 50.000000 - - - C=0 M=0 Y=0 K=40 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 39.999401 - - - C=0 M=0 Y=0 K=30 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 29.998802 - - - C=0 M=0 Y=0 K=20 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 19.999701 - - - C=0 M=0 Y=0 K=10 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 9.999103 - - - C=0 M=0 Y=0 K=5 - CMYK - PROCESS - 0.000000 - 0.000000 - 0.000000 - 4.998803 - - - - - - Brights - 1 - - - - C=0 M=100 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 100.000000 - 100.000000 - 0.000000 - - - C=0 M=75 Y=100 K=0 - CMYK - PROCESS - 0.000000 - 75.000000 - 100.000000 - 0.000000 - - - C=0 M=10 Y=95 K=0 - CMYK - PROCESS - 0.000000 - 10.000002 - 95.000000 - 0.000000 - - - C=85 M=10 Y=100 K=0 - CMYK - PROCESS - 85.000000 - 10.000002 - 100.000000 - 0.000000 - - - C=100 M=90 Y=0 K=0 - CMYK - PROCESS - 100.000000 - 90.000000 - 0.000000 - 0.000000 - - - C=60 M=90 Y=0 K=0 - CMYK - PROCESS - 60.000004 - 90.000000 - 0.003099 - 0.003099 - - - - - - - - - Adobe PDF library 9.90 - - - - - - - - - - - - - - - - - - - - - - - - - endstream endobj 3 0 obj <> endobj 7 0 obj <>/Resources<>/ExtGState<>/Properties<>/XObject<>>>/Thumb 15 0 R/TrimBox[0.0 0.0 841.89 595.276]/Type/Page>> endobj 8 0 obj <>stream -H‰lWË®e; œï¯X?pұ㼦4#„>` ¸ƒéÒÿ/QUÎZçÀE-õÙ••‡c—Ëη?}¿¾ýñ{½~÷ûï×ë×W½úîÅç¸VXYûú¸ñ¿ÿöúëõ¯×·ï©×ûçUõÏ®ëçƒÀà?~¾~Å@ûn%¬ÙÕ|—µl]ï¾ø‰ÿôV¶]?ôs”Úæe¥šÍÒ+aÌViµ]ïW”¹&M¹æù²Ë°k—9Æõ1j±Æiæ%öÖÀèã²(«&܆#F{>ðýr/m´gÀg‰Ñµw؆á¥Öá†é gÒV&F+‡~„—5Ôj™°ŸâÑ/Ìj´ÛVq‹ [m|„½³Ò’†kûº>fñ«a…ã\ì ¯cÃjãªÅmÀvpæ¸ð…½Fëü«ÍŠãC‚÷ c„C°C/1nöíXî‹ÇÖŒP_+¦ñü{¿`Ç0X‹cF b7†[y‰U –YÙðñÇVˆ¸¯ýa¹'(® ¶J;œžD<¶ÓàX¡¹°Ïû‚×ú˜@;Úƒà–>ÏTbx\!²FÔ'bäg;ˆs­´ n×™´à´w.D÷Bìò7Féààù„ëU´Aˆýœ€ys|µcû4l#fÜuvþnp¬0œš@›0*!B¼ážŽÀ€†/ôD3bâcÐË5àHS$yåÀ•oèàòàì{ 50›£‹y»îd†hÓd:éˆ^KCô¢ÊÃ:F±—U̵hô-‘ðŒìL$/âfgòÈ>lÒ÷çwXD‡.PÞhha¢èj>HQÆȘbµôø„ŠLÝ_¾Ïb຋«&šÑ÷Σ¶‘ ˆ% Á.L,rè4à¡ùX¹—Ì·xÚæÊyÀƒ]ÉÇŸ#gO -yj×¥#6I <åÚ4›© ›}š”§!.ˆõ¨Ò·Ró€ÃÂýºó¯ƒéý ÑÉ_^ýù‹ö"²Ø~5ˆ0cN?Ú ­Á4ÿñ2ÈlDâS³(1Ñ;Àʘô͸.Ò+ÆTŠ"à jÙ˜N@ã ¬Ãý©kˆÿ apu‹½_XèÄ:ØÃu2ijsËœg0ˆz?üº…Šy|$ -ŠH©gH¨PU‹H(ÔãìZ%{nRŠêTÏ6ˆèí4¡rp«ËÕ‡aøˆ;ÖßôZ'ÏVõ:´:p”¥ÜV»2Yì]g-ìè3'ü¨Õtì¶œ¾gšÆ†üKád¹ˆb÷î‘ù™&âaOSÁ˜8IHÏÁ映;©@¡ÂÀ„ƒ;%PržrF33û°ÎDnÆSÈ1„¬ädª+;Yu˜#ÆËÏxOjçgÝk &tI`Ûn^ -æ–¶’KEc¡<1€äÉn„ J%Æ5W×F•x## ¤pGaƒˆòtÏbè˜TH\µtœU•Ï‹ÆV…ëF°¶îœ*Œæ´"&Xm ެ&‰‚í¢íF D‚qXüã7‰Œ>9Š2Ó ÔNo|m¢+FË. -ÒE!ë%C¸_/Å-TY˜Ôü -.:}>á¬I±¤Œ+'‚+é'•\˜„^"ÜÇD€Nn©*Ñ4WaG -?:I´¸k~3ÏüAp=@Š"QÐ~¬‹!ØÚ£Æí'=öƒ¤Ôò3‰ÏŽöu½‚$4XVÇY0¥¼’~d†sÎz’Hý kÉLñ!;œíFïëË ögËšM^Mßí¬À3=%ÍÜrµjO`õ‘YïÂÆ&Y¨2®P-P1ƒÝ".‹£g”ëbÅ‘à T’ù‰à”­ÍÏ6µžÞ†Ói;ÊÛRTò¾Ê›ô¦ÏQ%ºæ:$1‹¢,e•ž‚qyÜewé»ÙȺ” -ªÁ7ŠÖÏä-í¡õH)RÀÄ•úQçâJóaw7µc«_è´²žÎìãI–ÍÄÕY1qÐdUµ)€‹üD@0¥©Kä!¸á„a7²´©­ç9;g¡kãÊ.×Uµåoì(QüMâ;½„õ­SzÿWâC·Zx&¾ó Ä­ÕÇ5˜½h|#Gñ‚Ù;•t²qCÛI.Ì#êj™”ýAï×ÎûàÓ;séBf?Ï~¥±!kÙómfŽš)h]0ÆÜ90˜gpQš¨ò \·g‘8T ,;r3>eÿ£ ¹S¶²Z6hÎæ+c©f-)›ôù Ä,6怈žS#—ž›¨b5ÝÎoW—PÅ9Òˆ³ø‚æávb>Êò«z‹“à¹9{ -b€]ÈHj%B¤WÃ0±_–çL>4^®j¤t¸JtŽ=o$ÚUxãàšÝYèqõ€ªÔªTØÉägž€Ñ„P!»2™aи2»²B`ïCI™Üvo5ËõÔƒO?`4NdPÉ‹Êo|môÉÑ9/Ñ€ó¸zF: -˜“ÁêgÒ ¤:¸ñƒh€ÉÌuòt-† ¾žpYý9ÆšéÎê _»dv%¡g_, F -T™)×Û$zcúy Lª 9ù6u êyÇñ1¸×yÈZjcgYá혚ü»OÓdùÂÝÕ“¤’èZM1Ûd,»WæÆÒËí†êÆR"ÏwzE««”R¯‡%Ê2£5Ѻ©zUÔôúú^a Ø¥>,þ&í!ÇCrÇrM¦QõAõPoé÷Ë Ù†ã7¯¶²ó±»Þ@¼âAéHŸûùÌïX\Z->!Ôæñ¤<{€Že¬¦¬ëGIøöG†Ø½}í¨tÑq>*2›œuæ{—­Þ»³CA#³o#ì ΤqÏGô¿`Ÿn®&}€<ë¼vÙ£³ ß) îê³h~Ÿð4÷²ŽïKvîSikeá<½ªî>Ù÷¶šjÃ2èt û-=hâÖhlâÆì5WîÆêe`Ÿ*>Ô†Ñc8e[4ê!¹q6Ç[‰'|»ÙqÈlt‡³G`憜³2X]‚¿² åUÀbDþP½Sç@Ö°¾)H¹×ùîÙú’c®KVOyèíd™Ñôn%ô)¡25 %ccçtÕ> ciº -Ý“g:M‡V7É©Õ|ù9ÚæÆL¸]Ƙ -þýTƒ¯XŒ<+X´ÖƒèùÖ÷ƒáG¶-ØÊ°©ÞŠMËI0Ó·¶%¹-`ÙõyÃ$öûó{ÞTìbŸÄWë:¯ÏÑoÒRìŸÜxÉåÈŸ6¿`\WZñŒÐ’®­ë–8FË}›S”{·“óìnØ©‘ºŒ›™9(A):ïÿÑmhùmWCaÎv붉·5( ‡@èåçÎïqVÞ;Ewòq‰‡bjáRf¬©Œb*jA¥TíM³!Y®ÎònýgzM8ßu¦Sîb!mt…gïˆ} x7î,Ûº8#Ø´î ¦š³•îdxJåüëeŽdGrQ½OÑèAî‹ 3ŽFG€0Ú¨ŒÀëÓŸGfý)¿½–¬ÌXÜ=`ù‹õ‰Ò,N÷J=ê:V, í´n›õÑGc3jAš·o÷¶-¤§ˆj1ø( -6å`2(Ö›åõžÍS¦JGYüØgkÑ1£`7ìR~ÓÄoH¯·0l§ðõmÙt0²r³1Ó((ñÒÞóÄ>™ @u…ÇåóàÄp›A¡¦j¼HËÔíg÷ - µ}ò.:¯¯®9\ -ïÝßub<ì‰Á´O˜JB)®&ænÕy™òtÞã¡ÜFIGÙÉ´F°IÜÈUy )ЯÛ-”Šðb¢ˆSò/÷O}¸ÏáÔfŠE_ìÖ¥´^¸&[ ¼í¹’·¹ˆ\꜄cæ3 è”ÖÃÖ<V{*ö×Ìëmí³ýzP”þÿÜ¥!ÔKÒb¦ƒè¬çv -S/Ñl1weõ24é1L¡D*_CµúH˜ibMƒÞ(fM{äåñÍ^M' ™v0L›ñwøžÜÏäÊÔ³ÒÐd«ø á êEµ,£‡0N—pšnK¤’-˜–AkÔñTÅËŽ= ²%\ðÐQJ·Vë°öcE˜jÐË»F»›GC]¤,¥NkÜ e˜å?_6å©t'¬ŽFB,s=u g&ª@×§µjÒ×O£Ñ+ó¬¬MæéÈä'úHgZR ‘BÏNÚÙ»iÕODZ ;¹/3¶ögé@zºG¥ßÛèî6,"köSãn^+ö]f¼yÓaóŒ}.Óåt¡¢ÄÌûºPîËÛ²þwÔtIê!v}½[Ü_[‡ëHϳw{õº/hq?•Ö5öVÃbÌðÙÚV“¬2¼l|SdÇ ¥(ðQAÎJd h§MB»Še¤ß+Äkƒ7b%~çú@Š£­x|..LkÖçsÁ¾¿~¹ÐMØ|èz›M¢Z‚5â\MaˆO …)ÒógøŸØ½Ü.êî õu†Z*¦É-oÒ§2S^rƒoÏ&æéõCmƒ|úïå°h×x>ÑØ=¦Ø½ÞNFr«‚Q÷k1¨ÎSèó)jŒÒnúÀò¹@KLq‘ú}eŸ¦ägš;µ¨ÓýY÷`mFS¸d&±è#Ö(™Ã‰¸›8´vÓŸF ¦³l -›¡]·×…á¹À*vS³NóÓûYÍU¬ÜŒ´ï¶>ìP¿lš «½nKp{´JØ$áѵœIµ¢º’òö‚;BãÑMŠ6mÓ~²WPFÄ‹g6¨ç”3,¦rXÑêÕ¬\w7®bßÅÉp­Špº/öz‘ë+ÛûÅÝ#ÝÍEdeÞVÌ5û‘ÝBÉíž=ˆ"·/xûÓ¼q®Ð]®Ùê=Ò*cá¸ðøé<ûY¶‹Œg„G‚01¾"›ö Ÿo”ð~°Âe²ƒ™ŠÇŽÜÔ8 §(j¸ÁVþ†ÿ… <Œ”±ãIÄNœ”4º”°˜ÊDŒÚa”βB…/sRßCûñ嚆eбõLgºPË Lˆw~üí >xc9¥¾æ‚ÅgåÆpÈÉé)pî8+öUŒ!2a-V¬Ÿå†*v\ü¬Ã…:û‚Hç’ïÇŠûü~ZùkÏVðD^9[ ÈBœBÎq hE½þ)!6¸¥Á¼:=éMsÔ®–FRòb߸8M:Úù³Û‹°B1«ÌjøJÇôWlìÙFåþ$f8¯4Moöâeá,io)Kf†Þq_=Y凵î(¯ÍòºË0³ž”0%戛Mf /|âÈìÜ - hf‹A¢Ãë¹ AÞ5ડÕ)ˆx·“i˜g¤*„~µz3Û––_ ¶Ï/÷ &üâìBˆ 3®¤-°Oô±ŒÚôð …ןæf&X¶¤ Yºˆ¸j¦‰÷Ì”þ¤‡c/,ÅÆ¶¼0n&ÛÚ¨{š¸’ðY:ó…á?à@]¨uyÎTý¨Î —àðq ö¾×q#ç¬qŸêH¥j_(X×wÒç:£)Œ„ÃSÙ 7Êi:»B /;ÑIÍŠRh¬^Ð)ÒÌ.ÏØÕþŽ<ÆV ­êAûš%¶©gô¹›d½DËU7÷oOuYdz°‹ ñŸÿÔ§«ûZÖ6GËîSŸ­]­Ìx–Eºùyÿóç?¿y!Wx¸šj|öê¸:ÞÚ#…©Àw»™¿þâêd%lÕÎÍ\³_ ìQõüí[ŒBñªßlÏ -†‹™õât_{y޵À"‘_'£¨ŽE!õ•<¾o§~ŸJ[¦‡r/ýI[–¦¥wÄÇzx{l:øç–(`€¦VUTËŒ°ûþ#½ÿqìSzÿñýïoÙŽê_ïÿëÓÿe£}›õ~àñ´úú÷ûÛ·ßä÷?þ|ûö·³_î8Ã@¾J.ÉIü¬’kÐ!îßñ}ãd  Þ -­åñÌÿš÷´<ˆ¥ë1?0D©µ IÈ\ÔE®G %kúµ¶S å_SŸKÃáäœt -qdsG0„Ì#nÇv -w -éççÙÀ?"ˆo2n&UB¢I¬2b{]¼¯q¢æ)öUÖr¢Bq÷Ñ‚•i¦ùèÞPËì%® Ó|HnkÓvJ`Ô;Žâº”QË}&ztNéæ‹¿ö1åQô¢9ë@Ï|1l--´ 7JéQìfàéfvJ¨=ãÚkd÷wðœ¬ÄôUÃUÎÉ}ª®ó>¼Ï÷æ¹èmi:KÕØl-¢ŠLËÆäb·×tR› ÌÍ=ª±Œ°HäS¨Í+ɰ¶=nú“1oˆ –&ß"ÉÁrÌ7»ã¤XîN'ŽNŽ%çÈ_|ý_SÔþ2‰}׉™ôN3²yõ†.cßCw¹wØÿ‹å­ØG*>Á~˜©3‹,±û„¾4ú,HŽ ¿Í6:`íl+QåÛWyýÅ+¸ØÏí %azãA6y´eÆÍ…”ß]f0™ )Á$†î6c—Ïxòåõiáϧtóa endstream endobj 9 0 obj <> endobj 15 0 obj <>stream -8;Z\t5n\em$q4;r&*!#<$h)pKE1J6dN -@mp")aM@]333gC>D5)'cf"Z[;>G&o<,u0oWA8s:*3m7l>kbtq<7fE07cH@[r=TpBa -Zn,Lk[MLVG\>EriI%[mr$K_K9C\?O6>5Veg1RHCKjbI*Q=5<_Q\#N/7)t"a5'TOXJ -ZsL'KM%nQ9;=.?MMEHO%]T^sMA%)A+c74Mr[9K.FQYGiT;m]\IfX6ps8`_);Q4b(4 -X3N0kdM^#q]4uL3*HgbRFsZ1I?##6h[[eUdlC.[kSWZ(1=If(*U$k=Wm`(M__-K2V -^KiLo;Shas>Gobob:U)-Gjo@;]h8YTKFgHU+lij"!<=@C77d~> endstream endobj 17 0 obj [/Indexed/DeviceRGB 255 18 0 R] endobj 18 0 obj <>stream -8;X]O>EqN@%''O_@%e@?J;%+8(9e>X=MR6S?i^YgA3=].HDXF.R$lIL@"pJ+EP(%0 -b]6ajmNZn*!='OQZeQ^Y*,=]?C.B+\Ulg9dhD*"iC[;*=3`oP1[!S^)?1)IZ4dup` -E1r!/,*0[*9.aFIR2&b-C#soRZ7Dl%MLY\.?d>Mn -6%Q2oYfNRF$$+ON<+]RUJmC0InDZ4OTs0S!saG>GGKUlQ*Q?45:CI&4J'_2j$XKrcYp0n+Xl_nU*O( -l[$6Nn+Z_Nq0]s7hs]`XX1nZ8&94a\~> endstream endobj 14 0 obj <>/ExtGState<>/ProcSet[/PDF/ImageC/ImageI]/XObject<>>>/Subtype/Form>>stream -q -/GS0 gs -129.1199951 0 0 133.1999969 134.8740234 237.2716827 cm -/Im0 Do -Q - endstream endobj 19 0 obj <> endobj 22 0 obj <>stream -H‰ìÒ vݸEÁôü'ýVÚ/$ûJ"Xˆ¹þù§µÖZk­µÖþùõý¸6ß·Ñ7²¥«7ѲGGÑçQÔ «èû¨eøYôuu}©…ŸE_GJ³î¢#•©gÑב…8‹>Žõ¹»èãX¾‹éí+}èí#}ïéí/} Ÿéí¾ƒ#ºI[ó.~Ó]v§÷ÿŽn³5=þtžméᦠíI¯þ]iCzòWéN»Ñ{_ SíE¯}‰Žµ=õUº×6ôЗé`»Ð;ß “mA|‹Ž¶½ñM:[}záÛt¸êô¾÷érµéuÑñ*ÓÛ>£ë¦§}J÷+Kû˜X•Þõ9]°(=ëºaIzÔ!tÄŠô¦ƒèŒåèA‡Ñ!«Ñ{Ž£K£çH§,FÏ9’nYŠs(³½åXºf!zÊÁtÎ:ô’£éžeè!‡ÓA«Ð;ÐIkÐ+FÐMKÐ#†ÐQ+ÐÆÐU+ІÐQ+ÐÆÐU ÐÑYóÓ FÑ]ÓÓ†Ña³ÓûÅÑe“ÓóÅÑe³ÓûÅÑe“ÓóÒisÓëEÒmSÓãEÒmSÓã…ÒqSÓã…Òq3ÓÛ…Òq3ÓÛÅÒuÓÓÅÒuÓÓÓyÓÓÅÒuÓÓÓyóÒËÓyóÒËEÓ}óÒËÓyóÒËEÓ}ÓÒÃEÓ}ÓÒÃ…ÓÓÒÃEÓ}ÓÒÃEÓ}ÓÒÃEÓ}ÓÒÃ…ÓÓÒÃEÓ}ÓÒÃEÓ}ÓÒÃEÓ}óÒËÅÒuÓÓÅÒuÓÓÅÒuÓÓ…ÒqSÓãEÒmSÓãEÒmSÓãÒi“ÓóÅÑe“ÓóÅÑe“Óó…ÑaÓÓFÑ]ÓÓÑYóÓ ÑY ÐÆÐU ІÐQ+ІÐQ+ІÐQKÐ#ÐIkÐ+ÐIkÐ+ÐI‹Ð3§ƒV¡wN­Bï8œZ†r4ݳ =ä`:gzÉÁtÎBô”céš…è)ÇÒ5+Ñ[ޤ[–¢ÇI·,E9’nY‹^s ²½æ8ºd1zÎqtÉjôžÃèÕè=‡Ñ!«Ñ{£C–£Ew,G:ˆÎX^t± =éºbAzÒ1tÅŠô¦CèˆéM‡Ð+Ò›¡#–¤GA7,I:€NX“^u°(=ësº`QzÖçtÁ¢ô¬Ïé‚Ué]Ó«Ò»>¦–¥‡}J÷+Kû”îW–ö)ݯ.½ìC:_]zÙ‡t¾Âô´Ïèz…éiŸÑõ -ÓÓ>£ëU¦·}DÇ«LoûˆŽW™Þö¯4=îº]izÜ't»ÚôºètµéuÐéjÓë> Ó§ç½O—+NÏ{Ÿ.WÞ÷6®:½ïm:\uzßÛt¸òôÀwénåéïÒÝêÓ ß¤³Õ§¾Ig«O/|“ζ=ñ=ºÚôÄ÷èj;Ðߢ£í@o|®¶½ñ-:ÚôÆ·èh;Ðߢ£mA|‡n¶=òºÙôÊ7èd{Ð+ß “mBÏ|ƒN¶½ò :ÙôÊ7èd›Ð3_§‹mBÏ|.¶ ½óe:Ø.ôΗé`ÛÐC_¦ƒíBï|™¶ ½óe:Ø6ôÐWé^ÛÐC_¥{íC/}•îµ =ôUº×>ôÒé\ûÐK_¤síC/}‘ε=õE:×>ôÒé\ÑS_£kmDO}®µ½õ%:ÖNôÖ×èZ;Ñ[_¢cÍã¿ g¾ŽUše¡ïÏYt©e&[.BàŽãÍŠ2ÛŠ%Ư(>°j»… N!¬›dÄ`³ÄU@–Î2æqs%@3ðyá"þÏ,gô ÿ»“ OÈcŒý¸”"QØ#Çùm)G¤ÐWŽ5ìÏV–NáïfЇ±4©¦ú{a«M Û½—òÑß -.œn÷AÊG#r¸hºÝ')}*vºXºÝW9_},z½HºÝ”>?_ÝîPÎW5a¿8:Þ±”þjÊ‚At»39_ýÉœ ƒèxgR>ú“IÆÐñÎå|õ³F ¡ã}#å£ß›·bï[9_ý×ÄÇÓñ¾—òÑÿ™:äh:ÞOR>úÿ&O9˜®÷£”~3{˱t½e|ó›é[¥ë½ å£e?Œ‘3¾ùW_Æ ßœý0rTNøäô—¡ó½(á›Ñ Ãè~¯J÷d¶è(:à˲½n:†øº\¦£¡ ^ê½xÖtÁ+=WÏúœ.X”žu°(=ë:aMzÕtÚôª#è†%éQ‡Ð+Ò›Ž¡+V¤7Dg¬G/:ŠîX^tݱ=è0:d5zÏqtÉjôžãè’Åè9GÒ-kÑkޤ[–¢ÇJǬDo9–®Y‰Þr0³½ähºgzÉÑtÏ2ôãé¢UèÇÓE‹Ð3ÐIkÐ+†ÐQKÐ#†ÐQ+ÐÑY ÐÑYóÓ †ÑaÓÓ†Ña³ÓûÒisÓëEÒmsÓë…Òq3ÓÛÅÒu3ÓÛÓyóÒË…ÓÓÒÃ…Ó³Ò»M ç¤W›BGNI6…Žœ‘Þl9!=Ù,ºs:z°itètô`óèÒÉè¹fÒ­sÑkM¥cg¢·šK×ÎDo5™Î‡^j:< ½ “'¡gtóôH„Žž‚ÉÐÕÐ)ºûúôBŒ¿:½£Ë¯NïéôkÓëP:þÒô8–®¿0= ¦ó¯K/Ãé–¥‡ñô‹Ò³¬@o°&½Ê -ôKÒ£¬A¯°"½É"ô ëÑ‹¬Bï°½È2ô«Ñ{,DO±=ÇBôkÑk,E±=ÆZô ÑS,Fϱ=ÅjôËÐC¬G/²=Êô&kÐ+¬Ho²=šô*+Ð,JÏâéV¥wñôËÒÃhºÿÂô4˜Î¿0=¥ë/MCéøKÓãHºýâô¢ãÕ¦×}@§+NÏû€NWÞ÷6®<=ðm:\}zá›t¶ è‰oÒÙv 7¾EGÛ‚ùmzåt²Mè™oÐÉv¡w¾LÛ†ú2lzé‹t®è©/Ò¹v¢·¾DÇÚŠûk/zí tªÍè¹/Щv£÷~™µ=øËt¨ýèÅ_¤3mHOþ"iGzó—èH[Ò£¿DGÚ“^ý:Ѧôì/Љv¥wÿ‘´-=üt }éå ólLOÿggzûoé8[ÓãKÇÙ›^ÿ;ºÍÞôúßÐiv§÷?§ËìNïJ‡iúÎè.M_À ¥­zºJ[ô2t”ö›¾‚#ºIûM_Á¤½Ñwð•.ÒÞè;øBièKøL÷hèKøDçhé[øH×hé[ø@ÇhïékxO·hïékxG§hé{øK—hé{øÑ>Óñ‡îÐ>Óñ:CûJßÄ]¡}¥oâ®Ðè£øM7hGôUü¦´Cú,ú0V¥ï¢/cY}íX_F;чюõe´c}íD_F;Ö‡ÑNôe´c}íD_F;Ö‡ÑNôe´c}íDF;Ö—ÑŽõa´}íXF;Ñ—ÑŽõa´}íXF;Ñ—ÑŽõa´}íXF;Ö—ÑNôe´c}íD_F;Ö‡ÑNôe´c}íD_F;ևюõe´}íX_F;чюõe´}íXF;Ö—ÑNôa´c}íDF;Ö—ñ?v阆a×3ÐÜ{@1ØÌ ˆÁfA 63b°™AƒÍ ‚lfÄ`3ƒ ›1ØÄ ˜AƒM ‚1b°‰Aƒ Á Š/(^P¼ xAÑ‚¦M -àß 0†Ñè endstream endobj 20 0 obj [/Indexed 11 0 R 1 24 0 R] endobj 23 0 obj <>/Filter/FlateDecode/Height 555/Intent/RelativeColorimetric/Length 42467/Name/X/Subtype/Image/Type/XObject/Width 538>>stream -H‰ì×ç[”Wðl²»F“MŒ›Ò;HoƒôÞAš4 ÒAE°P¥¨D, ±ÄU׿mïçœó–)w÷‚1rî̼ïøÁyçwÝÏs¾øBFFFFFFFFFFFæ‹¿¬kÿçd6>k‚F6eþ[È&ÈZ¿ù—úH›(Ìa•HŸs>BÄWú|¬k/™ÿ'k¡øê#ò‡>¬ýeþ—¬¦BûáÿºVV"uüÉcI…ˆ¿­ FV×aí/+ó±±ÀBoBýùÿ¾z̘ú:þlYK…1ˆ-kÅÔˆ‘Ë:¬ýÍeÖŠ© cF ¾Ùjå¾±½ÕuXûëˬ ,ŒT¨ m"ßè£Üܦ)ÑÑxèuHŸxŒ](,„ -BÁ|Ëó}Ľo5&Š#ké°ös1Îj,¸ -ŽBP4|Çó½>âÞw*!DøPxXÒ!m|Š1u¡g¡¨PM Û)?˜‡Ýß®0Ñ€p#ÒÆ'½ Q ¡B˜à"ðûïØ±ãŸ”MÃîâÓB Â|˜ðÐé0Çaíg"cæ‚×…ÊBQA( æá'd'ò³ièæN|(”0!ÂëÆCÑ!&‹À!m|J1wÁꈅPA&ˆó°k×®ÝÈ/Jö¨ïèön|Ì”0":j{juèŠCÚøbÁ…RŒ+ ®‚¡ äaÏž={÷î݇ì7ÝÚ‡ðB@à!† סáЇ´aíXt!ꂳ@Y`€ -BÁLˆýûml8p9dº…OlllH #ò‹êƒñøA)jqèmXû mάîuAC„XPYp„&@‚8ØÚÚÙÙÙÛÛ6 îÙÛÙÙÚÚ’"!Šð@{°ò`ÝêÐp(Å!mX;FçTSlˆ(, Ã(ÐDœœ}膓“>tp€"ÂP¼<¨;PQ̆¤a½˜†p±U¸CD°`*€‚™€hpuussww÷ðð8¢®=pÛÍÍÕ•˜€ˆB>8¬\‡R‡(6SŒkÃÚjsÅ á.¾.4ª - € €‡#žž^^ÞÞ>>>¾ÆÁooo//OO0A‰p -E,¼9T–jÃÚkŨ0”ABsDçBc¡¨p!DÂüüýƒ‚‚‚µà*7üýýH‰7 ñ  -ð°g<„ƒšC³a±6¬ý¼6M, u¿ T4D ¡â3A$ƒ‚CBBCÃÂÂÃ"jp&LˆUùPx0F8ŒlðÚà4¤ŒŒyaÐ Q]ÐA]`·8pÐÖeáì‚®€ - `&BÃÂ!2**::&&666N ®bcb¢££¢"##€$,@Ð!ÜñPt°ê8ŒmðÚP&Ф±a±X´`¨.0F¨.¢¶ G<½}üü ™ˆŒŠŽ‰‹OHLLJ2 É,Çø ® II‰‰ ññ`E@¸΃•tPuˆæ âÐllcÛ†˜( - k?´ÍÓÂP uŽp¨ ûÃ"`²ð -  -f"!1É|,%%5--==###S ®ÒÓÓÓÒRSR@Å!qšâ!t¸¹¢:XsØìGq(6mº‰"ilTT_ŠÂP‰²_ÀÆÈ![{'gWÎ"0]A*â⹉4xÈÌÊÎÉÉÍËËËÏÏ/Pƒ‹¼¼ÜÜœœì¬¬ÌŒô´ÔT1ÄíA:¼½8‡Ãv¶Td3E« NCΓ ‹Ù$Ñ -çÕ¶ Ô…‡§`q42ŠT’QŒDn^~AaQqqIiiiYYY¹\”––”äçåB’BýhÒàïëC8\œ©8`ƒfÊΟPz²46*–&‰nÁÀy„æ\8²ºðñ `,¢cã’ "=3 &ˆDIiYyÅñÊʪêêêššZ-555ÕÕUU••Ç+*ÊËJK „Æ#1tD„‡…úûb¬¸»rXFYm(eë×[Ôy"Kc½c C™$ƒ/8è]ø‡†G€E¢áº"+'/¿°¨¤”HTU×Ôž¨«ohhllj:ÉrŠ¿455664Ô××Õ¨­©®‚ŒGz鈋‰Š8 -~(fÃî« Ú6LiÈÒX÷˜¯ìHB…Á Ω6Ø;™ #¨ ‘°HÏÌÎÍ/,.)«8^UÍH€Ã©Ógš›ÏžmiiEÚ(ô¦µ¥åìÙææ3gNŸ:ÙÔØPÏÀñÈÏ#©Ç’“Gxhp ·áäÀjÛ† Yë“C›$T4H˜ 'î"(„Õ†cQ•U5'êA¢ùlKk[{{GgWWwwOOÏ9%xßÓÝÝÕÕÙÙÑÞÖ -#|€GyéÈÍÎÊ@u$ÆÇFGå6°oˆÚà4h×ÐV YëÓCL­0h`ïÄ‹ÐðÈhª‹´ŒìÜbh -BÑÚÝ=çÎ_èí½Ø××߉rùòeöÚßß×wñboï…ó`ÒÝÕÙA@šá<˜ŽÒâ¢TšÅÁløy{²ÚPhÐj¡4¬ý?Ó˜­ìLÂŽ$ja`Ápóðb}—ˆºÈÊÉ/,);Pq¦¹¥­½³«ç܈€…+W‡††‡‡Gxðnxhhpp`àêHéï»HBà‚úà:*+PhŽŒ´c²äÚpuÖÑØþ=ÍYsÊ$Q ÃÞÁÙÕÃ{gõEbr*ÕEqiE%c]Ý@Ñ׃CÃ#££×ÆÆÇ'&&''¯«™œœ˜»vmtdxh„\‚HWG;tœ:Éq”äeg¦¡7b£"‚|½Ž¸ »Æáu»2O¨4¤Œõ‹…Ý“`°I ' o?ìÂEÕEum=cÑ h -B12:6>1yýÆÍ©©éé™™ÙÙ[”ÛìïìììÌÌôôÔÍ›7®>.õ_¼@:ÚZšO7GUEYqa^vFjrR| j#ÐÏ[ÐØ·‡hèæ '’ƺÄäP"vO1It…áå¬wQ^YS×pò cÑ CÃ@SÓ3³·nß¹{÷ÞÜÜÜüü¯”ûìïüüüÜܽ{wïÞ¹ %3ÓSdìÚÈðàÀ”Ç…s„ãÌ©ÆúÚªãeŹYé)†„X†N(|ž¨¥!ÇɺÅÆ“„Á`†‹»' -#ôhTl‚A¸¨buÑÚÁX\„ЉI € ˆ˜ÿõþƒ‡—–iYZZZ\\XxøàÁ}(™»w@f¦n^Ÿ¼zÕæhEqÀFEiQ>Õ† ]Ãé°íA>OXiÈt]czZe»§X1Ø$¡#‰(ŒÈ˜ø$슋ÓgÛ:{.\$ׯ¡bf(`âáÂâңǟ<]^^YYyöìÙs¼{¶²²¼üôé“Ç--B€™iÒqm8úzÏu“ƺšÊòₜLA#À×ÓÝÅ‘æ‰RfãÄÚò3‹ eÅø‘`ì?`kïè↠#…—x,-3·€¹Àiï:×Ûe`x,¦¡bnþþƒ…ÅG Ï{ñâå«W¯_¿þò†ýÅÕ«W/_¾xñ˜¬,C|<¸??w÷έ™©“ã£ÃWú{Ï““ µUå%¹D#&2,Ø_Ì¥4ä8YßX>” ¶b`’h…Aƒ$'¿¸¬’»è>ºã,HÅÒã'Ë+ÏžCÄëßß¼ù×Û·ïÞ½ÿoʇØëû÷ïß½{ûöë3*ª3øÙs²{v“¸nбFÍšÄXbÔ £&k4Ѩ«±kĈ¢Ø¢ €t:ô^…¡ 0ÌÐgh‚€‚(1É®Û7ž³Ïÿ}ïÌÜ49ÇáÛ}¿0—Ï¿ó<Ï?; PÒÓR xÈ¢##ÂBƒÃÇËÝí²3lØÛ?bme¹g£±|É‹¹¬Ot¡!ÕÉ@¿~aè¶'Ý$hv’°À@‘lÞ¶óë}µ%Nh/_b%ƒŠ¤äÔ´ Èɽ‘—Ÿ/—Ñ+.f - -äòü|8ÉÍ! éà‘˜#‹ŽkW}½=Ü.;9ž=õ퉣‡8u«W,[üÉGš›Mã¡1r¸®N ãw’ŒxO‚AÛ“&n4ɬÙó,.¡ÀظuÇK«CG(/áÂÃÛ/ („X @’SÓ3€&ä… P¢P”–––•••ë~ã? -E œÀˆ@Àƒt$%ÄÇFG†‡Æ—»‹ó…s§¿µ%»·oÙ°vú„‡-1º:‘†Æ€½þ`еʷ'›S¦™™øÑ'‹°0(’ýÖ6Çíì™ OŠ‹ðHYlBR -²"I åJeE…J¥V«+ _*UE…R %¥Š’’¢Â¦#3=-%)>V×ý¼=\/]p#Öû÷îÚ¶ }ÂBc–ÙT:O¨NŒ††$ÃÔ¯ÂQ‚íÉ'šdN(’c'¿;sþ"w|=":&>,²rHEQqI)LT¨À¡ªºº¦¦¦¶¶¶Îðð…ÿUWW‰ -@Àé!Ï'ÌFtDXH ¿‡£qÂæà¾=;_ðИAu#…¡!ɘ÷4Âöd7‰ÅÂ¥ŸéErÚáÂ%7¸ cq‘šž™s#_^H*EUUu 8Ô×k4Zm^£ðè·V«Õhêëa¤>Ô*ey™Ñ!ÏËÍÎHKNŒ“E……RãœýÉc‡¬(4Ö­þüÓEóçÍž9}Ê$64øÝ*'󞃎’7ib˜ÍÂM‚&YµvÃ!000œ]=¼¹‹¸Ää´ŒìÜ<°P”•W¨*ŠzÑØØÔÔÜÜÒÒrSôðÙÜÜÜÔ$ZòÈŽr$läd¥#6b¢(5¼Ý]œÎŸá¡±}ózV'$CÃ0A…ãä9i‚šôýF$CãåW ƃOŒÅÔ$›p’P` Hœ\®xù_'),.8 u%TE‘ˆÖÖ¶¶¶[·nµ‹>ÛÚZ[‰HsBDC8*ÕŽ¢‚üܬŒTЈ ðóBŸœµ·=j½ÿë[7ü‡+ ÷%þž×ê(:J°=g˜ÓÄX¾r šd·¥ÃþŠÄÓ÷êµëÌEVNž¼¨¤”±@VŠf˜€ˆööŽŽŽÎÎÛxwt>:;;;:@„|­¦®–ÙPƒFvFjb\tDhÐU„ÆE‡SvT';·nÄÐÐËxëÑ£$÷ŒºÄ]«8J°=…‰Ášä  ÃÝÛ?(4<:6¹((R”)UH‹:–T0 Aºººº»»ïŠ>»»ºˆñ€Ž›-ÌrC ‘•žœÀBÃ×ÃÕéüi»ã‡ih0 ž’Ò5ÙûEïMŸÉ``blÜJMrÔ§*F@pXdL|rZ&wQ¡®B‰hšš‘PA(ºDOOÏ=¼û¢Gß==$„xtÙhÐÖ×V«+¨Pò(4b£ÃC}=Ýœ14 ÃRÑO›H·‰É߯1›mOš»öZ:fwÊá" IRjõsQ[§ih$¤AA(ˆÞÞïé=Ð?|ôöö’ð@x¶VŠúš*µ²´¤0?'35)N~z<3ô;£ŸÛD’a²÷K0& 0°=¿Ü„‰qàðq4 N -Œ(*’ì:õh”È-Æ¢›¡ €ðÞô~Âc?ð<î!:ºîÜæ44uè„F~nfZR¼,‚2ÃÕYh“´@—-æWëÄñãÆ¾¯¼4x$ÃÔ¯/‰`¼? 0p”`{nß½ï nj/?i™¹ù…%eJîqq«X ? ‚P0àðWzã~r"àdƒÓ¸Ùܨ­«©T• 2âÐ&Ø.N§N;„ÛdËú5+>]d1÷ƒÓÞð6dŒ6„d¼ ’! P<ÝY"‚ñ"`¼2Dƒ]«8J°=¿±>b‹›ä²»7N’Ș„”Œì¼‚âR¥ªª†¹ ¸@Z@X@™@<¤÷wzìÂpÀ§Ñ‰­ÑÂê2X›$ÆE…]ÃmrùÂ9{Û#÷íþjÓºUŸ/]ðñs³©“ßyëÏcF :eC’aªg2ŒÁ€1ÔÆò•t”Ðö[ºÐbŽ9‹ ¬ !2¤ËĤ¯Œ×†Œñ“¦Æ<ŒOŒ‹.¾!²„ºIJ••B` Hú¸`,Ä&ŒmPh0<2heP—4jêªÕìbÍHŒ6,w}µ‰uÉüy³gNo’xeô¹L$ÏðÄgɯ‚qŽÁ¥í™™+/.­¨¤“„ƽûß?л0°à~6<Á†(¶2Ð%£­…îµRQ”Ÿ“™šy=ÈßëÊ%dz߰9¸o7Ý%+—ñ‹uêä ÂaÂW†´?M÷?KžÇ½J0†=vÜø‰ Æ|~®2ö€á雘–uCUU«ili¥…ÁÃØ…Ž…ŽÄ#‘ A 6?12F“F‚_Ÿë×ð‘an6•.VÌÏá€aXÒþ4Åë³>_`÷êkÃF¼>vÜÛß6ãÀXF0v -0\ãzT,ŽlÏrUuM" - #tÏX‹ ‚ñÝ%]·u0x•ð{ÕÆ–õ´>|<ÇÜlÚv—ó“"ƒÃxNŠŒg~}×'?K8Œ cîüEÆÞÇ`dç±íIãVãž.0úºxdôºÈFƒÁ6`¨ƒÏ˜¶1.9žªDë>2Fèç§neH2žù=ÆÐ¸Wcª™ù\‹EËV¬!VlcpqIéC©ÆöÄÄ`M"îTƒ‹Ÿûºà4xdÁè¹+ŒOv•çjD®7çól|ÒÆ0À˜Îa#ãÿì—‡S–WGgggw“ÝXvâÆ»fÕh@°5Q׈HoŠ` EŠØP¤# ˆ" Pz‘&¨4!"X°¢fÝdg'ìsï}û÷~ˆð1™ì¼ÏŸpçÌýÃÝ ŒAŸx¯þ zUi¤i3Œ…ËV®Ù ½Õˆ×ƒŠ¢ÝOŸ¿èyÅ|.(~ÁÇþô–pä“ÊÕêÊÒ¢kWŒÄ¸èȰ ?/œ«{­·›Ç@SBA+îΖHd îäfɘqÊ“¦Îœ5WmáR†¹Õ;œ«ŒÌì<EIKÄ*(Yª2\üÂûeˆ€Ñ `4ßi¨«®(-Ì¿š•‘’{."4È×Ëã «£Ýëm&¤Jsh0>c$CÚE¼,Á½:u€±`éŠÕëµt Íwì±sB`É‚„, ;$b\2`P¹úèAgG{+£ª¢¤ j59!ælÄ©À“'Žº»8ìÛmea¢¿EkÎU$Ÿâ`-‘ÈÜ ·„ÎèÕ‰Sf@¯.X²â»ušº†f–»íœÜŽxòÁ€ZÅQB)Y’>¹à~,=/Ÿ“(i‰%Îö¶»v˜ëéh®_³jù’jóæ|9]Ò– þúÌ’é†ÆâoŒ-f–»ö0ˆcä”VMT”<#ŠÁ1 q.¸_Æ4]P«-MØ=‹÷ öCîéô=U«›Ö­^¹l±†ÚÜÙ_B®‚|‚cÈ€!‘1Ø‘ ’%c•¨^]¼üÛlÒ10ݾËÖÑí°çÉ °3ç!WÑQYsó6 jIú₃­%ŠŽ{"ň; Šq‚R pO]ìžË©«Î5sÚ”‰ÊT•H`(ø’AÛ'›%ê‹–¯Z»q³¾Éö¶®‡Ÿ =s>>™TI5j•¸§`ID¹‚ÁÔ*‰âž%…¬bœôDŠa‹À(®Uc²Šò¸10þÈC"c 'Ø®}B–à^E`èo³¶qp9tÜ'04‘Å£[F\ðÀàE åžõµUå%Œb„#Å8|`¿½Í.KŽbPî©2~ܘÏF#%0|¢ö9 -Ù§2Ø'Û«ÆV{íó8}!)=+÷:–O Ö=كš—G† ÏŸ¢(¹×†Ü³Üózn£^ǹ9ÙíݹÝÌP Œ{ÄS"mÉ ®/ûœ2d êU#s«=ß;»{xûŸŠÀ`\ƒ£¼£··WîÁ‰’6¤5•eE×À=‰b"ÅpqÜ·Çj›©QŒ¥ _ÑŠñŧ(JF—ÀPìÉ·Oœ%s¨,^ݱÛn¿ûQ/¿Óçâ.¦_¾z½¸¼ºŽ ‰†Ì’ôö²d0`j•vOPŒÒBäžÉ ±D1ޏ;;@¬Zëo%ùöX’¯Ñ’0Š¢dp!†DÆ@ý2þ@$ƒkŸL– ^ݽÏéÀ‘~Á§ÏÆ]L»|õZqyUÝ­F08ŠÁãBÄ=Ÿ²îYUQR€Ü“R o¤$Võ˜%™K2–dZŽ{ -«DcÀ'"|û\„ìSGŸôêOß ð¨ØÄÔKW® <0¸îÉù0z{¹dÀ`£„uÏb재b¸:qc-ÉÜÙhI&(É(rO Eœ¸dís³žÉ¶6¸WƒÂ¢bŒü¢²µ -í|0dCŒÿ0~¢Á ¢ä^;qÏ2pÏìÌT¬AX1öqbuÉÂùüX•U \’ìS Û'Êë½ö¤WÏœOHÉÌÉ/D`Üii»×ùð±(.ø`ü‡€AEɳn%-M”{‚b¤`ÅÄŠaŠÁÄêâj$VÉ’|‚—)Æ’b(òäK†Êd U%`Ÿt– ^ <Ÿœ‘WXZY[Ïã5åýC¦VÙ(©®,-DŠ‘u:$À+ŠU#*Vi¨âXYI1xò$ƒØçlÖ>q–xx‘^ÍÈÎ-(©¬©¿ÓÜvïþƒÇOäƒÑû0èZ…(0 JJiØ=ƒý½rCŠc–dÅÒ…êò—DR ÅL°~4œ‘ }’,A½š”ž•{½¤¢úæíæ»? 0ž½ìl””— ÷LÅîäçåqÐÕ+ŽÕUË!VÉ’¨È]’ßKK¢ˆãl G2>ÉÚ§­£ëaOßàpҫ׋MŒGOž½@`¼/ž3µZ_{£œãž¾'Žº»8PŠAÇjK"}Š;Á–ÉûD’ö©ŠíS‹±O”%l¯Þjjm¿ßEÀx3`0èZ…(©½QV`PîîyÀÙÞf§%R :Vñ’LTV]©Iu}KÆ °OM]CÊ>}è,^­»ÕØÚÞÑõ¨›ã_ÆÏïÔ* &J’{úƒ{º{Zo75ÄŠc•^’1ŸKM2”'#Ã’ìSÇÀÌ’¶ÏÈèøäÌÈèÕÆ–¶ŽÎ‡ÝO_ô¼£·Ÿ`@­¶·6¢Z%Qº§+¸§Õ6ƒ-ZL¬Â’@¬ -—DRO?XE$Ùçf}dŸnÄ>Q–䔢,a{õÍo‰}ö ŒŸ0^t­B” 0.¥%]8O¹§ ¸ç c}M¢T¬*+.‰P=%0w¢’1Š/´õŒ·YÛ8 û BöI²¤¦²ä^烃ñOF7 %iã¢#Cƒ{:ƒ{Zšém÷dƒŽUfIþÄY‰ Åœ`K„’¡$CK×È -Û§O`XTLb*/K?0˜^}_0 VŒÚZP­b0p”D„Òî¹ÝÌp«6vOŽbHK2Ô' VQÉØb`¶Ù§‡w@h$Ø'7K˜^•C®|Ò`¼&`tÝGµÚP[Ej5%G‰Ïñ#ˆ{èja÷dƒŠÕ>–ä×~ÖßþÉnɈ‘C°Ž¯Â• S$Žzù‡DD_HÎÈÉ/,Ãö‰²DЫj‘…k•ŠÚ=5±{j¨ÑŠAŪH“H†¢N4XG£-áKÆN[G°OßàÓçâ’Ò³}Ö6 ûìb²d `<ìê0nl­†‡à(÷´bÜs‘†@1FŽþ­Ò’(úè/ƒ Öá#þ: -m ‘ u¾dœ -ŠMLÉ(AYÒŒ²¤›¶ON¯öŒ·0Úï6Ý®¯Á`\NOƵÊD‰¹1rÏï{~ýUŠ!-‰bON°~1$cÆ,dó =“ -’Q\ÎØ'“%?ɵÏwÑJ‘år:§VQ”˜mÝÌsÏ ã9Šñç?”–dHîwb[BIl‰ÚÂ¥+×p%ãTdt|rfN~QÇ>_ŠÛç{q³¦²´0ÿÊ¥´¤ Ñ‘a¤VI”èjÓî‰ÜS&V¥%QüÉn ¬“aKT5@2Öko¥%Ã$ãBRFv^a)ß> Æc!ã¢#¡Vi0p”0î9mòDÅ–dHŽ·%°[B+– M]#sF2ÎÆ^LËÊ-À’Áاh–¼Œ7¯{0Œ[7«Œ¼œK©ãÎEž¤V9Q¢:—vOb0K"¡À“ VvK Xç£`ݤ#" Íwûì3KúFEiA^Nfj"€q*€®U :JÔ!J8îI)†«Cv‚-ù gKp°‚dlجg•Œ$åU´}>yg–ôŒF, c=MëVƒ{ªsÜóC®büÚúÿrœ-!’¶«ò¤©¬°%ëµ¶[XÛ8¸öôe$£ìFÝ­ÆÖöŽýŒw‚ÑH‘™’{ötˆ¿÷±C®L­’(™7çï|÷”cHOvKFŒüoÉÄ)t°jn1d%# -$ã2– °Ï6°ÏnE€Q' \«(Jæ“(QV¢ÝSRŒ!>Ñ- [¢‚¶D ¶díF}SËÝvûÝ=¼¹’Q§¹HF¨d¼7¹Ù) 1 /ƒ.¶»,ÍŒ¶jo$µÊD‰<÷”C±'wK&Lš†¶«¬þ!ÑñÉH2Êªêø’ño""öù~`D…û1`êjoXKjG‰À=%ź٬Ÿ¥‚¶«l %qIéYy¥7j‘dÜÇ’ñêõÿØ/¯÷=¯;° Ç=çreÈ‘T¢R†Ò¬yžPI*)4h y¡h@%‘HÉP)ÍÒ`*Ê"qŽ¡£sïZÖýþÞaïýîýîÞu×ûý~ëY¿çóÀúü}hˆ~} -ãÙ“G8•F! FxHÀ¶-î.ëVÛZ⵪Œƒ!…ƒAnOfbˆïè]‚«œ¢Ê¬Vì`M>z<7ÿâå²J÷Û=ed£ÑL#!†Ãi•­… »VQ”ÀÄø‘½=™‰!¶£s ¬È%jàK;Î`Í9{Œ†–{8GÆ ß‘A Æ'N0Zq0®æ)‰`ØX#0T•±Zå‰fbˆïè]ÁŠ»‚ÕÜ\‚kRZæ‰3…E¥Õ02ÚŒaÑP‹ƒ‘“uìïëµÉyí*sã•ÚËT‰Zå% ÿóàù…*KÁ%fÖ«— `Í>]péÊõªúÆ;­02ž T0þÀ0>㓌h C_[céb%²VšŒƒ\Œa¶§8¿Kdæ/PVƒ`5±´_ë.!ƒ5ÿBIYeí-42ºéGÍú{Ø`X›a`,ÂÀÀk•%Ìö× t‰*r‰…í2X“XÁÚÐ #£Kä‘Á ”«¼`øùxºmpt°63Ò×R_`@”p€Á%ÌöÏ u‰!¸dÝFÖÐÈø©Ù¬¥åÕ7›`d<yd Œ  {CÀPY„Õê ®Ze&†8O—XK6oõ—P‚½¢ !`Ü↕™!R$Ìö”Ä Ã%d°ãÁÚÁÚ .<28×'Œ×/{q0ZÈ\åÆB ˆ I—¬Ðe»$jObjƉ3çŠK°`…‘Ñÿ¹„ÿÈ ã˧"‚aj¨§¹!;Wƒ³V™(ç p‰œ"Å%!q˜K -.²ƒ•=2ùŒ j¯ò‚ÑI†ÇÆõºšËÕ”10fJM#Áû%b<¡.1&\Fº¤\¬_ø¹D8÷ø€±ÆŽ†<7¬Ze¢Dl'‚Kl0—î"\RXTZQs“3XŒ `¼bƒQu½” KS0¦ã`LÀÁ`×*Ã…xNd—øc.I']Ò„\ -ÖÏ_øŒ :0>üðþ×·¢ƒ¡H€Q‚À`¶§dnX.9t$+çì…’k•¸K8ƒ•{d|£cè+ã7Œž§]Dc Æ?0$vÃrIRZæIpÉÕŠšä’nÌ%ƒ• -Æl0X`4ÖU•gOç ÆÏ³0$~l—ŒMuÉ|^—¤¤gç\Â]ÒÖ‰¹Dp°’_æ}ÆÃ÷n7Ö#0Î cÆ÷ ;ê—Aë;N—œ—”UÖ.é— -VN—`|âã>€Q]^ZŒÀHOILàc™* ÆTŒq :®ù)ŠKŠ‘KZ0—ô¾¬¼` "0Þ½èïëyÖõ°ã*F*†§(`üC¼G™Ÿ¸K~$\²€Ó%~„KN#—T×7Ýmëìzúâ%Å%Áø èUãųnFS}uÅÕâ ù¹Á†Ûa`üCÜÇ=?ÇO˜.™&ůK—4Þ¾ßñèIÅ%¼#ƒ®W10Þô¿0µ·ÞiºYSqµÀÈÎHMJˆ!Àp´·"ÁãÆ_I0Fú ÿ?Ë%cq—À—»DC‡tI¸$\’GqÉ+¶Kh‚•Œl0:p0®á`¤%í öçOŒ:.—`ós -ÌO鹂\r“Ã%ïãçn0†X`¼~ùâù“Ç­w›jn\+¹Xpš†/†!g•ЃÁ˜DŒGã’&£ù‰\²\bD¸d'¸ä ¸$ŸpI+æ’þld|¡VJ¯þ_Æ ôêÈ’×/{mFí²ËƉ̴ƒ{cÃCü}½0þ Gë4?çÈÈ)ª,ÑÐ10³É%CB\BöêGÔ«¯_!0:Ûî¶4ÔV–]¾tÀ8|p_lDH€¯×&A`ŒaÀÐád.Cu‰’šºÖJKÜ%Áà’d—¼Õ%$Ÿ¯z{žvu>¸×r«®²ìÊ¥sgNf>´/."4`›×&çµöVfô` ƒC"Çí’qÂ\rVt—ð‚zõý;£ÀxˆQuãØ‘Cûã"C·m0¬Í õ0FöØ.5¹¾ z—„‚KR‡åš,ùLôj_Ï3ãþíÆºªòҢ¼œcG’÷ÇGî -ܾÅãOqÔ/wÉdpÉläÕåZú"¸ä½K„õj;€Q_]^Z|>/'ëHòø¨];·{»»8FzZÆByYŒ:®ù‰¹dæ’yò /]¡kdN¸d7¸äð±Ò%÷»ä Æ7v¯~åèUF릛ÕW‹ÏŸ=•u4åÀž¨Ý;wxovqZec`¨/Q0æJ3`ŒÌ±À@dp¸D\¢Œ\bjíàäŠ\ .É8q†Þ%_…¸ëÕßñ^}ƒõê£FMŵ’ gOOOIܽ;hÇÖÍ®ú,0f2`ŒÈqÏÏñ&.‘UÀ\bliçèì. —=ž‹¹¤¾éN[çcA]òM@¯½ÚÜPsÀÈÏÍNOMLˆ òÛêáºnµ­…±¾6CcÆHÕ%cq—L>k¸D\bhn³z½›¸$šê’öGOž‹àž,ùÏã^sCmeÙå‹§³3R“b‚ý|<6`hð‚1–C‚‡,—`ós -|Ò0?•ÕÔµWšZK<|À%ñ¸KŠDêò¸³äý»_úY½Z[ ½ZpúDFÚÁ½±áÁþ>žׯ±µ0Y©­±tñ"n0¾cÀÜq»}“±ù)«°Hu™¦ž±r‰÷ŽàðØ}˜K.^¹^%‚KxÀbgIß èUÈ’[uUƹ3'3Üâïëé¶~¥ÉJ Œù2Ò3¥8À€/ƒCRÇvɨјKˆù9GF¹DÇÐ sɶÀÝÑ „K®Ý¨»%Ô%l2p0°‘AÉÔ«Uå¥E…y'>´/."4À×Ëmƒ£¥©Î -cÁ|™9³¤¦ý“c$Žúeà.ÁççüJÈ%&Vök]<|üC"㤀KΕ‚Kš‡çÖú$³¤Ï¬Wór²Ž$ï ÜæµÉÙÑÞÊÔ@wÅ2U%Œ) #pa,—LÂ\2O~Ñbp‰‘…íš ›¶ì -‹ÝwèHÖ©|Â%­Ÿöˆê®õ‰²¤£í.Þ«ç¡W&ˆÚ¸}‹»óZk3C]M†Ƥ&2`HüX`àósÜxÂ%Ò2rŠÊK4t ̬W­Ûèé¸+*!)-ódÞypI­È.áYŸì,^­Å{õxzJ➨Ý;·{»»8!0ô4—«*)ÊÍ0þ…ƒ1žCÂÇ=?ÇO˜H¸DVAIu¹–¾‰%¸dóV¿ˆ¸)éÙ§Á%å57›ï>xØõ æ§—PÀÀG%Kؽ´Ã{³«Ó*s#=­åjÊÆÏ³g0`ŒÔQ]2wÉT©YsæÉ£ù©kd޹dûΰ˜½¸K.³]Ò×ÿæ—wB]B¬OV– õ‰²¤‘È’™iI{c‚ý¶z¸®[ `èk©òS0Fæèæçø2¤eæ+*Ãü40µvprõô Ú“ˆ\RXŒ\Ò.é~Þûjà-æ’AÁ.áZŸX–´ÞáÈèÕð`ëWÛZëk«/QY(/Ëã{ ‰ÝüœŒÍOY…EªË4õ-íݽý‚Ããö'ÍÎ-¸TZ^ .ië¤¸ä« —ð®O®,9´?."$À×Ómý;K“•Ú†‚ìÜÙ3¦Oý cÜٯﯨî4Žã'çd³º®“5bÙ˜è -ŠÁc/Ø£ ¢ ,`A¥IïEšHSšQ ÅXh‚Dz³‹`]kŒâ±þ¶ÏóýÞ;sïÌýe&'Þï_0gÎsîçõCÕËÏ^~ŽÔÔ&[²hÙÊu›,l½üƒ#b÷J?žWPR^Uß [rnÉ+%[¢@Ÿ÷¨>+QŸL–Ð^Ýl -‡¡?ÖôÉãá0F ‡Ã½*†êŸÐ–P~ŽÑ™[¢g` [bãèî³/95+ç\qYemÓ•·%[òFÙ–0‡ñ‘¯ÏVÐ'd è“ÍìUKs3ã¥úógϘ<^g ÆÞaüM< Õ=A~â–h?yÆœ‹Ÿö®Þ¡‘qIG2Nž.„-iÀ-¹[ò¬G[ò‰ ƒêó «ÏºªrÐ'›%þ^®Ð«fØ«?Ïž1e‚ÎMèUzýÄÃPý£Ÿ º%ïÍð¶d„¦ö¸‰ÓfÍ×_¶bíÆmÛ<ý‚÷ìMLIÏÎÍ'[üìñ–°È`ôù겄è“dI,d‰·›Û«stá0´ÉahÐÃèÃÆ—âa¨êñ·„å'l ðsÂݹ –›˜m±vpÛ±3<:áàÑÌSg‹`KqK:pK~W¶%‚Èàé“dI›%L¯êB¯jkÁa ÕøŽÆ?ØÃ\†ºÿ¸¿ü“n åg?ÊÏFj¥ü4Ä-±sñ -‰ˆ;pøØ‰Ó¥Õ -ÀÏm‰ô0Þ¾yýêåsDGŸ¹Ç!Kâ¥Y"íU-ìU ìU<ŒÞ½¾úJ< U>Eüüœˆü\Jøéäá»kwLbJZvî¹ó¿\ªkÂ-é|øø)Ù’·Ýl "ƒÑç3¢Ï۠ϺKå%y Ï¤È?I–Ð^;c09Œ¾}ä³DÝÿÛ_ÿñùÙGÊÏÑ?~.1ZmjneïæŸt4óä™"àg#ò³ãn ùdÀa}2¸Èx‹Èàè³¾ª¢´ð4èóà>È/;+&K¸½ -‡Ñ_< u<~þ³/ÝÂÏIÀÏEËV®Ûdaëìé»ÿPúñ¼ä'n å'†B~ - ã‰ÕY}B–x»Ú[K²„íÕÁ¼^C•O–Ÿ}û}M¶äÇQ£u(?Ÿ6Žî;‚£÷%§fåœ+Æ-¹róöÝNà'gK?ãAŸ¾N¶›×¯¦Y2qd §W{Ó^C•O€Ÿyü\ü´vpóÙ•pðh橳Ee•µWnܾÛ[òœlÉ;á-Q€Œ»m7¯µÔWƒ>ϰúôót±³475ág‰¤Wéa|)† -ï“ÁáçH>?]¼üC"b>v"¯ ´¢º¡åZk;nÉ3fK>(Øyd€>ÛQŸ5}† >­@Ÿ˜%³™,ù~˜P¯~!^†ŠžÌ'CÊÏ1„ŸzÆk6l~ú펡ü<ü¼z³í^çÃÇ\~*Üyd€>kQŸ¹Ùé ÏÈPVŸ˜%TŸŠzU< U=n± -òÓùiïêü<ƒülD~v<ø/ËÏ‚üä"ãWšêPŸyÇ>%Ñ'ÉÝ©t´Å,Q÷SÂÏq\~zúïÙ»ÿPúñÜüäçµ[픟pŠø©W›ë«ÊK NŸÌ8z0!:áÌêsîLÌ-I–ˆ‡¡¦Çßæ“Áðs -ÃÏ-ÀÏ;â)?‹ŸMÈÏN ?ß+ã'n AÆ+.2ª 22S“÷íÝìïåbgÅÕ§Õ''KÄÃPñ“á'ýd?GðøiGø‡ü ô)œ%êþË>“Çãgo²%)?u8üt–ð3/¿¤¢º¾øy?Ì–|P²%²ÁÚÞz‹ŒŠ Ëͦ«3§)ѧثª{Êø9Y–ŸÉ©YÈÏKÀÏ›m„Ÿ/¤üT°%¼`}Ìkc-"#W‚ W{«-f&Æ€ŒYÓeô)f‰zž`±*ågaéÅš†ËÀÏàçsúÉø €Ÿ2Á -Èø­ƒUÖ®X¶èçÙ3dõ)†zž?á“ñ#|2Ÿ eù™ ü¬~Þbùùº~²[ ÖúªŠÒB@ÆQ 2¶2 /˜£+§O1KÔòdŠ•åçpäç$ä§!ðÓ’ð3ø™‰ü¬¬m¼Âò“l‰0?+ ãrC AF c/‹Œõ«—ƒ>gNdh"2 ü–§Oñ0Tûd>}ûý-[¬ÈO=ÊO'O¿] ? ¤ü|ô¤~ò·„+é²È˜7kA†}ªûû\·XyüÔKùi´ÚÔøéƒü<Èð³®éjÏù [ VDFs]UyIAÞ‰c‡“âe1‘¡@Ÿâa¨ò ñSc(áçDà§>á§-ág¬2~ö|KÕ€Œ3§2¥È°`1C‚ QŸê}ÅÊççr³-6Žî¾AáR~Ö?Û)?á0X~*Ý’.É–´Þ¸ÜXs±¬ølNVZJ¢¦\d 6„ÕgoQŸjyÅJù©©Mø¹ˆðÓÎÕ;€ð3ƒÃÏN ?9["ÿÉøôéƒì–°ÁšÁèãæ`M¡Ç ƒÑçQŸj|òÅ*áçÂO(Và§ò3ù™_RÎðó”ŸøÉÙ’wÒ-aƒµ¤‚õˆ…!êS}O¾X¿ÁOÆX¬ÈÏÅÈO+àg`XTÂÁ£™ ?¯pùùN?y[òòųÇÜ¿Ãë¬1»wùy:ÛÊ ã{!dˆ‡¡Ò'W¬È'cĨ1 ?¡X-l½üƒ~”VðøÙÕ ?ù[Òq‚µ©¶òi°d˜2 8ÈÌ"CÔ§šžÂbýÓxäçä§µ£ûŽ ðè}É©Ù9ÀÏKuÍP¬<~ödKž“-i»u­¹^¬d¬1^ª?_2ÄÃPý*Và'ë¤i³€ŸP¬–v.Þ!‘q„Ÿ….Ö~vñ³g[R}ñ?X·m\»R)2ÄÃPõ“|2„‹UÏø¹ÕÆÉÃw×)iÇsŸU”Ÿ¿öˆŸÂ[RV|.'›¬ëW-Y0G—"c¸ˆ µ?…Å -üÄb]lÅjeïêOøYTVYËç§ð'CpK:ïA°6×Á–@°#Áàíjoenf²Ü@ožCä‘!†jŸ’bHŠø¹m»³§_ðžØýX¬ÀÏꆖñShKÚ[¯·4TW”ž9I·„ëV@ÆtDƸȀÑ¡Ž'T¬ƒh±ŽÇb]‚Åjíà¶cgXtBrjVιb!~~ü¿·älNn‰L°"2F‰Èø3¼n‹øiaëâåGе°øyùúí;ÝðSnK^p·ä<³%!tK X—,œ«;¡)"ãÏðº+V,VGwß ð˜})iÙ¹ùçŸÍ„ŸðÉPÊOÞ–üñâÙ“G¿r·$¶$*ŒÙ6XÇi3Á*"CÍO¨X5†Òb~.2~ZÚ¹z„FÆ“b-º€ü„bí??‘O†à–¤¦$Æà–`°®[i( ÖÑ`‘Ñ‹E†x*~Ê‹u-ÖíN~X¬ÈÏüäçµÖ;÷)?_+à'gKÞ oIúᱸ%$XWá–Ð`Edük@ÿ~"2Ôù”ë,Vs+{7ŸÀ°¨R¬Åe—ꚟÃ'ãu—B~r¶ä Ý^—dIŠÇ-qs°ÞbfbŒÁ: ÖÁƒ>wd|Á>õþ^±~Ãë(V}R¬¶ÎžþX¬ÈO(Vä'S¬dKÞI·Dî“ÁÙ’§²[’ü?òË;¨ª; Óìn¢ØH¥7á"piÒ¥_ŠŠÝØbé ½ -‚ÒPAŒQ1Y *ˆš¨±D,kŒf7Ö5×ìh6³“™ý¾ßéç^Š -:sýþ¹÷ž¿Ã3Ïû¾å˜%‰˜%0X1Kxƒõ,ïu{oþU‹•(«5o±&¤eç—“ÅÚBëÕ›ìb…úù›|ýüƒ§ Q–|ûÍWG0Kª*0K’I–L ÆÁê p°¾`tÏÄ[ÀC¼X‡ª*^¬+q±bý<|+S?{^¬¿ÿYB”A²äÖµ+çOŸÀ,ÙµdI*/KÆb–(¬Ê^2zMÅÄC¬ ñb dkAÉ&v±vÅzïác¶~v¡ R?ŒgOEYrhoíŽJÌ’t6K¼ÜH–¼kƒUÑÿý}¹{ópˆ•Á_¬Žn°XAÜb…úÙtŒ,ÖÜb}Þ…2xõ³äñƒŸ~¼}ãêe’%ûëvR»Dœ%Ƙ%Ì`ýPÙkÏHtH?¿‚d *«µXCq±R‹µ‘,V¦~þü˳î«‚,i;sâhÓ=»¶máeÉ$*K,Íé, Vå£k(þ$¾nðèÇ—.V5\¬  \¬Þ§Í^Ç[¬¤~Âbeê纩Ÿt– 20Kî~ý»‹íçNo>´²d—ÈøYò. VÅTyø3þõŠéo6Xe,!ÊPe0‹Õ,ְؤ n±žÀÅz”qÿa·õ“Ÿ%ÿþ…Ê’›W/w,©Ç,Ù Ê{Q–(3 -¨àhèò„|ô+òÊÀŪÇ,Ö\¬!Qñ¢ÅJê'(ƒªŸ/º®Ÿ¿Sõ²äŸ%w„YRVœ—Æß%6–æ&†º£´”~°ÊcÁgâ/]Ÿ98úáEʀŊʰeÈPKÃc“3q±V¿Ôbe•A²äÉ£%7¾»„YÒBeIiaNF’`—HFAÉÐT8Xûö›'Æ‚£‚úÿÀ݇p¼Ÿ<>8:ú‡ ¾2²ÊÐeHQd±.NH[•_RÞÍbíeýÄ,9úëV:KÖ®^™'άjªJœ%".T°4à àóŒåƒ¥CGß¾%·XAj¨ Cs+[G7/ÙX¬K#VЋ”ÑËŪ°~Þùþúß.¶=y¬é •%ùÙi Ñ\–ØRY2‚Íe¬Š± ©à€Èœ -ûm ‹Ÿ޾fC¨ŒÁC†±‹U -‹Õ7•˜žSPº©kƒp±>eëÿä”!¨Ÿÿ¢ê'dɬŸ ûjkª Kr3’bÃ!K¦ÒYb¡8K” <,(*h$TðÉ>¥áè áès6h0eàb%Ê0•XÙ9ºyûãb]—’µfÝFj±;y®½×‹êç ®~þ€õó<ÔÏÃõýâóÏ6®]“•²pÎôà@È^– UÎ,sÁÇ‚… 7D|øp0Áƒ¢ƒ‚ƒGß±ñžHP?eØ€2ü‚&Ïœ·8,&1#§°´¢ª¦nc+(£“(£»Åú‡¸~b–ÜÅ,i‡,i>´÷Ëí[ËKòW¥'D‡.ž7sòøw"Kø\кఠT(‚¡pèSUU¥¿ ç„‚í'޾bC24Fhë2¬íœÜ½P!‘ñ©ÙyÅe[p±6ãb½tõæí^.VÌR?!K°~’,iÜ¿»¦ªb}QnfrløÒù³¦Nô÷v§²D_y³DÄ¥  ¤‚†‚ÐðÜÇ‚ƒ%ˆÒ¡BÑp°âè+6øÊÀ)Õ‹Õ뒰ؤÌÜ¢õ›«wÖÕ“ÅÚÙãbÔÏ_Ÿ=…,¹ÿ÷;·®_élƒ,i:@²d]^Vj<É’I¾.T–€2fIýwÞÞÉsAt!À‚‚‚¡¦6NwðSMM„GÀÁ‰£ÏØ(C…R†–¶,VT†O@ðô9 C¢âÓ²ó‹Ë·¢2p±^ ëƒn+¿~>%õó.ÖÏös§Ž·Ú[»£²¼¤ '=1& ²dÊx™§«£­•ÄÔê'/K”¨d(à‚Ò¸‚PA1¡®®¡¡¡©©9‚wð"!ÀÇG4Nrl¼Öë²YB”¡®9R×ÀÄÜ•!#ÊMÎ\]´as5,VPÆÙ¶‹´2z\¬\ý|ŒõóæµËçOÝÚX_·³zó†¢Õ+“WD,[0{ÚÄÈ{’%P?ùY¢,`{'Í­  ¤ @$FŒÐÒ9rä(rÚÚÔ'<ÐÒBBŠV}Æ¿~RÊÎ(ÃÞyœO (cÑò¨„´U%å•;j÷5et\¾vë‡nk×õ³ êgÓÁ=»¶m)+ÎËN‹Z¾hîŒIA˜%6–f&T–€2Ø,y_)²D¼GX.@" H24èèèêêêñ~êè%ÀàöÎÁA‰ƒÇoþò ‹”‹•aëèæ%›0uÖü¥+RV®Y»ñ³ÏAMÇNžm¿‹õî?<êy±þ—®Ÿ˜%?Þ¾yõ©Ÿ ûjk*7•æf$Å„-ùô“)d^n\– W¾]"$<.°[0Xà m@BO_ßÀÀÐÐЈwðÓÀ@>F¡=498ˆ8xl¼®6+C”!eøNš1wQhtbzNAé¦ÊšÝûŽÀbí ‹õ½XŸ÷®~ÞƒúyªŸ‡ë±~nX»&+%.³$8Àgœ³½Ôb´±8K”c—(ðp2X zÈ„‘±±‰‰©éh83røÍÔÔÄÄ1Ð×<dc q¤¼Ò+ ”¡†Ê041·Beø£2–EÄ¥d­Y·q˶]{6;y®ý(kwÊfÉ“G¼úÙ|hï—Û·–—ä¯JOˆ…ú99ÈÏÓu¬¥¹‚,Q†]" >„ -€‘037—H,,ÆÀYÂá§……DbŒ˜š#<8 Ã)¯†_)ehjéèÃb•:¸xø2‡Æ$fä–VT2[AW®ƒ2î?ìq±*®ŸG÷ïÞYU±¾(7396|éüYS'øCý´³–˜ÑY2³Diê§Ba¸à° T ÀÁÊÚZ*µ³…ÃO©ÔÚÚÊ1G<=]'dvŠ‚Hyéwf•1ˆU†©ÄÊÎÑÍÛâ´Ù –EÆ¥få­+Û² kóñSç.\ÂÅú³X_È/Öê笟×åe¥ÆG†,œ3=8Ðwœ³ƒtŒÉueË!”0p0\ . Dh,Š1À„p°³·w çH¾88ØÛÛÙ" €Ðî08t´Q4T}]m•1xȰ‡kŽd•á4yæ¼Åa1I¹…ë+ªwÖÕn=q¦­ëX¬½P†¢úÙõsÔÏ‚œŒÄ˜0È’)ãe%¶%†%T– Tš,‘ƒƒÏÑ…¾‘1ÈBbTcœœ]\]ÝàÜáàÃÕÕÅÅÙ q°<¬­ÆX€:LŒ ˆ8 T .Rmp#åå^šSd *C(ÃÉÝ;•ŸšW\¶u;(£•qY ŒßºR†¢úyþ̉ÖÃõu_ToÞ°võÊ”X?',±m¤¯­EÕO¥Ë^Å Á`¹ÐÖ] - © PáˆL¸¹óðôôòòöööƒ//OOqîn®ÀE‡•¥šÃ+Ã?Rĉò2/ÍS,VF6.ž~ãAKÂb“2s‹Öo&Ê8 -ʸʸ Êx‚ÊxN)£Ëúù\¾~ÄúYVœŸ–µ|ÑÜ“‚|=\lƘÑõSuè¥ËŒ0H¿ \›š™#¶vcœ]Ý€ /o_?™Ìß? \@€¿¿Læçëãíåéx v˜Å¡‚l@¦º1HE…Ò†0Q^æ¥ÊР”aM”<}Ψø´ìü’ò­Ûk÷5´|õÍ·¨ X¬~úìWþb+£ëúYõ³073)6|ɧŸL™ órs´µ’˜býÄ,¬ŒYBÀø€„ñòË4¨é;óûbu;³Ý}¡­®ÕZÀEh-š—XpÒ¹$q‘¨° _˜›“ÅçqÓ 8èÑ‘á¡ä `ÃÃÕÙÑ¡ @ãµèFÙ"ë+ÔეÁeˆJ*êZ»F' eü”ëºÊø·VšüüòóÓ•üZ3?ëæç6#ÌObKйŠâsã©™¥µ­ƒ“ëIo?R0s‘ÈNN,²ròò Db‰´DV*/+S(ÊñS(ÊäòRY‰TR$*æåda8؉ñŒ-N¶Ö–æG 6ˆEA±±54ÖPÆ®=+ÊðeÄce”U7uô97³p ”qo•2þµ¶2~лX!?ï¢üœAùÙ¼V~î7ÆüÔU±%¿A÷4ãˆ9Zg7OŸ€ ªš NOXˆÄÒb™¼¬¼¢²ªº¦¦¶¶¿ÚÚššêªÊ -EY©¬X* 82ÒR’˜j6ý}<ÝA6  t¤ì݃nM‡ˆÆjeìeì'”ááH¡1˜)™Hõ­ÊÁÑÉ ¯¾s)ãKPÆ3¤Œ„26ÏOt±n˜ŸG›ìû?ÈO´%¿ƒÈ@ñ©–ÄË—‹¸H]äæŠ¥%rEEeuM]}CcSsKKkkk[|´´4756Ô×ÕTW–+ä²b±(?/›Ÿ™žzš•€Ù&ùy«µGŠÉ~"6P‡ŒÆ/ÖUÆ1¬ rxL|Rº )£S_Ÿ~ûü¯[Oúùù ççg£ü\ž›†üìh¬.#ò36B'?ߨm|ù¹JhKP}ÂUÁððö $Si1ŒV2‡ËÏÎ+(’””**ªkëšš[ÛÚ;:»º»•ÊxJewwWgG{[ksSC}-Ð!—IÅ…Âf¼A £€6\ 6Ž¡EÁ±± - o× ”aï Êd0S3sE22–@®(ãû¿#eü¸Ž2~\QÆ·Oãü|_›Ÿuëçç«Æ•Ÿê/ù—ºÊ€-Ù ÿ€†%†PHXdl3)5ƒ‡¸ÊÊ*ªjë›[Û;»”=½}ýƒƒCCCÃÃð188Ðß×Û£ìêlokij8ò‰(?7‹l0ãéÑTr Ö†=^øDÑAcCi¦Œ¬|iY RÆ8(ãú{wï=xøè«ÇO¿{ŽòóÅV.VÈÏ¥ Sª¡žö†ªR±Ð€ü40´ÿ~e@dì?窵­#ƒEg%§e -r‹REeu]cs[G—²·o`pøÌÈÙÑQ•j ?•jtôìÈ™á¡þ^ewg{KS}mUE™LZT€ÙHJŒ‹ ÇÚps&bƒèP-ï‰Ê(’UÖ·)‡TSsXö'¬Œ¿l¦ õÅJäççŸàü\ÐägqA6×°üüùÿ˜ÿÕ§?&je -A‘aeãàìî…Á`$°SÒùÙB‘T¦¨¬©ojm,€Š‘QÕØ¹‰‰É©ééé™™øœžšœ˜SŽœèëéîlki¬«©Tld¦¥°H$?/¼( “-¡±ò3«aV+ÄP†VÆi¤ EMsgÿÈøù…Ë×ß{_O†]¬D~~pë+g'G•­õ•²"ÈO¶6?ÍŒ4?_“eìe È°wróô Ä`À’€0Ä%òŠjࢣ»·èÌYÕ¹ñÉ©™ó³æææç𛟟›»0{~fzrâÐ1<Ø×ÓÕÑÚTl”J‹òs\Îi&hjÃå„=Ž “7÷ŽÆ:Ê8xÄBG<=eÜT+ã‰F?¬«Œ•‹õÏO¾þ]¬wÞ½¶<ó³ ågÖËù¹W›Ÿš40ô¿å_k*+ÃÂÚî„ëI_RHXÀà -ò -%2EU]#âb`xDunbjföÂüÂâÅ¥ååK—.ïҥ奋‹ ós³ç§'ÇÇŽ^eg[sò†D$Ìæ¥Ú ð9‰bc#4Ö c=e6;f«£Œ‚e,"eÜðð‹Í•ñÒÅúHs±žG+ÊOQNf*ÊÏ #ÏÏUcB&Pè.-ñð S#cãÙ iiyM}s{pqvl°˜[X\Z¾|åêÛ×®]¿~¿ÿ_æAMÞiW´³³»Ýqvv§ÝκU×­Öªõl­ZuWë}BU¬7r -Ѐܠrßr( \Ê¡È  wHB€I ܧ܈H«¨ûÏ>Ïï}ß„£±Ç?éoFíŸw>ù<ŸoiiIIqQaA>;/—••™žúøQBˆƒ°áéæähkIiCû[¸(;¶mÙøåÚU -ÊÐAe¸x„Des+ˆ2º)eü€Êx;IS-VÈOX¬O²I~z»:Z›]Ô=£­©îù9å1ùë$ó‰2à–ì=ô­öi3+û뮞¾ ŒˆèûñÀEF+_X\RÊáry|~yy¹@ñù<.·ŒSZRT˜ÿ$/';3-%éaüýh`#ÈßÇÃå¥ 3ß=r`ÄÆ&@cƒŽ×÷ÿH‹yîT÷dFel¡•a22ž©ªŒ^åÅzËÏý†Å%ÌÏ=L~.RÏü”'Cö™I.^B)cûîšÇO×7¾bi`ø‡EÆÄ%"9y€PÁã **+«ª„ÂjxB¡°ª²²B >8¥Å…l0ˆÙˆ¸xÓÛݵab¨{öä1̓ë4`¼þy¢1ív=¥2þÁ(c­ +EeÔPÊ ”1þãÊ`«,VÌÏ/«+F`±NäçõÌÏ©2ƒ>&Tee@d]¶°½æ‚Æ‹Š”œN¸(ápùå•UÕ5µµ"‘X,–H$b±H$ª­D>¯ àÈ’“lÄÞ ¿sNŠëu{kóËFzçNׂ46ÐhÌ—¡¡|OfËýO«Œ¯(eœEeÜp÷»F+£\Ue(/V Y¬,²Xƒ|ݮۚ›èc~îÆü\½|)Y¬ê˜Ÿ“ÈÀÌ ŽÉ²•k¾Ø´*ã(n‰µƒ“»O‚‘ð(%#;—\ÂêZ‘XRW_/•6PO*­¯¯“ˆªJ„£¤¨€b#1.&*,$è¦7Ô†…éEýó§´µáDÙ¬„^í)ј­‚2NÉ)ãQZ6›VF‡‚2þ§ Œ+krbLx°¿§³½¥©¡,V’ŸkÕ8?gMWs ð¸vÃæïØsPëÄ]#Ó«pK¼ýoߌOJÍ`å—rùU€PÑÐØØÔÜܯþ4777566Hëë€GY)a#=%)áAthÃÇÃùšíÕ+Æú:С‡öÑh ÿžíyrª!‡ÆlšŒ9**#!93·€Ê‚2zTS†âbå³³Éb ôq½fcf¬ùy`ÉOX¬êšŸ“Ž6(ÉŒ…‹—._µŽôçá£'Ïé£2œ=ü‚B#bâÀ¬'Åe·´ØÈJK†“èÅb:ô…Æ&²PþµècüÖÌ@QL ÙSÜR“•a„Êð| SF#Qư*Ê{ñ|˜Q,Ö‚ÜŒÇ Ña¸Xí®^6ÀźgäçuÎÏÉd%›uùêõéc¢c•Ê»÷ 19QƯÖˆ€‹¦æÖ¶öŽÎήîîî§øàß®®ÎÎŽvBG˜ÄQlåç±2SA÷ÂCƒnz¹Ý°·2»dxFÊçp¸ÑÏ÷eM§Œ†ÆôÊØµ_ó(ÃBN…œr¡HÚÜÖÕÓ/¯ŒéòóåÄbm_¬Þ.ŽÖW.êB~ÂbýzÃ:’Ÿ€±Zæ§Ü4™KVšŒEôO—‰öÝ‹¦–öN¾  ¸%iY¹ùŒZ1á°èê~ÚÓÓÛÛG¿ÞÞž¤ è8¥õ²7…›•žü0.&òÎmow'ksd¼~þ6UûŠª¡š2ö>F”ᨨŒ:U”1Ób½‹‹ÕÖâ’>.ÖÝÌb•ÏO5SÆ´d|òéŠ5HfÆY=c3kGÏ›·ïF=x˜œÁbqx†¤¾|ÑÑ Xôöõ÷ Òo`` ø<޶V²!à•ÁIae¤&ÁE¹àãî<.”_¬YùÙ’Åð½ÿ¦T¡òh̤ŒZ?SÓ-V/g+S£ Ÿôb]öÉ?I~¢2Ô.?g ¦É— °Y!3Ìm®¹z‡GÇ%¥få”pU5` rÑ×P ?Ãÿ !ý@˜£½­Ù¨­®,çm¤=N¼bÃ×ÃÙqoþ³ÑX¶„(t…þN–20ÞA¬wPÆ›7¯_1ÊèFe(-Vs=\¬»p±b~.TÛüœ¥ -GOž7¸™áæ“ð8õ¤ˆÃ¯¨Õ!]Ý=½ÀÅ012òœy###àpÀ]¡Ø¨×àI).Ȉ‹ò :<„ aC¡¡upï7Û¶|µ~õŠed L‘20~†2^ª¢Œ¾ÈOé¤ÅzÙPçäQj±®a«zæç¬‰U ßÀÐÈØÄ䌨Œòª‰´©ŒÂ@.€ŠÑÑðÆÆðïÑQÄì14â l@oÀIm”ÂEIyäÇ qZ[ëàžÛ¾†-ˆÉO*tRj0ï”ÁCe´N¥Œi+*ƒ,V^ ›%¿Xua±î§+\=5ÎO|So(ÐÉd…FÞO„Ÿa>!*£­ƒc±ûžycÒæ Øhmn¨× -øwœàpÐl@‹vw¶µ4ÖCˆBläe¥Qhøz89X]1Ö;÷ÝÑÃûvÂ@ -ý”äÝJ÷„~sßûe•¡˜ŸCôb­&‹”àåÿ‡r‹u%³XQjêyKäÆÉð;\Àüwî?rüôøÜΞþÁá1ñÓ¡@¹¸&ƒÃÏžR`,ÞPÂàs6†‡ð¤t¶ÃE‘ÔV äÐôuw²·4½¨{–lW”5•pO([SÓ•Aã×PÆ[9eP‹µ¶’_šŸCë-_÷ë¶æ&ú3/V5»%rã~ˆ@ó¹qœ¬…ý†ü /_µsò€!x/.)-›]TŠAÈyN”AÀ@(Þâ£ècƒhk£µ©AÊ-¥Ñ€ñêãvÃх30P 56S©÷äC¹{ 1—(ã÷¿–2Am  !(#/+%16"ØßËÙÁÒÔP”!¿X?TßÅJ½©g+ó½'BôNÔý‡)Yy…~Um]C é pÆ ü5¾¢À@,àKÓt8´6z D!6p¢ ©IqÑáÁÞn×m-.êœ>®)—ÿý#jºþAƒ¼÷(»ýÒÊx5¡Œ–ƺšJ^I> ”u'ÈÇíš¹±Þoi±þŸü2ÿÊq]ãøò Û>ö ÉPR*BûìmXh¥D†²ÍÇ%Ñ@¦"Q*%¥D”PR!sdjS[%jG™ÚÙ¦°ŒçlYÇé\×ußÏó>Ïû¾9ÕÞgµzï¿à^Ïó½?ßú ʆ~p§i³Ý<á)†n‹M€/žsŠMPøäà&°3 M – ~(<‡›%×®^ʃš±?y7¼Ê@øøî`…Shj ©ajlÈÕõ/Òh°`4 O n¬%`¬gÐXãwD„ø®XæJÆjÓ°±¶¸`(v_ÓÐ0ìfÖÓÒjààŠ:··gÕI!Ö búi-,PzŽ,‰á‘f;嬗/Ö°C)¸5@^îÛîê…+T˜–æ&âüÇ—‰Ñ ÓZ@†v#QÖ(dÔÕ Æ -Èxp÷vy)ëÉ#û’vF…®_ååæŒÈ°ùycýÿË?÷H‡†ú:avÞJv’ß<ïòÕ¢ÒòÛw°N`h¼~;ãw|?ò\HÂÁ²AØ€FÁ±;´’EãÜécÙS“vEo ð]é ß:M(ÔûÛ¼MëÖb0¾l‡Èèðyd\‚K6ÝW/d惱f¥%X»b© «=\L3Œ•éÐ`u¼•ã¶c >¾ø¥´Š ñ‡Õ ¾Hž üäÒl 6dÑ ­qåâ¹S9Y驉((°B—¹.˜5ÕÑ@Ö'DmˆÖ%ÿd<}\ƒÈ¸ŽÈ8Æš¸3rKà:ˆ,Þ‹«¥&+;²¡¡À4ÚI„†}ã !$ƒå‚‡|l@4h†þÌ>š™–’ËW(LÉEuû„M ŒF›6_ðùùÇ‘ñIŠŒ·÷ª*n\ddHÞ½uó†5ËÝÍE–Q`5ÂXÙQ®“¯è£áÅçh;V*®liäJ—†¼M>‰G’ßÙØPD£¢‘!÷$¸+­ÐM45æL'lVîŠF -›zdln2>0d¼|ÆúàÎí²ë`¬¹ÇØÐM~+=“±ŽcµbÆÚ±¥+;jêDÑã4VžDÆÆ§¤eæœ:§Ð|’rh|’¥h€¾b4h†–—äŸÏ=q8VhôVpÅ fMs’ôI—μO0t ÊcÈÆ~‚1i2ÐXŸüV}¯ò—E—Ïc…K…oö_ãíq4ÎΜ AÆÓÚ¢• ¡NøeаünàÒ“™ó\–â‹ ß¾+)õ`öñ3çó Šoürûîx“µ/qåÐøÄߥ<H )Oƒ¼VÝ*+-º -ÜÆš¸k;Ÿø>Dz>± ßÀú£A‚¡Œ ['ÑM@†ÜX2n••2²Ó÷&Än Ýèçãá:+Îf8rLCŒ•V B_ä÷ìíÆ9M›íìæ¹ÒocHDÌîd¡ùýõ·ÇϰO”¡!¾Ky4hé½Dy­†F/»~íê%\¡i{qj®_í >CÚ'€n=!/1ªÈ˜¬ŠŒkBÆG 2 ®€ Hë%† kÿjï% ÑXíFÄŒÑâ•ùeÐ@=Á'i5pðpøò?Θ»ÈÝ[¡iY’>©a}BÏR …ŠÙ¨ åQMõÝÊ -pWZ¡85b"B6ú‘ºJûĈ÷ F²Á‚ú$ c !cšÉ2n6 x¥ûˆŒÂüógŽe§§ÄïØ²Ñ(«x‚½f+;*Ð =¡7Ù«ß_i„:òª¦Oª•úD),’hÐ  xTCîJ+§FRŠ€¼OàRŸèjQ4  dš2fÎwm62þIÈÀñS ÆZ -;{òÜgWTXÐúU^,©¶#Ùu˜±"2Zúü¤£ è€ùáoý™ -tÂ>h@60„4ý®JÈðþƒÈc½_#dd¥¥ÄÇD¬]±ÔE¼ Cîž–o¬üÈ¡ÑV±ýMÌñãn3FÖ'‚Ÿ\QøÉsô¡O”“¡ˆFkuÜ{ÏèWÜ‚©q{=3 ñÊúdÖTVìèÔ',¯1ÑäÈðk2êï822®\$d@N·®[åé¶`ÖTG"˜•%›=ºÚš`¬ü( Ñh+¡øõ}‚~âµ315=ûØé¼ËE¥åä'Ojqj4Ô'2E$z¿á‚r¯Š¦ôÉ‘ ´DÖ'.óPF  ƒ6Ø€¶Vûo¿Áó-v _AZBÆì? Åùy§s²pöl Þ°vùRØhZ=`¬F¢±j2T !¡¼O¬GƒŸÌZ°ØÃÇ7 xktÜž}‡¡Oò ‹oVTŠ}òVèÕdH¢!Y¡Ðì•05 -àÇQ]yŸ,Y8gú¤ñ£9Á©OX4à´×ÒÖÑB‹M§@Fh3Á/1ÅÙS~‘qâ0^%r˦u+=/ ÑÆjÅš­£†«pDh(F¨­<ÑOÆÿ8}ÎÂ%^«Öm -ݶ#>ù@æQøW~†>©º/ªkƒ}¢6¸Bijº²>A?ñXL·EQd}¢¡Õ¾½£³AW2–x¯ño2$wù"ã"ø!…Ñ““ È@|­Yî¾2:D‰ŠÍD“Œ•ŸVJ#Tèhsó^}¿4dĨ1'ϜǦFªk:WWè“;êûäßê¢!®Pìvaj ºž8rHè“åK]˜)²§ÚݧF]m8::zÐ%†Æf=z÷1ÏÕÑ›˜*CÆãF"šeô: #!62tF=i Þ£Ÿ¦Æ`¬† u}‚~‚Ô6éñý0k;Ç© ¨kåÝꇠ®ÿ¥O”£!LJRW¡O"·‚*.AUÄ· -õ}‚S£c=]]]†>߯ƒ†J‘Þ©¯W#ïß¾~…Sƒê]è“)»c"B|}–¹Î§~% >164èÜ©ƒœ!âþ±QAÆ ¨¸Æ"ムÄ® ¹Ç³Ó÷&Ä¢%ùx¸Î'tÁÞéOÆÚŒ•!C#æ';’>‘ø W×¾¤®ö¨®Š©êzôÔ¹‹uÅ>y§Ú'õõB6T¦†¢Opù,&íÚ„¯•9ŒPà¸ô F£# †1ë8Ž gŽŒ¸äÍ@Þ‚£‹òyK /KÈå@s‡ ‘¡!Æ*– IŸ`4ôäê:qÊÌyL]Ã@]÷*©ëÓÚ¯^«ë1òŠ}òLÚ'Çøs…†÷tÃJú¤§FC¿3 ÌOâGÆ2¿„ŒƒŸGÆ'dðR{TsQÈ8CÈÀ!Œà¢µÃ‰#㛯Úiб -G}ŸèuÒ7d覩á4U¢®I ®ÇQ]K¤êúî½JŸÔ+Eã£Øð/kùö+½ÆÞ+YAð†µ+ØÅñ‡}SÃȰ‹¾¾ÃLÈ*GÆjŽŒÌœÓy -dÔ6 Ti'À­° õ«½Üœgc8¡Ñ¬h¬ÚÐ%š† •h0u¥©Aê:`0Lñ“¦K¦FòC2u}òìyÃ}"‹YÁ{¦®Ø'7K-HÜÅFèBÆÑÖàä--Ìͺw5ìÒÅÐ‚Ñ ï3œ!Ã…± ‘q˜!£'ñg!r‹Q+ ƒ°••–¿·f“¬`0V#˜Ÿ:4?ÛjбŠG蹺²©Ñ §¨ë„É3æ2uÝ"¨kÞ¥«E\]ŸruUî“ú¦Æ›×¬Oî2/È;à °ÜƒPÖ'} &ÆFx æȰ¡#í2¼Vû‰È¸\X,"ãU£‘@j=IS'* ¢ééæ }F#تOO3cD†¦«pZ5fj8Nùû|œªêÚä>Qä89š‰£c4‰± vA*HQ\`iÒ‹€Kg¥÷¥.i‚( ‚ŠPŠå¨±DˆJPQƒ‰å$ˆQktrN&羟·ì»laõK>,Ï?ð¾3Ïõü~×¥˜Lêó³°èPÁln@›¥%Pa K!cúG‹ŒIjT P»³»¯Ìt=rôäÙÞK´OFUùäO>»¹Ü{JèARBó˜Ê·±43Ò×Áh,˜7oî¼y ´´/ÓÑ3µñÉ0‰MÉ’È cˆFÆõAõGDfMÛª°‹"~TÍÑ_¹„FÆ)24¨~Rçoʫƶjà}H§+ø¤óøésÄ'·ß|Ÿp+àÀÅ gäK(úÄP¢±h¡Ö‚ ´.Z²lå*¬Ã¶Nn>› 2Š*ê)dô2^j!ã9"ã§‘á{ 2¨o—f§ÅG…@˱±45Ð]†È˜)E†F-VöŒW5ôq):¸zûÃSMÎÄéÚØ²¿£ûúäšJŸü©bŸ<½³ñjßyº„Vá: %v£µ¹‰DcéâEÚÚÚ‹c0VCåáCåAdˆ%eÕÛw!2¾¸piàÁN£~Ò^%‰Â¸Œ¬ÍôV,Ñš«ñȘ¤FÕ ß*5]‰Oö<Üsò,ÞÉ7ãîE>ùõñCRB‡®“ÚMæAESB]ìm¬H4V,[ºÎÒå £5 5'¢5‚ŒZ(<‡²È¸?Ê ãõ¸Èø‘_¦pEua# D†öü9ˆŒ©ŒIJ«Æô™TÕX…UÃÞ™®è“}m„ãoç“gOa_.µ°„V—Q%4ÐÇ |bifl §»rÅr8+t ¦k­yP…%¥€Œ}m]H.DÆ=@ÆCõ‘í÷*‹ŒºÊâ<Œäf_7G[k3@Æb­¹ŸÎš1BÆ{‹ ÙªñλlÕ˜ÆT 2]iŽ'ãt­khÞßÎúäÎûä9w \î={²çð–F¼!ìAèÛu ýUº:ptõ æV ž€Œ2Nb:9Èx¡ Òï"¬¿ü‚ c{MYAV*jÌÓÅ~½…ÉjDÆl)2þ¡±ÈPY5>Ÿ¯½LGŸ™‹Ä'’ÒªmM¬OßÂ'2%ôk¼¢.rE帡„‚Oø<ˆ†‰áj}=8ú ;'7ï€a “Òêzõñ‡ô³/eq¹¬"È܌ʰÍ>nŽ<+Ϙö¡,241*«gºzPŽÏ),¯”">¹|õÆbŸ(…Æk¦„Ž0%ôÜ©£¤ ÂxÄàãîl¿¢ajlh```hd¼ÆÜr½­ÃFÏMh´ )2μ 2˜~3HÑ…«)“d¥ÄEoBd¬d,e‘1ùý1Èø«ïé/8 -«™®ŸI§ëF2]‰Ojå}BAC¥O–ÐÁz=6ÃrÅJ|âæѰ4_cjbllbj¶Öj=ïâî*Œj•QÈ8ŽÈdü 2ž2˜M„;JVQœ›žH!ÃpÕràMGÆ$n4(ŸÐUƒ®àž£+ùXŸ°ûä×§jø„)¡O¤%´J(µ\«áý¦Æ£O<]!VæfpÖZXC0œÝ¼ýƒ#DI¹Å,2zwޔŽwQ½ÄAƼÏfÃb*]¬Œ EU㟓?NW]œ®ôfLõ ³O<ä¬FõJèO#ÃX¯ 4z:ö·4ÖV` †úzlt´ç­·¶²´°°´ZgcËwrõò„ECÑ)(£† "ãkÒs@fê  6:,‹ŒÚŠ"D "W‡ Vk >ÿ‘A/VMG†²ªA|Uƒò‰½ ñI’¼Oîr}¢F }I•P¹åŠ%4=1&"8ÀÇc£ßvƒÍúuëÖÛðì ž¾!Bz!2z2nÜ"Èx¤”À(dÖûÕ—§ 2ê«K%b‚ g»ukõW.]8Œ1G6œéJû¦+ÏÁ•”OèRŸ¢¿”–бÑàBã»\oݸ®Îö¶<ÏÖÎÞÁ‚“˜–]P†Àjë<~šAÆ42~S‰ Rz 2È÷z:´4Ôn)ÊIOdx2,‹&!wÆV vºŸèp}’Fû¤­óØéó\Ÿ<Ï'ª–k[ën(¡è“¸¨0¿§›‹“ŸÏwptrqƒ`…GǧˆóK*e‘ñYdüW!2d?GEŠMi¾896"È‘aÈXȘȘ"EÆß5òUƒö T ôÉòU†¦RŸdÐ>éñÉϘ§«Ä'Œ-¡ô#¾z™^®XBÁ'I±ÂP¿¯—»«‹³³³ËFWw/_A¸$#—ÉäñÓç¿b‘ñøÉ³çê#ãRïYì5Í€ŒÂœ´„¨Ð -º2¦Ä ƒã N†Âé:…L×90]Wê)÷É•ëCô ±o÷w•%”yÅÌVÀåz¢»}sc]e‰$+-Q*ðóöôpwss÷ðôö „ -Eàø®WÃÐp¸ÈøŸ - uMŠŒªÒüÌdQ„ÀÏÃiªÇ'ï¼ËõÉ'¬Olå|‚o·ŸP}ò y¼êø„~Æ£÷áß¼ÚßwþÔ±ÎCûvŸˆSD‘aAþ~>Þ^^Þ>¾›ƒB#E ©b`ÕÖ»q˜Ðȸ+ƒŒßÇCÆm‚Œ32¶"2â£B¼7òm,M t—!2fN Cþ(ô ™®Ä'¦²û¤}òï3. ~ó-Þí“WŠ|"¿Èrh°Ëõð=Mè“ÜÌ”‘0,Xè¿ Ž  8L(JHÉ—T×7í‘AÆŒ"µ1úã÷ 2Ž!2¶U• 2Â~îN¶ÖæFz+–hÍ•"ã½ dH"ŸüK¹Oªëwî=xä(¹¤ëC·ï p}2>4à¾1˵ßÕÞÚÜPWYZ“шŠ lÞ,‡†c02r -J+ëš[Û)d\£ñ3…ŒWª‘A !:ƒÝ€Œ[Ë ³ 2¼ˆ íùsS)dL,VÎQ2]Ñ'óµe}’žS¸ÀÞJ½ÿÚÍ[w „> Kè«qJ(Y®4F† 4Èr=rhï®í[+J$ÄØhaDX(œ°ðÈèXF¶¤¤bëö]lYdМâöÏ?ä‘ÁFðÔ1øNÓ¶Ê’¼Ì$@†/ŸÎš1m 2&\BŽÜtUê“äÌxÁC·I }ü䩪ÊBã5 º’åz¬³­µ¹q[u9DCœ–œ+‚—œš™_T^U×õ³“|Zïí·@~åÈÁ=42bÂ7d˜2SÈøp ->±#>‰ILÏ)">iï‚' Ö¿ùí]ð‰%T®â˜¸xá údßî†:ŒFnVFZJRbBBbRrjº8GRTVU»\r¸‡.½ˆŒQ¯•"ƒ+­@Æ ‚ŒšòDF°¿—‹½…Éj)2&¿? GÞ'“?àøÄp¥ }›œ™Où„,…Kׇ¾»G6¤:%TÊy²&ï4  Oîݵ£®zKIA^Ž8#=-55--Cœ+).¯ªÝ¾sÏ~R?ûúd~!áŒìübȰS\ªRWF/*£(#%.*ÄÏÃÙ•q@¡ŒÙ3gLý@P†¶SÏ“qãIžÌ€<™y²óäé©s6vN¾Á‘â¤ôœÂW)¡O%ôN ì¸ØÈ° ?_ooo_ÿÀàЈhQBrZV^QYU]ƒ¬Urbž£³2 -@±áDXeü“RÆ4A:œæ<™õ•'›¶þøËþ#Ç-/\qp½åŸ’™WŒ%´U¥„Ž(ç´4¨Ú&k¬«*/Î4’âEÑa!ÁAÁ¡áQ1bKFm}3Y¬ênz©¤ êÇïQÛ‡(#C¡Œ“¨Œm›×¯Z¶x2þ((ƒïxóä=È“é'†K–¯Þ°eûÎ=Žž<{ÑÖñ¦wPxl"ŒIN …—™)¡º,Wèˆ'­ÍX5ÔäqltdxX(\XxDTŒ(.1Á€’!mjÁÐbúÌ3v©*ã)£ ˆª&iU)­ /7TÆa£]ß»‘(ãKeep²D CåxódæÉÌÙsæ/ZºbÍ7ß~ÿó¾CfV—¯;“šÌ”P¹R }Á[B¹Ò Ëõ^?æÉí¦úÚJD#3-%)^+ŠKH–¤#åÕ°Kð¸FPÆseeÈÊðõp²»D”±cÛæuŒ2>eüYP†öSÍ“±ïŒƒ:yÊ4Ì“…PB×m†<161·´¾êàæ“ ¡Jh‹¶Ê/Á>Ì“–¦ú@£ 7+=5%)!N,‰Dâ¸øÄ$IZFv£¡Ë DÖðCmÊx¢Q§A?ƒ2¾f•1y2Þôßðÿx*y2†ä Hc:”P,¡°„~úü{¥Ú&×8(y¥Á”Pø Û[eˆFyIQ~nvfFZjªD"IMK.ò -Š P2ätýT¬•‘ -Êðru¸‚ÊØG)c‰¡  =O9O¨:Jè4(¡ó OV¯ß²mÇîGOœ½x Jh ¦ªa¹² ‚Y®˜'P5ÚªŠÒâÂüÜœì¬L¼¬lࢨ¤¬’³„j2LÉÕI¹¨Œ`_w'ÛKg5*CX¬:7OFQy2É“ù'k6’jjaeÃ-¡5L Õ¼\•¤Á”Ðû'=ˆFS}]ue9°QŸ—‹——\TTÕÐ`À.¡ë§Ve ò+ÃŒVÆò%†óPS&Oš@)ã-AZ/O&bž` ]°xÙÊu› „îW+¡(Ž.Ôý£Ç#,×—Ê%÷ ‹Fƒ´Ù().*Ä+*.)+¯¬®•660¨]BzŒ®Ê(/ÊÍHæ(Ãx÷Ž­›e¼âiÊ“é'†K–¯V-¡b(¡°\¥0ø–«š4˜úŒÊ–æÆúºšêÊŠò²R¸²²ò -䢡Iv›cà§~jV–—nTF(# ”F”qž(ã»m ”1ëcAúž²4Fy‡Î,¡ó-]Á-¡®¤„J2Ér•‘åªO Å}rˆB£½í6h£¾®¶¦ºª¯ªº¦VZÂh½#gÀ YBM–Œ—|Ê«+ã(ãÇ­›Ö®\¶hþœÏ?þÑ”¡×)KƒÍ“§Až` ]¥TB}‚"D‰i°\«Èríæ,תËUEÔI£ÑuWŽÚhj¬—Jëjñê¤õ (  õ,y©U 1a·\íQ‡öýÊXÊøbÖÇSeè}ÜúÖÛTžð”P3 «Ët MÉP,×~¥Aíh¡4¨ÀÑØ€×ØØÔ,»Ýz§½£SUaôP†8RUKieüUP†¾÷µ<7~‚Z 561?c}õ†Y®©Z—«š4è•<3–¯„î?rüô…+®*˵–+Ÿ4~U!ƒ© ƒý}½Ý]w;äòvêä„‹¾50ôV†¯2Þ+(C¿Sɦ„Ná”Ðí;÷8zòì%[GwŸ H,ײjFÃ?Ñ* .0^AÀFÂA_ggW7pÂâ‚¡Z?uS† 2 KeèyÊy¢©„6;}þ²½ w¹¶å:„yBip^t÷‡ï "s=½ÀÅrñ@z(c¯š20Keè{#•Ð…‹—­Z·–«ñÑgp¹zÒ˵š,×f¹**Ÿ4¨ªA¡ñ„ÖÆ ÂÑxÀõõõ÷ Ò\”ñŽ·„RËuY®PB÷<†ËÕ–k³\ePB{ûµIC ¢ `Ä7Œ‡<|ôè1Ú•áËÅþ² Œ×{šJèŒO?'Ëu .W£#Ç-/\¥—kjVA)Y®(!"g¬4^²dð¢Ú Þ8@ä¢.?.P<`ü*(ã܈%”]®Çp¹:¹ûGŠ“ÓAìrÕA ðÊmPpô,‹Á”ñ{¯4°„Âr5`–«.×+ö®°\câ%Y¸\AÝ}( \®ÏŸk–† † {ˆÍ‚2ÞÔqK(‘Æ»´4fΞ»€³\Ï^´ut÷ Š%¥ãre¤1ŒÒxÊ' ^40R¤Cqðýႚ«|`¼‚2Þ”ñO½„’åŠ%ÔÀ]®fç/Û»xú‡FÇK2óJ*@d¹òJƒ – „é`¿2\¾¸`訌3‚2^ÿ)åÉÛ$O&Ê]®Æ&æg¬¯9ÞôŒ%¦¡4¤¸\{úAO4HC ¢ Š €ƒÂãÁ‚p¡ íÊ0U(c® Œ×t<%tâ¤Éœå -ÒØ{Ðô”•ÍugO¿¨¸”̼âŠÚ%û”4þÃ# U4(64ˆÅ…*#*#«Œÿ±_çQ5õ{Ç×½îZ×õ,ËZæéQæL•© e(SfÑBÉËU^®“Õ5´æ4,í]½üC#ãSÒ³só !×%Aãg»à68ëàFü%g|Ãhï0}1²ˆöÊAC ¡ -ÐÐY.W3'wŸ ð˜¤´Ìœ¼“Å¥54Þð¡!zhœuP"þ¾ iÈè‰ÉEt4ÈËuØÈ1Ê“Ô54ç-Z¶zƒå&WOÿȸ”ôGò Ï^¬º Ðx@E£a”iÛ`ì‡À00¬GGƒ¼\Áåª -.Wù SGwï °˜D7›žxÐx‹Ðø* Ÿíôm©½¾ Ñ“!U‚—kot¹Ž§ÐОoh´ÖÔÚÁÍ;0,:q÷¾ƒy'Μ¯¨¹"!|Ûºú*dGÆ¿1ÒÄp¹öé7¡1Qmæ\½…KW[Ø9oñÛ±3yïþÃÇOC4®h¼‚h´ð¡ñƒ¾ŒŸ ÿë"6ÉèD ¢Ñ ¡1j¬Êäi³´õ&­Û£GåÕ\4Þ‰CCpÂâþTŘ ¹ÅEL£+ïr -.Wˆ†.Dàá»mÇÎ]’²Kµ§’ !Ù6ø‰Éè$Ñ/WˆFÿA -ÃFUhhé,_cbå°ykÀö¨„TˆFD£¾¡ñ¡„hˆõg$m˜ ¶£_®$Š©3æè.X²r½¹­³‡oðŽØ]{4*I4^pÐø& ëü&£ÓÄ€¼\ê³´æ46ZÙoö -ŠOÍÈ>ZPt®¼ºŽ‚ƱhH&£sEG£{.ª ˆ†™­“‡Op8@#ëбS›wî#4Þ“hü- I†ÉèLQ/×?À{B 1rŒò$u M„†¥½«—h$D# q±ª®þö½‡ 4>Ë LF§Kðrí .WÅá£Ç©N™ÐX¼b™“»OPxlB£ø· ÉètQÑ@¡¡#ÇL@h,Z¶zƒå&WOˆF -×ï>È Þc"Ž oL†¼bFC@c¶Î|€†©#D#&) ¡QZY{UÆhPÉøÚÚòé£02\0rŠ ~ 4Ô44õ ȸ”t€F!Dã:DãÙK¡!ŒŒGLdXc2äårEhô"ÑPhhsÐð -‹ILËÌÉ;Y\ZQÑh"ÐøÑø.Ld¼i~N!#“!ïø/WB4&ªÍä áâé‡Ð8p$¿ð,¿¤FƒBÆwLF§I†FëL­ݘÑxóþã§–VéÐŒŒ8L†¼c@£7BC ¢1WoáÒU,ì qɪ.Ë LF§úÊEcب±*“§ÍÒÖ74Z‹Ð ‹NÜ Ñ8Ѹ!+4ˆaü`"£žŸŒ -1rˆÿrå¡¡8BidÜ‚d” 2’ îŽÖ¦˜ vD£'BcäåI4,!¡Qñ©4ê -CCì4D‘q¥º‘‘A’±Ébƒp2þƒÉø‘ht¡¡1|ô8 ->¬ÃÇO•”UJ…†¤d„d¬#ÉPd(ÐÈè‚Éøý‹ï=áCc(†&‰†+‰FöÑ‚¢såÕuR !‚Œ€Œóñ2V/]¨‡É`%hL™ÐX¼b½™ D#<6iOÖ¡c7!/^½}ÿËh0ññ’qùRYÉéㇲö@2|+ÏמÉ`%¡hL@h,Z¶¡áŸ’‘ и(42¾ 2^½à‘qâèÁŒÔøÈPOWHƲE˜ ¶D£@C@c¶ÎüÅ+Ö4Ü}‚8hhÜçCã›Ähˆ&ã ã0 #‘aƒÉ`74ú 䢡ÐØ`¹ÉÕ“ªºúÛ÷>æ ñEb4øÉø›GÆSg2¢Bý½0¬' €†6DÃÔÆ¢“”ÆCãNGÐ ãIƽ[×I2öïÙìãîÄ!cúLK‰@Cm&†D#DŸ%Fƒw²¶µ!2>ÿEqÿÎ+5û € KD†& c‚Òˆ¡˜ ".W±hxSиÚ4„‘ñ ã,IÆŽ`'3.ãFdô…düÉgŒhü јÈCâ—’Ð(ì Ld¼%ɸZSA’‘€È°·ÜÈ%cÌHDFÏÝÁc‚Éc4þÇŒ†5D#,&1-¢QÚ!4¨d|mmd4C2ndäÞ¿’á ÉXÈÐá1L†|ãC㿽yhÌ•bÈ(=s2/gßí^›k–-š§©¡>‰KúþÄdÈ5qhQÑ8Y\ZQóËh’ñ‘GFÕ…³…ùG;¶fëW.YÀ#c&ƒ„ 1B‰@cáÒU ¿ˆ¸äô³ë¯ß}DCßyd<ºç&ÑÛ¶"2–`2Ø þƒ†«2yÚlm}C£µ 7©Ð †ñƒBÆ›æçˆŒ:‚Œ{“wîØæëáL’1gúUL»1£¡8BidÜ‚d”2² A"ÈèŠÉ`+f4†È Qd\©.Gdd¤ÄE†øyºØY 2æÎø?ûuùTÕÚÆqüåsŒqFQÄ•c€‰ `¢twwwww€¢ R -Hƒ -ØŠRõÏÌsÝ÷Z{í½aƒ¼y†Å<ë;³ÿ=¿ù\÷_‹ÈXÀAƒFACœ×Ë^hüËÆ˜ÈˆEdؘêi 2v1dЩÑÑæB#93;Шn ÐøÀB㟡h !ã­@F9AFT°›½…‘¶*&cëF† 5ÃиÈFã 7¿¸ÑàAF?ñ‘ÑXdäçd¥Ä†d(1dЯ±£hääßæFã+/48ÉøÉ"£§“"ãæÕKçÓ£‚}Ýì- E¹=sg3dТ±¡a?g#¢ÁŠ‹ŒO÷k*JŠòsϤĆx:Û˜êk(?¼׎ÍëWB@CÓ物µ“''÷Úž¼xÕÑE¢ÁýáJ “ñ7&ãóGDFGûóÇ­Íõ!¾n–F:ªŠG0dа1|žh“h$¤»Xp£ñèwhð"ãåSLF)&#.<ÀËÙÆŒEÆ– CÈÀË`ÈÇþ7h %£$ãnýLF"Ã"C -ÈåE³Œql46r¡L¡Q‡ÐhGh Œ€›ŒÁ¯ðþìïíFd<¤ÈH ôr¶5ÓפÈXÉA³Æ€†‰Flr Q‚Ðxh¼íê ŠŒl2^dÜ"ȈÆdë¨Qd,_º‘1ƒ"ƒYÆx÷{4TntŒFU]óèh %ã}÷;‚ŒÚŠÒëW.^.ˆ e C“!",4_€Ÿoú´©S&q¾?Çûïù?nd4V“h(6Nžc@ãßQÈhCd_»œ‘âçî`dœ:zp¯Ô¶Më(2ð1aÈ G££±GNAQUÛ£E Qh´r ñ "Þd4ÕV"2ΦÆGz»Øšh*Ÿ—•‘d‘1È€cÂA—x£!ÄBcÿáã g„FJVN> QÓˆÐx hôEƒµ ‚Œ¯Ÿ?~JFfRt¨Ÿ»£•±.CÍû Ž 4,íÝ|ƒ£ÓÏ_ºz“B£³ÐøŒÐøÉ9ad¼2Z€Œ2DFZ|D·‹¹‹Œ5#’Á,c\úðØ”3¹ùE%5÷)4> Cƒ$ã;IFAFC5IFL¨Ÿ‡£•‰®:"Cš$CpîìY tk hèY:¸ù†PhÔ7·>~ÞÞÑÙÓ7ð‘_hœd `2ž="È(@dDy»"2T0â ´m46la¡afãì‡Ñ(Eh<|úòõÛîÞ~ö‡+ZÆ/bl2zŒOÚî5Tß..ÌËÉLŽ õ÷p´2N“dˆ1dжÑ4¤(4Ü}C¢30wêï¯x ÁƒŒvDF]eÙ‚‹çÒ"ƒ|\í, µTN*ÈîFd¬^ÈAϸјÂFc%…†¦¾™­³W`x\*F-|¸²Ñ ¦ñoŒˆŒ #+9&ÌßÃÉÚDOýô±Cû¤·o^/¶bÙbÁy ´l3‹–.§ÐPÓ1¶rp÷ ‰NÊ8ùÚÍ[€F‰Æ |Op'£¹® -“‘žìãjoa¨ dÈíÞ‰Èøs "c&"c2C͆Æt>~ùBÂ" @CYÓÀÌÖÅ+0">õì…+×K+k›Z†¢Õ †ñc“Ñd¼æ"#6ÌßÓÉÚTOƒ70dÐ,N4ð”BcݦmR{=¥¦klåèî”™}ùZñ­; <Ð úÉ&£§‘Ñ -d”dDû¸!2TI2Ö"20dж!hÀ” IYùÊZæ¶.ÞAñi2ŒÆ³—o8Ð §‡ÁEÆýÆšŠ’¢üœ¬”ذO' C ÈØÅ1ú=ê ¿Ð˜dŒÆíê†{mO^¼êèâ@ -ü~pÑÑþȨ¯*¿yõÒùôĨ`_7{K#mUE¹=;·n$ɘÍAÛFFc ‰† - açê‰Ð(4êšZ=kGh 4¾}ÇÓ †Á&ãåÓ‡ˆŒÒ¢üÜ3)qážÎ6¦úJÇïGd¬&ÉàcÈ mÃј5{® ‰†4 qZ]×ÄÚÑÃ? ÐÈÉ+,Ah<4ÞvõRhÀAßwøbe“ñ¸õnýLFFbTˆ¯›ƒ¥‘Žªâ‘l2æ2dйQЗÜ-+REËÐð ŠLH?w±àFYU]s+Ï_‰i a ~û‹ŒÔ¸ð@/g3}MLÆŽ-B 4oD4ÄÖmÚ.½ïбÓêz&ÖNžþa±ÉYšF„Æk_¿ã¹Èh2na2’¢C|Ý,uÔR@†(Cƈ7 …EVa4äNªhZØ»úGh”³Ðè¤Ð@ÓDÃà$£©¶¢ôú• @FD —‹-¡ dÈ 2Vr‘—ÁA¿x !0Opñ²bë7hhè™Ú8y„Ǧdåä•TÔ4Þg£¡° b$¯H2Н]ÎÎLŠõsw°2N9°WjÛÆu¢Ë—.BdÌà$ƒYíâBc2Bc&BcÉŸ«ÖŠKìDh¨jYØ»ùG%¦Ÿ¿tõfùú»­ŸSh i@_á–`2Þ!2Zšj+gSã#½]lÍ 4•OÈËdˆ Íàç›>mê† ÇÆÃÐØµïÐq% }Sg¯€ð¸”3¹ùE¥‡OI4ð4ppK8Èh¨&Ɉ õóp´2ÖU;uô ±‰M>&ÿaÞŸ´' 0·îÜsàˆ¢ªŽ‘¥ƒ›oHtbBã ÑF¢Ñ÷äóØÆ|Kú8È(Cd¤ÅGy»Ø™ha2$·lXƒÉ˜dÀ1™Äùþï?‚ih<Ñ4V¯ß¼c×þÃÇ•4õÍl½#âRÏä^)*­¬m¢Ðø O n ‹Œ{ Õ·‹ ó²3“cBý=­MtÕÒ$‚ "hÌž‹Ð4¤j:Æ–î~¡ÑIÙ—¯s¡§Á0o 2ê*Ën\<—–äãjgn¨¥dì–”gȘH Cƒ@cåê [vÈÊšf¶.Þñig/\¹^h´hôÀ=i|úô}ï»O0%…y9YÉ1aþNÖ&zê§܇È#ȘÅ1†Æ „Æ¢¥ËE×mÚ&µ÷àÑSjºÆVŽ~¡1É™ÛÕ ïº{û`Á.> À-Ád›šÚö0€s^è9z<£¨(JPPš R¤÷*Hï½Z¨ $@HL(^¥JAšt±+z®÷Sܵw -‰à½/ïÙÌ~¾Ášyæ÷_ONF - Ãav€Œ(ÿøýA󆫸„@CUCÛÀÔÚÁÕ;0".9#§¨ì§A¯ ?þ ãËÇw|2ú»Zêª*(ùY¸DL¨¿§“¥±ž–:D†$J’"ü !#§  £açäáŠÁâ²ò)UuÍ]ýÓ<4>~ÞƒªñãÇ¿þÅøôa"c&£‘â‘ )q‘A>nŽ6j(ˆË!h\¼,%{ý¦êm.A‘q)„œ"‹ÓØÞ󄇯îûO_ö¾}ÿäûÞ×Ï„ɨ¯b”ægá1aþžÎöûdHˆ£d )Ð8¡!¯¨¬®¥gliçäé†IÄgå—2ªê[ºúG¸hì¼ýUãÛ÷ïß@1>¾ß}%øe4>dÑŠÉ„ÔøÈ`˜ C]Mˆ i@†J‚òßÐ04³qtó ŽŒO%‹é¬‡0ÓsKk›¯Þ¼Õøº··÷õ (ÆÛí>Ռ҂l|bLXD† CEQþ*JÒrL?xhHòÐ0±´wö ‹IÂgPÕ46¶Á=ùøéó¸/7×öÉ “‰©ñQÁ¾îŽ6æJ -rBdÀÍ@ÉøçG4¤jšº†æ ß਄Tbn1ý°±ƒûÓXƒîÉû?€b¼·deA@µ ;=)&<ÀËåž•‰¾¶†Êk0gQ2•CЗ¼*CECKßÄêž‹W@xlR:©€Ê¬~ÔÚ ¡±°¼¾õjg÷Ýû  ¯·_¬>‡ÉèhzȦçS¢B|ÝïÛšñȸr邨™Ó§N¢d ("hœhˆ¿xEZNA  adn{ßÝ7$:!-3·¤œ]ÛÄEãùêT·ï@vA16×–çg&­õÕLj)=)6<ðð19Žþ?Q4Nýù×Yk m}S€†w`Dlr©°ŒY¡1<13îÉö«7»»»ov^mo®¯,ÎNñÉ(É%¦%D‡øÁdܽsK@8&¿ “ÿ÷ÃÑü¯ðÑ W10\!4nݹkdaëäáMËÌ£”Wrјš]\^±ýòõë×P1À-™žàþ2˜ÔBRz2 Ãa -ÈPdHIˆŸGÉ@^xÍøm¸4¤ªÚ¦Ö®ÞAqÉ9…exhLÏ=_Y±µýdkscu |?G¡a‘‘ “áádg‘¡¬ 'ƒ’ÐІŒœ‚2@ÃØÂÎÉÃ?ƒÅeñÑœ™ÕØØÜÚÚÚ|±Š1ût|¨OFFrlD ·«ƒµ©ÎmÕ›×e¥ÀdEÉ@b„þ ÇÃU - WÕÛ:fÖn>A‘q)„œ"®þ¡ñ§Ï–VÖÖ766Ö×V—Á-x,  -“a ‘¡(‘qîh3á?(ŒÆ¹ —$eä•ÕµôŒ-í=ýÃ0‰¸¬|J@£½gpdbzváùòÊêÚêê -(ÆÌäè“ÞÎf>qAf(ˆÏ/ÑPÓÔ14³qtó ŽŒç£ÑÒÕ÷dtTcqiyyyéùÂÜÌÔØPw[G@†¿§“¥±ž–:D†$Œ?P2Ñá -£†+@C aÐð -‹IÄÃhª—/¢dˆ qòÔix¸JË)pѰqpõ -Ã$¤ddç•Ò¬Êê‡SS]Éb€bäeg¤b1aÞ®¢dœƒÈ@'+²#ŠôhHAhhhé™4Ü}ƒ#bÓ¤¼¢RZ“Å®¬¬d³˜4P !-16"Ø×ÃÉÞ’G†ì>'P2>‚á*‚†µƒ‹—hT\ŽªA¡ÒË &ƒQN§R@1ˆ¸¤¸¨P/sCÝ;·”åQ2ŽL®ûh€ŸÆ}7ŸÀ°èød<‘”[PL¡–Ñh´2*¥¸ —DÄ''`Â}ÜîÛqɸy%ãEøœÐ qÏÙÜ“˜„<1›œ_X\B¡PJŠ óÉÙD| -6&"ØÏÃùžµ™¡®&L†$JÆ‘É!h\¼,@ÃÂÖÑÕË?$2›‚#d‘ró -@òórIY\ -662ÄßËÕÑÖÂXO ýe¹ü<\Ïœ ¡c`jeïäî‹MNK'f‘rÈdr)‹˜ž–Œ - ðvs²·25ÐÑ„‡‰ä%q”Œ#‘á*@C ¡®u×ÈÜÆÁÙ|5¢b’Rqbff&‘ŽKMJˆ‰ - ô·Z¬Zê*<2ĸdÀÍ@É@tŽDCü’$ŒÆm}K[GOß °È˜øÄäTŸŽÇ§¥&cã1ÿa¿LŸÒÈ¢8Zm&5 ƸàduCÖfQ1•(¥¢QGÆh*ñÿŸ{ß릛Ś–{¾(|²ÊSçþÞÝõÅÙInI&ú&ƒÌxÓôDè.ÐJ"•-ìVjõÆåõíýÃã¯ßžŸŸÿüþõøp‹bÔ*»…,¼X1sj2†)b ˜Ñ ¾4"«ë›¹bùð[½qquóOëáçãÓÓÓãχÖÝÍ£\ÜÚ\Ç+$â>L>þMûSÔhð‡« —/'ÓÛ;{ ÆÙyóúæî¾Õú·Õº¿»¹¾×Ò¼ÝLÉ©'S3³ðruzü¡hœ©QÞ¯TkGÇ'õzý伨ìï™Ñ Üx±ÎÎP2Ä£_4Ø=YöÂÔ@5¶‹%pã°Z­ÕjÕêae¿\*ä2(FÞ%l~N)ÂÑ7ìžàÔ5R™\ÜØÛ?8¨ìï•KÅüVz#bxñ–XL3“˜ %C,z£#”Ý·ÕHl¤³¹|a§´ ”JÅB>—ÝÔ‰·„'ã%C(úDF(Ü®Ftu-™Jg·¶óùB¡ßÎmeÓ©äš"Œ ~K(Ò/pO`j€r(‹'’©Ít&‹dÒ›©d"‹ °>mø.[BÉÎh>}æ÷ÄbG5¼r07Ö“)d#¹^DÃA?Š1gƒ‘Án %CDôÑøÑ`÷¦ªáòú¡H4¶_K kñUôBöy¸³_@ íÅJÉŠîhŒŒ25Ì Æ‚ÓåñÉP8]‰!+ÑH8ð{ÝËK\ õ–à‹•’!Ñ€ªL ¦ÆÒ²Ûãó‚¡P ²ƒ±8ïPÅÐn %C0´hð -ï¦Æ¬Åæ˜_t‚^Ÿ_–å€,ûý>¯Ç…Á°+bàÈhßJ†`tFƒM Tf¨ ²n¸Üñ¸Ý.çÃn5+bÀÈÐæ'%C,ÚÑhO ¦ÌP³Õæ7–œÎeh±0Á0›fºÄPo ™!J4ø=aSÕ˜œÆ‹bµ;ææ9 …ÃnÃ`ÌL11F1(B"ußE ¸(&³Åj³;s‡yaú2­ˆñIƒ’!$]ÑPÔ˜€‹nÌ‚VÇjµ pIŒ\ ¶>•ùIÉ]445FÇ& èÈ¡Z€Œ1&†A{—P2D¤N5Æ™ `2™ðh1Õ†á£ndP2DDêUCscr -ìP@-Œº`h#ƒ’!&Ü åéŠj°lp7&Œ`LjZp/ø%QÄ [",’^ ¬†š pclìàŒƒàÃÀǧ"™!*Ýj(ÙÀn|;8£ óB™$†øhfp5x6°L•‘¼#Ì c@èUÝPäÐÆ;‚^àÄPÅ 3D¦K¶ ê1Ìœ0 Ü  cT5ØÖ`Ù`n Ìþ¾P¼`Á 1½j6À ”C|¡z¡ƒ‹AfŒ¦†š UðEÛ -$†ØH]ÙhË¡ç}‡$Æ@ IÙÐäèà¯^/H áép£-ã}û·wÝ^€Ôë†^þq¨Ó c ºÜPåh3D^ *=nhz´?K$Æ@"õ“c¨ŸäÅ ¡ÿçK/jA^ "ÒÿóÚ"ñJÄKÄËÄËAAAAAAA¼>ÿ 0x•¦ª endstream endobj 11 0 obj [/ICCBased 25 0 R] endobj 24 0 obj <>stream -¿­«æ endstream endobj 25 0 obj <>stream -H‰””uTÔKÇtKˆ”ˆKJI,‚t(Ý‹»4ìKÇÒÝ%Ò¹´H—4J#‚Ò’‚(H -w¹÷þqÏyïyßó~Ï3çùÌ™gÎ<3óÌ„Y9El -@¸ ]äÕ!OÔÌ-@ãÀà\€ˆ¹¥+BVKK Àè:øOX×~òþ¿ÿWCa®–iHKL€…À0qÍY¦–Ë `ø5àcü€Âæk–Ë -XŠ]¶x=†äß ðš8¯ÙÚÜ üX€„Ŀ׽ƒ>Ÿ.Èf’‡›#aP§®¡¦Èünæ óD^{y8ÂËÅÖÚ  €d¡p H×Ë stå©:Yò€Äô׬cxc IŽVÈ?SÂÅ!þ:óè_úÃÆî¼9[ÂYbQ’PÒ~²+rA -SÊhª¦›«·HhÀ´†t^ôé ·'0±ß…€Â™kYæX¯ØY9îYq†q•p÷ólÝ'äãäW°Œ£…z„EÎÅèÄE$´%D>,–ê‘^|t*K)Ç%/§`¢è¦¯\¦Ò«úùñ‰•:¯†¼&D ®¦“¥[«7¨¿dplDa5–|ªmbõÌÓ4Ê,óy•y‡Å{Ëeè¾¶5• «­œ½Žƒ©£µ“3Üìã’âš…,t+w¯ñhòl÷êõðõóõA  ö½ î é m k -¯‹xYU“›ï“€H„&%«¥È¤ -¥q¼ O'M¿zù3ãKæTÖ@v[NUnn^\¾o]¡a‘b±Tr£ä¢t»lºümE]e~Uü+¿jû×åZÑ:¶zª a·q®i ¹¡¥°5¡Íÿ};¤C¹S¤‹¥›¢û²ç[ï\_ÿÛ†w…ý‰þƒCa…¾Qº1¬±÷ŸÆ;>”L$Lz}4ÿ¤:%8M7ÌlÌŽÎÕΧ/ø}¶XT^â]¦X>\™Ym[Ëùâ³n¸!´ycsk«ûkƶóŽÊ·;ßöv{¿§ýpßÓßúIþsû ÷0ëÈõXõ„éäàôݯÔ3ós®ó‹’ß&—$—õWÆWW˜*ØÂ)ÁãÅ!è$Ü$Æ%¡!e$cºÁHNOAKIMEq“„—úâÖÍí ÝýÃâíÆ•;KLów§@˜‡YúX;ØšÙë8^Ý+çDspçñäðfßÏàKåOˆÅTC˜P°p J%†’D=ðè+å+íûÈO%$*©«˜¤”®œ¡’©šý8÷I¡Z½\£Z³^«U»K§_wLï“þ´Á¬á¼Ñ"dÙxíé¦Éö³]Ó}³£ç§æ–†»²º°>³9µ=²;°ßsøî¸ã´_G¬8/ºÌ¹N!GÝzÝ[<ª=‹½2½|B}½ýàþP”Q€z lH0Wc(EèeØnø|ÄPds::5&8Ö9îy¼F‚T"oÒídâä³”¯©³iƒ/ZÓK^&gd:fg«äˆæ²çQçãælÎ ·£kJÐ¥™e‰åá¨J*ø+ëêç¯kôj5ëÔêU”›š[”ZUÚ¿Ñh×é0ì|ÖeÙm×ãÒëÓò6ê]BÚ@ö`ñPÕpãHçèÀØÄû…ñûçñ?QM1MsψÍ*ÌiÏ›.ØöZ [JYÎZ)X-]«úR»Þ¸ÑºÙ¾ÕõµwûãÎú·£ï¸?¨ö@û¼?Åä5Ž Ç–'v§N¿àgçÐ ƒß -—üW´×÷‡3ƒgƒÿŽŸL¬C#u!ó¼áMîMáMéEåvAmsË”F‡V‘NŒž‹Aø¶£ÌGL’w…A¼Ì¬,ô¬äl¸l¿Ø¿s¬Ý›ááêænä©àͽŸÌÎï+€„‚!Bê²"¢¢ 'R&k?¾ÿ´3?Ã4+:§6o¾€ü¾˜¾T²\·Ò±Ú¿6úåÃúÄÆøæÈVß×ÖíÊÌoá»ðïF?LöžîüT;:ä>¢::>ž:©;ýeqÆvx^saõ›òwÝ¥úåÊ•óŸï'_E˜F”O\D’KšLtAnF¡F)FÅ|“èæõÔ­6š\Ú`º@z?äm+Fý;²L¼wiAh›y’¥Í–Àîͽ§Á)ÁÅÎMÁý›g›wú~_ Ž@Œ ØZH_XA,Ê"F)Ž%¾/±*9ù ïaƒZ:ýQ”ŒŸ,\ÎB^_AUñ¡’€2« -­*‰êÕãƒ'[j êão5[´ª´óuR1uà­ïh`f¨m$1æxJgBdrölÇtÁläy»y¥E¦e$Ôfe¥g-g#`ËdGbwj¿î0áØáTOC9;¸¸Ê ¹Ü¨ÜÎÝ¿xŒz6zåx‡ù8ùøIû³£ÈP‡ó=A¥Á±!.¡†aÂAxÛ‘ü÷ÏŠ³‹˜Œ}bŽG-ø‘Þ’Ä“ª”‘•x–`—G˜/™ššê›Ôœ¿ªž–Ÿƒ q¡_¢O£?¤0¥"¦§§ý¨ó©êªâ«Û¬Õ­Ð®Ë¯Ç°Ä±Â²Á³Á´ÂµÅ¶È·Í¸Ó¹Ûºã»í¼ø¾¿À Á0Â@ÃRÄfÅzÆÇ¦È¾ÉÖÊñÌ Í*ÎGÏcЀџҿÓàÕÖ'×LØsÙ›ÚÄÛïÝÞFßsà¡áÏãäM噿çè6é†êØì+í€î×ð1ñòïôMõª÷øZù«úöü:ý{þ»ÿÿÿ€€å´ÍTÌ?¥~òŒ”~i˜ž~L}ãÁ~cbAþ~’Da€dþ€ü¸~t‹yä~W‰²ÊÛ~Oˆ±>~\†´—/~‚…›|“~¾„º`éƒôC–ƒx¢€äƒßú£}%—âH}”1ÉX}%‘z¯Î}K•Ö}† {N}׋<_´~7‰‰AË~Àˆ-ψø¿|¢°à‚|žÉDz|+›®E|[—£”s|«”¦z} ‘æ^•}wO@¶}ÿ-Œ~Ä‹É÷ {G®uÞÚ{D©zÆ{]¤Ä¬Ò{– f“{êœZxÚ|[˜»]ˆ|Ï•M?¹}R’‹<}ÆŽðõ…zªº]ÝYzŸ´Hħz³®‹«|zð©=‘â{L¤Nw²{ÀŸº\‹|=›>Ó|¼˜v|ÛIô8z/ÆrÜ z¿;Ãbz'¸sªMzd²6ßzɬqvÒ{D§[®{À¢0> |;Ÿ| óyÑÒÈÚñy´ÊaÂIy¸Â©?yð»açzY´ÎvzÝ®Æ[{^©‡=c{ФÓI{Rò*yŒß„ÙýyfÕÓÁUy`Ìî¨Vy”Äâyþ½’uKz†·Zi{ ±Ü<Ú{z©%žzÈŽíí‰~+Ö~ˆ}±¾Í‡}W¦µ†0}3Ž…„}HtЄü}ŒZk„›}è=Ž„Ÿ~‹‹†z€ëɇ®‰Õ†£‡}½…¹†!¥~„ø…ŒÐ„d„*s}ƒôƒˆYƒ¨ƒ<9ƒ¸‚àù…w„éû†p“øÓS…w‘u¼„œ¤ƒõ‹‰ƒu‹VrUƒ‰ÕWÿ‚؈|;,‚뇔ˆ„{‡¨èR…sžøÑ²„ƒ›ºƒ²˜;¢¡ƒ•:Š8‚ ’¡q)‚PCV÷‚Ž:4‚.Œ–8ƒ‰ŠÉæÈ„ªªÐ2ƒ»¥­¹‚ñ¡‚¡?‚U¤ˆîãšp £–çVu“ÿ9S†’ ‚šŒçåc„ µbÎÖƒ°·µ‚Rªê ¹¦.‡ÕN¡Æný ¼U€ãš38‰€ó˜8¼ŒAä/ƒ‹ÀÚͬ‚œº‚¶’δzžô6®ß†è€Ó©°n1€“¤íT\€e Ø7Ý€tžXT‹·ã)ƒ$̯̕‚6Å;µ˜e¾Cž€Ê·Ë† €i±Ýmw€3¬SÆ€¨V7M€ -£\®€l‹GâN‚ÐØ»ËÚæÐN´ÄÈa5€tÁ…N€ºzlÕß´ÍS<º°H6Ѽ¥*ôŠîÝ<’a|˜Çkz|C±V޲|šÇ|ƒ—‹·|IkžŠ‚|±Ru‰ˆ}26މ'~2‹ÖÞÛF‘-†ËÆB…„°*ƒ„o™²‹üƒ‚ƒŠ­‚òjŒ‰‚ˆQ^ˆ£‚@5zˆI‚`Š˜ƒ†Ù³‘$ÄŽ"Ží®ýŒcŒß˜€Šñ‹&U‰³‰²ijˆ¦ˆ|PJ‡Ë‡v4v‡y‡ú‰V†»ØG›’Ã.2˜{­‹†•——Š’ý€ˆè¾hS‡æŽ¼O[‡Œÿ3†ÂŒˆ(‰„ÖéŽJ¦ÁÑŒx¢&¬$ŠÔžs•Ò‰h› -~æˆ2—êgK‡>•-N}†o’Ì2Ù†‘Á N‡%ˆÿÕ§±°ÇÀ›‹â¬ªöŠ>§w”´ˆÖ£A}⇤Ÿ\fX†§›ÚMµ…ݘù2,…Š—ò ¨†KˆÔ3»¥¿’‹g¶©ò‰Â°¦“¾ˆ[«¤} -‡0§ež†6¢çM …_Ÿ¯1… ? …—ˆ1Ó£ŒËÆÇ¾³‹ÀI©‰^º’ç‡ô´I|B†Ì¯dö…ܪwL‡…§e1$„¢¡: ®…‡æÒàŒrÒW½ûаÊñ¨]‰ Ãá’1‡£½S{†z·|di…‹²ÇL „½®g0·„\ ð U„އ©Ìû›ò{[¸“™G{!£ö–¸{ ŽÞ”`{&y’E{xbie{ùJrŽÚ|“/cŽ5}~ -~ÀË:šâ„ý·f˜#ƒç£•™‚üú“M‚Kx+‘CØa|ušI~÷.yWã ÏŽ›ƒÉәߎ­¶—%ŒÂ¡å”˜ŠøŒá’[‰‰w!^ˆT`ƒŽž‡^H˜*† -§ŒŒ† 5G…èȨ˜ç˜Ž´ã–=•Π§“½“4‹»‘rív†Žÿ_ÒRG±Œf‹û,ދ̋ͯŒ…ßÇ|˜,¢†³Æ•„žúŸ{“ ›¬Š„Ò˜ªtìŽÙ•ô^1“ Fî‹Å‘Ä,;‹'‘¾<‹!…‘Æ_—›¬¥²²”õ¨Yžl’~¤J‰„J tŽS#]¸Œš'FA‹4—÷+®Š”—;ÛŠL…PÅf—&¶ì±É”€±Þ‰’­ˆªÕ¨šs@å¤]Œ0¡E±Š´žÓ+:Š›ÈÖ‡SS ð§ ¨ŒêjVPp,<XQ)‚XVl€(Js•&ÂJ!ÒûK{é½@B€ÐEŠ. œe°íÙWqQ䆓³â½ý~3ßÌ7ßÌçF£HI^ý\¨¯§¬ÁïÓ¡ƒåœ+j&YÅöÉbƒdMLÅÑÅ{#©©úè÷)Fø£ÛAŠ;q²ïT0Œ±_à ^¿£9IݤšxÖº¶Æ¢YFh“9Õ…ÆhX;Jõ²Æl¬ äÀín+jÅÄ¡®(Ým,Ö¼Jƒî(Va÷V/ÀÈÄaØ‘”@ÈQ~.æ8_]±Èš7]ñÕÕÌÃãõÚgÕ|ÓFÕ) -—/ªºMH—|­Š¼øP´œ8™%èoFxàCÿ­<÷ªR×l® AÒ^«™ ÷QÊlÚÁzÙrJAUžUÿá™ÿd*ÿùüîNî$iÜÉ #Ÿv b7FÚ*4Þ‚÷ú¯ - Ëû§pv€iâxÖ–¼÷ŒÁIªão¥‡ìžËÝJÛ±á#ç -í¾ËW—àh…Ô‰Šlý"ùkx wVjGá“Dzˆ–ç*¸ÇßœxŸWÁŠ:Æ©d¿ÙPÀîæ$¹üΠjÀ4W·Mž¨[ÎÝ,]¢Ê«$ˆ–ÈWæî$I×&®åNˆ¬‘}ì7¨ sØþ»‹1@À9‡TÉŽ.íEÙÓzN¾Dfº*uwr þ„úØ ˆ *ª#Øùòë­,¥hØ…Ãð"ä8=”³NÍ ¡%ƒ=(‰p¤â…QuÃ9søLÓ“(î&Ãâˆwì¥Ú¾õ?±‚ER AHi.RÚ~ Ô„Ë6õ ØÑâ¿:Ò*tД3*»–¿Û±ôx#ç‰m?r~3×û2ÿ+Êwù‰¾šp¸ÞÇ6‹yXaœTOÃ#Œã -U‹Â¼<·_)ÂIø(»Œ}¹ÓW2SÎX U€àwü õ¦ M¨–f›_T‰)•úà“Ÿ?k–YYF…ôK8ãÁu;J$óÀþ*1T…>,ë#h›%TÓ,…QÛ¥{[ëõsˆ:˜ü9󅼓¼&^!Õ“˜a£â@Ò!ßÍ" yð -µ´.—÷Jl6mçHj†uÁ,béU6£+s©ì ýhܸdœ-Ê¥ª}…wiÀ-s´un=0ŠÄ½äi-_ˆ*)UÞâ_ˈ“âÚãb$ôöna+;ϧTø;pçpA7Û¨¾¨CÈ4åÏ.*·Iߥ›a‡8õ°MÌæm.‚þôACiü“€7»\j|fi³¹Õœ¹•Ô«)Ö÷]Þ­°jö¤À¡Ê„ÃÑâUò]¡3(íž ðwhöJÐÛcÉh-®4x7–“öh׿*P0¿HëŽL‡žŠ®ëž‡Ú¡uê‰Õ¾°…Å×Ú,Ë{ÏBz}§…8¦v¶gÊgŸÒ²¤d[û!XTZçZ.ø·vlµA°¿g­Ñ -{;SÂm¶`vâîØ¿`~È?g¥a.œ -3žÃŒì{§­Çæ†L¨^–»¡¦ÔÅåW€ƒ¹¾ÍY¥eÆ4õ‚]¸Lû7oÞðýák˜õ¦ö!w·™™ð«Iý~»Ir»a^=¥Cë#ZÀ»hçÓ`Wï‡u}p¨Ãçê)¸À"®zœ7ff&²3© ’ß$ß©ôF’ø‘ÓÒJ8Ò¶5ãùÞmµ -u´®Râ_,^V¹àS&åaR´~PfÄÜLL_Dw¸ž*Ž‹`\Í-¦9]qîõ®¯¡ ¦Ý –Çô¹T¸Ií6)ÿ>u6 DÝ`eÍ¢/»–¢xq¶Y%¤9ÖÛÊœ¼;«Ã¥Oêd\˾ÊP…&eRËzïç;¡]¥¹‹é.íRµ<oªÊΡù]öP{?¯:¦ r̨\˜µÝÊ»b õÒ¤â3|ª×íâm ¨s¢ØŸîÛÖW°9¢¨ñµæoZt]Rn“ÏÃ…\½c…Wö#+…n•I&õgϽØÝy¸œAjsÑN‹06”HiD­±¸È'@ÿ¥ðõJ+¦ša5V~€cRˆIµõÌ«ïvwtUÓc[3+?F|l(ÑÑiÆÚU—žøô¥^+O—?Rí°Þs©šÿÿ—1ûHqilÑè$Wþhü›û™=¨ƒÀ(ÎRE -ØÁ1¯B›¹vÑŸ¡®nÛ˜F/±ã BsßG³åÄÄÃMœYø«ëç9>£Ü–3……È—qIôƒ Ú£±¿5ƒµ¨ÕV‡Ü_À1È£¿Î²œåiÃñ¬JiXû0ÛWV–ÁîH[8g„«_î/ü¡£ -n3¥ `€ ¤Ðþ38A.|«f|ј0Iõ½6Öbå v«%Ó&ý ;Y®ý㿜ÕÒ„#ç‘dÿ–D.)Âì.p‚'ãï3ËJî›12K[DÌuÉ¥$s8·I<ÁR¨i©mì£J·¨8Ž}†ÿf€Ó ²åDJ¼Æ€àºƒws€!ÃÊ«®ŒÄr—æ¢RXÔÊ-\y^Í*ÓÌLd'¨é›²9?) "Øqé×^…¦5µ/<4þ§i -¨ýQ<ŸÁÑÇ -›˜;/¹¥Ô±ìJB"¬‘ü7þØtÍšù©,mºóXdÚ¾^ë;ì´žŽñ&sà`«YÑ"à놠ÑYŸüŠ™¯{>ÆŠÕ.õßÀz^48¯‰e!¹ôR‘6}ØõvcûöìÆMiŸozî²Úoñ0¢'“=˜~µ¯Âi,3Ã:?-‹©Ý?oS,Ùõô9áÎüwß¼„µ¹þ#îÎRçOaƒìÙü£˜˜—ô;Æ ?àþäïŒåpïÍBäÒ­À -±ÖžI̼üO´ ÝŸ÷e#}Ô¯èN$í\êŠl?¼],÷ý Å·y,ÜÑ>&РôŠî䩸q]Âyhâ0AqñâK)ÄBÍF¶“ÒcH:ñì—-h-ñÇŸcȵêf)¯K9T£ã127]çÑä÷q·EjLˆâí•<>hÁ‹;ù|ãU -Ÿþ¼ø×à”dÜp‚G -Æ«½`¹&!…Ïä8aðÉlû³›`83>Ì.‘òq„ŸÉ‚ænç…ŽÚÀ¤Aß9 -;èœ ï ¡‹­ê`¨¦H†ÚBÕyåg¼ Þ KBý ¯*kïã°—2fF„=#Ÿ OMã· éeT? -m­òTmê_¡å“ãOBÛŠV¡¸<ɆF('—n3u£G~†È®#Æ7Њö9•Á[ÃÙ¡`NísÞ.°P..콤 'Knpƒ–FÙ‚\„? ÇB>ƒöÆ-àî±Êõ`’ñ†½¯NÂèWßOŸOW«BƒªÊl¯f°¡xßìW ^ÐÔbžÚú-_Œx&*/¹ê(Œj´•‚µ_…=߆󑊢æŸzF–“´`éLd¯ßE:»¬—SN¸±Ê”@S 0Óþ¢3•|TÖO¹Ko³kÈÄtëßo}bF†Ùz$4á«â-ÕÐç,¨.mŒä'j*J¨œ|)JË6²Bµü¤‘°P ^Ã3ewÜ«pXúÁò.þ”*íù,0ÓÅ7xP­Ú³Å:2¿XOTç21|"7ùÄ=Ç0ãžß´©ãŽy}ĸB)Hä[èöžÝðFs´ ²òV¡+Ÿ±ò̯+Y(IŠ•™(º³‘Àx&Úá9JAIìÏã'tXmyœGÄ=œX[8éTK³)¦2œí©“<ÇTSRüv¥x£ã€lÈ“GO¤|Òg‚/÷{Ø>ãºÑ4Öù/’gËRçáŠF–ȶíà&æAÓ5”š2Ü uЯ*B<óiò뾘0Ògµr+OÍåA¸¾,0Ÿíî™-ŸbÅu¤g[ï\~¨-ôÛÙÐ4:I>幃A¾ÕuF‡ÇžÑ§©™ué¼DÅùº)B©,©*í?nÀ` 'qQIzÁ—KÖ—ö4Ž{ áB_g‡6Ÿ8­#ªÊ‰2.Aî$69¤!ÄÌ’Ñub1&Dð3Qxƒ"Þ >ÉnÞïνxüVG¿&•±TÛ¨ü†ö’Ó)©sx·d-5ºÄKx‹¢ÊÊàߣD&®1ä§ÁŸÂ±”¾œjðdû€GjJ•Ü|¨JÚ¹{ÅÛZ¢‹— Þ²Þf6ó½˜/vTñpÌ„ubšÙ ¼…P ½mBU#g«ëåB©gË·’)‘×-*ÔE -ar†Žæ>Ý>Ƶrn®[É­äFÓ-I ÊõByѸPå”=ÕþĶKâUC w…òG ùDéÁ}óØâ¤íñœ"¿µvN¯à.p•]ò]QÊÿ8uÊYµí{Ö#q¯éCÃáv}saü·¦x_o±òyÂiN™rÞÚœâ›À(« d8aÅw™2CöQç}Vš8ÓUWO\²g®……æ éÓ\«Ãôyk—@°¸d‚ óc…ê”Zñ‹t¨¿š9$ÜÙu -p†-œàú1ÏÝÛz(þ=Ôf) -‚vŠë¦Ä›9øØ¡Ö2ƒ w uûþç…¼çïתÑõ#è…­Á{P6„+DÉËq3HIiŽû·Ù%„ûBóC‚’b¨!ÔÖÔkc5‡&U ):X®$ÜŽ–ºˆ[®Èb‰2¤*@ÀPkcÓ˜‚dÓoT©B_L1Uwi"É)ÉÌ=2#ù‹˜pI9, RêO>‡T@>è;¬bn¿Ðø¢€DÈþPuCý«ë›ŠfkÚéã^´^ü°\ÀŒG~•  »o—LÃRc¬HqÜ®=-8^5OÅ„µy*9‹û:-\òg8:T<á?*ÞýŽ´C;¢¶[‚µ·y¿XªÍïµ+±•ÙÊI©;÷lRLß­“$Dõ‹vYþTQ6üDyƒ€VmÖÛfy%/sIsƒ†mXP1ªLռȭ’¥«òvow)„QBµb™†ÐÀ_L¢V˜wuÅp§õ¦¶eÄ—šõŽO*Ò“|Î+é]£(uH¼×°4ÊW¸U‹õ.ã{ Ê4Ó\ÿm.Q¿¿w‘ª¾ÒÕR~MAiÃçÀRz+%µBô‡ýóÈK‚ëzçª?'à{Àèñó kÏÒ‰ßaãêa§{ºèÛH]ÅþsX}dŠÁ°°a~ø3í›§_•auƒ¹«Qz‚ ô ö‘¦žú˜VM\ĵv5I0ïL¾M£)ñºÇ„€pžæÂ1¿ý:5¡Ùð,ˆ&ºý©á4¨í %¦®Ã!$}o±“®cªÞ¤ã¨Aí]R^x„º‡Tâ—¬M&/BÔ:D¥wAêŒë2û4öÝÄÊ?šcîd&g]5¬b”¢’4³ôëa?«úèßû…iÐÇ»„ ìïĈÔý.OA 6Çvfv™sïd¡õ(5yTéÃHÊÍ/Põ”=…(¤a…›;zUs” ÎbÅWµã¶xéDa„)Eìʼ¤ $«sgÂPJár¿eÊYè3 wƒ€`¨cƒ™FÉo0|U[j5kÑçŒ.•5³Jç¡à&ª†åø°eŠT™Èo‘r …¨ÃˆÂ´´}I l­pàjC­á–û8cñ‡Ì5ÚJ=gç%U’®oó¤|L5çç­8E" -Ùÿ®ß[Ak]Ùíûí„åJÛó‚͆áVBÆMí"{íNrÜòãæùQÕ¿òihЦ@€Y?ü 6³ñ^ß«ZWÙ¯î]ذ¼cØ‹h¶KSLj:>®O ɲ…Õ÷.þÚáݰ™µQŸ{5mm<Ù·?^Öv"ïÌ}ÙÑŠw£÷9«ðOãØ&šv…ÄXÎ7‘km[ È,70nÎ’7|÷éeÙážPÒÖ\I;<±çuë^`u‚!ËÍ$P´øŽ…B -ET9¢¤Ù²FÞW¬Àc»ƒã¹ám$úµ="G -}KÜe‹š6±~Ö”G_å³ÒìMËDÔ¿µs`± gî]£v¥p-9§\Nôžd{:´:Žgv¶HÖ_3fC;}ÐÆ“ PŸƒýNƒËp—H¯‹ídŠU9uÎ;ÂÁ´9$8wÈô ÿÜú3†rþ#¬AÄFÔiD1únŽà˜Ô-}õÉtþeÂÜÚ“D´í‡HÒ!5$Éš,IŠQ:¤È–RÂÜÏ1Ï3ÏÌ<Ï7ÇÇ ãj†1Œ[ÎЊTªÝmõ¾¨WwzU»xßù|öøþøÕ¾+Ö„{×úÈâöõW¿–>öò®p•ªœ:ʤùJRqY®ø¡^Éôê`ǯª<à~ó‰jûØ!mœr.ÜB­+‹Ù'«Ž”OyY+«äfN*żt ©WÑ$þ¨W\½2ï°Ôù½M_ê½c[ëgÕøñÿªjjö%WuU{{­,_¨°pêWœ–n%}P¬I^©WÑ|nˆ;c!¾p°…éu¼cʨÌN!k#¥´å)k„ì¯sdŸµ5nøÂŽû6Q{H10À×ëUì -œ¯è‡-£"{â ‡TZG ôˆRØ,†ÍɇjࣶZw8j ICFöêÕ:½b [ Æ\+å?ˆ -èþ™oA n'a - -ÚôO¦j0ÀÕä`ê%µ|¶^é/si×_¦XÇ£ýLqiÔw]–Iç©Î­÷D"J¤î‘!y½!ñÔ¶®nˆ0²Rá zå WC¯³câcúh™üÈÅ«KÒ_R [úÓ*)! OSo‘©u©µ¶OÕ·ÓÜŒWÖãíze¯WÞCΓña½²¼ìÈÏ’Lê·Í÷/ÏQB´o³_“£kÇ/cg£ªÊ&MV?ÇÿÖ«5zõÝ !gÇôÌë"?wœ–PÍš—IÏQ"´Î§ÈP­AdtE)q4þ±jŒðúGÁsƒÆÕnñ»{"” ‘óí}e®ÔUMÑrkÊùzvi89C}´ø®XóLB1W¾".ê•^M ”ÔûÄût?WK¢LÛwÕDP]t£U‘æŠ1r»J«ð·ÓÕ¸KÎÿV¹—ÈÿGM;phÌÕ·m æ‡v>èÀ-wgØõ©ë¡FNý ŒÊcIÜPþ#q‡òW• ŽÐËI –˜;ÇNáÙ¶þÅAÐ)ŃÞH~¡7iÛ tòhlƒ~ýÁ~dz²YË CûþåÎxŸ2>*c&Ÿm‰b›‰­ä{9f¢1X*²L˜ ß#>àíú - V@÷…¤gè’¼]7n249=MÅÌÞìK% ¬;™’,ÙÓéœøFÎ\j˜ ð1ÅÑkl·Zå‚i¢×ÓØŠ’áíôœ²Î¡.|Q¼…9˜ŸÐ°¦$_.!“Ë;¤Ì¿¼l€ÎôEË,€ž²ûÉ¥Di­ùõ‡´¦êŽ}D‰É3^Êa`Y5Åg{J=mãìɳØéËyž3¸¢‰C¤M'¼jMê£-iŒÐ¦ÑmêÜ »n5? ÌSJÙE+U~ ô;·ˆí™Åq.t¶Xd÷¤Þ~œ˜Í~p*QÐeS¯¤%·.‘ŽÐ‹œ"ưºöBs¡ª…ZÏ6-çîøÄ•6[\d;¤^z4’`;64è—¸ùú¤æͱw¶àô;ùŒÝ|ì+&®A©f¬LUàŽ3¬å¦åŸXòT»ßm°åô–¾)lãFí'l óVæÉºÃgcGÙObßbÉœ9ª;v \ºCƇL,Ž >B?ÝKGC¬·e"°z -@¼E´É£³Hûù»ILp<÷5¥À‘­ƒÄ'ðÐÒ‰¤èôàÞ$>ÜÖ8#ê„gäLÑ2®mš Áæc«ð¯Ü꨼1 èÀc‘ ®ðF¤w)™P¡ì+ÑrâkC q’p/Âu8#å!î*g‰…°ÈPaœ`vïu¡Ÿ@¾åo¡H`"ޤþ:üz–‹Ö_•‘Q<,DÒ>'Ó…©ÓWP“²ó”§ .ÙÊÿ¾`¿xåW˜Ä3±|!6õ… » -£5„æù Elœ["Þ,0Ê eØòµÙ[Ozò‰Ì0~lúUO¤›+ùÓÚë&x£ì®kPù²c|uÉÙ$kþ.™?Ý{Qp""kÉí÷óÃÊrš6ÿisVûa‹ÄÏ=~‚@Wí_® -.<Ë7ª -­Å2Ë#h?c~¥m'r°ÕEÂ_ªxs…ë6‡ò¾øÍa»‰¥ÝÙü±¶Gµ+øš¶K 1¢þ4Œªä‰L^kUpø^^ù•_–ñmÓS^d‘Ш…§Ä'º­ùàµ>µ»Û}¦à5¶¾­—ð$:Ï„!EîÏ[«búJx&Õn› tº(ÒçmÀ;ZÿÏsF5u¦qX´.¡Õ‚£BqKPÁ € *£‚¢¸ l‚†„%{¸Ù“{³'÷æf';›,TT,¢bÁŠâhë¾Uqµ2Z”¹çŒ3çýü;ç}þžóüóT9Ävw´ÂRñR;GDž - Kû*ÃË/@hUvŽ„«…‰¯$÷jœ!@ጠÃvyבmŠû´,W²|-Í¢ –^¥ Õ~€Dô„²_Áô†­’"Æüĉâºàø#cØç¦˜Ï*íX/‡¸Ïœ“e>Ü|õXHÍù;û:˜Å)Ý÷‹d9gÆ–â4àaBŒÈŸÿQ4óEÓ»w‘,C -Û¯B UÊ#à>ÉSV§$Lì-íÏ5gˆV ϯ*¢BüüÂ#‚ß}¶ npþÒtd½­¼ U˜$Dõb&$^¡\^Ò&Z"/˺+Ü-}¼Ò%è‚Z眅:}ðû9•‡ÆA‚ºYu§À æírTÜlP0Ô"šî·â~! ÎÍš*@5Kâ?óß«Zæ-â¿PÍó÷˜³µ=jô³>—܈¬[üO?)¼a5û -®™›÷ñ?靯WóÓÐUs»ù‘°yÌ5^(ge›${C­‰âÏm>² "¤éÝG¸Õ£+Ú$Øè¸¿©—Ï«&¼æ XwÎÝÍÓêÆãÊ8?´Ígž‚,'Å=Ò"‚·—–/xÕúÏÂNþƒ¦îM)üïê'Ôòöº–ÌåÍEFÆÔq§rfÔ CįQ9ZY‘$r!ñ¤Òê6m†)–4¼ Ö÷V®9 k­Jˆ$#…Õ FÑŒê…X†³Ù¥úCŽ”pÐ[ç¤èģ)C¥ŒS¨;rFP#õIÊm„K£G<ƒÃh/ÜÈUÚ-,C€ÓO² kOKžYYµ;èMH-¾t–þ%û^NªŽÎ¾œB€‹Ø7" -´ãÙO²ají×X†_84ÍV(N“à-{D)´éF¡0«$½!àoJþÃdv/_øõ(ÿÇ€ñÚUØH8ÞB§c×1•·ÅÉf¦b5u­Á&ë*®Ò½—nÛ2s ™«G4 0:¼\=Y2è߯ ”$bi”8亵Mß$Ò™:utŠNÎ)êÓEh²¶Dhªd«j£21|²*Téç¯Vû˰îÀÀTª5È®M7-´^¢èSÌø¢84Te›µhRÒ¨jé[ìUîGþ»U±²Ÿ1•ààK{“Nh6vÕgÖ¨Ñ+Î…G?Ûõì` lÝ”$Rq-ß.Þ¦2ÞóSòeÍ8pSÕ–ïk!ÁXÖþCÍ\”z0µ0 nlhÉòjÂ<~Iª©®‹ý,K¿r²L„)ÀIM­ËàØ9M â_ˆùõc+"vҜݤôL©í¹$>ɺ¼’zÑü¾j>>¢îXå9,ZL-j‰«Ibk¤×ȉ8÷Ëš?vÎtÀ”òÌëx”žš‡©ÒP¾IO}„_ay@Äæ:à ãñì|VeÃ6Ö覫‡ubd/eÙ3<Ö­¬zÍtžÕeÌaÝÁ'˜cØLaM -Šlz&,f^ª_! ì?ãlâÛóŸÛ2x2¦X’yÎèåÅ„Ü3Dó㌼)Ø\?ÈyÌež ~°ã4äÍOª+9$æ É -™á´øEˆV¿D<ŠO0ä‰Vbj&¦¾mLBÎ2Z܈öb9êðU¿È;nµ©â6L0 ƒ+–êoˇCrÑE.~µ>TüSØ“Ô7X¬³‰®?MMå!ö½†Ô¼u–Oèôt›ñ’÷P Cbt;´·ðièñȕްÌãaøº@gWÙÛ#Û@ë¼ô4cœ9.Dõ¦æo‘ z2>M÷5¶þi—~Òu0¬ ¶qºsæwî®´QÃ9çǸLt–ý삟‘Mz)>©ûßkÉ£ûËI÷;º”îioï"’²›U)úÆ]$YÔ†L >á$Ÿöíò$Tîÿ¢¢:gUo$÷öUK÷¨Ú,ÕCÙ`sCMAJMÄš×KŸ»C(ÕÁg]‘õ¸Ù®9ÔsÿUìGß0?“ˆ‡­Lóž5­ÅÌÙQM%Ìì”0O³léç5&Üéè`ÚÆ‘1,ÇÕxä'¾šä{ˆÄk+èmYÉ}“ÕßË-Jîs#\ÿ¥d‡“ó:Øi/àNûKõþÏäÂ\8©H½£›ŽstQ#Åá-­ND¥)….s*ŠZÆØõyžmÏnÏf›³Í\†1lØÅÂä’{¥Ž(Eé=õVGW¥Û9êsÞ:ïç“?àûów¶½’ÇŸœQûZºsCÈÕ6ªôAò1óöìÆƒ‡ë6KÌ@8ëõOUYå^ŠÊ`‰ßì7Ÿ„¶¯Šóºù³j6ù@£9?,yމ¢Ðt4&Ë}"ÖT×ç- -ü\Y&kñâëVÉî»x—ë…£3ª¢9µô1ÙµqQ±=ébÃeMq\—Áª¨¯Žêî©`/nņ|ƒËþ2†ðÍŒJkz¦DºmÍ«ŒIR4ü\~5NlðÔÖ…›ôÇKÉZ]TÉC¯3ål•Ì…©…Dî3jSS©)’tÍêWw$IXß[›wºV Ÿ -WT™«úUòŠw^PÙeU±‹®ô‚hÝWEµ^Ø“~WÓcÊhŽÿ¾s ±s£õI›g‘ù¨ƒ¶`Šÿw…¶gäs (€5£ªè×ßmrâÑÎÀæ] †¡ ÏB`7JfåAÞÖ°ÃaAÝ3Æ“¥ïG³Îáê»þ?{¼O[ ü?ÌxËjø/´ÄæÄZ„*7¿e…xÍ÷XÎzà Ά¥î})¸ƒCÎûð?`ŸœKã‡cŒM£ÈúŒÕŒ¢´&)¢ð°š¡YÞ5õŒJ]q':]$ƒØžÖ]Yv xÖ(¾ƒŽµÐı¨ò†H1Æãe½U¬˜¥¬>Ì_0£b»?*ñ¸¨Žä¿bÏÚ÷ç¤،²•èæôÜD”±Þ;W´xm]|Næ¬7UÏù13* ;¦è.´=¹ð>ÖÒÂSÏÜj)¡CMŠü>”½.¤ÀeñI£¿ÜÛ1²²™/QßvÝÐÄýÜ6T¾kk+Æœn\\‚F£F‹í¨V#§ˆ¹Xdˆ–£eº&~ûWEùÜ7á"Úbjª°Íúu‚•^I@ïj@ébQ× W´»kåò8ÇöŠwüÛ_ÕD …Ÿ^Áz‹¸ÙÚ x’Z‹KA Å_`‚T}Ú] -xÆ}ÃЂM0Sõ,r„™ùV+ K¿×O&ÿƈ´`¨;­E{irf0F™]©— w86fŠ ÿÏðfm_8ÌèƒÍc3„™ß÷V“<)¦r1¿¯pˆêÀÇ +h‰Ès|pј!QÁéP'ïÕ‚úÞÊ›ËÏó¢ºÇ2òrõÓ¤eÒ×j4Y“ r©î’À, ÊrÞ?4‹! Uø÷qð]fÀ(œ¦*&umM+ª;”1® --°•cœ8òC¹êÌj¢L=L«Î1þTDJ—º»7ÅÆë>©Òê²Î)BHå*£cúHY}~ñxI,{驯7‚W…îûjWÖÞ§…ʇçãh¿g»_óÀ·YovÜMKiÖN>ü”˜ QÕÖRÒÒòǧ}È¥Aû–Qžj^¢©G ¿s› yJG"ñ?txt,LýÕ>«Öp°±_‘˜>ÞPo$¦Õ^<…%}¨ÊØK¦D”ÔS4¥ -·*S’<Ü–Òyñd;’šÔéIJ»Ó~J®Mn>ÉÌȸcI6ðu¯¢†É–Ú˜€å䩊i77Ê_’5WŽ2' 9­øtÆ^}/ü˜8%wdáÉó0kšò)¹…ͦñ¢F9ÀôÆœk—ëÞihóà3úSh›ËPB“UÕLz¦ìs€±’'0®$Yá/L3Áª‚—oø“ól¼„ƒ|‘Éf ôɪ\AW#ësiŒÞSè-ãO^ÛIÓÌ+Ú3åÑ6¸Àxasíì îø…@MÜä -¤A h­ämŽ4™5VÑ-âî' ѵÈ1¦¿ð•S+ ž» ~* £%~kÓ¾ïË×ʉl Ø*¿ -à lÙƒÄ=ô3¹þ»_2~”–­OgP³¥s -˜C¢‚þÕÔcº¾ÆdÚú²[ÚaÚÛ’{<Ð¥ùƒjÔA {! ß²Û“¬Èñ;O'Èá9+wEHŸEÞÀµž¡&†J¯ÒVé?fáiÓº j0¢5瀶bhW”ZxÝo=”¥Æº 0–ˆÂz‰äöhK5å’m¢ä•oçÑv (Y€Ouô±óà¿t¹;‡ÁËe=¾ðRÉ*ÍïyMVõn²,¡$v:•ÄQçÚ³EãÔ.›yŒVlò;æsvn·Ö,ôõWi.[¡÷ÂÉ¢@•¦­„¤34SüD_!“ÔMú›ßF‰>J柣ND ü»@$ÊYÀ~³-³’µïCÎMu (+lBpБÌÈ^úâ#$œ~÷2è /@Ì£ë6 3nðhÛ -ž;÷ª¡û‘Ûª.âþ—«¬æÞæŠ3F¸¿q3ã™Ü\ÙŽvÞZnþZ"/ËvNF”ÜüÖNŽJ2V{œ#ΚV¹sØâÏÈe_ì‘®àTˆöa“8C¢°!ΗÌ÷>FîÎL¡ó\ÞþM{û—ü5®ìeH~7;FÎã Ù ²AäúÁ³»¤—B?³ÿÈV¬ùÂYš=Û©‹»Q i9ÌJ.sÓ¿c%–è•FVb‘dÕ¥iL–º`Ûa)kD麛=W®Á³Èœ¸œ‚ãÍ \nÀèçÃeÕ>ŠN¸X7Ƒ†2Iª¾Yf-Òto7±¶«/®~ÁòUýè„as[š†`W›®*v3_”`~©û‡æ:kêÌâÑjêR(" E -*Š€‚ -è´¢e)DDIØÙss³Ü,÷f_n6Â"¸€Š:hm±h+Ô]Aqñ•ÑQq¡âSaž¤Àç9ï{~8ç|³~bÌh6âG«îíZÄ ×N\Èh´ü+„Ë(E3§0ï~Ýùô–ÞÀ†kô–T‘MGºÎ²¼èüÎ1ü:zk›a'LáøG‚±¨‘2‡>ˆºýÅ,gt ÉÎîX&¥@Ù?e%œ -ýœ¸ˆ=@€ÆIhÆs¨ˆ²á)H…ÞU§OeX^mÈÁ7R7Ê~Ë,ñûê,é ñ\jí´±»¿J„ÔŒfÌͬíÝð¥ð8!*¥]˜JR:æ‚öWâRÖ]ÓMˆÉš” ‹PöœZ«ð;¨¸JÃNÞ.8ɦ®ÏÙÆÀ,Á[¶r*ýΑ¶]MˆòŒM"waX)Lbjd‘`ç>:ÿ?ü¾íù|:?uƒ>^ÒÏGÁ©û$˜ÖfaŸâ.Š -Ê¥_àƒS‹ø%²EDð–´8½ ¤J¶Æ=£Ä•K¢Àâ©{ƒ·À´Ü6r zþGÄãGêò«‰  ‰Þ óÆU‘ií<‘‚—ÄK’gçå"÷¦¶^ €qŒ -ü£ôùI6±°¬±vP¦¶‘éWÜy^,š†u¯c/5ÚÓ@¹:ǹ+[ùŠN¬þ+ðlÛiÁ{ÞŽPò#^¨Æy†úìïvë,ñ±Üæãþï¹-ÚÍNÕü˜ѳH×⺣<Ö¡gäx ­ÝVÈ</·Ýænb¬äæ6Ðýë¸ó´³œªÎ¡ ¤+nhB˾â¾PÑËoT(§W–æ#ò#ĉTwÄZU} ñ w-„ävòÄTç½½Öç-9Oá­ºHIãz)¿®Ž®ø ï zí9äR'·dÁI5ÃÑa²ÎZ©ïGSËŸêag‰ÕW=Ÿ™.÷P1Ùœ y?³¥ƒä2‡òÂóX¾)rÙ4VaGº¥òåÈX¬ò©æËBe`9üQ1‡ßÍš@8ü5åŸÃàÉݤ·ÀÜäß$×ÁÏW?‘Dò}¿”zò†ÿ2‡*È -ƒŒÂÌ÷pt¬ +ü°;Br\Ü•ì'> ¬Þ-övø÷C¹NeÊ”Ûô²ÈL-ÊŒ•qÔçK×H½”¯r Ä7ÉIÐ d<òޤù¤BðgNelB^ÖƒRγF2èÒAÓqCR€&ätŠƒô³7ß„ü´{" D9š·Þÿu)—òCð­’w²¡æ1úˆt³}?"þ¥¾'Ç[Ý7oÉÌ©«~ž1…{Ÿ>Rìu*ô ‚íÊ–dÌCãôlÉÈÁuøtqf‹2[ÍlÒ~{S4Ã>±J$Œ«÷÷.nQ¸nlåPÝ#üxÛò])By`r+ÙwÞð²âçLH?‡˜ºñ¹VD:º|«æiU·G¹þ~Õªÿ+öå&+RµÜbö gPâÑ>Êú}¡úÆêàŒWÚÔ¹÷k–QÇ–]WSkqÚwÔ×ÎZ -ÔDæQ§žd¶¡‚‘V„Óídè24¥Œ±ÄKªGÌMËv«ü˜U35«˜KJ~Ù4&j´’w·J àÑ÷*µóy¡;˜êXà߉˔ÝÀOá÷£@›û5•hw)Œñ˜´•ÛoØ-9E:¦ó_ÀÓÌ‚îo¿¥òƒæ&˜6#¼Vþ£(ѽSé-te$ íפp}ô4Õ%Ò4m¼rn¶z‰üôöhe4‚KX*¯‡KÑð÷î2ù9‰Ê©à~Ô×ü'Ǥ»l|O5ÃB† -§;•^§ jÆã›‘Qõ`ØexHý³;±JË\*Ë`lú¬Ë´KÄh¦×k -&”ƒ–ñtùˆéF|(²Ý8VÇ¡‹Ü·ÁR×€§:ϳû´oÛG*UjSKk¨ÌnR¥“ï±gl¸ ÷ÛÞ…-‘6¹ûÉ&¥±NÅ—´ÍÇÇÖÄ7Oâ4”ÑrGm´òO[du_T’×vYÖ{‚ ÌIyØ\ëaR Ky¶¼&•Pï7ݪJ)éløƒ¼"—Wÿ5Å{K¤å åÓÚS_õ¥àå¦j0WSW;¨wèixêÆF1Í^lÓО伴^£'¤1ï¢ÛÖbŒ%ôOAÏÐÆ¬ÕXÂhq)Ló7ÀŽj¥}=€9PXŸÎ=‘“n¹`ŠŸÉ—KX#Cù…A *7{È ãjœŠWÜ´TœByÕÑö¯‹uf×›éÂñd=úA˜¿f±Á]è”§ÿFè…íÕ=à_u*`¨q›²‚·¥+î_iÝ‹\Ê^`BaEò|ÔS&޹¨Ÿ%Z® ‘a´8á—ª°+Qˆüg½Qÿ[ñIK«Î-Ëj¼£IKr2T¯cjuñª=A ´ʧØQÍ"7—ò²{Ù®“ûê×*æX|,YƦ¸¤zêˆñÑÊѽý¢Ž‹}ªÆˆf:÷jCo[>™]×ãx^¼ÒìhŠÚõílhNrÓϳEDkþªcÕCúÙØÇª ת9Öýc —ðîH›t<)…Ó}ÛzËÝ!ÇhE~DBÓ³Ê2ßS͆Êòi{;éo•×u…˜IøõpÉ??ç ƒ4™6Ùº«ê^"©¹€Ü±¾1•Rêû¸±…’<í“-–êš65s²jžpCµ‹óÝÚSjqi6dzھİÆç´ˆ† 41Ú.·ë$Ã5E©Gü9ßãÇ:Øï=µ­oØ÷Òbšµì¾„ v#»Å[éÿx¬ï¯¦ÀAFë+T㳌€¢Œ³€Ô(C@RQFöº7¹7ã2€°I$Á^a$ÌEqƒ >µ.ôAEõµbŠëõiÿ€ï÷O0‚ž®Œœ]Т™K5ΫPÛŒG„´æ˜ ãÖ ÃéÒðžZªdÖ¨J’ºË*¼$dôýò·’ó ^ª}E¾*Ö¤Á>?‡°©‰ÄëçÆ„Ÿ¨áæÄø¯­$d«ÝO”£³ è_t¦l‰%¦Ô$^7¯Š[“KüñSú‹ECq´zŒ"$]Ý*óûµB]øÊ}›ÎWö zT[Rk­"‰ƒç®å®n¾]¿EãçU—Yv¯ÚFUãW\þBÙí6£-RBè^M¿ì´e¶2Bœ4¯èÇ/wÕͺÍhú4EèõkÙ5Ë–’<1òU‘¼Î[¦táþÝD°Å>îQ!˜.kR¿Ïæ¶§ä7˜uÆJµ­cû>òcªÂ -lûªˆ/ûüi^¸á3;i÷°š‚éÚÝ0ƒæsÜèÁôÄ€­˜ZÛnÃSÆ -¥»ÀqW7ÏN³“p:ó([ˆ½Ý5Ì6‡8·½„Vùi•AFÞœì~h¹9£PÚldüjì2îdO -²+íÄñŸ¿6Âü1Ÿ--üß1Eú×üwvø¦ þ=JCH«šW¼3ž’4Ü&ŒxÝñ½8,¸Ëà&ºëãÐÔ#RcŠ3Dåvzý6‘…RS„¨yuè´_N/nm÷ÙƒØÐvTÖÃàòÖ¥Y˼?êåRF×°KzÁnª¡9ó¯ã•ÉþQ®æ4ùgœC×^Ù5l`ºP\ܲº€âýG£&ÿ¦¡Þ«`÷² õ±9ÏP¨ÒñÔÒžÙ²XÜÊÎrÕ6Ô«Œ -V4,ùÖ{²a Ø„ÑÖý\tcY`]lÎǿԾ¿a®§°r‡é´¯Ø=bèÖ!ÁÊ&ÖÉûYÃb Ó^[\ºaYt­$w -¥êø[ÝÄR¨)¡§§iÛìº[ô{$™ì7f"¥˜o ŒÓò“¢X‡‚p -»Àz¶BzðÎÒŽ'hºO|ÅŒ4ǧ|-•já -:ž•É}Ì´a«%Tvœƒ5ÛÚY9QK¦ üd0 ?ˆ$˜äćH|õ#òuäD3µ päôh¤éÔrÆdì@,@XmíÝVKY¯É@oò„Ûu‰°([µš´8#!OM~.…7SoJn%< çI¶îdZ‹ó=ž1ËD3‹!P#4÷£“ö(|:]SYd…„_škG’äõ²ïRh¹+OtÐB²«÷%Ð/eýä±–á&]·hऽr5¥slÿor šQ¾¥êÎ"F”,bÕ“÷*2~®£:ʼ÷Šiõ®[ˆ´©¼«‹ÌtéÂ˦“ŸíÛ~M -ªUé6§DUÎeõ"udPO:ª¶Ç]£x+¿ì ¶GºÿB½®˜]´€q:óÊ¥—'ý:¶-ML3\ÑF$ç7…Kà3êCÄô*sü2µÜzÏFÊsõ'÷Ôû¥ñ ‡Ì¼K¤„ÞsŸ§æÌÓšŽ¤wER§” Ã!`%aTÆãH—êvçR¶W¸õSPÚÆ…TÆòÌ}FVArB«[ŒïSS &ñ™}pù`Ç ¦~ˆãºÓ_íŽàÙ1{Ý,yãàªo„‚ àÜÀŸþ{ó30…é[ÇbŒ K™¶È:wœ3Ùã¡MnÞÁµ†e6òsz/0OGò°" -Ü3NÝÃ|›ó/Ð'ëèO¬-R_—1Vh&׺ N’Pz8de™ºî 勊ZTH;X‚†ÐåŠQÊ6Æ}™ñ˜+°¢ 'h´Ï_î|È‹¶ÆCc’“˜¤©u¨ýHù¶j£BA,·NÛOS«§È{é3ª÷Ñ L’è `]Ñç³Ì1ÆÚ> Aº¯Ç ªÚrxÓ´ü*E^ÿ.¢ר`ú†Š“Q5 šv{`=W6뼟ñ\9aõ®’ÄvGOÞXc& võÈ1äÏÆw‚~êÖæâ0Wÿ:ÊŽ~µˆfª–: 0¾/˵‚§%þm äKRKåAÁüšÂï‘ÄcˆR %ã ŸP“#CS£éߥÞfÚmD¥5ÎoEx–…1‚7B«¬ðæ»0ìóÇù<&ÏæßôøYdø8"1²üwÜ¡5˜Á TaaJ3p˜†5œÛ¹î7îA>®‰+ç…ñœy¨”Iž–Má¯ã½ôcðuüð Zd?ÒBê†k1ÃËÄx-—Šr·sVˆ†9s¥H6p]DÎGâ¸g…O| y»„5ŽS¼$a´E˜`$íLs -[•¤YmÎ Ê~uå8Áíp`6*I ß•Î`S8‚Ì»8s™nŽû9O3nX¬OE /7f•^ê…lËbNÖ[®PBµF¦O¬‚.Ü9¬€ó·ïZç¾_.Í5¡>¥F ›õ S¹µÌ‰µR×'}ΪѬ`_dÿXš|ê{´dâÐHXÔ¾3šQlZe7Pã©R¡ªq¸Ø´³O5¬ºOkZþÏrx5u`aÇ‚:‚£*`ØT–)›¨,¬ -DPQÊ®²²°dß“——¼ìûJÈŠ ƒ£Rk=H+ -*­#u)£èèh§Õ) …Á)B§6ü¿sï9ß¹çžøïHªZGòµzçG’†ÍTø"9ªÕ3hŠD×ͺ ³Ïs¡îr|ÆÅbã4þÅy ¤$”õTK ºá•"ù$×IÚÖ~Î$v(ˆBìÖ#].²ïqiÁ?­C°¹N ù–ú¯ñ~úÞ±Ë|ìíÜ·àLcOÀnÎö¯T~ø³ÜýäþvxjºÀ̦5<.ïÇf\K<ÏÞ2pô:CpËõá•Sây,66‘ê>|zŸC -Œ“¨ëE -T)ÔÈf/À•:X1È}J+Üç>¢½Œ_Éý…~–˜éQ;Ù^Çõㆪvs&¸Û¸>œ.®k“7‡ÆyZþ˜SÃù:‡Ë©ãœ£rØÝ–ÛœçœåÆaKaŸ³”!l–è.g5û ð7ÜKv¤0 ì!;Ú—­fï´îîe§ þ¼%‰]"XëTÖ –J3©aÕªõlwV²j=v†å§ ”αÖÉÞe=bùI/ìög…H&ü†™ ’:§²°˜g,(y 27°>a™b°aÌ8ýËÒ8fŒöVV®qßÕÉŒT‡ùµ0†ãNÕöбÐù‹ìãɉB¦‚ÑåÀ`( š_"f´˜o!• Ðt}WgüŸ_0}Hµ¸¬X Î9»,‰QÕÓÈxÏØÚ=Ž~JÙ¹§x‰>Ö€ô£ÿÓ±eç9úM«Ý÷2mFõ¥Séî)×Vk ¹µö-eZF«FÙ¥btøg0Éì¦O?‘DàžÇ%7eyÈÚš6þ‘WªÎS¥C‘ÒyÜeäUS}Ýñló`a‰8ï‘®i ˆèg"1лJÂ"ô|P¬KÚÒc¤,¿$¼Žì+Î&ÕÞÕPš°„vê–´GBoj_t4I òvq¨f熚(ñeÖßCÔË!οbÇ×¼†^°SŸ†bØYi1‚Óì¨;2ûˆWä`/7u¬h?4 -®†Šï!Œàzõœ@#(˜ãóT ¿œ¿6ªŒ ^ó¢ð!§R S”¼#Ð>E/áSÁq9¥z_ /G%Óˆ0Câ9Æ[Û¼‚@”(Ù©”ÝP•é À, }ð¾XŸT…Ok‘‡p¼µQÈ«U¥G6 ª€ ¯x2¿e,¸Ú>¬áÛ -?Ï­°ÊQÞ¬Yzð/ÑTº·è5ÌFLÉä†^`ƒtÞ©‚Ì3ó\#¿¶»¨¥¬ê̬Dðª:¶,ŒvÓÞÏw[mDWÑ¿¡…›)½ãTB¦ÉZ½‰Þ`0Õ–`3tÿBQËŸkkðûs4ë1yÉ ê`ì\è¸Þ¸ÂcV#þ‘z`XÄHhwõA¸0­Ú†FTy¦úqӵܫ*F˪¤%€¥*Éøµ/>Ó9 -ÖgS'µ"b'zL=Nª)c¨‘s×*æÈ÷“bä¿Rˆ¡ëä÷)W<ü#–S ²µç™›)Kê÷Ìý £ª -&¡ô”âLã¾\¹Œ9WËtÌW!Y1󲫬å«ä7i*ø%”ÕþêúwJ_°½ä¦Ü îÌé–¥ñnÁWJñ„!pÉåþ³ä-0éT`¿:K6BÆ+»¥SõÅzÉl‰¯L,~“³J#ZL´HBEeÛ߈E÷q1 -Ú¸¬ÂTD}é­bB;º*OÑTCÒnÕlŒ$OYŸ¸Qœ®0m·‰z7ÝoŠ„9NÅ» |hý–DV¤[V¢’eÆÖ©b7É£Y¯ÅZÖ¥H¦Çlþ¤Ûä~Iè)Ü»¬J•5§ø¤ÍòoO–ÕÝ‘ù´©%(ûŽ,¾hÞZ°GøÊÒ¼mR˜d´¹ë!¤ð/NEWutV57z;ÂjôÇÿj¬s·ý»^’^lDçǾ0-a®û­_a®ÏLíØŠówÚûö44ç°b^³ppi‡úÌúô&nÓXì ã¾æûþuÆ»-«Ý‚ ž-cÎY4Œ½_Œ´¦¶³šg ?ÚjºGìIãfHõ±ú ¤%ÿÃJÒ‚[ö%Ï©¨C6OzvWÛzåÑoZ©tËA¦±˜þ$õ?z€°ã;ÝÆؼÅØéFT2÷/+˜å0…™ñ@ßÑ@S<@>0bð¢SëuqÜw;j4SÜ'°/4sÜïÝÊE®Õ©(P[¿øú×V‚^¡5ÆŠH’õk„g/´Û„«wø©û  0¥Ú*ÜìÖ­ ƒaËjƒyBýÍ5ÙTC› ÅJ(¿_©ÈFà4©òü˜!ÕmÙ, RÕÊNº Ë?SœŠü¹9³õ :×´Ê–Oèÿf¸“OÐVê×"ÕµÚ‡1ÕÊí,V)Sâö@æ._ -Óú°#ý‘Qî`”Kõá¶ò|ͨ%cï˜j¨Í/&\™Û: [­øFƒtû^Z"ªqÕÙ¤Jm•뙊jMar×—µ`VCg -ªžw" ~><¡Ã óŠ8Éièú½µ}×XåT8°Ç³QÙ†VY<…Ä<ÏJ=áÀEÀÛ7G8v6Á¼Z:¢%¿·¶hBòò94^™Ô¥m!1¼ˆOD>íp%¬HüÙÞGè‡ÙðÄ/Û`rqÑ;¨±N…mï~MýÓñs§\©‡/žˆ¢Zh:•(¶í¦¥…MXžÐ°^FÛ.꜋‹.–Y½¬þÏs}5™`aŽ Òö(ƒ(X0ˆTˆ+JS°‚ Š4©&¡¤~ù’|iB!! !)$ô)Ê°Ž‹ ºØWFÆY]EÁ±ë¸Ž3x,Àó˽ç}ï¹›å|d´ðcâ -õ“|ï°i-‚¿0ÐWs…÷ -Q_Gå½pRjŠyÓ0˜òÑ׿’t¶©¤øj²ÑTÌŽóԊD1·ëÚ…”Ú›NÊ:ka? ¦øç7•eê“æ£†k–Ø_µ%]a;ëזו¯F«=Ë9™Ô-b= &¿óMm0-­vDÅ'^Çj´Û+µ¡/5(erÀ^õ+E·çL F1ä$¨1KŠæ•×WE|fÔ×O£õFMK¬º¡¦ömèí:ƒ:1`»Ú¥fÂóƒŠXýЩMÕ*i€9ÈÊ -l?+Lw?-NxÕÃ͈Ôwɳ\C0–óÙ瑃f ÎsÒŠë·M;iÄ‘`°Ê$èöµµO»“0zúÁ*RÙ¹œB9é@Â"´k¬5ÏÚíÛçvÀü~•.lð³B?îugØ]Ãedº¢†…è8J³AÞjý­Ù͹umÌ.­ÕD›O–ü^^ÁÕv:‹y;Á³skeÄæ+,LȶvÖ²­ÅíàæÒ¼½Ø®¥™Ã‚ÁdÃü_¾ë5÷ZüŒ¥ƒ;q®#k>ÓêÇÕ ¥MU\ƒÛJ“”«¶{l*ÍŸ¿’Ò‘¯3Doéü¹èyüèÉÂ"UDcû†¢ÍÁuÍÏ#HŠ)BPäæitèì½/ vÀ`Ó_êSÊã{Ìeù5mpñPpyÓ=±-2Ýè[Úëæm¸+v·Ï6*.WÛ¿©ÉSÇ”áñ]ã -^DMk„œ,2ý.#£÷ɲÝ\õ³Ò!{®^âƒI4ßÔˆåµ.~°Ã§ælDc‹BU\bò«"c äj¤Ûvý‚ÊÃöJGÉ|ÏH`_ÕÈ2ÜrÔÜÉë„§ëæH»³Ñ¥ä ¸§tøŽHH®†BaGŸ :‚ú‹ýBÖfšó°{–'ñª9 -[ˆj¥øaЧøñe -&hè´°z6FdÄy?¢>g¦Û‘x&él$¦ŠÙÜ^œåÎ:¬¶Áæ³^Šèøn¶‰úŽx-'-µ¸’û]èO 5×⽜äØÛÎ@Sä UÞÚy]°Tu à_,zŠáWPT„|BJ,É•}`8Çß´y?pŸ7gË¢uóŠÏ\JO(_ØâŽvOUue4•öÙ+Q±¸Œbi²?A.jÞÿ´Cxy“˜´R¸ÊÖJìé§¥þPÕÝt㸲rðƒTôúfd½d$ÖºâFR>Pâ¶aºL·ñ'î´ˆv÷¥¥äê2M—Òë*‰¨×µ¾TÕ]`WÖÝ*ßcDÜ*åûhøA¹e#ï"ûɆKO9öJ“ùíKL‹Ò2š¹JÍñçø( KgK3jÔ‰f¬Z¶÷nLåå5‹ýo•‡M(½_ø¸×>éFO‡‘—ÚÓ¹Gi}<´ÃÄ@w¶¬#Ndº­áÙhoo4öYÜ Ì¾¨FÙ¸2¬…—ºÁYÔAƒšz$ÃW±„Öœ5ÃÃCoplâiÆÂ\ ³ˆÞ3Ä2‚l„;…òaá¶<;šSç?B»>‰zü“pr§j–Ðs m»ß1tôZÏcÌ¥Œë{¹sì”/¡‡þJ{™Äcü*Í#»3ûóÞ€f–Ï¡ne£ãæhúœ-»>Bc9SJ ÿå"¬Õ¹O8á'•8Þ€ `yHÉϤu¼-œ*`”· x[c')OyÁ»ÖÐ\x!ë¯Qïñ­S9q*;ÿ$;Éd'=–NYÝÌ ,|ܶ3Û4§¾”ù±q¦T˜=©ka%â´õ™h±s¤€ä¬ººÖU¯X7Fl«”[õå oµš1apuxf¼9µäQ¶Gk4³;e -˸€7è‡ß5ôîxB:¤y¿î¡¬Zd¥Í«,¼`2²—?_Ía[0Å~9iY Fs®3öÿ˜g¹× ¸Ã‹9u¤¾ ò<,yxí87¼þ© ´1‡‘‹¯¨JaÆ¥¢,O€@/g½½ÐOÙã”›Ñ94 ð|.]»1÷6ýè'Å^Ô¹@1'¬èp:XtŸ±„Á€ðŒwL,j–ùVÜ÷á¬Qv@àwl{έž­Ì±ù¤ò\õ?R^ç½UVÐ\¸GIœ+¡­¤¯åž9ÞÏðäÎD…0׿»ø3oæ¬yÉdç[R<ƒ""ˆ’" -.´¥2…}”´"!<ý4t¢H~(®-rñç2¢æ5DºH@ùÜlÚÀ"K”»““îæ¿£,“¼/S}ÊëÓÎÒÂÊ"+ ïÅ~w ¥™«£—F}V dRz™,¸¨:Èw&?­ÚC~Fq¨ôJ}JÝ¢ð‹˜¢ÊJý’i¤´ÕrjzEÒgUÿ#±Úp]ZFšÖã%‘Õ+Èþ [íPjewÄVjlõÊíW©7ªà«wRüœ/*žð¼C%µ%íj…òG€xÓ @EFH)&0_ýËõáÕ•Ç|Xuˆ -D¥¸àRNXAÐ\0JSФ£H‡é30ý½7åÍ›7½3 C•Wc+–ÅU#Ær# aQOL4E³Ñ™?às~¿{ïïÞs¿I§èªÖyô?›y>ßÒ’LŽÔÖ¸Kd-Þ£J1Êvñ*fH ¢6§àh¸æÞöñz²+~BÜOó:IÝøQqöZ³¡UÕÊP±[àU’åD#ÔBMË Ó>$Î ãz|”ð?Ìë^‘¢Ìó• º!J0ÌèW8µN¼  ¼WÃzX›òfÁÏØÑ‰@'hƒá<ð¼ -%sdŸRÛ”eï[þ$z,¨åZù2ÁH5ŸÿˆÊ[&Ht LÁÑ ‹UàOà ì·¯Ë<Äu–ºÉŽrEðá¾€{i¼,8+ןwM2ßÀO–œ\ŸÏ7I8Ïùi’l»2Ýewkv"9kÔr6±ªŒU\¬RšÍ*¤q¹oäGCxxy¨‡„;€:j™œY°E)a»Ô²Î°!Xidÿj¸” q(ú ¸8ÎSí“Ð=œ÷Úl(ŽPíø9Iueé&¾åk)«­‰W1Éþ¬Ñ=k˜Z¦b;8˜zBÈC޳ùƒû4›«Ú•a¾vþÀ?àsuú¯UuPÝæŠ\Vf»>+›5Ð2ÁŽj&…PØ6»šuR»É®Ô! -aÑ+árù‘Òkô!¹o©4 í«É`¡ÌÜ—ëP)fíô%ù¬VñœôQñ‡T™èFÂ(ZÖ]sŠö‹,”ÔTR‡|O)O?‡¨hŸoÑ# ]¿6yÄл£)ôéOçU ,FœéÙ ¯´›ÑE})gŽsá©ÚÙ´GðyÖÒ˜p/kw~Ë–»¶I'Y¿Ù;TdÀÜg‚YÉUž•Ò'áñƒ‹I8ÿ@Fâ*ð‹ ”8 $¸I¸à+A2((²+yª8O¬æÏ‹W‰»È—EÄ {Õ¢ýbÚWÕ"@œ}@‰C׌Štâe®Y¢¢gv…Öˆœ•üHáÅùoÂfE`áea±±gbN›_¥ÁÇÌ4Ê!/¼e%¥³ÁÐO;m¯ÅÈht§ÄWv6¨º·ú›´[©ÛÌiô¹˜¦åÌÅÁyF˜yÃËÓ4Ê”aêt V„]ÑÍ auãƒõ ¬#ùëÌÎ쪭QÏYm3rMè/q{Ž~äÁtjÈüD»¢ ×7†Š•fàiÓƒ¾É· ‹Á„èºÀê ¶. =[ôn`4æŠêÈqSháæBéžrÐx_º5wÔ Ï%ÇènÀQˆ…ó‚ðš~x“'G[ ×`+qb«]Q›2Ô¸²iÃ=UGnŽ~»òç¤Ú‹JÏ(¬†£à¬ýAݪÈôd«Ç ȉE7ªÍKÊz +MÿÊ]¡ã’ˆš!}ïæ j–nh-CÕ¦_èé­º¦Ùç䃊ûíŠò›a¦Ù­¬DÙfrÎjï6–$-Ó4ôn®UõZ×F)¯Zpï•×ux'º@ü]U•ïÏ/ìÙ³¢Û¿Ø3íUg`iUì¡}…åÚ°ßÛUÖL¶»W©–Îuœ¯òù+ëÕSïØUå‰î[¸Ýû;u•¯ÓÌû¨„žXJP“v§åOâÅ€©–{$•KFÛ,qƒQÏÕÞr¨uÏHìÝ.ûúÈ}iÞmõ”×ÛfZšh~a¢æåtŸ€ÛM—訅BÇb0*€ÛiW­ÎÌCä¶§µ‚¯jZ®×mÙÆn¬›[nKfi c¬+.º&oV.£ô&Ê­Æ{•ÿ¤5›_°s¬9†dë¸mÿüIëA. Í*sÑ5: ‚1ù ¨Å®Ê m!â¼|f‹—¸l'Û6#NÞ -Z§Ä>øÿó\ßoMŸkÀCZ8à)* µ€bEE@¥§(2ÂÌÎ7{“IÈ" $!¬„0a…=+vU©ZÅ`-x´E½ ŠJUǺ -êðùå~®ë~ï7¹TÕSîs¦‚V6·÷¸ÉÏØØiŒ1=2J眆Jhê@ UÍ•æu¤;·¶7!³0 -ß½Ù\Ó醮õý¢ý¬—«±ƒ%-;Øþ=.ÒÜeÏ/©TÖ7DŒ$v°{·.Ê«“‚–µÂ|ÃZÑ®³mcDÖ²+-C¤Åu_Ïï{¯>ë1H±1]ÿ"—D^n×R¾ ÞÙº“:êëÖÜE3ƒ[²h9¶»¬ÏèÂÅ ‰Ù7–TJOW+3 v«œLimc @ï6'³é[Öcì`Ǧ8v§­ª!“ó÷b‘RÝ{•Ð1Ï_Óµåòu‘¶Íoù³PµE°2Øð\@;4"›mOÕÙ §Óÿüm{·Ä ߺE1Ñd‘ÄA‘¡’}C=W²¼®¾B¶}[3']\PJäG5ØV¯ò‡mnY¡G XyÊahdÃ'J„[U~ v¯óWæÛ…WoÌ]¼Wn÷G¥n½R°¶²9H7ÑѨÔ¡Au Ú1¢v£ö‚ëÛZmˆ]lU§rñÏTØV¨A -sjÊ6lŠhm,M†Ôy•4Aˆ–À’*0¤æ¯ÂÛv”J–²RÈ?¨ï ˜Ä´>ëîÄ2C!*Ë#±q0êMJ!:ÅÖCö‰ÜÂÀRÇ|ÿdüF¿a?Àµ2ÎÝ‚ÅcÙh3¡«dBš‘z¨ŸSIÎt?¸%‚Lý›mïëF[ÁAÚãxÁY‘GâÒ0mê;GY1űh%[Ÿsጒ@×9é q·_8ùG>r˜ íWó¾nêå¦Ü)jod€E“…z¤Cö.qúJviN&¶IfÅÔð8b›øgŸ - ñŸv|ïs÷‰d¡ò©%:uTÉÓÛfü‘û—è&òL®ˆ0Ñç~–pï¢èÝË.(R½ŸâU -; _)§w%$ò‹Õ/à÷ Ýt#é« -~ö#œu`u[ëwø.–¥qÜsï“¿Æ‹ååYìü_•-þ‡Ç*'̳ɩåßk/§––)2* iø9¤¼$é°7fU‘zÏfl¡“×cô9}£]´¬,íˆWYCÌIëê÷kSÒ-¡§t¼y7>‘T!¢ž˜ÆÌ Àä×26ŠÔ¶‰ÓKݲáç mÄ&c‡•Ó£h’¸£Œ' Ù..+œupCë6Ì&@j“5Üt«¡™døëÌåêP–0«Ÿô–=ŽÙI™Ë‚Ä–Óú¹ä -úC¨{Þ¶$tR:(Ï­uO¹ÖR4›ÞÜâ$=ðjl¥÷¡Äuq—1?פþç9î”ùSèi|cåõqÎF!¨_z^žSÂêK}¡Ÿ`d¢%DÎT íwVŸ>Å;à¿<É'’V=(è5HÁ%jú»à¸WMÑó±…VÆ#àß9ÛY‘D2ÕÊÖ“Òîp~¡žˆžá~J ÚÉ}ÎD»]ážgN¾Süs•jJmƒn-‚>,v¡g&SÔLëlê#–’ãíÆ>ŸÅÝ^Åiææ¸á8®ñÊž®°%Ùñª4'ÉùR‘’JD“Ãh¯RN0hBëA0ó”(rÛ0K+úaõæ‘þ’²MõÀYÙò|"±E¶G¸ƒŒ”E_R^ÉvÁÏÒ4²/ì¢?”ùm[˨•y¬N`ØK/5[™7«1äÁ€[G„Ø’'¼ 'æé“¯RªGöËhqê­Ú]Õø>£úiI‹±Xà -5€'”\ãGBŠ „ć…ÓÉÎdƒ^u•ò¿x +•¢ë[¨^Úá%e¸‰ ¨Öªúp»«xEÀò -À œ6…%!ItãÞ¨@ò•Ò¾#% Ô §¸¾ß:¶é™*‹h$rßà7שð׫55‰×ˆÙæÕ‘'I×+6‘Ý*ЮwȰ¢Ñ%U”#¼›zDá+JÈtàü šBùa²UØ• 6âíøñ}ñ¹uOÎrÆç7¥ñdôÿïåP˜À›Â þCu}FEuæa7£RVÍ"KS‘ª‚‚T‘¢Ò20 E†éåNŸ{§÷^˜ÄlkìëÆ•$vWš(¨,¢F7–b ˢÞ¿ÜOÏï¼ïyÿ÷¾÷<"_–³)“ÖÉ.kîh˜àÀ[n ‚9Wñ?gÜçÚˆ7¢ºyÈ™¾*¾œôöÓ¼‚uA@ ¾OpšáIäRùŒrPý(ó$e[i<ËH›—±¦££¼Ø/€\ßt®/ýâGõinQ­Àaˆ@ûšq–Iïaî¯{èYÊ’]Že–°?D˜§98ß™¬çìéy©­dx,¥8¡?“½¨½Peí1ÚNˆWŒ¦wAíi‚N,» Bçà=Ì%P—K©ÔÄò~þÒˆœGò!OʽÑßR=e’"!Í]:™:BûIê\†¢MJ1ðj Gò›K)b 'xL"W?D\GêÔCÃÈ{µæÂAÊA ?u#U Þñ•ªÆÁãèUJê´òÆÃǹ— H{ÁF[Å5h’ÚÒ[N6˜á){((ãÛ%Âø>ƒVªYâR -nñÖ#ܼ°ë(a¡«ºFìqì&mqü3%—\g?ñ%ÙÓ†ñÙM5«Ÿ¸”ø¶X‹D3‘b$ÊW×ö Ÿ‹¡öÆ¿‹5&Í”D4Ó®Kƒ°ãc²ØáŠ ˆ. -úë1Zëšo -^`ú~¿`Ç6z ëq¯©Â „ãaX‘Œø½Ç°ø)±ýӼ܄'À¹84 n"Db.ƒâÊð¹yC<K¹³ú d§Í},ð§õ{œ*h® -Ú¸»hè>¼¹ôwMv^ cö¦8ÞIÆ»õ(~óÆj?¾’¥ -eðo²®y†¨l/—‚Dl5¯€úžãŻרãpùy1œùÎܣܵü‡«´Ü^004€{ ñØÃïí.™·%CÔA2¼ù2ë¡dÿWuìõ’šœQö>‰oÂkL<ÒÍßòò‚Å5.ſȠiñ”f¦¯fh³7S­ñ-ßÉ|¥^–³Š…T¡jX[•wCòY©*¸sG^1­VeÖ—ä+À˃€˜…åó·L³²·3“ —â/2y†èà{Œ†¹ì+ò½.Å;Cçt‚äJ ¢}¢þ Ðáô-³¯>Ù«y6q<ƒÁ³Í ý–£W±·åx¬Añ_P¢Z? ‹Q ŸÞy¡ÀËÍØ1ì>yîÊKö”Ð\.ë!¾O—qÈM±î² -0Cî¤l…ËçÓ];¤S¥k)ÃÍ=RZ@Õ[É·ô5…ù‘¾JBe„Ç$ÆÿNi"ã0 -úìR4HœÅ~€9.☫|DϸahÑ·-Í)²r~"êåeùoMòæÃÈâþôÝKÝ%4 _7ý"ŠÂ‘e -QD~Ÿ0Täæ.¾Œ>"x*õO¼>é…§…ã.Ey+HVy55R”ƒ•Ws•éùÑÐEk*êPxEGBü¦ß;(J³ Xâéµ(8h©iqãmh¹‘^™ Í0`óö€}ú´”_APWµDèÒüLü£Z¬‹]Š<–·4âÁzGÖ¦`Œíoy©üZÃR|½u^ŠŸg™ôCð˜F#ÌnŸr)ÜÌVaåô5ëÆªwˆ–Ššó9³­nój®yIÃÁÅtó¶Æ -xI«®1bªIyâ>}•‚›-A–ðÚ¯ÑÁæõOShãšìKF¦xµŸá6xq†qQ -Ž3“°SóU„‘›\kaÆþ椚̩ÌDi©~ ·?{‘>¾J¯3mátßÈðZƒ„øtªÝÔ»]ªY §‘N¡ju]äÉ’ÍÚï©QYílZZÂsˆNÑ´Ñ·À“´Wé>Sã§Ý¥0ÕBÌÂj»æëËâõ+Ö7ëºÔqìÒ„fU7û»ð™ªmì ¸·ºœÝ:Õ8ŠÁé¿^Ó;õ#ðeÕ¿Õ+ ‚â*,Ä_ç©ì†ôñCÊY3ì®â´õMåÿßSšªU*™¶ªLùXÆ.jQ†È–g²_I¯ÅWËJ5a"9Rª†'ÈC¥\y׳qH)ÒåVÍUü¢-Z®Ö.Î\+ѤÆ/—aÕÃenš/|F¥[•¯?S âPkr¿"Ô -^”YÊ×>”•˜ÝVŽH9 &yaŠòùI‚ÖxýQÕ<èzU1\–µ¥ -Ù½þl§*7ù}ûô±hu;Twfa^Çåúcn²Îü†YS q;)oZ¹uÞG!öuTÕU¬Orþ§›lk;ÛHF´-Àx/,uþ sÔm¸í>fdêä}é+] -U×.oÊ.ýÕˆù=·Ûq»-y¶]†[õÌv¿iáR»ßçîgëÄÿk*ï`/p©êþ¶LBuéû±+çºÍAº²ßÚ@‰Â[»)‡î·&P®¸ÇYQÃ?æù˜iåm/¤‹K,¶Y*g…u€±(iÐ2¢`æ½ÌØ€VË"f¨ûJSsèÓ=çRU@ë÷»7+ÄËÎ>d‰ãÙ’¦§¼s‘m¦Y¼áš)–wÉ=U?οþÇ3D¾´Äó‹í– qjv¡é™8<ñ…Ñ!zgØ)ºbèýì^ª[$zð™RYjTõÅËÍ)ªÔ¬1ãcå?M…2qÙ=½@éæ? ”Ü£5Ûew?SIfƒq´ØÓD7þÊú kò\ãL24A<D1` (T@@+½“7{½Ùd°IØ 3*8ŠåœÕ:@-ím©âq´*îÊå8ràýþü><÷uÝÿÿݹã\•‡id‹ÈXmümÍÝJ´ñ¬×ÛJÇÊö’²¬"§ÏUúžjb'm_êz$bï"ì¶uOì÷ðßbûM¬q_'Ñis¤äͼ?M#mp_=~müù=Ãİ­§í+È¡ëŠ:¢Lùâ;48lÈþmbå3ï埒-mÛ$õY|H—™nÚú]ÇcÀkk{>3Ç×½} »Ö1Ár†@vÿÁy:{­Eͩԡ.öÞ¸Î,爮¶~žCH{kâsÆvš_l‹æ¯@š”"ølûORÉ."0ÞÆ§Fl´]]Càְ˧²Å¾±ÞMÍfˆü‹i n´q”ØË¶QÅóð{Ö5ÊìàÇÍ6ef÷ e l¬¹[I‚€u·ÔY_(i«»&;¶Àª×t„ïo¾¢é ئ±ù¬¶5•°k–ûÿßýÞZ§ü‘/”¿ jÀÆîj‘Àpíî~Ch°Å⨿íãÜ䦿ëiRês!Gª-…¹Ÿ«”Ö ŠªØ5ÍõÅá ËÕ -Çàâ¦Åå&wó«òïa7W”ÆAÆ«X…æÏU÷î²Ûr8„Î+}E)ÆoV¦ÓƒIÃŒ}ÀqZlúÛhˆ<Ëgw® -àˆ¹Aæ°ø?ï=‹ê$ù6-ÁÞ¡|,)!<¥*‘ǘ*z!­•³8í߀ϸu”ùPpÕÆD|èÅœeÿ=s˜m4' ²¹Ò¢÷ؽüYaPøOZ(ù„èvj?VGÜgxI=V¦–-ƒ̹¬ u¾MCJÆëÕH_é-Cª]ö‹ÔûB~‘2A\¢8°¯*÷E8PóTª•Δ¯To¡ 9/Õw„¦Äh¾©½“ž¨‰Óýˆü aƒß£šbòy\Ÿ¦'ÅF,ÕŒ…o%¯ÌwðU/åÕª—äßnëMê*ÁÖT Æ‹{µ5ÇŠ¦ÅÖìÔ¢TÒ9L·¡;y _fXÖD\¤ïóuÀ¿Ö·AÅêõ:x«"¬)¯V%V/*´¥]1•ˆÎ# ŒÎÄ)Ô‹Šžä@ìX¹"ÌSVåÓ…“—™¡4u¢.Éfˆ?ÐÜUÕ¹k%¥àNj´ù¹È;cº~?]Pۺ˄áWßÞÒŒ=VÕë“1ý -E© ºÖ»qd{qÕ׉Ï;’ -²N­Âæô‘–¥„ÇYH‹d×fttcâ– Çì»â#&®vŠPótQÖÚjd¡©‹ 1ùo˜ øù­ìR)ʽ@}”Áï<7õ ç³&—ßè8³w¯àyóyÒb¢ÌH¢¦04·èôÍ‚@>ðo` °•‰µœ~î½Mž<¨Àa9‹‹úÎ(ÉènŽ…­°c9̼4òß<o˜–/ˆÂE1”Ì÷â“áìÉ:ïã¬É•¥Ô7e¢þ ®.{Šó“Õ‡ˆPi WEî—Y±Ž´xùÞr¨è -¥EJ…7ˆTöÏæ¶Ù|4ëê'¥r41—œ“„ãæžâ¬ nÏsÆÌ#?ÊCÅ›hkòº6¤/Ï»¿¬——7›n Œ áHIT¶ƒ¡‚c6èß±=ðzz!ñ?z·¸_)þºc¼)¯uÚe‰T»®}nSê3ÒØ&iq4r¾‘¬ŒE¥UÂYÑX\ùKÔ¼ì—88IXú`}7yIi¦× -¢ôSëÕiG›¼‹r3æ8EiÖ…ú$f ú·ÚÌl+n º.¶’ð®*d}0QeÊó:FލœœSµ¯S”í†Éô°Ö¯äSȺ–8æÔ~‹%;[Ú˜Œl0~{‰à^ÿÞ KbV+æT †sIÓÛx)èoe¯øÏqÃÀra5),k±xUõݰ‰Q"—ØYNîפƒ¬¡ÙܾWu8GøQ‡d!Hû9í´2”'Âˬ31cÂC\ïà!Q-ž;UüŸ9£ ¦dÚZ¦oá8CÂN’T1+¹hÚŸl-?63ˆ‹zÆ,å=]ŽàÛAw7!üz6·û*HŠ„H*[|Cü‘¾_@m¥˜ô¬s2Uôr]^9ñnµüÅÂÙû0¡"ž°PuTó’t$‡#:HõÍù@¡Ð±jyúb€¯~°=å™›±ö*‹kuÛÄUçŠæ¦QîŠmÓ9æ]'pu „¤—ÚÛd€ºSÛ–HgiÛ¬€‹6r-ð×»>`Oi#çTÙ2´¥"-÷!NSn\ z•$™É¥SÔC%“QïèÉ%;‚þ¢O»ö±z‹cçT)!ûM½.wf².PàŠo¨1ŸæU=Bœ¦l1åFá©#F0HD“\uÌžrÚœ*¹™uÜjQ÷ O5·òu8–E$®7:"âÉІ¥‘ï(Uu£ANägulWàYEãœ*ZÖ"cT\‰õŠk—öáTx»lŒx)™$Ÿ¢<ŒhTÂè',Ê·À —ãêóÌy³={V?$¸N€)ÃDëÉPŽVl Žàìà¿Ò]:é|ædø„Ìãà"»Èíw1*§¸=3*gB'æãòë|–'#ÐsB±FáS^PR“8†?¾ -$ mþ:ð¤è@®•Ïöl¡ÆÚ(蕉9 Ea,·_ÜŽiàÇ€/O IýÂ4ÂaÜßS8!ûÙ%UJ’õÍý«à8C¢(”&2¥J:ð;{»ÊMጫ~J¸ÂÃådméâ«rFW øjg°Yý?ªë;®©s8Ò€€Ò´Š€Z6\Fq€D,²ÃH Y'{“ÍÉ$a@QÜÞkÕ^½÷ºÐ¶ŠÕrõªh¥Ò"jÅ}+ˆmÁjÂôßóy¾ŸsÎû¾Ÿçù½Ó™Mµƒ°D]Å_CJQw’)Áê/ʹÀ5õ®¬×´2µsµ7§v†½¡}§Þå7Ä TëVÊÓøS¯†€6~Gú„”oLF¢ø YHÀ©²ŠKmÖkÂv»õ¿~öUÝô.+ýjpâo8Ɖ$Â5û)Ô\R‹‘¹²´¾(ùàgÝ–H¾m}ìw”5jœN) -+¶¢£‰›„ÛïüŒp·vO[n™ŽHj©ÍŒ%=hº• ’Ï4‡^"‹ýl’q:iƒ“Òç%S,˜ŒPîÀûË=ªÆ‹äG°±›²Tü²åï” ‡B†ULR8LW÷˜äêÒÏ$GÅxLcLSƒÀ},ù?þEÑ;ð8Q¿±Eª$?\.”e£!&¹õ¬KKûç¥øJ¬øÛéG„‹È7°ˆ|_ ÿÒžl8!>Ë8Žè“(Y«BæIúY·`[å}ì®.úB”Áõb ѼT–²ú$¿=U8OÉŧ yˆPá¿-x¯ð$¶]ú¥0Æý_Âù¬ -ájûˆ(³“sO¼¤À‹ûH|/=wKRŸØÁ—ƒÁ•¼ç`ì p±túl>f*ºÓ¡œþuU‰þ<‰=¬T­§s®(·&zpK”½A?sLÊo`N0M¥q+~*m¥î-¥‡~F7‚^ÈôÓ5惬H]ò${žö|›-Ò¶9Y&í=X'¸V£u+^°Ï–Eñ m®ÿ² -¤·Y/ç0ŽX® c˜ÍïøAæùdPcÜü_X˜ª ™VŸRx­6²ŽÔb¯|Cã6‡å‘éÚÆî´^FeC]¼…¡oÀ-¾F?f7Á˜ Q3éVÌ>Íy­Fsõ•£y]ŸÝ¯M©F®½¥íÁÍŠ¾£kã^NÕ¶¾¿I¥’¹öë#F›Z‡.À7¨¿Æ†–Qêfã®eÏ«CJ×n®;AjˆB JFÀ€æw -îýmÔ—îê6êÕt¢”Žã(úI5¥beE‘•¨l ²×X•ôè¨Q•…´¨ÍŒôõ™û i,)…6QS ƒ1zJŠezVBfžì Û¹ú¬œÊ¹•/¿Ëí HQ8¸9ê…SnE%Šoòþ‡-“4ðÇŠ±`¤`,£Ü)ü~u¸tŽÈyQŠ“Nˆº]çvØ­p‡´¯š+e„"“x©ìææNñ6yä*µøœ©”,“7Á$'äx×\˜Îç”Cü‹ÐQL¿ º[8.d@}›çïCõÉE)1â‘àD?@晹—ÀÛbîÄ$ó?7£ - ‚Y¦ÞMÐä• N­|ÙØÏÒ _ÏTd'wa£}†Ó0Zð„÷“ãî¦ùü<ûÑä9|•Ý3Áµ¯†Ãé–—3~o=§ØY>Ûú¬l0Wb=P1‰ÜjmE ‚¬XòR[lÍo¾uv:.öC=æú;.Îa.©œBÅ™á˜S[þnºWƒJ3ÇŸNì1='\XrÉü–8á»Û²Ÿè:KìX÷»j¶6â‰e †g΀aép£´%z"¨±K†1ê¤ï.c1õɇzÉ­°G´TàRi’VB“eßÔòþë-)K@iͬ­!÷uì@_ƒ`»&2ˆq«ê u¿p%P——õ -áéS­Ð§|NüWP´ !€ì¢o-t_àš Šƒn…y­úV|¾Ø¤ö“è³êe`HÊêE =>\½‰TÇ€¯|ÑêcÒŽ™kˆIS¼T!‹”…ª¦Ì%Gu,%[IRý'®ëý““Áß+#Tç}mÊëê•3ú \ªï/Êßê¯df)‚õ`Š·ü•n2Ž#ÿ·î\°MŽÖû–(ªµCQëd¹6f‘—lÀ—qGö®žµvŸì²µ).Z&²æ¤w¬I¾ùò¿¶ÌTe{Ú‹¹õ­¯JQéæÖÂòÜ•Q­ž•¿E\m•`¼p–`öÏÒ´ìÁÚ\éz[•v7OV¤o9ÝœQõÓú¾¦}ÕØ$SSFMWÐdÂÜny„ï˜u£Ñà:¼ ·*éoœ[“ž3Ø ÅëýO ¬·FRJøˆã0ÙÐÕ¸›l÷ô¶õ€+­L+û&ò†oÛEÊ+dŒ- -­@Ô?¦ý^f›E¿ko¦oó\fyÉÜíJ×Óó«8Åz‘‰ÊΰÒXmi Ëö¿– -NwØ}‹…‹„OYÏpz&˜@>ÚÕg¼ÝªHcþ‡ .«Ã «×]7MŠü£¦z‘#Œf¢ŠàÃæe"“g¦æÎ\ÃÂaã°ì@Ö\ãqyºJc™\„¨3Ü”·‡Á Íò½ðý†r«'W§QVüEá Dè|¦ÉP½L½­‡sõ\h_h—í…#ôÐ9ÏZ-ÍTúdóÊL´>˼!õ„îWS/bžn­iAè3­ÖÑ.1Fx@Ç §3UNåN^nýPOZñ¦dÚ t<¦jGLyÁâs=‚г>»vW¾O&-8ךs§»hvöö‡eSȉî§’•`wPU_´cÛaÌéÅ™Ý=Õ·}öm`«<<ìƒ$•+­»UV×6Ì6dòÖìÁ¤oº88{ÔÝηµçºzkG}Ú»®Š<<ê7Ó\õÒÓjvg!5M×Á!®wž&—GmpfS¬ÙÎg€·O3x?Ì÷Ø -wZs«LRûqê/ë~lŸK]QÓVÉØ¹¿õ:ÓoÑïmùÌ<Që'ÌÃÆ R]çAMXyÀu¢¨ º^¨ƒÈ©¢€œá ä$÷}ß!ä 9LHÈÁaH8«µh¥¥Ê¡rTtD-Å*ÐífY]]wu¤®Š¢²®u[bgÚÿßg޼ߛ÷û¾Ÿ"î÷ÕȹÚÔ I7ù•ŒØH”R·7ŒH§B—Hðu»£d¿të ÷*µá²²=e¢ Jޝt­jŽ|먾 á¹#T­¾•ÎIõ/¨¥W?þ{úÎ¥¯O»^ëŸÆ'º`ú·‘£õv½'$»^®óEÖ=7¨IÉÜßÖTF×2˵”­õ7-¯â^Ô'Zú"[x ;ìü“Åæ[U‹7ã–,©”þQyWrrÛ9†õE†—6cày'I§ ÀgIR“mŠ 2›¹¯Z½Q¸¾ý´ -ç{0K”,è^ÊH©/>€>G’@l`á—Tîë=FZ‹àÓðnZ¢H ¨Ñ³$mžÂ¯鵩K¬AÝ3D;w³¨Å×7⾎ԫÅw·‘^J<á«È`i$ M_¤xÐò8w¿ˆU-,ì/h!Ó»pbPê÷ 1²ª|*¾k _U¾;ìÁ£Nè“45‡ü²’”jûõÍX£šç_€:Õ]¸‘$ %¯Í«X³+éè óÃMiwzô‘z{7í”Ñ`fOEêÃù5Fo´hÆX„}»fLÏ}ƒ¼k%ºJŽq¡_b£Œ_†ôAÆ5ú¥4WK­àÌÕ»ª'h?É:lÓØTHƒmm. ÞmÕ&Æ"öX7…rÑãV7à²Þ×ïžßl̨b§]r+• ÙOë¯pKó[œ{0ƒEÃuwrf ÓµçFaëjCCP§k®ÆtïM»Ý»’Vwô[FRçû(¥YÙ-Vî²¼åÍE8£‘™Ó Á»ÿ™à€PÜð?¿)™p>ÜÐÍ›ö5˜ #ÿTÈtäF%‘ú“ÌÑ3ª qhkü”Æ ¡;ã`¼LVOp¿œƒZìÛ“. ºÛj¾&ü\CÊ¡ <¿*Ùgü!½°r)JÌü‹¼;ȳ–&x§K0äNÁ\“úB¼&æ­£Ô³¶‡ùï$¥bÔ7fùp¹è¤t(Å0ú±HÉ2Š€—ª3¤Ó²G1d?źµûè -ùbßVÖ†|­°®ÿ­\[æ†w+™tj§j?b7–¤öÏhÄ«§wJñCmÐmê#Íb.^V÷äBõ–ÂDR¹bÑ8¥¡E]4œJ… 7LGÏc.Xd£°à¼/ÅaÏ&ÚŽè ù@›é¡¢«˜zQuÖˆ4TÒq¨i˽èèbË• 4ù3˽Ê~,ãyýmoθ³[ü 0þ ˜ -€l«} ûÞTóCÚuäLõBt 2¿úZÐò¬ã°WÙ>¤´EhÃ@+[‚Řüê¦yеÈÝ0= -ósç¦U"r]á;îû]÷(üºÌ{…Ó‚®¬eù ÃE=œm˜òa^2þ'FKv¥~.ôОmŸµ0O­j(›eñs¶ßº´€ ÕPk*!“þ3ùIËBЦŠñs4å{^§|{6ÏÆk\* à}XÌYž¥Ç å©ú‰DÆ=A %ŒÍ$’hǹW‹øŒÇ‚ORV°Ž‰ü· ²ÜâÖUB›øê¯ª¢¦rÏç+C°a6¯‰ ¯ ‘ãK‡ÀÔ£eàä :ZÚ¿u6&»à?ÀWË&ïk‹).”Ù]%¯ä]Ü,l”b7MX]Þ[H"}ªìØWL)RI·rÉfõr?Aâ­ÆYÈ&íI¹~_ÂIBåë$Ø{XŒîlÿZ·°·—X¯E¾&|¯wø#ùºßþ؆û`ˆ¥_vºß¢fã–õØu¨·æ3ù—°füÞõømæ¨È8—9Ç?ÿ˜9ð£ -ÌŸ¯ë”NûÕŽ²º`çjz1*äÛêöüíè.Ç@’ë爎܋¹ì`øoåÙ’¼J_™·±™+-—4α6@’Ð/DWEÞjE}HRæDl¹‡†;Y+ï öÛÏz•®‰/‡–…þˆÿ1†DçÑ“(z)oι&ù;éÝ.§4š¸aZú#Ùg—êsÍbÎZ+úX½Wi;ç<~†næ"¯(Á ô´µ ¢MŒ'‰“b6›·!G ãúùlP<á^\ÌÙnM8-—-aœ G+dyXPä^އs:û¯•÷0q \p3bˆ¾ññW¬u.æ,Rå&År§„m„òž#<„HeÊĸìvQél‚˜Ã.ËÀ±ïJ>«Ò›Þs)·àlej…(‘^ Ÿ¥,—=…/¥¡€¬FVù’ø6fjùðÆ;eð‘­²x•â%ºD³‰Ÿãk¾%!FW@aäæŠo2QT‡vs¼ §5‡Ãhí0ðŽB¦{âUò÷H—iGCO˜®ïãzL' pb¢Ižq”¼Í‹+'_1…¯¤Lvà -‰‰ÿQ¥ÁAÕÉÜÓ%ǃÄ$Ìû[H~‡}{†1Øf½KÔÙ²Â:HmWS -µ¥Ã«d}Ð2w7ñÔõá j¾¾‡­<ø ½O¯›Œ­Æ7ÖiÂ2‰GêÖ;SW•Ý’Ê!Ø@YâsœZ~ß*âî¦ÞPÆ6îx¤Qþܡާ/9iø7cñGªöHV‹fÑ3ÌáR>K•ÌüŸí2jêZãxH"Z‚"²)ûvH€Dö} ‚@ز} YÈJ64ˆT(P_Ý(*¨C]žŽ¢mi­öéSžJq·OZÑg­Aåé(nœy8}wν3÷Îÿ7÷œï;ó?߇‹*x"Dü’Ö6²Hae¢Z -Ä5Kü e -tE=H÷º·—±ÿš\Æ’·W¢8 Žù7¹¡2ƒy÷èm¡ü]L´ËŠyô‹ÚÄ ªß1N<å8͸@û±û±“ù:ï> Å>›Ë6p·„ü›ãÈÓ¹à¹<ÞAМš*ìµ®‹5M -àåN5Ðøƒåg¨Á³Ü¿ÑÝ]7ïcðDþ!\Æ÷¢ÖO¢Û6Nê(©·•bž¯‡H´õ‰éÖ²r¯t1gž‚“u¡ÿNÝ'w„ߢËeÎGrºÍuŒbÞJ1îÖaÔ#øgøiõ|™ÑGcŸƒ"½ÐÀÐ á'xù¾&ÚYH;£¡Ùª4›T1}‹Ï®;cÚV*¯‡™Ü²w¦f”™´Æ|œH2;ÛR±Æl›Õ{í‡ÝKÿ²*²óGÜùZPgìÖ׸z *Û¿ù©?Ê—ÖÁ -¦×“:ÌN£”Íæ9u¼ö”•¢L7Ûo©­¹Û|jëBñjEV—QRªî!Ū¢Ú;~;ÔZüZûËÚÝxÙ²ž -ì(Åñ†œ¦qìƒê’ïªÁE蠟ëV¥‡Éñ7å:b¹Ÿg3ìh\yˆœ²¬gå.:OšîÇé«ðÜ0ˆ7ºÒ¢›¦bÞ4=¢Nø¶Hdto{¥<ƒî¾¬çWZgHåNR•üRZ\á¨às†W*Zxˆù Æ»QÍ®ñ™l<ÆI³‘Ts|—õâX)‚…c?®€p \BÁ —ÉK‰àuðç¢eüÁ|0|/aì½hBÈ|樕ª—ÐsEG±¶ÌMâÁüs¬Mâdû‚Ä?ê*G,õ°ïHÞÁæ…ýRÀg®[)|å3…¬CëQÌå{0b•‰Iט%9*–ùXÙáÝÌôR‚MñÇU)+ë^Ü ¹P,¤põy*šŸ~uR½Hw,r„~L×îõ;í_º“°vN§øˆ"¹í¿—“‡Û¿Ê]¤ðÌ//S{Ì‘‘êͶË^¨Áæ0X 7Õ4¼’ªžÓ㊈Å#úÞ’{Y´<mg¨$…Œµ5`·ÊVs-*l¬Û»¬gÆùr¯’iŠË•[ šÉÊöL¹–X#DEhgq!N-_׸Îêc YP‹a ˆ!wÖú¯Ê|Emí±"¬ÒŸž¡PþDŽOUHSðr5¸a»+[ý– m×þ• -äWh¥*Â¥{I/1YwûôJyõ׸jùkú@p[³†ñÈåzó3*RÝeñŸñµRXwq3¯0´©Œu%­BÍAÅ’%\NÐC)šWí’'5ð꡵ -&ð™F+U,ŒŒd5ò…gR "JãrØVDB‹SDO]V‰[EסdyVÓƒ•1,+åIevù"þŒì`Ê‚WrwÄKa¯œ¸Gø…|—óÞÆ`¹Ú%+TˆVR¥C¸Fœ÷œ{Ys*¥š÷Zó5â߬±Rùe³s¦ Q3 -ÅÉöjáQA¬4Ó‹<>ÇÆô$ù.7Ã$C$p¯ç¹ãçõ¼Aã)h©ì„îéJªè¹ewáTÑÂ*Fmž´èK™g-‹ØlïŠm„Ê*{íö{•¯–ºÑv\ܲžsØJa>3_*Ý‘¹ÙüÛ>ÑîVµ5ü|»¦æœWG_Ç>ÁâR÷óR_YL¢!ì´R…Fã›jz†§ Sû5fÚO›2Í’<ï¶Þ `¿¶}I\:ÁõXêi Zk³R–—H*‰Ñ4„‡Ùè[(ßxúÔX›á$u|I9̺TkV—ÿ…z––œ×l£_‰×¼g¢C½µ%¬*íw¬XR ‘õnY)N.õ9+•wZñ[”ºE9ÊľW¡øŒ¡J%ðw«p`Nj“ ˜[ÅÊæ.Èb|JOsd“W,®ùRÓ~ø#Å* Ľ±yFâdwC‰’pÌûˆ*‘L(8ÉOeÅò¬lL±ÇËž)A›ÞÈ Š·vÜf¾ˆ¶F¶’ʹ.¾£Kn•dë~Aò¥¾º]ðDi‚ã(iÔÕÙ]YʯíJ½õ™Þߟ“?‚>Úwó[ä¾¾Å7ü†ûKK“6Èw"ËÊ!eýç°DÈp”ô5‚µV* Ý3VEa{:KoEDôôcɾ˜ÞôJš#¯oOU4ä“—š4¨å¨ä›l¬TjF·k…,>±{SÕ?ý»SkÁ>‰ÝSu»×ïÞñ=Î|­ëj}à€‹Š­T -¿SU÷.nkç.ü«°“„ƒÞômâc‡Å®)RxØbÛTÈ<ÀTÕV*y Ã™<+î`R‚C³;SÅ^ýŸúÓ0û-ëitp¿ù<È—º2I—Z_0ñÈ¡¶VVKHWÛol¶—­9“=ëfdÿ j…ðb€¹%}D¹C¡ˆyã{Ásø‚I*”{ZL߯›1šÀ½†óâr`n}+©D¥îŒÔ×_•*áUzÄ3Êð¹ôÀº}†i7˜§7‡ÿ9_‰kµÃjxÌLË+u ¦;¤FxLë.«mÖm×áÔQ`sK›zñÏ´¸ø¿›K#—> ¥&ÁôñõÞ—xiB½VÝ^Í\sË3Ú_X»è€X_ïØ±á¤ÓC˜+Ò­j£|SŸû kžÏ½ƒ|j|[ýŽúßX -ΆÓBL¡ûÃ.„?Ž\ÃD˜‘C±ãq÷ãߢ7nŽO(Mä&™“¿JO½Ÿ¶á˜š•™ËiÊÝ–w0ÿ¿ë÷ÃIÝJ÷LM,NCOYPoQ†RœS²TÉUßVöX Y#Z:[Q\f]x^Š_›`­a¾bÏcàdñfgh#i3jBkRl^mgnqozpƒqŒr”sœt£uªv±w·x½yÃzÈ{Í|Î}Í~ÌË€Éǂф¿…»†·‡³ˆ®‰©Š¤‹žŒ˜‘ކ{p‘d’X“L”@•3–&—˜ ˜ÿ™òšä›ÖœÈ»ž­ŸŸ Ž¡|¢j£W¤E¥3¦ §§ü¨ê©×ªÅ«³¬¡­®}¯k°Y±G²6³$´µµí¶Ú·È¸µ¹£º»~¼k½Y¾G¿5À"ÁÁþÂìÃÚÄÇŵƣǑÈ~ÉlÊYËDÌ.ÍÎÎëÏÕоѧÒÓyÔaÕIÖ1×ØØæÙÍÚ²Û˜Ü}ÝbÞGß,ààôáØâ»ãžä{åWæ3ççèèÁéšêqëHììðíÃî–ïvðUñ3òòêóÃôœõsöI÷÷óøÆù•úaû)ûëü§ý\þ þ´ÿZÿÿè§,„¿ç -    üóæØÌÖÞááßÛÖ Ñ!Ë"Ä#¾$¸%³&®'ª(¦)¢* +ž,-š.•/‘01‰2†3ƒ45678€9‚:;~<|=|>|?}@A‚B…C‰DE’F˜GšH›IJŸK£L¦MªN¯O´PºQÀRÇSÎTÕU×VÙWÛXÞYàZã[ç\ê]í^ñ_õ`øaücdeeÿfügúhøiõjókðlîmënèoåpáqÞrÚsÖtÍuÃv¹w¯x¥y›z{…|z}o~dX€MA‚5ƒ)„……þ†í‡ÛˆÊ‰¸Š¦‹•ŒƒqŽ`N=‘,’“ -“ú”é•Ù–É—º˜§™“š€›mœZHž6Ÿ% ¡¡õ¢æ£Ø¤Ê¥½¦±§¦¨›©‘ª‡«¬w­o®g¯`°Z±T²O³L´IµF¶E·D¸E¹FºH»J¼N½R¾W¿]ÀcÁjÂrÃ{ĄŊÆÇ–Èɥʭ˶̿ÍÈÎÒÏÜÐæÑñÒûÔÕÖ×*Ø7ÙDÚQÛ^ÜkÝyކߔàœá¢â¨ã­ä²å¶æºç½èÀéÃêÔëåìõîïð ñ,ò8óCôNõYöc÷jønùoúlûdüVýDþ/ÿÿÿØhÆ 2 -R e r xzzzyuph^RE7)4=@?:4 ,!#"#$$ù%î&ã'Ø(Ì)Á*¶+«,Ÿ-“.‡/|0p1d2Y3M4A566+7!89 ::ö;í<ã=Û>Ó?Ë@ÄA½B·C²D¬E¦F¡GœH—I”JKŽL‹MŠN‰OˆPˆQ‰RˆSˆTˆUˆV‰WŠX‹YZ[‘\“]–^˜_›`aŸb c¡d£e¤f¦g§h¨i©jªkªl«m«n«o«p©q¨r¥s£t uv™w•x‘yŒz‡{‚||}v~oi€aZ‚QƒH„>…5†+‡!ˆ‰ ŠŠ÷‹íŒâØŽÍù‘¯’¥“œ”’•‰–€—x˜p™jšc›^œYUžRŸP O¡O¢P£R¤U¥Y¦_§f¨n©xª‚«Ž¬œ­«®»¯Ì°ß±ó³ ´ µ8¶Q·l¸‡¹¤ºÂ»á½¾!¿CÀeÁ‡ÂªÃÎÄñÆÇ9È^ɂʦËÊÌíÎÏ2ÐTÑtÒ”Ó²ÔÏÕëרÙ6ÚLÛaÜt݇ޘߧà³á¾âÇãÍäÑåÒæÐçÌèÄéºê¬ë›ì‡íoîXï\ð[ñVòLó=ô*õõööÖ÷³øùbú/úôû±üfýý³þMþáÿqÿÿÙ T– ¡ -¤ Ÿ “ ‚p_L7! ðÖ»Ÿƒ}tfUA, ê!Ó"½#¦$%z&d'N(9)%**ý+ê,×-Ä.±/ž0Œ1y2g3U4D526"788ñ9â:Ó;Ä<µ=¦>˜?Š@}AoBbCUDIE~% ô€Û‚¨ƒ„r…W†;‡ ˆˆê‰Ïг‹˜Œ}bŽG-ø‘Þ’Ä“ª”‘•x–`—G˜/™ššê›Ôœ¿ªž–Ÿƒ q¡_¢O£?¤0¥"¦§§ý¨ó©êªâ«Û¬Õ­Ð®Ë¯Ç°Ä±Â²Á³Á´ÂµÅ¶È·Í¸Ó¹Ûºã»í¼ø¾¿À Á0Â@ÃRÄfÅzÆÇ¦È¾ÉÖÊñÌ Í*ÎGÏcЀџҿÓàÕÖ'×LØsÙ›ÚÄÛïÝÞFßsà¡áÏãäM噿çè6é†êØì+í€î×ð1ñòïôMõª÷øZù«úöü:ý{þ»ÿÿÿ€€æOÎXÍ™¨~Í´Ú™~yœ£~eƒLÆ~…j…þ~ÀQ¿€c=9€üÅ~|‹4äµ~c‰lÌ@~]‡Ì³š~n†fšØ~˜…C‚~Ù„OiZ/ƒgP®¯‚¡8€væú¼}6–qâû}0“’ÊË}>ϲ:}iŽ^™“}¬Œ,€ï~Р׉_L‰p‹´²‡K­-~~,*éü„~ŠÄ&E()D9ÒvüªyoŽw¿yóæÍ=ÓTS3ÝwI÷!¦D)ŠJ²ª%»­„ßOBvÿ‹ïwýN6ìÏ4àø;ç>ý¿FVWíñÑm¬Ž -Sý^ÖD±iéÀ*÷¬bPk‡ÙÙà†ÔÀpØ©È?³%"È1ˆ#!ϼK`ùµŽL±<ín-eª2*þÀÊ+Ý) X䥂C@µv2l Q?ñþ‹í(=ì0Éqåç½ Mzï«ÇƒIãz·¶¹¤7MEïY;ª¦ä Yµ¥@K (×-ì¸\ó»÷Uðù¸Ÿýžºõ&>©røäðŸIª^»¶ï“2ôò¢ÕIãMe;Ya•àÛ"·ÐVà®Né½,SÙ;¸²oª_%sœáD;»õfÞÆŽ. ±çìãR?l ;0Dq¶>´™á8z‹DÎKG)ò3oÁ”+Á&ï<4ý§@nÍ—0ÏEÐÉO¨9ò4©‘#ÖçÒµ°ð nƒW9 Âb¨å¾_Þ‡7}B¯2±yËvú„è/Ä…JH삻Ȧ³Õp$±È«Þˆy;ØÃ†ÈÕÉǘ¯fþþoè™”¸F¨LsŠà²I¦Ž‘,Kh€W2î!¡A˜×jHE^°Ï„ØâÁ¨ _ÛwÑd¯lX÷gÔgÁΩr!jU)þ[%ÊB¾†\D¿ÄCf‚pû <_€Ì\?ØkŽ,.©¯ë†wäŸÈ²ƒ²²¬irJ·Œïä½R›žñâÝ=>0Á+cvØÕñZˆ{HlÓlÏLÔø×VAåc™Û …ù‚ ^{6oÌCÒŸÎãSõˆäÙ…ðbÈã‘ï:sz à 7ÇjôP@ÝQ¯ùÚÕË;[Øw‰g“|z3Ï0Uq`!ÏP¶Óîž-~|ÜôàX·3ô°+°z2lIÑ’¼¨:ê_<ãòÌØaîXþ’S5\}úçýEÜ^…ÿöîGêÑè -ÉXÞùJ4†^¿Æ¬w¤ï¤/€ûI!5 -Šbnñ#ÙF§Œ‰Oìüý®æÈìŽ`.Ü™‘-âJ\Õ!gZ0¿ -)ýÝ¥q A߀«½O’°kðVÂô¬Ã^B™Ûï ™f8KĈza>p-Fð„þOÚJ*€ŠÆÉYr(º—¨".À°Ož˜'š±ýºqÚ䦨ÚéfÕårC¡üRJ'–dc~¤‹híÕ!€Î?‰`ƒÔÆ}WÑzBd;…hѲG¯„ϲåm½T •ÖSAi©j9š<î „ -ߍ׸%É@`æÅ8xL¢®T­qÄ—=¬,Mk $hJÔü®ìöd˜ôÓþ€x_§r¢Ì°gʱÀ­ht­G,ÒK±ÝytomVK0õ¥¢¼XÇ?R<¬J%žämqþ–=Ð ¥]Ù›¿“aÛò`sÊ Ç7ÕàÕg&Gr§Å€ç?>ÝÄr&Åz`øb>&ø“ôz¶Ë%¿•sxÛbwì®&å{ô~¤Ú…ÜÈ]"WÁR©%¼c"z•D “¡ÐãzA ´Ýýßr¶™Æs!Ö=jcf]­‡rm“ýA¦¡NJl$ÎäÛ’™#Ø‘ é>ÜëøwTfáGF£F69¸9<×µ.ù'S³Z*¯Ëº#½-ýJl.ÑàZÆZxä‚%m*Ø|ìÿùÁí ño± Ç2ÐÓ_TÚÿWÊK4™eRsu33'jRF–B¾ÌþüWl|ôÿ -ÝíF§øgml0L×1¥, ©œ—¯Ïy¤+•HuÑÌ2‘‹f;Ë[¸T½0BEÍÊ{Ž:‘¯Åqn¶ø´àõtoÑT†]o®´”•–úkI, - „îLgV_R:­KÏ‹0¢dêP?= ËÆv¿ÀEÌ·Öƒ(M4èm”‹šÖ\—ùT·k׉o–ƒ´”‰,ôHœ§=³Ãù”‡¦Þïé¤Zµ»w/³®E½Iñ-LQ¦ÿµ¢[ú©ùÇ ¾8¦F/˜gÖ–'$‚?‡[òçu~fÄgõÔîhÜXÒjšñÝš-³ VIñmíK˜ÂÕ€,%išbÁŒQŽþç*e9à7‡·ˆƒÀ—®ÏèWK«‹üÜM™YiHtÑXÆT÷¤ÅBUD”ÚúwÜ-Å4ïþ9#i¯Ô—/ñr]£hGÞˆÕ/¤ - -lD2 h§Â‘Œ%TTòT*FÞÃédw">ö­âGüðY?é"ì°Ï[f r5ˆÌÊŠ4€Û`ÝùTAoº‡4H5”rWôS¨8ÂÖXy˜ ;$YrŒÿÜ'‚q ævUPñåV&Š×çèÌ4mù/5LJ¸òEÊ:•ªSË7Æì’HvŸy..ø› økâP¬X¬¸Al…¢â`Èå¥ -Ȩ,eª‘: EÁ$@B’KrÉå².!{A$AÂ,ÈCåY[EA;|Š TJkU>°ý¸4í1aÕÆœdžcT.Ð×ÿéîUs Rã&ùBc±h»R¢)¿‰’¥Ä -† Pd;‘ÆÝÊŸH«b¯‡ŸÄl?1;_Ñ:i»^«½mùMh9Óœ+,—xê+(‘‡j3ö=P6uü>§¯a}Ú&¨‰¿bß (ƒúÒ0ô€®=ªá.À<ø’2&îmÎ%ë…u9¶’_ª~z¼Œƒ•­L½å!îS`(ð6ÍŸ>Õ©Váú”l¼ûW䨸¤m5y‡pgó!ì2¾·<îÉ PêR%wCý>èÅuþb€v…Õï«bšFñ.0¬¯ÃUKÒ$áK¾;¤Ø‚ÿPà,ž!ôrAÔ5­Ñ%\×ÚvüÈò"Ú -[2Ég”wãõdx©ŽJú±æ“:”_Ú'ßEÚš_+^CÚ˜ÌÜ íŽÞÂI! vüÎ,­¶V7—2UJ§¦÷Œ÷ìL­‚N¨ITUÒK×ÉŽIy/R+“=+(Ö¨§°Œ²vÇð6™×â°Èø·!Mܨ²ñ ¼‹@P¶B‘%…R-›—-â3’ì|4Ý-ÊÆþ”ï˜)#Í¯Í Òwòƒ¨á.ËܘŒ<Ç;”ü b»ä#‡’;*œ…>$eó†˜G£Èì -º>à3ÆêïÍñæ"íÙˆ~A•ÉZ¡±Ë$xOU¼¬x›ä äf𜓜x;Ù¥QùÐ h XÅ(îZ”¦·–ã²x=É`dÂšàÆ 8…b‘†i¿•dç, ÏÉÄ!îenZ -b°èî /šÞ²ÔÔІ‘þ ßÜ2ÁôPß0~‡ “+µ›1ÙbâaŽŠkÜþtTÉö ð ? g›)¦Ô˧¤9ºÇ С¾`.Þ“§`>'4ºä\DRdí‰åÜPaxԗИ°‰®¥?i|Ü9,¾t §¥Ä´˜q]•Œ"¼ë·m-¾9«O™D Ä'ˆ«¡ExÕ>÷#BãÄÍÓÎàz6¿ºõÉNkî%­¬tmÒß6BDzV®âQ÷GÃq,2ˆO:â× yÔ{i¾îHcy[]èvòaËZÚ÷TÃ5 ºÈ¨±R 3§ä4Š5Nß@qü‚G!ýfYXr‘º³ý{î3^¼ëëM7HX1­Ÿey«ŸƒÁ™õ²ëôÖ8´¨‚¶7Ò™;NðßÂü•ÂP9†Åtn§/Dê=ÌÅüñŠ}*÷ðIƒ:œý2s›Ì‹%‹óþÜñG{êŸÄ¢7©ÿaìbÍ“œæTÓB•m…6ÛºÒ4JZmüIçÀ×¶ªFÑ×”zé\F…D*ríE²yΓðÖ„¿ Ì£ÝV¦µ-ï8ˉi±Ú#ˆ7•æXmŽîZ®LW:2 -$Iâ·±d­`UÄ+¸ƒíæßzÓ3„Ó 8"}×åY\úE^û\Qñ¼íܵ‚³¦)<&uZ«!FM)V×…"çÚŸ}ÎÃçˆ&aÌ€/éíÎå Ķ» ‹­¶5ÄÏ Öÿ¬›O546ƒPWä눤0ð ÒfŸGlEýbd¼c ­´'œÆªžrûŠÓ¬[©ò{K(ÔÔ"M/y%•“Á0=²Ÿœâ»zFŽBx}霚{w6{Y5¹í0Ëóº%,ËÇ4ù0œR}Ô“´vTôp>¬K@¥fùR¡$Ê7HìU( ¸ÞÅàªÃ/1¹¯0’f<,È÷1äB§S>¨ý˜Ù¨RI3#&ì&ÿÿ´pßaÖ5j¬àé•Å1€Ð9#yTõ®õ±H†9cùIÀÏû[ßÎÕ§Œ©jÙ¨’UÌŸÃ~á¡?óýÄÊÖ À+Ý7ÀÞNŒzMÕ`k†|-ækËqJÞ}(Ò˜2¨œçþÊSØÁaÓ¼Gài¿ æ;ຠíÊbË:`uǤayU“}T“€ 2÷ÉûòFtmâÌ”%OpuDìÁ‰›ãU0m~L-_°¨·±µ:ÛqÞÒW¥²gÜ0~æhuwÞ-] NVº‰rPå =<×]x;Y×ýÎ1Ëiw@á­í8,áÝÐnœù™«\(zºÆq²¸ö‹ø˜„b“ !$zB³§&5d©øníÈ6ç1Q&ß &ô «ÊC¯Àu÷ÐŽy#«À†­c%$7ý]w'z\½‘×0Lük{8 ú;ªËfºG‘Sè —Fx¬P‹~ž®Kƒ’÷†Êm %t3ÒM÷ö€êcúúcM(¡ª¹bCÙB·§$ _Ú œÈJ±,âÿË@øÕœ †¬%Ó¸Zš;ó‘˜.6„’B˜š)éPé˜é Tï~¿û~÷Ùù:_÷«tH·NI¥TØS„‘c„ÖΤ5Ùýþ_Ø3¡ðšÂb¿Oö6É-[o­ Ã7ÿ$ý–Œc†nú•ú“:¸zN‚q–³n¨E2~Œ7\NíöTº¨ö'ë ²"[–fTT°Ë^2Fþ›©í©²&µ¿+¥Ì—ô®ác5r~Ô•¿Í(ôìÑ÷jæ÷é¼ûl 4´8mWDC]X¸#Œä×<˜¶ñÇn”_ ÖT™¸ 45 øC0 V€‹~ mñ€&AGçAø¾­ã7w¦¬Žø@Ïw‚«ØóŠ;˜½ÖQ8‚à îQ³  òä´?d‚ä9¯€÷#1ó·yÊ•q¼_eS]y|ždÌ*Ø&6Q¿ô3 0J(¡WG>ÂHêãËN§ vøµ×Ag÷¯+Ü[éo:y1Ú‰²GümªUVá'p´J{"„¥âíÞM@3íåX­Çÿ|Ëñ*ñ¿oÆ™ÑÞžÌ%sfJ¹<·Ìß”§ù[ÓþÎß-0R'G äiÞ++q¢ãNPFæ¼\ÅÖÔ&ÞíàXT~yýkÎPÉxÉ>®¢àŽÂ–—ýŒ·ƒ›þç ~u2LX'»äP î MŸO±W· ÇrØ© Z?qöU·ïäü·\+wå>-”q}y/sRÂQQúú÷„ÞJ‡Ó@737ÇÊîK­a[tÌ·š°»›„Eû8X­¥,œ›T¾ÈÑñpý•°¾!ëã‰PÁïêVûK$³`ù¹üΚì×µœbÇu~*LàìlB¤z-ì÷<ËR&,Žœ+?þAP«]²oœÅèãLâKžÙ 'y¼¦¯ð ¼Yý?± -~”t-"XÏnÃv&Ãô­°ç„?úøÍÀVAìG³˜+¿‚™¢Ï7¢WÑûÑh¤KüµDâYùèZÄ%4YF¶g£[öÐ äÄC_ÔMæ6Ð…LJ®¥!˜¨)¯9ÍÕ!6ÔÛ-²‡l?¯@œ±W …Ÿ³?‹t`k¦;Ýé½ýæpŸö}ý -P»ç†À>Šf†{Ë’i8DbÒÃ̯Mp/Ų›FñÊ_<`w½[ßUèq®°. ÊÏËY!é'i7ÓL'‰ ƒRüz$û‡§v]²cÁ-äÞ©%HYÙ~“Ù•­ªç Ï鞀ws{)WðaÖñ˹ÐúÔ‘`è¬ÊùØ{¨[zÀÎ ö¥ä“Ï¡•ö½Á¢Z¨& ­èËz --ù† ƒòU‰Ï@uÖBP.8›jz …Bä£{G tϤ1˜Þ•q# Üßò¸^×oÍ2†ªN*`®D—èZçm錞cß@QYîÕ@ÕÌðÄOàÉÁùžy`þ†Å•^ £Í)ÓH¬?€?˜Ês %Àý¢ðÀJð@öf»à-ëÞHˆ%ª{#}Å™ìPKn@Åu±5w:=ŒYæX9ýŒôÒ(5#p ü9#ôô½AÄv(~”-Ñ"]íQb'ä ¡y¦Ñ÷a -­¦Æàè‹ÏÉ'ðÏ玣· +¸ÅvO@¾Ù%ö7_ß*Z-r*Çé~z Õ‘ê 4ˆŽ!ñøwÄBp‰°ÂõG-q.Öa+Ãî¡cÃÃ"wmqk=½¨ÞWfB +k^ƒ²«0‡ø¤>n‰puŸ5àÝ㞃= «m]0oüüÊÄæ-1ô·:Ç’~ê³ô%ui¤„¢;ÕpÜVœ”Où/a3’î;0†oó½KܼL·—Å6Ÿ¢EÎÅõd@þZU%¡{ òñÔô‡±^Ø Í°ÿyÌO‘íV‡NHLm®¢©uƒ?ÑuM¡äB•EQ1\úIÙOéu¸iÎ@L–’‘ï7‰Nk\ÝÐddƒ [i‹|ª¯í‹ÆlRܰ¢è3"²ú¾rW^ üˆ -¹1ë°Í9ö~±(Vì¶ýZQjsf¶ü•b÷5~Nlï, $LAE ¾±Æ\ù–ÝûY†vÄø¡‡3kŽô"³*ÆÆIežö´¦.²gj4uœä¼D•’k"Õ*TÜ~~ˆ¬œgˆ^– ò›~½¼<|ƒ1â­c©Px7 í‚kFÔ84‘K(/AIƒ\%óH©âG;‰'¡â6‹¾€æ ½à`kK’…¬ï -Ôè¸Z¯J¤äAÔFqµŒKšqÓ$5„GT´è#.öa<±ñC]”s¶zOŒ£9ÇÔÕÄ@yQÑ’í-–D¥RPÒÇÉßGªD{˜Ÿnš¦ªùoˆ«^Â7é°PµëvU±vºƒ²ôT_F"éT‹Øeâ Šñˆ^‘9ùì¡^‹9®¯É(Zi•í*³¢”JŽ|1¿"Ë]º˜ýœ.˜g:±ÆE¼TIÄFzNõË÷%T„|ƒk&ñJ´lÅÇãIùõ3ÊŒª+År^¹qfŠ”SNM‰‹òX»™eÝÆ $’vî{ò…ꢫ?I¬Bð-”º-Oi×0iX’oª È葚T\£¬Å.%kR&-?(ümš¥=ó€Ôþ?~3ÂJncu ¦/·mS<ìøÃ,»ÙLeØH:ëi¢_+MSVüaù kò@\Ѓ*ÕÃ+w¸­qDS!h”²e -ô4€–9"(hÂd}+É—½7Œ°)T« Þ b¹ÇªÔªhmõê`Y­Õ»÷ÿ ís<ª†-´,@˜­Í‹ÀŸ‘þ¸ÏSCg—ZwÓñN² CUëcªÜWš’š¤Ê-JGm\Q²ìA]Q†]Æh&D‹¶0±íÐZvnw %’õÎá0†°zºÊMT“XÈI¶ÂÉò:¤Ól¿ŒÅ<›7åaÍ` çù™5æP` ±Ý°Dh“k?Ø(ªbþø Ÿü?²`Fº‚gÝÏ—ñÖűàîy¬¸À=î‹4EJ‹í­ú0 GvQ³O4À©W²‘Šd SAšnMÿCäÄí€ -Ò±|`ïŠ7ø_·ÎvY—£¦Io©«ÙRÅyYqE¨,Dò;É&Q‰\éBÑxWì¡rÀ‡­v tÑ Û[€¶Ë\"]£º©ËU¬ÐØË‹¥ ʤÅâwÒ髱<¶ |Mc+…éÂåç ÛZê¬9Ú(WZ¦Ê³Í°Âäf“ïø¨t‘Ž~X)†Uái—á?¤m±ð;1 pš't.t-SSUn;Qñ›à¹Y(sÑžè§Ð*ÊMº8=÷œâ -BHZ# ƒ–GcDáSŒ¬ä{d'Ù,UÚt†£‚lÓ=í´,}¼*–¯vËcrÓ+Ð]‰(_ý1rœÃ˜@?A[ã˜ã±K£Dl¡v'”oî>=¦ëÔ’‡ÕÔÓ•[‹µ¨?úQÔ ¤ôn!ܘå€ÚÊeÖÞæoø…ÆiÇÜéB]²u3ùˆªßPzP'ß§–%•4É4Qw‘ Ñ L7@ì?;ˆgSVjgoÞhoùpê7í´sy‘ªÄüRö\7”ø¹äˆâVÔ%xL| 3¬nëÛ|å´2Q|ûí-ÓÜGo¯tž©uÙVÖ˜³Gkî}¯©fdã'å‰Ì°yQ/;^¿Ò+bÆ#Û&™~Ìôæ„úÙŠ2‡—¹¼(ßÊ<•c 3ß;¤n`9ðã2[Lqztö.˜ÿ)ô<äÍ×ßlJàaËnìæš X>ãîÉšÑ×pöòTÖ†‡ø)Òð$œDrƒu:5¢zøjë,|·~›û0T\~È>®*,6Y -]è·7E9!À¡7ùëÂ;aˆ—¢ÕuŒ*Àñ­8Y?ÃäÒ¢Úã#ö±¦àWñfÅiAö~ª\âûÐmB\ø$ôžOÒwDh°E©•16:_«ºçÙJä³ qBšùR%–Ÿ*Xð÷ø3 á!‚ýO»Ëþ:Ü`Iª»è¤š©o¬ßk×ì2¦+õ™}šYñºü'1ß%Y õG¬‡°ÂPMJ{rK ´²Ùæüwà˜³¢žµæ_‘æ µúL¶&N¹‹è‘óÑ øNñyA'…Õ³mﺾo4gz"ëv;L® j•eΆ %ÆœÌÃê{š¢N¾S6òÿU'*ÈÍ@†dÙÀÚjNcÏÍvêo^=‡Bi ­ŽÉ7¹95¹©Ïlµ€AÇÀâ¶«62ý7ùIÒØÛÅCçkëyV_}BÕË.I=ëYR2¿ÂU^ÿcê°ÕÌ~÷oîß\Ƙa3Ƹ2Œ«è@e»ÔæU*Tl©çm«cÕÓ±½ý¾Ÿïå~í x˜¬¨„–ÿn’ÂN¾îú«ÈÚïûU¨Ÿ)o`Iχa]é¯ÚþÀæP£³ÐFÊÅšVTC&ÊÏ£Öà¿‹Yè=ßd]/æ§Ã.ãó.FÈBX´s+$íé=­}buMÃ>‹RWm6“Å–6á¢ÅFX ÷5ç½x{v*ñÇØÇîýj;‹zv­²<½¡¥_~‹ú•ýA¢”VUJω²^IjˆQ…x‰ï½¼uÂQ¯oÁ=l°K§_ÎÚÕ‘¬EÒkZ\4³¨sêqU7¥ÔvOa ëJ?ÂèÂQ)›4C«ƒ^ŠÅÂì\ç¤ñk[{»Á3½¦y¿~ÕõÔMí|JµÍ'g¹¢4Ay,$µ0œ( j§H†¼lò:Q"VÒ‰”1X¡í&eº½©»°ƒúí© ùs)M•Zœì(éW ×|ÒϹÙ\¯‡ò8Ñ8&ít³ícýp¦ÓÒ”ÞÞaö ’ê…Í”«•‹ô ò­’C¹›CŠ GU®$^îfb¦·³|8u´ÓÈ̸ÇÍûÇ&áÑØAÖü9kÕe€7”;Îã¥¦ì…æñk¢oÁ›ÁÕAv¤úò´…Õ0o5±‡Ÿöyœ'ð‡ò—Üø¹ŠÆ€ÿâ<ä!îÉûÁ¹HôÎ÷P’4þˆ|yÆÜCÈ{_³2eî§ÅlTrÉsôâ*¥·Idg_à¯'V -Fvz|"ïÆž¯¦Ò©n2=µ3[ï°"s¬¥ŸÙtÕ ù}Nâ8«”ûž`†ùñÒÌ1`X¼iý]àx¬£½(®U2\o[v1·d%JOO&rØ—´ß0ܵ9³YxÅݨ \–´€¼D‡m§¹ÖÄ]=Hßjíovfd—¥˜)Ì:ÓtíbèdH9\”ØÏ›®YÉ?«8°Þ <%þ}ÈçdV‹Û•hšÁnuŒÓ»kV˜¶3+ª¥(+µ$ŠÞÁ-¼›¸Š‹ë¼#¯ð£Õ‹®‚QÒ°ÁØì·ÚåЖžÙãX@ßâh-NdðëÜ$Ìa«š>•M3¿¡q—œ"yÿÕè$À[YÚ@S¶õšgÏÓ=ÝŽÆÚP‡³±˜1·)L<Æd×"´döìrÕ•sÎÈÝñžW¬í ˆìDJÿ -tÃýbêÆ µcèj¥è*º!®Aq -hGÐ|î3j zŸO‰ôÀ¶öú|·Nƒ -»°*ªÏ€› òÂÓðÔ%2#ÐÇ“™H¼7!¹ ;v¤¡‡'ì;äéOð­ôëÎ~hµÄ¥î%”,v6ÄBÕ¢ÕÂTxŠðDv"Lt¤Ã¯K"N#œ¶.ecÍ}fÀ‘˺ŒçdÖ&ƒTÙù R¡øASˆ;Yq!óþ¹ö–¤E,ƒŸ‹ëü‘AAúm?~•±çÜÌÐ[ý7ªÒ[“ö> \¦!ýB;U¬ø!ˆ)/×À»¥¸ÿC$NøþÖ$ïAå³uüe–uU}ü,3Y'äß/JÀœcùð .8_[O¨Íá®ÐùN-<"N¹ù™·¡awG¨…mý¼_+•yj~P™]Å¿^\y¼ XÀÚ,r-¯|àã’’›¡Ü³ííã<§æÛL^T}½ö,Ï^eDRø»,êãn€kqÖ%Àë|r,ø!gÿJx´±×Î=~ép{"·î\ee÷E«N;“·ÃžŸá=©òÊ$øöÒ{q@Qá¦í_À†\?¿/иLe–´>­u£ä#²¼M²œÉÑpÄ'Y¶Æn²_e<…qã¼…¼±íRa8pÖLB“=ƒ’(àYK—[l¢«`BK´´‚½B#4;cŠð;HñS’ ^±íOA<è·J°¹~¹I4,ºÖì$"‰œË,B\HV'&fಀ/pO_*ø¯Û_‚_Ã#¶¥à&l”ôWcÂK B«Zí(ü¥|Zú½à b“ê+|°ñKpTœ¶ -ÿAr3vŸ*>¼õö§ð>É„½x\œ+¨0lk¤àïôçÍOðÔ¼`ÅöFÓÀÓcfupê.Œ®øwlCÄnÍÂöKJIi]¨ïšÀÊ&Âðõf£©³½äXPA·ýnÍ1Ú©©©FÛTK·ÎBoI!èÑÓ®…øæZ£ý f°)~‰¹XÞ“h™ÎyŽÆ¾9ú ݨOñC5§¶´¡‡&Ò|´T2«óÖÓ²¶¾ón˜SLB5eD0ºØ:‹³yPÂ;…Ü(ìßwéÓ9m¡¡ÎªnßWhŒÔåÂKŒu¾¬Þ½{Ú`”£wØk“ØùÈÉ -û‘kH¦éÍ>*çÚ²éÈ1ùö£ ¥wÏp5õ´Èê…êQº¶àªÝŒß$Š;¬ÌÛòLvvJ1ƒóf¯›ÔÊ3nž*ÙäøTŠg@oO#9|}•¯ª˜?V0Mù5.‘Û€zÆ{" NÖK?C”_$ PÑ&B̆e>ˆ(q´ªIÁu›‹ò¨`«þ£|oùb|ã_²0l2©Wê‚ßsCü¦Ü´LT÷Iæa?µf(/+¬ŠP¹Iw‘¸B WÊhÿg°ÌšH ¼»EiÝÅ®´ ®(G6 -¢€®‹¢" À -"(®H2™Ì™™dfrß $ˆxàZEP>ŠÅ£ŠºµúéÖóC¬ÔÇÿáû~ì׋EÎF½:}< ï\{ -š„‚ƒ¢%“àø± rHÐ6N$(ß«Ná·˜_%ó1š¢«ƒÑ]²2Î:$ŸÈÜØoƯ-†8È¥ I-qt;ù'kŠã¾TjJW^Â}ôkÀôŽfQUr\ùuî¦lôƒNkHná«‚æH¾‚÷õ*‰W¶ã¯ø¦˜Æd6™¸M2 *{¶®¼`ÌV%¥…VR£oJJ‰²À÷`+"èyO|s8ÙÎÃñ®6ÅVÌËy8‡ µ:+¿‡˜;ÄÐ9Ýî´ɨÒ=Ú.q“qŸ‚›Ñ=¸ôàÉ¥­ñ¨^Óöw÷ld¯‹ÞGÁ­å;<¡bShÚ ¨ˆ®(÷ÀÛàŸ±%]Û çªN™}uAܹ² õ`MV󨬵—?gê=ò&¨o:R¼ÉãüÒ(£tWS-{66Cð¢S¥ø ‰«ýšÿ”¤U®ÆÜfoEIã½òhzEœ'ûÜœ–¬&úÁQÉ8,Œgÿ]-2tœ×%€ÁF@}S|†èQJÄœ¦h&Sì½§†bor 2wàqò|éoÀÃÈÏØ/À$Û Ĭý7´AÒ£±êŽIÛMXIc>Œf–¹äH^Î2®Ø`•zBÂÈ³Ø ÅžO€*×{M2MoRŽ0øi†¦¸ü:ªTï~%Ë$œ9ED~cÃj<}‰£ð${.œ-ù—+P]cœÐ=VzëÃpwz\SÍ;!?¬ìØC:èGÛF¤ÝòÅI×±¿q‘YÈÅž ݇>;ÕÇ]„‚¶ý†m§SÌ)¼yr½¥ÔEz©_nË•²aI"lµ|säGºŽíð‡öªº®vm§Âßµ¤îØ_7e]Ö­¥>„ÓГýÿUý)i:ÊøD΂ÿGº}±V WÚ5*ßÞ{Úfüà?Ñ -ë(¯ÿ$Ëp\)9D$ZYÙr|§¾öçç(¨4DÜOÊH¶»äʳ ;Ü«òévÛ±j‡xÒLr_rû µµ©;Wi·Ô nV¨|ªRé›üu«ÊdܦÂ;þ@ðÈÑú–ç YÖNþÁl-ÌQôÈïnJ¦úȲ¸cä/øÓðËø14™µÑC:Ã'K£&‡ŒÛÌ•BOÝJ{ß´z‘fŒsWñƒÊ|F-§Ùq2˜ì ?НÁ}Y[pXdY<\‰ävÃ+ÇM{Á¶i©rš¼8µÌ~í£LJÞ¯´ÇŨ ávlL:ÀÚ ?@Ö¢ÌoÓ[gŒ`¶²}ý>?Ur‹Ç›I2¾ËL•k.Æ}GpI8‰QäRV%Ü‚L0/PU³ ûEÕñ ?´É¹TÙÂcÍÛ¼Ûf¢HÓësÖÄ^QMò½C!º¤)Ã$ ‚; Òej¸ íuIy õWïêîË6„ÏÏ#LÜÆMi9ºÄ¦Í±嫿šPœ*ŒøHˆÊ˜…¢FÉgˆ]Ÿm¯¾ßnÝ+|“·ÄX$Z6K'Ú»OñQÔJÊq Üþømì(B~—ljçÀSu‡Z áÅ¥–bhW®ÓP"¹žz@³U’»V±¼JÈ΂\ÿ,<\H»¿Až ö·5’çë¶ÄO¥afÎÔC75O ¡þ±ó˜UÝ®¸xà7€óÌõªF›õÜ>QöóL‹ª~:Ê¥ÿ#ï]ð[eTSì2»Œ%…Ác• žüÔÆ~EWמgØ9ÌÌiíå÷%¾3W4ÞŠ: }Þ¼0Ä_ÁX|¡¢-ðƣ鵼‡„‚ÿV”u×8H{»àßYêF¸"qÄ”-´F95E!L¸èŽ/ŸÈÁ3z„—óLw@éå“"‰œÒF‚Ÿ²Rm¨O´·éQ&õ[Ð#¤ŠÎ¿ö–ZÅO¡î/©xðºË¤Öró~9žT0ä0b³³Ü¬ ò4±ÁPß‹bÔ>_nMFY%÷ý<ñip -Ê;R¯xÑðá@ý¤|‹ëŠüqj#ßlºjù?Ape¢Ü[ÇÛŠíx¶{ý -ÑsäHýt-bM—#¢²¥t8fÆÜß5ó}sßcÌÁ0†f0 CcB¤KèRê•mc³EÑíµÕÖk÷ýÿ×¥ÿJH×[ÅۢŚéžãýMOÔa±î†N¥$ʡ˖~ &(Ÿ‹$~çÅ>ø°žü–tÕBM%^i¦Õï3®¢Ï§Ef8UB ô¸ƒà™ß'`-èicIaͨ“ô+°¬ ¹دRž€î†§´= Z¨¥Ÿ‡¢È¾ÌèÅÀ=¸5ÑU#ÚÈ5H«îþâRžûå¤>njky/§¥¦Œs6öÑH؃âE oLº†y‹ÙþìCßGØà/¨ñ?QE%Šá‹Fâv¹—MMz)=ZáB.ßçϡƋèÄ/ô¹Â•ûç3¦ùÎíûO¼èˆ8•Ý5&éY¾ÕõŸžKìÕ¬(—STî e‹ÒðжZV•Ëð‘x'ôxaVø4ÔËÉ*H®]z~¸h~ £i0d,ÛKëý8ÍäCZyÑ{j¦êCF')’b™|x´…ñNJ>ÇVÞ{½0Ùêeí#|S—¥ÛEì¯1bç‹›*_…RÚË"÷Ñ–ÆÇ37ÊÝ€ÃBßÛâ“oξìõ(p¶3_<Ý¥%-ãèt±·É« B÷eÐòt߯“·”px H¥ÿuRuɵ)H?§mü©ð£ãŠæÁf@£IzËÍ‚qrgM_æõ«þDü|Ceû -†ˆÓ¯_–ùw•CÕ„YK/¼–Ô¨ 佨/²“êY0ƒ¼£ý–yŸÕ̸7Í.]Â*ѳ°þÚa Œ!ëd™[ëm ˜9­„Â#{-ô;˜Wà[¡¸Ìï ·›UŽ$‡mb?ciŸ3¸Ø˜s’™Œqê6ÿÄ‚TÀ tùÖ }ù® µdl†¨êÇv¤å­{ÂF©yt/Ù¼Žtú„¤ÇṴ̈‹‹KŠ™Q8ܰ„ Nº"þ‡½4ÈÊ»ûcü'׸NÏâs6ôÿI½« <ó–fWÏšvЕ*§Ò‡ÁT² –ï9á ÔI#Ò{Á ÑÇmVx—@µ. ™åŽ\¹EyT³©}€ÚWå*€fÒ¿S¸Ñµ:'ØÀhP³ofޕħ/7ÈÞnÛ u‹3Ö­FèüÄËc”åÖȶT—úÍ5멦yòŴѪàý“žLÂÄiÃÓ”€¹¤%vÂI‡¾Gæ —É’3îm?PH-k« ©;?ÉvÒbkk€ôãÕ‰y“Œ^ýÃ4o B[Ž*‚BoÁ¡åR 9¨§Þ^Fñtþb¼Iyd¯’š¨W›™$Ú«Ú–¼ÆNãêT3óVYzl ((ñmƒ‰"Uïa.\˜vzŒëCˆ5H8Hó$?sZŠ6Žœ¢=:ÈhhêB…!É8 -`,ä g6ô$cNúŠæ!LÄp©<Šb<ïÀ™Nz;¶0y%€ )°è"Î «DÜB@Ž ;€.aÿÒ$G=ØW+²ØïØvÑFöv-­‹ý…Ý|d%ºŠÝ¿ÇîgOŨÐÔ;$KF“»î²lbšu#«^$(ŸbÕ ¥‚,‡ ”jdMð[sÃÙkx÷vÿ͆¹ïc–³?sÝB–¡fl¶S ¥ÿkø€ ªMe͈Ly˜i’Ã3òTZuøgÖvñàî#¬áøV'{ÿFð,ºË8+„5Žå%\©ŸÑY`kY o¾­ÙC¾ˆ,VQÕ"˜B½k”5WjØšÃê aëx·­ÐhÝkË"è© ÕA³Æ›Ü/ðŠŠd˜XöÃ!xJ±ë(}¶~ÏÚ&¹|–ÊÿËé íh‰®?í·¾Ò.„òê¸ñøÿÁXSàªçÕýqX×}|Ö#CiÁchÙK™ÈVF @ d’·w^ÞËNH L -jkÅuÖjµ­³ˆV´JE)ž­£Çý¿swmºü^ ´«VQe¢’^*ð…¡7”cô¸·Su›5Ô.›:’Õ5!{3°A/—ýu@vÉ?êaD§åUNCþkùŒH2)ú 1¡z%­9ì­Ué±›ý»éÐÊçvò¹°—Ë!ÆkšÕ0>][#À?¬wÏsb‚÷Ä,4U²á¤_ fà)…ýEêÄÛ* uä6Go7°6ɵêèù¦{¹¿ Èæ'C¶É‚Ga‡â+Á¯ðRUõA=±@ù¾5_ªrgs1OU¸ŠGÿ£*ŠƒÊšëO¨&øÂQÍ¡Á4ÒÜ%¸¥©×€°nl¡c=ú%Z¨ ÷v¥Y ÿZe‚ëà¨4í‹? eC‹ƒÁ—`…àØ Þ_‚ªÙwvĦ10áKÌB/§ê*BrâvÎ4ÏŒwMèñ  0ù›à¸rë“ ó³–`ø¤ñ$½Ðï’÷C‘ÜŠGàa6;g-N_&ɰ .` ¸`ß0üØùM/s\¢Pœ’MƒfóªŠ`p‰¾3ÛìåÖÆå $‚ˆA7° iÚç —î¬c±û(y <ÓNÉâÀù·ò6¶ôe/ü ëcÿPOj± mê+”YЪCËzÏÂXa-…¶T‘Ñ`ao¦t°tÜ._ ,qgõªO˜ïÄöªI]@«f"µ ´?4|T¹]ÿT7[úr¾5+í¬D4UffNÕ]Ϭ¦ç‹'¢Md’dõæù¸YÚã%A'-í:›x–>œj˜‡”±Ó Æ5!’U¥iMSDÆ-rBŠFL„É&¿ÁÚÍ^:O•ãò…F¯-TüÆ4Šw ³è÷ÊèÒõT©²3c q]µ2ªRÙôdó/3²µùU›\;?åãØUp=ÔÀÔ@Óbê -TYRJ¦€3éÝøO )*+sWÀu¯.˜ê[ìL6—Ǽ§ÝAš. Çê·’ßhñ‘‚oN_…=Cÿˆ|€òHW  Gz}ýžw\ÒÜ2Œh{?ÝUàÅr_סä,[<§™¼4ÛDmˆD‘øã€·ðòú’C/ÍFÇlö MÙr_ò‘¹¾ög"ÇP\T‹MñIiÿD­¬wä$™ú=` öÆI¯Ó ü§}ÓßíÆ6Ò.íÃjYîx^›†ÿh}]ø¶Ûø"µ]Ëßÿl -¾«8"ӽ΃ǫöLúÁ¥ì"êHï”Úü°k›è:^Ö–T¢Ïm©©éÈÆøÏ·.À‰©^—@1Š~qxT¼ÅÇlùU便óÕ#U7‘¢Öü5º:ÓöLÎE|4â™&W2Ëÿ5e•¶xzãß*̖̆;õŸâÙÖMâ0™ÕdoØ^•lpm¬ÍëâaIòSö7©kD#Óùß'ÍŠ$Ï"lL­?båA˜ÑüDI§¹ò«•NÝmEhž —«8‘¬ÔŒ*ó"vÒ±ªEþßýª»ž¶ ùÝŒ5Zž5‡¤‹ ¾`zÅ~xŸ[«MN&Ïaî|Äbª(÷ÂÇ$Ïch¤ ±øãè|cq«)Mï‹_¨ðá¾Æ†w>bSО$ ýé ¿Dòp«¾ÒzŽ!G@o3ÇñƒÈÃÜÉa]PnÑN2);K4 U•´ö"¨p+q ëð7bÒáùùÒóËLíÖø…a“y¡$‚04”»iCøc9“Ì(û6ñ>E3aá{ˆ Rä¡ï0`Ý?às0Âí7y9'`LÔq·ñ`SîécLr&MŸˆP.Ú½áü,ýÒ_†rÄÚu°ÙÁå/÷FÀõ»º°Æ=ÜÎÄ=1ltËÅœ ô¸9>1Šl× -›ÁKX_ÿt+ ¬ €=‘çÐ#¬Ø«Líø³ -u¶uWçKìñ¢ØÌ¹– u)±F£@Œ†´ó»ïû…jÔRÎý_$ Ð®ÝYuBÅ›öGõbQl+$Ö»·,o8“qÜl¸Åègš!) ÷ÛÁˆnî2QùãήÒßUÎ>YtÍwŽ(Ùþ¢^ëù'Y¦ÝòÑØºŸ!…Ú %à—GËU¸û9¼·ù¾¼ã, ¿Ô&>¨YcwU­± ¨Mé÷Éj"æÿ“Zòo6VÝéWF9Õ=æa“Àl mÄyù–Ö¬¿šnqA/2æAäIºÌЬŽiâþï -qÑÔAN?!9NxlÅbÀO°¹Î{ìeÙi‡Y“Q½Ì¶>S¸Òô€ZŸîª‡ .&ŒsÃÞ¿búÑj?1_ñ¡Ç¡P´ÃékœåÙŸè¿°x²`дY»!í¸n”ù6f®VJ•?ffãoýš‰n”ð0¡‰°šÒ6l¿)ó±7ØB¹u‰yMAƒÑ¢˜&×mæ>>ÃNjÔÁ#4J%ûÄ&|žÇòE]ÛŠ:íÒiû2g¢0ÀÒio*ð6zXŠÓÂø†¬h +æÒ‚÷Ò3;üšñ1"Æã2ÒÔ+Oô?©ˆ¨Kܳja½÷´ðÿú÷èY|£‚nßMHŸpüA/šLsI5§•cu¿*ÎDx„!W‹ {|m¯pq%ªØqeh¦rÁYb¢Bt M7»uA-™ -w%5»”,xë+ zî!Ί}|%­¿w¬pÈ©™xe¥Xx|Y üy$Mœ}yòA—Üzè5À“{ù+=Ž}5"6‰~‹{άq~„p^¨Q~Md¨¤~*XŸÄ~,L¹›U~S@é–Æ~“5½’ ~å+f2T"ˆPÒ{p«U…Ip§<„ŠdG£ƒÓX]ž¶ƒ'LzšJ‚˜@À•Ï‚5·‘)¬+‡ŒgI"Û‡¥€ó{ª~Œo³¦eоcþ¢,‰dX؈ L;™o†»@¡”ò…}5³^„E+£‹¶ƒ#‡ízЩג–oy¥»ÃcÆ¡zŽÁWÜŒ­L˜»Š£@ƒ”Eˆ¢5·¬†¥+½‹„¦#N†¡‚ÁzŸ©O˜ïoK¥/–Šc“ è“ÝW©œˆ‘ KÕ˜$ŽB@W“¼‹|5¨%ˆÀ+׊œ†#x†Aƒqzt¨ùŸ&oM¤Éœ9c l˜ÇWŽ›ò•Kׂ‘‚@V“Ž5³Ž¨Š‘+éŠ@‡)#š…õ„q¹£hÜf«µj’[Ű1lmPʪÿnpEÐ¥ip¢; Ÿs 0ò™Xuœ'£’éxzÂŒr{‡qD¸äo×f«´pÈ[Ô¯qñP䩪sNEñ¤tã;=ž%v¬15˜x™( -‘®zÄ W‹W} q(·¢vsf€²ÊvÉ[º­°wLP̨OxE뢳y -;NœÞz11c–Ò{v(_•|è ׊b~mq¶C|ÏfL±o|¢[y¬[|™P£§|ÃEÒ¡t};O›²}›1…•¼~/(¦~ä!E‰§pÞµƒf&°B‚p[Q«/àPt¥ÛgEÁ S;Pš¦€è1£”Æ€Ã(ãŽÄ€·!£ˆÞ€ºp¸´,‰Rf¯Oˆ6[Bª5‡Pc¤á…õE°Ÿ`„ð;U™·„1¾“ëƒ*)Ž‚^!ðˆH§p—³y`f ®‹È[>©fŒ P[¤ŠAE©ž—ˆŒ;Z˜ø†é1Ù“0…U)FjƒÒ"0‡Î‚op~²õ•7f ­ð“![B¨¼ÀPY£_ŽEEŸé‹Ü;T˜\‰…1æ’ ‡C)kŒç…"d‡jƒpm²–šÙf­r˜=[M¨,•1P\¢Ç‘üE£SŽÜ;`—ЋÔ1ð’'ˆå)}Œ„†"‡ƒ›Î¡mfú»ènìi=¨µp­kq”êr^mœ€µtoÎlVuÚrX w¿tDyßw'0É|Yz>̾j´qźjlÁrä§`n­t“¾p‹u0¤rnvgkbtgw³WIv~yCtxÏz›0b{x|bÊùh§|~¸´jÒ|¥èlä|’^næ|¶~ppò|ój\s}AVtu[}–B×wÚ}û0z¬~lÉ;fׇ ¶øi† -¤9kD…ëmh„5})oœƒviNqê‚¿U¥tX‚BEvÿ=/·y÷€VǧeP‘{µqg¦‹¢¶iåž|l‹»{õno‰èhLpãˆ(Tâsu†SAÁv@„Z/ryX‚Æ_d›Ö´2f}™¡}hÆ–ŽMk“/zámtLgdoþjT3r²ŠxAKuœ‡I/8xσ´Å[c&¦³5e¢[ }gÜžrQj.šxyýl¥–†f¥oD’ˆSŸr Žd@êu‰þ/x\…Ä™bp° ²vdÜ«Ÿ²g%¦”Œ€iw¡„y3køœyeøn¬—bSq‰’@—t Œo.àwý†UÄa÷¹ë±ôd`´RŸfŸ®W‹Øhè¨-x‘kk¢emn)›ÐR§q•\@?t@ŽŒ.Àw°‡ZÀ‚tÿf4¯uþh¦åvÿjõ‹xxm0xŠyosekz.qÐR|{itP?Ä|Öw-Ó~zK¿'rÈp{®sÿqÔœãu#sŠ„vFtgw£wtu½dx¾w*QÏz%x®?E{Äzb-Ž}¯|X½¯pÑzŸ¬êr'zò›ssj{@‰(t¯{™vxv|c£wy|Qy -}>ºzÓ}œ-R|ä~H¼(o„™«Yp‚ƒþ™éq݃^‡Ûs=‚ÁuPtº‚;b°vS¼PPx <>Ayû€¶-|0€º»mŸŽ{©øo˜p‹‹z†r‰èt?s“ˆ^aÌuQ†âO¦w+…T=Ôy>ƒ¨,ï{“¹’lu˜S¨Ômü–—oou“†…{púùsGr Žlatq‹ÝOvk‰?=tx†j,É{ ƒ@¸¦kˆ¢ §èmžÜ–‚n’›l„–p—ÝrxqØ”M`Ws¼¯N“uÈŒï=&xˆñ,©z„·÷j׫˜§4lg§ˆ•ÁmᣃÔomž‚q¿q0™å_¹s*•9N uI_<âw¥‹6,z@…¯·†ja´è¦³kí¯í•)m\ª|ƒ2nà¤Ïq)p¥Ÿ _|2s§o„|­u]Ê}@v¬LO}êxT;"~Âz-*æÕ|L°œy(xá¡*y«yr‘ z$yù ÖgWT€aË¢¨Ä®kTd@D\dPPp-HGÀ„&]¤30Àô¹½ß;s§Cg(†  1DE²*nº6ܵaïzê*«&šå<ŸÞ//6è± ¤O?ƒýQ}Ð0$ 8 _„·{¡Žˆ[w¨ ¢/«¾!f›UmxrIŒWBø”´5À(Öµ¼Š)2 ØÏ.$½Ý5U±o¶}Zƒ!O¹O“·ªæq‹‹mªÇÌõÔ õº>’>P‰3 ­Ä¸g| ú,ûXñ¦“ÿ`£ÏS$š>BG”‹ çDÇ•u†”#êiÚï#§þÜŒ-ý`­óœxJâò!wÙûÊúÙ…:‘Î(‡«‹ÁŠ`[HÑW¥¿eQÊ2U‚áFDƒº‚‡¶`à|ö™§:CÍèd¥õŽ2Á~Tv­±Êkêè¢dEeUb2¯Ì½…èp ʠÿ†¶£½~íè[ô‡Ë@›šQd€³F!•œ7•ŒHÙÙ$‰ÌùŒäÐÑ Žˆ…Ú#—Èd¿Ltæ!BOáKš*áŸÌG-i°ÙäC–ÃrƒBú.ÒUålmO¼>Æ †,Bæ2Wü<+Õ3¡6¹7¡›µß›íÐ@µÑ ´)¤Û &KO¶ 0ÞûÏÔOþ igmúŠ82=DÈ É4FB[†ÌÓ!AãIb4½~—¤ü£Z—í *†â¤fz\½ÕOÚt®æF&”ˆíÏÁÓN&3xôàFÜê[®H–jûŠýz°¹&3n14µÂ¼böMá ­zB¹Ð!Õ Ÿœ|‡®+¿å -/—hw{ìûV³\lÞs—ýT¾jçgæ?Ò›¾ú¼â©ÛŸÔu «ê¹®¨D­}û”.ƒ5ïʺÄ(wM’ °ýÒ±=ÉL·üÄÎjúe³o(u\ý›» yÚPµXû³üžæƒÆ¢8ùp2¥¶¤©23Î2"uhì0 Á;ý(3˜àû-ybÝ·‘3àÑÊÕWd÷‰sFÂ@wÖ É,¾É8#åóè¿!ÆØH*9¾ã)iF^ -¿P7÷½DšgÛÄ3Iœ33D_Âè«ôºâ)ÔJœQNdOÌm2‘tØa':‰=ÂûÄJÜê.ÆÛ±¡ -sÞ`d³é+úuu—-Š Çµ‰i—ȵ\L -±kw„á/iƒð®&GÜ1|91É:Hÿ^ÙÈ£gWü¢ú@-µEí¤¯ifš?Q®·F?Â/óKvüßÅ™„MØkzÔ݈uNÖ0:ÓŽ3BJ]÷PU@âÕ׊VV’z±¸D‹ñPCÿöãØ9>R¨ÅTl‡{=EŽY^ScíôyjNËå9Ûþ¿Â6b~mwj[™íŠ Zl'±d}¯°[¡YïÞŽM¢:tU9‡±—óWI“-#…dî=ÓsÑ£½S ¯ÄI‘KuÊÆ·6i/JO{×sýŠ{®˜c@6o°©ŠP³U,Þ'9cV–~ÉM6²åíI½Qš1ÀWÉwoÍ×T+ã¼ml³ºF0‡î…\´O­·d­È?Ío䉳iº¢4çÁM™•4M·C%ŠHÑf©M[×r0pÑ[Ï€„‚pê|R»Â’¥Ã/Ld/_c8]Ï×Àó ùYpFøKM(ÜøŒEwoŒ@Újjê£I0/¢¦Àkad[™H’>Ùþ|/Ñ“ðLóŽð §¹|0ð0S‡ïõVRŸ±×‚V2C¸Ã¦¹üaˆÑv”é4x«ö,'LÈÓ82Ã'ú7âô¿&n­„Š&CÄ¿ñÎÍf]9é-f]i{Ÿè‹ÌT÷¾¤¦à×aéÎ4EeNÙŸ±¯¬¨äή"“VöÐ_ªÇ”3½Úµtõf6š‡ú5¥ÙÒ·µ¤,®  jP“úÑ6E‘«±x)ͻ֣UüSu©@Œ×6M6ødµŽ‘Àõˆ½®¨“FÔÌþ›ÏV€»SˬGÀÞØÅ¦wÐÆ û†uÐyÿ@>.õŠåTȆÕ×éVäOdj­?#驺syc¶¿ãA)w,zl<æ¨ñÿþ÷Ù€»B–*€7Ši€j,\‡©€P#„õ€;}}™~ˆÝr4–ñˆfx”O‡"Zh‘ †NMŽð…BeŒ@„(7‰—ƒ8,i†õ‚A#F„aN}˜®ÎqÇ–*Žlf “‰ŒÍZÛ‹ MõŽ2‰HB-‹Œ‡†7ˆß…Ç,y†Yƒý#pƒç‚9|½˜–•q—•ž”¨eÛ’ï’NYÆ*µMº}"AîŠíŠ˜6؈Uˆ,€…Û…#’ƒ„‚ý||—´(qW•,š¦e•’s—ŒY¦”!MŒö¾AÇŠje6ÇâŠ,…}†Ã#®ƒ5ƒtë©PcèjO¥ãf=_`¢rh¡TžÐkH¹šîm¾=o–Ùp2¾’ŸsŒ(ÀŽHvÙ "‰øzbtu¨5k#j¤¸lº_-¡$nnSÿjpDH©™ƒrB=t•ytn2Ý‘Ov½)yL žˆÛ|t¦¼riŸ£Is ^ÙŸµtS±›ýuSHt˜#v­=_”.x02ëyÎ)B‹ö{›! -‡â}~s¬¥Ùt(‚o.wþº]^”«©©`c’—˜CcHl†Vf+Ž;t)i0Œaèld‰çOþȯµ>tsw…[-òwn‚¿¹w\-ž¦¨®_A›®—Mb0˜‰…ke#•SsShA’!a7k‘ŽçO|o‹’>#rሠ-Ðvö„0¸±[D¨n§ð^a¤Œ–‹aS h„ªdLœ%r›g{—ç`˜j哟On‚-=ÜrfŠv-µv’…m¸3Z¤±ê§p]­ •û`®§Ü„c¤¢r fØC` jR—ëNŸn’b=‘qþŒŽ-ŸvB†wµ~o®`^¥£q&cc•rfBƒÇti qõu£kÚ_ôwInÅN1yqØ<¸{u8,-}py´mjy¤”nÎlp”ptnR‚Þrp/qsÏr_;u¨tMžw§v2€ãû>ƒŸ@?ÈnÀíòC)…HKÑ#èEu·ä$þ%à펭û¡`^>[» -ø(?`~¤^xúÍ0¬¬ÈÏ_©+“OË´§v&"YÀ˜DŠ>sÝ5x'²±]¢¹£«~¨Ð-ç€iÀfð¡~…>NF" P²ØžÙñ^O—ÌGî# » ëÇ–´ªî ëª0Í<”7íÓ† 7 :sÊÓÃXL’‡!kݱrÂÄçá×x{6ÜR¦tå"+·¨@ö‰q£ú*§7k—1Ç÷U誘Y‰}(­Ù~\HÝë`Jäž‚æ“à·\áµâ¼ -±ÒÓþ5ç£2[{Fõ;‹On·–çÛúݦÛà *Cæª{2H<¡è¬N-ßS´º2ÍUråÓu÷UKm¹³5ųBuœ• -}£·ñÏÄ3À!ãJ÷H$‡œ#h^Qv¾0ªqÓUô·ºY:‘Ê«ªa¯Z=V.ˆ¿ }VÒZ¦Ÿfsôy îå½ÝÃÖ§¹MîßÑP¬8Û:Æx-kîÕ¶(+’rÞ©ãªGãSµIå²Ð­€¥øºO ø¹ùw÷ðiιþ9јy»¸—€³Ÿ’—&÷†z8,kÊ.$†äxì=rmRMêï¼ÎRŽ€Øíu×Mb;d—íw0yÁ â2Ö¤˜Tý{¢œ¾§—¨WÆŠñ6m|ˆ+±8êEC`ð òGd]íÄMm²µ"ˆæWürS禜D¿ò²~AêÿSƒ‹‚»° ¨Ä+ïG6W#èÿjnA>p€ÕuÂw0ì‚D(ÃMHOÖB$õ™ŽüŽvKÑ»X¬—{н®'VáíÕ' 5ôc - -èshø]T4ÚIÏÌ ƒÿDµG§Ã£TDÐ(2BNlz9“eB_ Ý«.ë#­J³UÿÒbG޳ºÐôà¿É°€Œ¯¢ú¡ îPÌc36õ ûèß…!Ø3Ьü•?ào/Ë¼á ½¥4Ta1l-vKWZ§¢’¡õæAÁpšÉ¾©éã<„˜‚>\©Ð©æðÞ€kía8¸Z5$¯GÊï§çd¥W#‡À{{à¨ß¢ßÄ! ¦eÂ8l&ÓVé¨lu4õèÊš‡ @Ô¸Q®žÔú¥½×÷WJ"­àçû׆Ò쎛’àÿò…)è9(©6gf ¦yÙ'ô¼1?·Jöî¡L)­ábåɡ쭢ü­©l’º]4ÂLþkÛ˜ëPpñuîï²¹ô)nCA ÐŶ+2d‹ØõEHÊ'æœêHøm&Y3¹uó õÑ·ÁÕkïѽˆ‹ýÓ­Š¡Ž1‰ênÝ]ú_Z—<Ú®£RvÓ›‰ðpjÊm9GÝ‚–о§®À#j}d£ÆAù-uÚ í¡¢¡û -¨0”\C"‚°ÉdhK>Ù…¶‹ÈŒ©Ù¸Æ:IF“qž\BVhFˆ'$—[Iœ&3’—äæ°B¢™tÙâK\ DåÄ'â`;™I [¿"–ð%„#þÖôNã‡÷š\÷¾ÀëI -|?·a×ü8+ñÅŸ3"-AûöשÕ_ZZ„K±O%u6`·îÄÎX{cçͯw1Ø ÞÙÿ$¾Ñè+ÊÁO±M{›'±øšEØ],¸j¹z6¶¸¼+§ý°~¢ ›Qœ½k a=â_ˆµò/E ¸—q¦ÀŒ¿bVškÐÞ&S ß7füg\"Å&]ÐÇKOÑ:´ %ije¿B>ä%¼à™j†:l=®ºT1e~ ª/ðߪg¸ ‹”ƒI0^YÕV) <õ^ÅÏ‘ë% -·Õ¦ÕQS-WõÓGpaθDû8¥ß 9•DÖ‘ÕƒðXM'Ð -ÕóUÊÈJ•]Iå"­—mtª¿euuE²)ÓÌ-3`ŒÒŒù SoOŠÓÕýŸ£™«™‚6Ju@$¨Z­ZÏùÇšá§Œš;œþåoŒÃö–üúaëõm>Ý„92)@m¯{«Öêþ†¶¡š‘»”>Õ-ÆV³|¥°³— À•´WUæ>rß$Ó²]ªqّ¸ÿÆz–EYuÉ”ÅÛ>üGT@èš©©\Þ'ßÇ}ÑšGÊý¡9¤mp.d.@L4c&,·”r;b ӂãá îdýlúÚÎÍt3Ñݦ©]Q<äbžÌ-w °ô©ŸNÂkÃð” kÍ búKâ%áï´ÃíH»„“@ j™à"ŠW4ñ²sfœ‘|Aêa{¨à8Âc%ŠJ@bœ W’Õ\“þEÛÔæ':EhsÓÅ™Ø=®}âó9fǹú¼T¾ÍWÿ !3ß”ô% פԘÝ]YzÄ€&XIkWdPÍÙŠØb§]±9g‹ý bIêëiù ¢Æä“Ï$ õOþ1wu_)¾‰xª®S$Pî)m/þUI Ã.mÔpsf5œUwl}oyøhÓ 4á;=DUóIKSž€DSÊjŒj:?è2ž*w¼0P²Ï4‚¿oýŧ+G„Ë4O6‚jµeu˜æ ÙH”W)Ï›=öƒøÝ®È†Œ¢sœ51û  oº½ÕkæÜ¨aÈIÈÓ½Ö’WoÐ0Õ%>­ª#}?Vþ5NÚÏÁ_ÏÛØû r}¹ë%é7¥­ -ßÔ¼‚Ñ{Ä!`åD}°üÂK_á4ˆ -‰!ƒ¶ªQ\üîHÒ½¼ÏzÈ”ôHçNÚ>«uA-^á²°²bg¦¹%+kÌ5‚ž°8ýWâ #ýwi+‹¯öqéÇ0ÞîŸk…hcûuTT™[ÀÖÁÎ`î5ØZ[›`ËJ¯ &º¶œ-áêvÍ**–öcs0”®Î:¿-Š7•Øôo‡3G„(ÖZ!žd ©¡ ‘z Q}v°x“'EÁ¬éð}èà–aQ#*ÚÕ'–Áévi½Æ·|'iÞnˆ’Ž…ËµY²çø;½eR{ñ‚îEÒ1ÌÜv–ôiúk·Y¶èTË2á¼Ьæ4o—/Šõ;äªðKšù |O cÿê -Rr_´ñT—Ó'›Uät£Kyγ•zØa“L™= z”s#k–)Æð|O¿ŠèÄ€ŠÜ‡­ý:aúxÓim&ò™ã¼&^c½®†ÂŽoIÑ“` -‘¦îWÊñ8Ï2K/ÖױϬ˽ßòŸñ^º¯i©ÁùÊáp‘u£€óÔO£ßè:JD­:WtG±Š<Ü8YJ] - Õ„Ñy¬iZPí-|xm«4×Är¦Qe¼`þdZ´H ;4°SÉìí¹X1Ìšå`¯±wªŠªŸp¼u>Ê7¢ Hô²2ìã%¡C‚d>«zES³?«É+éæ&ÓÎeð{™\ÄQ>‡Ý+)ú ”^T9ZîPàäåF­ê†ëü«‰ðV+ú@Á£¸Š®l@ A ˆŠB„ -r‡3„„“L2÷$“™$ €x ¸¨ë‚Õ*,^-àÚ·[]ëù<**è–R®ÚíßðýôIˆnpdkŸ ŻΫ ‘:ÈÃà„£©‰Cö‚û­ÆÐâ>ÛKXi­õ<¼_ŒTTЇ¤Õ–¼¬qùcsØ.žJmZ¹E›ÂŒ¨„Æ:ó^΄¢œúhsVIbm8tS”äX&^ a*ÉŠ‘ÍÚnð^Ým‘îšãæ=A2äs§ó^»mICca|k‘é°`‡K±…{©"YÙ¡:nf¿,Ú±šW xØ_n~  ª¨ -!fÓç¥üÜ#ƒ ÞäA¶É§oÖ(u´ -gį›Vgæ”·ÔÏçE)Õ?nî/Ø  bd£éSûu3ßQ·¼Q¸–ùI»B`×\áC!ž’ÄdÑ -ÙPÜ,ÎÀˆ2QC³—ë[PŸ¶å‘Ò»µnª`±žRŸ”ºƒ£šXYUé^í”Ò'ý,|Y5G 4-}Ä<¸Ÿí«kÀC÷ûÈfÞŽoÊÛ†3¨²«¯«7‚á%ÍRÍëôå/4ç…ä!=öa¯'‘mý»´fUgÉLˆ•¬(CfU]@::& Ô -¬wo²¦ß¿“ÙD[D$ú”<ÏÞHÜ×½k”¨ÕUÞÒ*Iï#;He6Åi*µø80C¥¤ÕTÎN/h%ø u£hvLµ5'‹Æ­s*=%s+Ä:V9˲OÙ%ç›û‹ž(LWÓ–¨sÏwœÐn0~~ I`¼ÙÛñFÚÛ嵃v®¸¿&“äK^U÷*^Ê6U>,ŠVœµ{¦:T5zG‰Fd„Ÿ„šìx|“×4ZšÙ²ÅÖ êuÍ'úʦœ¤"¥œWw±°[þ°f^ª'0X·cxÕ…×Àþšˆ2ª›rKìJÛJáD»Œà”·¾“Jm®äB®|CÃ=ç€55oc/ƒ¼ªàðhÐÏNÁŠL9õ'0ßjIÁî.ÆÖᔑÑÂëùÿ =$!_û3²sÛ^è>pX0µ]SÐc¸„†Ô¹`giœ9ˆ¦Q?”+©,OÄ|ekòÞákC)6b«ŠÞf!),ÌM÷î§úð¯jQ­›ì‰ÉZ¬FÖÀ_û¤Y[-ÉÛˆfiv&¿ÃþmÞHø!‰¡ÏÈÿ`5oõ—I¯šxu¿d€“P#ôF» -î”P&ÒÍÇàÓh_2ˆnÁnm½‡MsC¯?’™¬…ºÿê¾wøƒäÏôOêºtí[P¢k+Ój®ènæöA ú»Ç¹ú”­úHßÚÏê§Yº*£zÒל`˜L﵋TL˜ø0ïäü®ý³1|w:4Å4oý™¡¥ð(ºÅ%úªøò±jҨ̨5YÀJ_ÑÿÔ|ÛÑ©f¥•ôò´y†l0û‡Ê0—±š•DúÓO+/Ê.5ÂT­"»á$8¨¼[g)T`MHö³?ŒÆ“”Ú\»fިÕyL/Ø\ZÌüŸáúðjò@Î(­WÚ¢u”Ó‚d>ÂP"Y’d'$„$ßÊ—ïËì©V¬J+W·>pG‹ž[„ç^ˆºGÚ»ßñãªß2|M 5òòkÚci{‘ÝZJb«»ILF<†fB>òP­óCº­R7<]'äwÂçõ‹KÖÃúÜQXb¦Ãã*Š -Öÿ$f»‡~ ^¬Í„:¡)É]È•}‚pèæA(žŸ+…R ©´X¨zE©‚ÌÐ;b1tš!‚óá9ÚÝ ÈB”jü`™ dã>› Àµ!×L7g¥Þåh%³ÆëÒ7n·×…¬§á ô_Qg1ÃR2Ǽĸ:@“nè\ÖKžXù¦å°Ÿ)Â'€WIØC0hݤù!XL}†¾»É4œ‰l5É Vh2–°,ú–?ƒžbLb#(sÀyÌtk]:ibP _"ÏÈ2S&‚FþŒ ߆*:Ü/~‰·Ù5ülÒ6ÄÄþfÝ»¡ ²¯Ó óð¯©vÍô(l1‘Êïu’™;8ãíŸqžµ·ŒóÒi¿7mœL[Ú@¨ÅWxlâäg™ÑñË íY<€#nMDý«yYùZOÃEÑXê;/C<_í•IfG‰ªuÛúR›äÄOè…M­++cØ7šS«ò -4ÆŠaZ²÷é­ÔƒîŠœøÍÎuª¤í¶ Mߊ]–>]o°¶ÓÐ/ôm^&=ÂNhÌ•.®g*>d_ò$«¥ -]ókÜñßÙoj-¤½Ö]ºŽwðzýí`ùg`¹@ÝXRËê¯ïÖÐèžSZ^6Ãu¸VŸîø^×ïo«×ÿÔg±„~X—ñQðô Èæ¿®¡ïa%{s Tú·Æp4{®³ÖHLydWû)YUÀ&R?îçFDš/€'ƒgÑHÅ7yOÃÌG -âS0᪄ïg :¤poÊ›)·-.XF:€eÙæ¸*diÌåG{.㯙nwn.ètº±ÓY€½Í<’"³þ`ö¡ôâ7ÑdÒìsSC„»!¦x$¼ì©g‰ó:SîÚíøX9ÛÍY¶»Öá•å‰%r_‚']4KÌÊ ‡. q cÈYvã.㢂MrÎÓm½ÜúŒìÎ*ADbW냊ä•M1âDq½by¡Ô9 ¾þm­T'bu÷q…7¶Oñƒr€®÷Âé ßö}¢y‰XKˆ8‹œÜ`”ŠÚå¾®â.;²´ÔÀ«¿~1Kü}wù¡ÂÒ­³•ÏrB;ªÒ¶Þ’ &–6 åRrº÷*?jÄÅÒ䆑luµgðICkþ¶ñMú»|¥Úþv²œ®hùZY˜Hn8£V’×zšƒQ3N?Ý?ÃôÖ«zGPöÖø‘ÙÓ5¬|Noî(RGÐJ[Ý5ªô°&Hs)qq}^í&2Õý•n:z¹ý®þǰ–kFmPÆ03;7ÕNÑsÛÞèi+ZêŒiÓ±úµ Ó^Ãòzçsã7ÑTÛm ,ì þŒzb–@Æûp…¾°õ2¸2{¦9¤§6„‚Ê„ûž/À=ä Ç4ø)šcýñ x -t&8…3µ¯Bõã-ã(;û^S‚éeÚÙúdSyÂ7yG^íH@¶Es­¹7<ëA–³Q|•áh“[ß\j‰²–eÿÚZŠÒŽy1|iÂ-—ÞM»']£ý‚š¦|§k!¾¨õ3h{ß&«Õm¹›5&[K¨iªƒK%Ú}UEüÞÚkÌ€Êåu hTñô[Ÿ*îF‰÷´kkOÆÆZ £e¸è ³œe€v]G ‰Ø¼;ÀG­¼LÉÞWݺ[ä“àñº…d;oä‡àošô3xòÿ‚°¦ÀêY{OEk[@Œ|lÄ‚2„€ì„ý²^ò’¼—÷òF6a ò¹9«uUQ[¨EÎmË'*u€WA¨»ä³wÿï:^WfA×w:¨Ržcºè $DZ9-Ná±7¬É×~c -» -?µí;”A¯34VfO× –ý–ßæ 5âœÄ*DvƒÿUe_ÑRqr…_ê°ü¨«pMv–‡]€ö½{ûÒ›[;¶õfÕ4¯( Ìc5Ú‘Gìùõ÷d›xþµ±…»EÒjOª˜®ü-n -|âš ë†g8žî KƒöٶŲˆ]ï{r3ÚJ¨(°ù?Ò±’µq…lá÷u;S7¢êÇqWA}Åǰ³†ÃÎé=·Åo -¢nÉœxgº|ŒGCT˜pŽT§aHžìÓÍ—O¥0áU`llÚ¤»Cçltîî0Ájþhý~±¯pÚ±Yú_é,xü'ù,ÉIU·jnÁ\Í[Møæ z©ÎóÆÈDBéb<Ô¾Ó]ò«ÜT7S0CùoÛ}ñ2õ%ýsÙF͘¸MQ«óÏ Å›¡!¤7füSÑ•&.!m›àôéFk´¼õ†(+OÐ ãºóâOÈ¢í@ ê•Wª1ù¦f­ÙôG Û0J¸èZ¬’âî-#¢=¾ÅýÄqËb>@@Ñg¤IxFzŸ|˜û½âÅÞ´\“E=ˆY®g6ÒatÀÒº*ÿ¡SYõŒÝçô5û—T9vh÷ øŽ Æ%2{}n€ü´}Iò90Ýv zRf‹¢›Ð8kOËʼ™¨jV‘oõ:*¡xÚýH3Ê_ 6ˆWWxç4Ç\;“5juÙêèK:«ã:iì²7‹rʶ‡»YAd§«~XÃâõ×:J1<Œœ¥²;îäeªî -(ú;èMÙsºÅÈrlÚªÜU[y5ÿ»¶ýävw‹(k --Í×Olí—ÖHº«¢WeGã£ü݆L9s›¸ªè‚ŠFp6§ i&xëÁÙИp0üC2}ÉTxmÌCûÀôH#îѽZy—Ú‡†m{ìç+ËEŠAaî­ö‘ìWÏÓd¼V¨Sy%ÛÛ®™8bôש"SLL„¶òé1å4Ø$BsùÈ&±¿Bµ—j±ê±&©d@ãÜYˆÒ?O¬€+82}­£ù-D^½—†„ÎÝ’DÝÚ(ûËP¤˜Rœ„{¹ÔѬ.íús¢!Ê$Š˜4Žá´PüêÛÚ£ü§¦o\á„iÒ(“ÜÆëä#Àu"„»Dùž8Ï -Ф:¡]ïC­Œ¸€¼¥>Ð6àÞæášÚ’‹¶ÇöÜÒ×¶û*ö±‘ûÈÂm@1GQm ÊlìOrus¬g#ö tk-Û¤²ò<‡Šÿv+lŸUÙw -]õ@‘äV9È9'ç¹Ò3Õ«Êè ¡Pç?#Ü0eDãaã¶ÃÕSœº†#Ö[%ëÀó™µLYŸø”‡Æ‰U‡U•_n¸¢¥ÊFÀ†η4FÙ/V[ØäŽ&+‡;Õk¨+å4e[D7\ì~YCm[†D¹¯úÄ·æÏÊó»ë4MÑn/ýé«9Ó0ÌÑéÆ:/[Òo¬¥`ŸK½Ó˜Ž(Öá -™ƒT™Þo…Ír M­8«ÍæFECw…LhT¯–l¿…ç¢ÂíÈ ¥;=í×ÑL×t¹Ì^r%´¢Ë|ÊTXN©£dñR3O>¢Ö«Ä^º°¹àž¡Gû)­ y®·¯ýÛ‚„2ãbþ+žPgˆDWŒB¤Á;TäÑ+ô4Q”v—¡§€¡{ˆ§…À9è¼µ;Œ¯°!f»é6~/Ä—|@Ír~’½EÃMíŠ$,<ª²`ÿ2Á‹¦+ù´oM­Ò¿$ȵk뤆éÄÆ)<$‚\nnu|”LXìÀ·´«¯+z-]‰:Ör"ï‚æ®åXê—º.KWü;øªå–YFC² Ê:ÃAÇ”+ÐIéU× u€œåŠ+íU>.+Í‹;ËSN@]Ž“ñ— ÏìãLUÛ†Èñ÷¶X»Kx»6 Í‘8=Ó*ðUµ±4^qÃÝ—Û¥>S韒¢+—Å» ‰eÝL®ñsf»í v?Á¥ímÆ!±»ñ'Õ粈€Yþ¢v0—«zæÙ’2GãîÏÒwTÄ1×µe{BªðHÇóMœ,Ø Ï&fr(yÑÁË)©% ­ßPÉÒùÉÍ Ehl§% -$EVDĶ¡¥Ôt o§Á \~6-s‡ð/€/²›‰EÊÔä 2<뤪tñ ‘Åù:¨ÝÚmb“Õp®´V÷±ÊçÈn(Q®7: ÏziZ¢“îàN½l*é3ÄÔmôiИ¥‹`® ß sµ‘ÒnX -ýUœ‚\ПÈb¡i0™¤Ä^Kcà=Ÿ!!{pwp y€ÄK´™H´ö&ôȘ/ùU¯DµáÆgõ#M@’êÁ1“¦¢“¡&®ÓÁyf_s‘IrÈÚùÚÅ”\ ýBcŸ×¨7¬H˜eßxÔXl¶Åtbuæ!†ÿhI -&)¥å Ö©êršbp›s³˜Ô;î²Cu GFæqÝ~~c6RbOŒñ 'l—"<Í–¼‹üÃz [ª”TÿÏ0}°5y Vá|E­W¹rŒ„Ñ„\˜2”aAA‚¬0 „¬/É·W&aã€A -¨åÅAK]מ ñq\k‹P­µžU´"ÎJÑ»÷?¼Wåù¶{¥¯j#¬'rG”^–‚ô$U¤Ù)~VÓHDäÎTup©áéæ7©ðe’ÖÊ⚊–˜R³"«I˜›^Çw0Ó^Ô+m¯âO©ïX¾‹ÖiM£Ái†-T‡ß5Ȱ©'ˆ†œNÙ]¢~{e‘œ ÃÄr¢©Ä5Õƒè-wŽ»Aœ•…„ºÏ-œäV•˜YF~¨UgÐÖBOÙJt¼8yÇ0¶´¬.{KªèOÁ(Ùë¸vlJ¡ uÁSëœ0ü¾ÖƒyØkØ^?‘6W­…c+Ý -ÂC²½ÙlÄ]œÂEškoÆ% ¾õݼ§è„¦™“‰õg}“h0š[[¡ÑãˆtVÛƒw,ÑíÇU™£À^|}²X?†½Šõ4:a<X… sšá%˜Ã×Ù‡U)¦Õ<‹@Z§Q›/ç[µ6ÓóÌ Ý.ú ÿ0A=¹fˆ œxãIÒ—³’Q¾lä3\¨P©BoJ]Ô´ã\>[3Ô²õ­ö‚­Œ?,·ôÆЛMOyIÆOi>§ œ'»|“àÄ2Óökޤx¥Ío»ç6‡–oy*—¸Zo×9‰—XYÊýi£¾ŠŸ”fNP?ž1…kÏð¾ £€Á *ÙÐã_BàïûÄuøpÖ²B[’ š”¶Å4XŸÑ×õÑÔφ“¼}äãë ‘P¼¿•Ø73‚d"ÎdÙ®&Ä<¥ÕÐîăTô>º–üx4ñY´…"ƒþGðÑXF%N£góÍt2®õS 8.h¢ªpq¢„ܯ#~Âð2‰Hlò’eÒ¢ü€(j =äû™‰~ðn$¢± Y9P²KàÚÐC‰/íq䢘Ö¢&lÁŒâr³èSøÁ1åË|ˆ8Óû+üìûýáÛºþÇp5q ¶Z­«‚ô‰(õQÓ¸©AX‚‚!\Ž$$$CîsrL2É$™LîÂ%,‡ –*OQ–uÁ«OÅ‚úŠBuUX뵊]‹x®¶V‘î~á£nš,ÿŠ[™|ÏnCÖ --ÀbY@ªX?(Ûe±92í"Õ¯)fÃmˆ€6@>íÄ_æ|ÓXȼ ½ÿæL N§+VJ2¥v&¿Áœ’Ç‚•ÙgÕa:yÊ*=>ˆC¦,ê½……zq¨¡©øwΣa™çáVîbP$©àßÔ†3H¦ ‰ìµ* -|™tšc^Œ7½Cûv›ó¼fÝC¦›ïºUʆNì\ƒAœ X)ŒM¢×ÈŠ¬ÅQËr’K{¥ãÄFÛeÍ"jùó%§§h·¥C¿ŠùÀªi¸24¿Ô.$ÛÒ²–ɹ™DÑüÓ®Â?2ª»ðÏ]HMtî—Ôa¡¥»PZé¸+CØÍ9èJ*™á_r%QNÊH®4Òrõ{W)þ |Õem}˜^…e «ÎÙ» -¶.év–¬ö_.e'TÞ)ñV4û¦(FéïoÉÿUgzfðÓ0Õ=ÛrûºÆ£Õ[(hÃØGjKÝÝÒ¢žÚy‚}¬÷¡%ë€]ÁÊŸ%ö€(yì­¬À¿0Üóæ·àÒL1Á½™sÚR1w^N³JÛO÷7ö „ÌÙ†yÅoxõ™”OÅãÕ`òi0) Â¿6Tù„­@JŸ­ŽJL#†Ù×´©²C‰¿[!ì)º«9!ËÊêw+“Ž™î@,Ü&TŽ›Qö0GU5ÌËãýaãò¯ -ê5\1(ù¢â¡-è9]³‚¶¶¸†¨›s4ú±Æ1y3¿ÆyÊ©ý©À/Ì ÉÈG䇫~IÄ´41î¬ñ_Þ35g%@‘ØÉ.1N§” N¸Ì¡þP°ïi'„7ó×ëŸÈ4Û˜@rÂzË8äZ?ˆ ìêiþ;Îfç -ÑcÞEÃNéO‚ri@ñDüœ¡Ž“ï¤úu{Aî6.ѱ>1óáë_Ù:,¡ ƒûJf?È/•Lèí¢CòNÉNÙç*E]Ù­!ç½×ø ämqú=pñ)²¹¿Ýî -œc«F±M¼Hõ´Œ?¨£b;t•%› 7órÁ~ãû´£ÎL&ž3>µÅï°žÁ~6slD'9ã?´ÿ6š‰T­æÏ™‡¥^ 5÷;Õ -k[ }€Î»ágX0^h™q$WâKJm3ÖqV/fÔÌ”¿Æ&„í|ª}3ãø¶½1—ªèsÌO[­9ï¨"6Á‘εñ6 Ç9àK¦ÿæ+´|dÔøæjÒ8aÞ&Ák ËɈŒž=9wåUžÍ©ÏÔ?º¯ƒ®Û¿Ó|ÍáµÒÔ0,Œ’«¡lug™³z®äeU,}*Ì ”åe-þˆÔ^u„ïG¢SÁëßoyˆž7ž¦7æñbC#Q½•ÝÅŸánª½[Ú,( õçl•^ 6¤¢ !ùúâÊŒø>ó"ï:Ïj°®èbiq¼‡„¹Æå2š$V1¦\$›åÝÇ•wŠökGÔ£þ£îQÊ“Ž%´[ÿ„ôÅÎ`çѽJ¦ Ή `·¼]–ªæ…+Yº)úu—!*ó5´Ñ(HáId„Ða¾oEâlw1¶•7£ïÓÈáhY³xЈr—ÈãM·èyªA39S¢ÝcLYgèBÙ„*d‹ŽlQö·¶ å¥ÖÓP/ð…¤Ç…m¶ðòl)ÅÖIëRÆ`i™ÿ?Äž‘¦ôAû‚Y訌ó :§Íe‡»ët¹/Ý Òyüsnç¸ÛM ­‘º>dìSG¹&HÃP³ëÁe*p‹ûÖ:éê´v°¾FëòÊÓ«´}9àà|%*öC…‡d•ÚŒíàŸ•T¯»ám ØýžÎ¸SVØÝký‚q¬ª½Å~V÷Q< „×f±Ï -ÓC‚B¦´'ÌøL†HÉ?— 6¦û¶ÇZWzªÍjxAñ|õ+cs£hi„#aÝ43¾ü ËKÑZr?Ú'H:‚µmø2ÇèAŒÄ½ ÙešÐ­d€cÐM^ðkÃ^þCÖj#,®¤ž@DòL2Ëâ‰I~tHGÇ«íøJÌ€ó¶eñ Ô W`_qÕZ•bµ¬ -„"pÄpß„CH„æ I&Çd2÷LÂ)x‚ʪ­°*j±XEtJ×J]«EZ_=@±XžºY¥¨íî÷#>§(§Uûª÷TªÊ#tgåÛE Àíª¬ÌU¦O4E±Á]cD®•ix`FfÁ¬åøw0b(½”ÑU -Õ•Yâ®ù]sAˆêvjßfh€w@A,búÆx#‚iù”ðu+•ÝE¹†Ì_÷X™¯x˼U-ÆEŸW'šÒ_ë½@ÿØc˜e2b1(·ž h±^EN±Ø -¬`V´[@-k…¤b½’°nÎ_PëáÅéeÚ:øÿ60ŽŒ„š³Ðñ€èl–u-'\õåj|Dþme;±®tHÞGDæËª&ˆø´‡Õ¡D!ߪ¿@Ðá—M…Ä?Bâ=ër›äéΕt<‘GHß8šJd öÿlûM¹×žiÜlv>Swžo2î§‹ŽYê©!Ö;DL…é¢Å¾]²¡ò®†’±ŸÖ˶íRÔÖþœ¬f;Ë·¾Ò-rü®0ÌÛ첸ÛR}Ö"‹¨?5#ðÍmk+3§(¸¸­(.ÏõÇRÄÝÉxP{K$ ‘™ê~?uXÅ m‹ÁµÒÂ(U$C[Kží·–Il9v˜®ŒLæ"çF¿À«]C2qƒ.ÍOI®6Ž1žQÐçªxã É1i™QÑZxlµe_³Ì)O&uZõê±èCÈj7$6ôøÕã} âA~««8zXþ°ÜÓÀ¯âm¿b|®nÍ^i>¢]œäfQêë¢ùÖBchÈJDj^ðû k‚æ½]èr¥ou‡ö#I¸î”îšh -8ЂTc£1)±Ã¼ßüWÞ+Ë-Ë*îkxƒuße´I÷~€ßÁPEŒ:À¼±LR] &‹‚t-¬^à*$MŒ4-µb¼› îãÎB 迟c˜œé޳ñA9ZµuÿKíDÛ„§«§Ñù¬T}–•pËp;dz›…x0w -ú Þï7 ? r™©ØâŽlJÁ§ûÄÑU/è3B®¥K3hÿ f@ jçm1©¨Rî×–D*Øï¿pù–©ÁÖ“ƒ2ÃÿOö(VvÔÚ -n¨‹œd¦³úmMAOßëˆá¹;í1ÜS`§Mà÷Òú-a6 —)²ÂNßË›,ž£_ -ßl[có.HûÑ–%¶Å–Ø´+#]l‡åcÙ¶ŸÕã¾ö$ ÒsÔ~’&ªÂÞbâ~In^™Yö6-Šìª¸ÊŸ£–É/FŒRˆêa` ýŽçÛEi|à•o Îò$Ô³¬¥h:)=’kÇZvö¥6ãÁŠg|V'E´Úë;ÈRèûˆÊ¥^ýt\"ZWÉÊ -Y°»ânN'⢒¤ü†LiøKÑ[ºž!6bؘƒÿjnf¯$= + *™.Óƒ¹ÆöK›vIöchP÷*%Õz˜ÙÚ‰,1-ŠpGsÀ¨ÀDì8D±C7x†¡&µXš¸Ó8e¸!j5kLÌì4ð–Y›ü Ä·&Xqˆ¾ÏY¿LA)$•]€sÐ_g‰^.®[“…³Æfx™ ×Ì{’sÀÓHqÌ ‰® ¶Ž£¿Šöo¶“à݌ǃ KåúˆFa­a)1$Po×’×™DOÌѸþwèq?0$×°›ÑñÞ®xYZòNÛâ8$ôÕ8‚ _Ù$ý`‚l·cZÁ­6ŸðÝ?ÝȇY´+û0šH5z‡á‰”kQ}Ö!•±¾~QQüýº2ñ&P{BúcH˜ó…|ó7g¢zŠ9^sy”×lïÎ÷Ûïä­u^ÚA¥ œ;ÊRâckUó>®)’§‡¬vÜQá 8ä:oV¤cƒñ‘ÇsûKèÄö6û¨8#7Æ>^nNk·_ª<wÒ*>ÙÜmç¨Ú¹3¶§Ú"Ψ­ÏÅ¢lÝ`ª§ÂÖ D#ÔýÊîö”¤Þ£7W-ó¡#Ôã›óhŽöî<ãLÐqf6«Ÿ6@”ç0}¹þª•ÙƒÄiÍÆÔhâ­ ¶• ÕÓ›—“RÃ$÷0Ùa–pž’s–¬—T9¼ÛËŸú sJЫú’lO¬˜Iùw7±A8j^Õßork‰lèç17øx&Ôäe¤Üñ˜‚oáó U°¹d!SQ2ñïb+¬Ú(k…m\5†ôø/ÃÇÐ>ÉDÎô:G"DxA4 >…¹èXò(² â× 6b³Ü-X>ù»à®Ä*þ'qÀk’xOºäOXÚ+ë{è5íž| fP|~NE‰ãÔz®EÉy?¬’|êSãä-Æ2¢<“3¾÷}îñôÌ=ö‚‘`·[‘ç~³£´#l²é®Üt¥ÑG¾—P«§ûj_º ëœ_ÓÙß·,ˆúÒò›Ÿcñä®n±$kŠ“aš†³²ÁMê=U¹lMíQ‹÷"g¾ñɆ 5iЉì5Mó%7R›%q£þvÞLýS¹G˜[]]ÕãMÖÚÐê vKÍsàw‰ã>Èõˆq|Ç - 7épÊL=º³#.´®[ÑÌCjÏ¨Ž†^wÖUOl’Õè€Tv·CeúÚû¤½Êöè¶Íÿ]òŠj2Íã0–uuFfÆÕ±®ŒÊªè:„AƆ"E©*‚±S¤¤'_ -!„ôZ:„¢ˆQpt±Œ4»â7Ÿrv÷윽Ys›ÿ9ÏÅ{÷<¿óžFràù׃dË+G1 F†~ /b¶m1¡&ªÂ&x“¼Æ,Š ^ù LtZn‰·D<ót"Œ„7º4Æ"¨Ž»×£.9Ô¯¢m£-±‘6§ñIq¬5£¸±£•5.1gù)œj†‡ßé⬌¹»ã!0ýÊOD<%në…{?ns žˆ³þû±Èê8¶Y°œ…£F<¼p ›œárÆwPq’DºÃ!\ üˆíFìÌÆKA®´^‚…¡ZØÅ¨·ËØ èR÷÷ŠíœƒõEÝÙᥡ†l±a¨·¶«±óÑ?ÛøaZÐ뵘lt—U¦³tÅW˜zŒŸ…þ‡ðs1¶Ð͘iea1Íâ¿ï½§MŸ1ó«¯gÍžó—¹ó¾ùv¾åwßÿ°à¯ ý¸xÉÒeËWüm¥ÕO«V¯±þûÚuëm6lüǦŸmav¿ØovøuËÖmÛw8îÜå´ÛÙÅÕmÏ^wO/oŸ}ûøôó?tøÈÑcÇNž :|úÌÙsç/\ ¿u9:&öJ\|B"D¡1X Iä¤dJÊÕÔ´ôk™YÔìœÜëyù´Bz£˜Ébs¸<¾ D(K¤2¹¢T©*+¯¨¬RWkjjë´ºz}CcSsKk[{‡¡³«»çÆÍÞ[}·wîöÜûíþƒ>z4üøÉÓg¿?ŸxñòÕë7oß½ÿ0ù´˜6múô3fB|=kÖìÙs æÎ›÷ 4Å|KËï 5~Xí±p´Èâ%Ð&Ð(Ð*Ð,Ð.Ð0Ÿ–ù<ÍÔ6çó6Ð4^Þûöûô?|äX@`Pð™sBÂ#¢b®$ PX<1‰’šžA͹žO£3˜l.O I¤rù¹ñO‰SŸ#?UB™P' -•B©PëàT;üê}ú *~>22 -EOL¼€º_½~ýæÍ[¨ýý‡““Íý g ,vó˜,òcy]âzFÞþlç(§‘}FVÏPÕq㫪¹ØÚJ6©AÅ$µ*ŠH¹·$ÒÏΡ™ŠÙÁì`v0;˜Ìf³Ã—9¸zL2¼‚ÆÙžQC|¼QàM5ð±›x§¤zA¨R+Œ­Ô” k*x½ŠGj‘sH%Ť^VaÒݼ¼´û¦r~…LÈ¡Ø3hœã5$Ø‹»#òÈìú2›…'$ ’ó -,ª¼F‘P].ÁÖˤV!‡ØÉfoÒèDc&2`* ÷_'ǹ{# ݰw%{2º¥>ŒÙaQ“*Xª¯ SV*â5r1V/‚¸\‚¡ˆôä2·Òèd£©L9¸x~dºŸœà»E ‹]0ý -çôÕ^z[ÙAKm€°º<¬L!¯‰± |±¥˜tä³®,&p#…Aºe* ×aʽ'pBàré±Ì }¯Ü1µ·zw¾AçÁÒj$ª²° -±,¾Ž9°KÍ…| -—‹7\cÝÉLâ Sy r฾íRꈼ_½blØžÝÒ°—U¡KTáU|YB[ŒÓ3„@S¾ßJåãÛÓ¸€!‰Cì2•)gOç|â¥dgسò­‰u¤þûôºzw®¬2PÉW^R³d‰µÅ\=M4æ -æ Ðz•Oh'óˆ¦ò7Ã@önOPàðJ¾-äyõæ¸áV;Lg·]f¹Î“'¨R±”‘j†^[(ÅëòÄ€ž -y¤C”B I@l3o?å ÜqìM™Ãù±zÛ¨{·6ÀõöT©Ö‡Ï*?]FW^VÓˆš|¾.Gè2 T¡!YHl" Iͦ -~œ]ž tË‘·Õ¶Á:6^ìüm5ºÂàÃÕúò +Ï)ó”ÑU¹ -¤&GŽ¯Í’êÒ¥ÝU ¡>ILÔŤSKì` `›;¨‚m\¬oÀêœöÞjœ¨c{.]wˆ{]}Až][žUЬÎTà5×䄚T9±–"#Ö‘¥$-QJÖ™ -(²…R;×7µë¼îõ®òn^¢°§ìœ†£¬ÌÚaº:VžVT¥•áËST@e²’ &—«‰ -’PkL”l‚ëvw¬ØÙ6´Ô·ì¹U8{`>§Û5#½õ8-EʦhcÉ5Iƒ–jŒ ¯ÆÉ±Uxº(EU•¦Ê×ÃÆ–Ùu=X°U=±ôï¹uxî}{tjŸG -¹ó•Øš4Çaã™(=œ¨Gràõ(n‚Ë×âøqZ¼ÀTÞi–­ííÿvUíè‚Âɇ F7†¤ :&|ðØ¾“ôÐôĮȬ8CLNlG\nt{BÞåvx~T2?²]ÑŠ¡™Êíî¹?Öÿþí:éäB':¸ÚÿÚ û³¤§n‘èA¿øýSè+ý€Ëw.‘"nG%…÷ÅPBûâRBz®^ìML½p‘zþ&*ÍT@æ–õ ÝmHÛïÿ®hÿÙæØ‡¬ƒDcññì΢³¬&Z¤T_Wj ð5yI5•¢­LOÑ—§¤5«Èm - Ç ÇÒ»Eèâž/Åì`v0;˜Ìf‡ÿË¡pŸß;†Ï™Ö¾ØA΂‘}„ÚUÌl¦‡Kôô8¥¶SQC#kÔדtY”FUÚÕVErºAF̾!ÆÝâb7¿”ÏEŸ|üÞ{ŸeûÄ òöw¸þYÝ쓌æE‘¾8¦TË@VÔ4U4²®<7©I™Ii—¥¦öˆ“²û¸š‘A(êýRþí@÷öûƒý: j:¿ã8þ¸öÑÎvÚî´®ug–±«Ö*t´âE@EQ‹€*—r 럄B’î;ä !„rI†C@V@Å]Ý_³¶µÓ‡äQøàýü5ßù<ùnˆÒ.>—¥UßWÃ/)³aÅYØ/-Ry%•F·¤Þ2"Æ  InK/iÜÈ"‡tôY£{¦p—8dþ|´Q\ÄxÀi'6Ä©/ä©U÷Uiø¯5ÇÛgÔ§ye¾bLYª÷(kÍe“Í&‡\1qÜ(h©¹-³¢®ÎEv;w©‹Î›6 þ!5kC‚(x¡H¹ö@ƒÀÞÖm›ÕäNêþ&וôy UF·eÖaœf5n\+#„$Ê,—Û¾Èìá.µwò¢íƒA€ÈÚ&ç¿T%_}Ø—ŒY6"ès¦ ö”9G°ª&†¯jƒ æÆ¡ü½R9a²WLšé„éót~Ï-‡·mï ‚ˆA˜’õNv$ï¥&©ò‘! ½2p„¶0t¬{z$›?5Z uTj¬–º]Ä ÐÆ`9ì–Âtù& f,ÜùhâØˆ!á%g¾S$æ½Ò&T<6n¼cK /¹ó'z&¼ùbÛp¹Î`F*õ§8bà( @‡š¢ˆH3x!}'ìŠ: yo8ÄI™ÿP&\ø»þPùËÁ†{ÎCÄå@RËt(³ÛéÉ“‡ÊŒ*rH1ðµ—¥‚&ÚäÐd³”x'¦McÄŒ¨û`$fü >”ûm|éSÛ~䃱Ø•¹$Òx0ƒmq]Pª­e&‰ iƒ#ŽeF6AWB~’œ8‰“QChiëT´Éž <|ÈÚøó¯ö]zîÚ[u*¾nz!žb÷gôö9òÔ’Ár3lª³q X«r3"· © >’’4SPh=m@ú£áÐA 8 {ϾŽ+\÷Ç•--íFŽÍí§Ç3a™£@ì4³Mõ6–;Ò©'¸Zûª–8JÔÆpjŠ­j6 ÛDƒzßQ0°'ûåè®ÜÕ¹=Á»;QÖéøv…ï„€ï(ÔôX¯™ºÌ ÖN#ÎÎ0-z¢“¬#¹‰}Íœ–2ŠÑÒ¢Èÿ¤ûã>ñƾ#óAhûÉw¶•8Vw 5áC°/[Àr«:mU†5ÐfÆYéýÕH´7H‚žìÄë)N¬ŽæŠ6 øS PÅýX¶'>ó}™<·¼5Ó½þe©~y'NNdtOäðÛÝ—dô‘jM‹ ¥§Z±ý̓x3ÁYðÄA¬™dE›ÈCM&ª-Ú€jG ÐoûÎõž¥Åßíõ>Ù’m\©-ïÆuÍ ÚZƒçÙTßÙS#%º”xG“;‚Ñ¢í8]ã0^ß`#ê‡Hƺ¡æ¨š¯b~Úºãnø×øÙAóë-9’õ¯*ºV÷¡ióéTR8· -–ö`'ªyM>¤ a¬ATçm#GÑ’ZVZãÆËªÝ¼ÊETDèÿÃÖõ‰_l ®}²mð»Ï’do¿8Ëz¶»”¶–PÝÎÂcò)¨ÙÒVdøjGÍT ³*„ì¾:YϪ ¢z*‚Mìò†SÀqKýPÔ}ëüôW7–öKÛ«Ov*Áo°ßm;C¹¯zœz­qít}ÝJ¦úöe¨òV©l±Š|e¡–ryI½t³žV2‡j)žk¢Íbè…³ØÖh ‡ú“‘‡?ÿ|º«lIl»N^Îmz‘Qr}ý\Eå£üê+Н—¬•ÕÝ«l(¼[ÕXp§¶1Ù”¿Z‡Î[m@_XiÄä® ±Ñà¿üp“övôfžyÍ?qé)œ¼Ë?GZä3Ã=•‚@WƒÊÛ =T³«µÕê 2lvsdÏrÚ°÷Pì±¢¤Þzµ×E    ÿ÷†Þ Ç€—qæ5œYòTp -yOC¼Å»Øæ”ÃRéeb &Ùìl§[GhmÃÃÍÔí¶b9£ƒMð˜%ó”>]8îóˆ!pÒ€Ÿ~æ{áñ’g¢¬ëkâlÂ’ðBÇ4ï2?È©VŒ±õžn¢ÙÙI³Ž´Òí6 -Ãe%2G-8¶o ûûQ²€¥˜P6n¾c÷ÀÇN/J/þF’Q»&= }-9ß>#,ê ò+å>n½ÎÃÆ™,ŠÕΠ µ´z,Í>3¾'ЄԒ´I£åþÍÿ6ôM‚´Óo$G‹¾‘¥×ÜWdáoËsÚf¥ùܨT:×jGyh““K²Ú»)k[‹×Leøû›Ù“¢ø†#¾ceA>VØlï ¼ˆ—–„©Ùo¤iEÏǪ¨2p˪lúMe.{J~Iì—T©½"”ÑCƒvn³c°‹æ5µ3ü}-¬’"ÃhšI'Ù,¸ÙþkHM"Dö[YjásUZÕCM:f¥ïDË‚ö+¬)U• -Ÿ²Nç–aìÈaâ½Zfk@ÞÎ é¼0›,œî"IBL’tr³AlÄ øˆ N9õVžrñ…:õÚ#ÝQôª1ƒºhÌaÎ x!c¹ÄoDªÜjÔ€E‚éã½òš_ÀdLqœi&]8ÓN“LµS¥“›íƒNI’äSÊä‹/µ)WÒï˜ÓÉK–lÆœ5==\[jTv]½Ù¨@(…WØKñsØm!fwO¸)œii“LÑÚ¤¡Íö?é‘“ª#¯tÉ•OLÔ=ë?Ù¯“¨¦ï€ã‡9Íë,oÚ9̳³´tž‡Ú2ÏâÀ¨UAÙÅPÙ×@C6²‡¬„-!d!ûž@ BB6²BØÄ‚QDÁŽˆk«§ãop¦Ï9ÿç4Ÿû÷úM§Üre²—½9ó¾*¡ßÓš›†ûŒ—RMð‹dÔ0WÜ:ËrçBÙ5*G1GëRÎBd; âib"P'dþh8^øý`Bõ–5yÏ•J\ ž¡¯L¶Î„*nWÎ2Ò bÞß­¦L¶)˜3tç*E&' ”sd¾r* ‹Þiˆ²„@sìì?ûâ/=:Vùh,~ß—‚½;{–´ôu15®àÛÆkµ}6¸EnA;øýxo‡bhéS$uÇ,N%œÃÉ•³8©jª· òã'€îè™×q/¬qåO=Ç`›S)ˆÛËçг ù,÷T™Àì­×¨ísïÖÞ=@o5’-zê$^ËšFk¸3(l¥UAä?5(ã€!ö4°ÎÛv¹ò(_uw1­fáf:Þw-»}hâŠXë©ÓKvz†q¶®A²›ÙOñQŒÔ NÏœ@ë:&‘zá$B/† Èÿ$Gc€*î8è?”ñÚz0û;ß——î]/ZZ‡…V’É#sY]°X&q×zlK×NC»™ìd P¶ÂGÚF¡ê½ÀµŒî‰ÞœÚ=¿¶;Îóø³ÓèjéÚ!,çz“5—Ï¥…+D”`ƒŒèCª°^ŒíÁé‘n‚á"áNJ£“f‚9˜0 2 ü˜?ÝóÌñÙâ­ßî}øÉ‰Áí=y²­èΣÆòi*ùë‹m„¹JnóL€š„÷6M $_…še Aœ (êü$e­¯EUí£««½ È€áÓ^9þ±²ôþ‡þ»¢,¯>I–oGæs}YEßHBWÏš—‹hÈÅÊÖ¯®ÕµÃæ»ê朚YTwÕL3¯r×S1Mà—O“eS-ÂÒ)*d`ø¿[ŸûÕ¡û¿ùhhûÃ%ØÒýj߯ӣ¥„Í“õè\$|­[«œX½RK©¼Ñ@-_‚ÓJ—Œ’oÐŒ+×›™Å‹8VÑ"¡õò™]ø?¸üå/&—ßûõÈË÷ÿ¬ØÏ{’˜¯d_$]B?É,kÜʯ©¾¹¡üÛ2xÉF5¢x½uùn#ºðsé¢ùâ -[°†ÁÜÆâòoðy«DÈÂs?ÿ™íÎ{hÀowóÁÇ1,°÷8 Äf L?ßðCVAÕ÷yE%Ï -K.?)-¿ô¨²²àamUÞÃúš [5¹[ðÚœˆºœȺìMtýùMì0¨€ðo»?ásÐ}*ðÏœ|-ÌÌ.Ì©Ü ×{JZV¸u (álIneêCô6“¿¥Ëâ%ñFÆñ‚Q¶×æÁÈìn”jÌÐ;\Mƒ{“’w ïÞ5üŸ6ˆÞ4Ýq@pê$Í{Þ{¾bK”‡XQVxÕ œ&á\^fÓôA{ÀOëò’»­ã¾m§ÁîÆÈ.”ÚéBö¦ç0b’· ¼“±@”‘ħ/¼d–?”ä4mˆ/’o -ËÛy0Á£wõA6kÀÇloáŒzˆ=vVätbd.RãCô{,“Ç„4¹¡¢˜]@°ÓГ zÓÓ4#÷¥ìLÙ#yüž,xK|…}]XÿÊCÈÂ>ØA𵲇Çi6‰çpàDî1Œ|ÜŽÒù,H£ÏˆêóêPý(@c‘€Ÿ Äii@–žûRqªô±2³ñ[eaU^ÈúFRÁ›6J§zšµ!Åäë` {˜ív'…ëà ½¶f…­Qú€mô)0}^(Þ6Rc$5 (Òr~P,y¢9ÓðwMî¶:Ÿ¹¬(å^“Ö‰gDHõD±ßÏ¡yØl»ƒÆñØÈ"ß0A4át!5F’5üblŸŠ· ”#@šœ -”)ÙÛšô+Ou§`÷õ™Ø;ú\ú mqׂªZ4+‡+'Å8ƒ¿‡bqu2Ǭ¬NÏ ­ÇßG‘†t$í„§â ÞèƒôFïÂ7 ÉG€,)¨’³·µ©ÅO 'ë6ûÎbîögSoö¶/+øWu²°£÷ˆˆ–Q.ml°½Íc`rjš($oQM -ˆš0¨rIF?ÿiˆ¢¤#@ž˜ÔIç_éSŠžõ¥×>8Z7g‘W-[—ÌåÜ«½ J?&Á[Ìü–1Ck\ÕÖ°B†"ÌmÑ„;[ô¡Žƒ - þ©¡7q§áD -Ð$fýØtù;SjÍ–%qÏzޏfÏg,ÛË;¦-°^¡Q`-}"Ò˜ŠG÷HºÚüvÞ—- ·35Slº.ÔJ7 øoÉÃ@‘ þÅ5pŸNgÚmwº³ÓÙ±•Ù™mu»£ë±*ꊸ‚€Š¹/#7H äNH  Ž@HB€„\ä$77!áP¡ˆŠx¢EÐ.øo´vŸÉ[öáóþßOâ¶©8—¸¦¹¹bD>±†æÏ÷ÄßîΠÙõ°«)Q‹ðÚ6AYÀ-ía×WÚj™LGU-o¸¼F7k1FjÒ@3äééÆ\=Û‰ï <ä„'¾Š#GÞ™¾?±Øùu×êߎ·oí à¿q¥¯xeP I÷Éøh1ênzY=‘Wu MÏÇÕ¦ÝÌg¦ŽÔ¥ŒÕ'Ý(e]-gCGi.Š];^÷üå‹É¹Ï>ï~ñ×o”[?)û µoOÄP^žM!=†aÇ à øì¹tRæl69m^œrUš4¥\û O…Þ%–%Þ-,O¸]T›B‹›*sÉ;ãŽ?MÞúôw½+ŸíPmîÜÓv{Ö‚C)›§#H뉘ÕðÔÜ¥¸ëO¡¹)‹iˆ¤…ëhè£\LâC.á!ÿKˆ›'bç‰ù1÷ ‰Ñ÷‹HQs¥.™´ÿá·ýwþø{õÏŸï‚/÷2À·Tpü øc6ƒ#s6"âÓ×b¡I¯ )‰«©iñ+™±Ë°ÌèexVÔâzä:;â9þ –ŸsåYAnØS²KúG?ùvö“O›ÁŸÝêÀW{ÊÀþ£$àé…þça øR*½º÷óÕ¨è·É1‘oÒãÂ7²â¯lÀÂÖá ¡ëˆÄ˯‘WC^£¯†¬á ÁkhÐñš+óð€qf7¨ :êB|ëJÈ+*þu}Bæ2#½P“Cž«ÄѦˋ˜S%eÜñÂ*Á‘Ñ:Šg· c¥ChÜli·çµ)á -Õ`®FmË5©{òÌÛökï ÿ· ôÿ5„ÿ·!>s™™Œ^ ÃÈsUXÚt™9UJ厓«šÞ7ŒàYΆ™-PØß7 $*gƒz0W«î‡›Ô]yÛ÷¡él`\:XA>€s9ø7<î5'&c™E=ffÎÑÓ•DædyiÃx M8ZHë¥6°.¼à"4F½m IzÙžó¬9ÿ°)©d¶1»ú źŠF+)mjuû@a½¶7Ÿg°âDfFiUcÔR¬ÚÚŠÓX„x­i»>6|XþGÏß/@@+$âßâ k¯ÄaÙÏÅÑØQbÑœ0ƒö/¯n‚MàÒ‹Åí]%µ:c!רËZTxY§ §êjq4ÖF‚Ö¼]XÎöywÀ?çþÃ=5a'ïÎv:î´u»]»»ãŒµº¶u=©¢«,‚"È@n9 „$$!„+äàÊEä€@ÈAHGBB€p„„EA.±"( hAÅ P×ë·™±Ó—}ÉЇÏûw¾ -oPùE¼i„­·„ ×Ô‘9Ôq‹Í©¬[ üq)Q<\UØh.gêºY}‰°WS(35Qû•EJÖYj)zS µÝh/PókƒÈë¨ó<”^~ É'ü­Ú?aS„|¢‹ ¬´AÉ :8}F/¿RŸ+¶ˆ¨Ý|š®­Œcha -Œ 4±Y^¬´H‹´ÖjªÖZU 7 -Ûûìõ[C­ç1 ?w¨<}A‹wØ{­_ÜKýyÄ“ŽðŒû]Pâm¢p\‹+¿Ø-¶ T酟ˠäðŒRVYÄ[…tõ¯X;À-ië¯(iï³×Ç7[Ã9GPïq4zøgè‡6ŸØÍÎ@ø£ž0Ì=4kÖˆ \ÕcÊ-M±ANTij ´Ê*†AÂ+7V— Í|¶ÌZQÚ4Àf¶°mýl†Þd/ ²5ÔØÄŽ@áî -šÝ½Ö#ø]‡wÌ‹ž€¤Usri07mN¥ w¢ËŒ|­®!W¥’Q´RQI§°Šcä fWlerUôòÖ:Gg¡³õ&{ýÖ q? -”n. ÅÍ ´¹½éòŠ|fò0–rg$uÍšB¶ö 8†6œ¨¥9»A®$µVËŠ:¹bVoi ÏLŠ,EUŠ‚Jí@!Og)ªÐ›íõ±áÌ@êv4œ>4§=A‡[À+£gøÚ $fy4"nv,9r1™ÔgJcë:±5J-AYÛLÔð -:J匞Y¹‰*©îÏ—È­y­5¿Zg!WêÍöµ¿6ÈήÎ@çâºÎ@6,ÁGüÃîÎD†OMBÓ†F`ù+¢´Ù˜^-íÄ+*u¹j¶š¢/i¢u‘UcnC¥9K)ëÇ7hú³êÚúsÅz³½€ØÖ ²5È]ÍÎN ÝÙí­ÑÅûÙ°;äÁTd~>T©J”4& *íêo‡w‚Î}»Ÿ ïÙuóö?¿»¼zðXï‹cÞêçΑ’ggS¹+~™ô¥P2u.ž–÷3‚M˜ÆVà&³éãÄ*ä¥1Z,‚_eˆ“¯°%I#\iâˆP–pYRg/Pphý×ö×ýßm¿sãYÙý}ïÆ~'õkGˆä•s4÷ù¹Túj`ùÞ…¼ÜX~>­3‹e¢n؈™¼2ø4¹"yª ›¤ñ'™‚ø‰²Ê¸q~t¼Zh/ Úû5èÚùõƒkùòêƒoþfÜØµOýa¿‹8ús߸FÓ_ø$“‡¦ç¬@3q˰<ôR ±€¡Âïd%Íç'ÎåÓnQéq·Š±³4fÌM+úçòÒ¨|¶½€îÛÏ׆·n›û|‹qý›íj°ëpå‚ChïÝ#È/ⳟ_€c×bQˆG0lÊ -û%=+á>>'n9;7ö^^^Ì=1ú.5?j©ˆ¹D'_X,¥D,–QíµnúòÓÑñ?ýÁtÿ‹/ÔàïßJÀžÃ\pø œò&ï ìw!‘˜×‘±©±0Ø‹¤”øgˆTèStZô*j€Ž|œ“ñ˜ˆ D„=¢bCáBÒ3‚WYx{oùÄt}Ë5›[ÿ,ÛwòÀéÀñ$ ¸Í4LBÞA# oaQQÿ…Ç\x„†¿ÂÄ…½Êˆ}IHÙÌNÞÌK ÚȇߠÀ× “ÖK’íeºúÙ'­sŸ}*_}Å;vÐÁ¾½$pâ¸;¡€ï™$pþ\,÷Ž1~ Á?$Äù € - -˜à€ ñ™! -9¡~òÂ|?Â}ÞSíØ·‚R—€éwpƒÿøá^@òž—·YH{ÊÆVDòrqQÉ"…Åž'VpoåTUÞ$ÔVÏdÈD×±Jñtz“t -Ý*›Bµ×M"{ä“i–ú1äÅúËèa¹=~oø½áÿض†R[ßÖ ó•Q!ïñq/¹ð´§e˜ÌUV.y™VH[(`²çIåÜÙÜʪY¢š¼Lå 1KêWiÕÄE2®c¾¹•9‡£rg0]¢«µÙD•¬g©Q])·¨ÇÊÜšd¿Æ]ѯµWöiMU}:oªç@õ:£vN¬?ćѱ£@”œþF˜qê?±Î.ª[cTà—(˜öy‘1‡oçM70Å“h~÷8J¦«Ðh.#lºQDŸ®¯Ò­·W¹õF”[¯­öÎÇ3j;àEïÂØ#@’¤ÇO¾•¤<ÃÊ~œª½Ç.;»ÔYKžïhêœk&ñ¦qtÉd=·çrT}¡J£ª°ú+zPU¯ÑX}Ψ®9gTÔözÐ<šçйð#ý€8:Èââ€<1õÝ)y/ä™%O$yÕ„ðÆevUëm:–>Cnæ^!R$£,Å@½Pë®í18Q„r™ Õ.³ºÖeîF»ÌÒºs&o|<°#¶A„DÝ1±@q4ù7剜_•éðÇŠœÊ5yvATÑ<ËÆPÇhxî0‰,íÃw*\µ^j4¢5]­ÍªD;­ò:§UŒqZ—WÝs ÿ~÷ÂðŸ€,â0PDÅuü‰·š¤¬gڴ‡ÚlIJª}³»âì8Maâ¸îŽ)ÔBU™ð,½¶QdRbÖnŒÉ&Á8ì¬ÃÆmpZ9.‹7>8a¾@ºÈÂ*2 -hc“^ë3~1¦Ü3ÁJŒE¨i¢é¢¸†ÒÏÆr!:Aj$“Uê†^Nà›ÅMr›§·s!ˆ…ƒìÌ&§•Ñä´xtþ~8踡ۀøÈ>Ð4á‘@sìWóÑ´Ÿm)9«PVÑ-kQŸ¶¬iPŽ¢8ùõS×Y©šFRÉÈ4½°•cæ4Kl] ÔI°C4<Q •à´zãáÓsàÙ¤!{2ä Ð……ÿfŠNxfKH~èJμëÎΟuœF^4”àÜŠÊ«ÍÑp¥rf³J@ëг:ºÌô6‘BRBd’ÅÑÖ -Am-[[‹Óêþám@¼¨ChØ[kdì†+>é~rÚÂ`vöä¹Sˆ!ËœCƒìÐËkØÝB¬DÈÅ+Y]d=­“a&Óø¶JD ˜„;Dlw؛ɛ7cû_Ãsoò æÐ` - y툈zÜŸ°6š’tk4 6Ö—è³7Z åí*Š-–ÕKØ¢&%ת#qèfB׆cÊ¡¦Â2 GƒìMT‡ÍÂC?€žƒ».ÈX¿è [Ž‹ZžH™¹œ‘5:˜Wît6è­¥d™¾’ÅU¡EôîFIҬŋ(¦ËZÇ—ØkxZ¨šÙÑ,‡­žé´z0=¦ç >ø=PØŒ~„?‹Y˜9=1žžy~4§ÔÚ«t–V$“a¨¶i°Ýx%Aé!jäLs¥LdEÈÔ¶r™ÃV!tZQ<§Å Ës`úiÀ öß ,{߸÷?¹xÄåÚQ¿ë·#/N§e¸Æ`%º‘‚zÉyx+ËUÞÙnGññ–z)ÆxVY­'i‘ºNC©V`‚k•æ"Ý|FyÎT&ó`yÜß¹ßî'_ùíz>°ûÞÕ#»n/Fï›\LŒ¸‘zÒ2•ïËCs/)ƒ¥Tb%Ó‹¡\8y¹£U ‡èšBˆ¯+€ú|Ȫ/°¸õ…:ï¶ç ðß{7ÓžÞ¸ÜöhÌoëí;A[¯®„Œ,ÇÅ8NœÔÞÌ(ÎäVÓ'O7µŒ•±—*¨ µÜòœ¨x¸UzjˆÞ;ÄWdõ(aCV%l`P™ãPyp<ñ¾õîï€}çæ‘ÿ^¼±gÓÕ•€ÃB‚kQG5wãaÂ…gèóÕÍ7òpØkÅ­¨Y¥lª–ŸhdŸ¾ÒÂË¿Lä^b‹²/IİK§(ãâÔ€ç9Èw} Û¿yÝ÷ý7«S[¾šZÞñÝðºßh=(Lý0ì¸ð~l.}-±”¸’ZƒYÊn@Ý."–ß@´Á¯ÕP -gèSDFîd{Wöƒ5ÁãdŽË¸ã:nú8ä ð”¾ÿо\¿¼éo3K>^÷Ý=Ý»_½%ø%4…ö$&ø8±ó ½ªj%·¾| ŽƒßA -oÕ´äßĶå^Ƿî‘:²f©ÔŒ&íä Ÿž6-ïLÖzH|ÿ ›ÿúbð«?ÍÝùâÓ‘uŸ¯¡—[}U¯ö -^ÆÐ^…¦_Æåb6’‹QëYˆÒûù¨âU8úÔ2“¿Tݘ³ˆi‚-4á3ï4Òo“‰'çiÍ©·ºZRn -Z“oÊHÞÊÍŸ½sþóÓ¹©Ï?}ôÅçöW>ߪÞmÝÉ¿7ˆ -‚bˆï#’1¯e¡ž§”nÀŠ ”æ?‚#s"*a÷Q¨Ì{u5ék µi«xtêJK}Ê -“¼LÿËjHºË÷ -0}ùÉì…Ïþ0:÷Çÿ³[gAM vÇít»v§3ítÛÑ™vZuÕ®]ïÕu‘Cî;r†CDD @ !`BBHHBî„r;€ \BñÀêZP´X‹ŠnÅ‹uåß Ý÷øØ‡}ø¾f~/¿Ÿ÷¾ùô76ذQ Û÷ˆá@BbhŸ\ñ™Y²œu¬èun^þR!êø lQöwLÖs6ó—±H-M{ÊÀ#ŸÖRŸpËRžÊ’KÊ“ÄéÖ7k׌MürMßô'¿°Áï?ÕÂg›Ä°kúS!"œ q8@&¡ ™ûþxæ±w¨¬¬ï‹³3–KsÒÞ–G¾!禼®:‘üš–—ôŠ‘Ÿø²® á%§àð’…X~Hç<¯é½¾vmñWkaýïİuö~A…ÀýDˆ -(Dh>¤FåÀ‘Ø,ÈA¤C~ÐI)€óžo|J"”§&‰€ª´x ¦Å­Ô¤Ç®03bÞ×gF¿ç}P½ã­±M}ô3-üz­þð[6|þÇ“°oKî@Cø—' îë£éA©’¹¡ PŽtD`#c¡Ô{xÊ¢¢ä˜H¨Š‰jl80bÀ÷!sÛ'À<¸jc¶/q/Óƒ@” Â|ä -¯8ç-» ý‚QMxFeU>¦ðiHRæ|™Šý¯ãÞ/1òç°ÖÆ{—ð.ºK<[Ô'™-<+™AIgPW¤7 ¯K¯¡ÿ ÿg–×ÀŠÞ -‚„½ N ÉÑŸH]iDç¼åâÑ/XÂ"IY¨æÑÿE–°æ‰Mœo®Ô(¸‡µ -g‹]âYt·d¦¨_6]8ìí‚|ªpR~ =)›ÀLÊ}¬UÃz¨ÚÂÃ{@† yf4HsRAŒ:öVPRô¢¾‚°X[CY qè«Du*ܹr­à.Þ šÅY%3X—lsZ~= œ*UN^išÄ\U^,¾Út{ÅgPç5Ôy ÜÈ- AìEr(Ó£A‘²¼ìeQqᯜ°È>IY`ÔÕ<<)`?¨”ñî5ÂY‚^2]b‘ßÁ¶+§0gšn£Ïªn]T_Ç\Vc/«=¸Ëš³%>¶×Àöx[@¿šýA…Œ‚¦#I Ì=º,-B- ñøg ”ÊVmÍ<Ǿ_%áß­P‰fÊZewJ-ÊÛ¸Õ?Š{5·Ð#Ú˜ í%ì„ÖSr©y°ô’®ï»UC½× ˆØ Š¸Ý >ìšäФ'‚úXÖ÷Ê‚‚—Rlé³FyC§Ï×rØsT¶²I†¸0%Å­˜Ò¯ÙÇžëóŠþÝŒ!ÜW’ªîˆiÌë õüKÌFÙM¡vWéZ†ÈfC?±½­§¼ßÜ]>jq‘F-Vò¨ÅTyÎl ø¸?d^ƒ6bè#¿Sl0˜bÞY‘ÉK–¬£O̹ÿ4¢KftDòuE5s¼‘É÷px²!¦DÓGS·vWL•v‹“|Új'»mm•›¡ÊcÕUZµÕ£_­¼EÈ&Ð…mcÄ~0Ç õÖ‘’øÜ™™ñÈyüãW퇿kž:nv}Üþv ásv$4¢ðy4AÖ‹K磻°ž2ùŒn¶˜ÔÅÏÂuJ¨ÎüüÈUaDG¥2¬óª2´ëž2ôqwQ؃®º€RÛpaÿWPgòõ’ú‡M/ú ×uL˜nmÖXíiœµ³¬™vu:3éí_0%yNä¡I/ˆÉ´Q:‡8šÌÇŒ¤ £‡%bäPž,|ðTvè@™¢ûÖɈÎ^@qñ†«;$¸¹ËÝ8ÎîáBOGhO©ÜÛòtºòP_¯¼__ïäò—úrøÛ:!͆Ûi`ù=lŒ£Ài_(x1÷¤¥Ärìa q²µ#Ьó¨$ÚØÛÆ¸¶Ç ý˜ ˆ´$vº@²±°m¶³dx™‚8Ð$ö F{€€¼8ä -;»œ("½Ëa¼¡)^STÉó±çSæ• ¾–7‹½’ö -Ó˜>ƒ¾ÉŸAµdL£ ¦búcè!Á3쨠÷bUÿoø³¥m`kRŒ´S2i€ÈÂí@Šò1ÒÈ©lìr²ü>>›ñ†^˜¸@=Íš#KÕ+¸³øÚ´W¸+ülcÆ4æ–`}_8…ꎣž‰†1C¢§¸Q~uÀÖ6¤hxŽF ö0…l? È µy´;Hñ ¤!?ò’°Ë)|ò»$YÜ"3?ižVœ¢¡œå¾Š­HŸ!ÖfLãë„SØë¢IÌÌIt«xÝ#ÁŠ{ðÃâv¤MHù³!ý¸!ÈÜM@0‡¼cér¬?HÈ+ñØe.—üŽ%f¾NÈMžcœH¥–¦Í/dLk„“„ºÌ Ü ñV-Ç<–¾ÀöIûðÏ¥„AÙÃØÁ¬û±«‚Ô=jµ2 !ÛÍò}ÿ…A¶P€t…<´dÇF¬d20Ëé)ä·©Bæë$9KÃTr_ÑKøÓ”rádlMæ¡N2ŽÿYöÛ’õÛ=€Èî$d·Æ>“«Iý9wÈ«û_GÛ ²7…ë^Pú„6Pæ…ÑÈ%„¯È¨˜eaù-Ï\`ËXšÄÞLÜéŒ)jYæ©Fú’XŸõþ¦| ך3ŒëÍé"öç´’újroî/”ÞÜ&êꀣmàjÄv;!ÏÅNz™A1ŠƒàÒ -1¡+¹dÔ)“ôVÀaÎsÅìYV.o*¾X0N?'£TgÄÖå<'ÞTôZsŸ{óZI=yw)=ù?SŸ4О\¥¯¸ÚžÕÛ …ÎÆpÊã¨|Ž€*ÐN‡{­£‚?(‰Ñ‹ -Q#eÇMeX¿qåiÃÉJѳøRéSF…¼‹z9·ÜXÐFR¶’º”w©O”M´nåUzwÑOqÝÊKqOVgüþKx}E5åqcuÇî(ƒ:ãŠÊ¢2à‚ R¥‡^Œ´P)R @JHƒôC¨"BE0 A\а ¸êèg–ñnÖ‡}ŒŸ÷ï9÷üï9? ÷^Çÿ!HŒyY“‘ô”ŽÉz@-ÄüF*#ÜÂ1KÇóøcH9}¡b_ÉR×h2/s/gÜàõfÝ䩼 97y¹7¹ HóþßPå´ðÝö‰ça ÷±ŠçÅÚ0Ï÷Šè€WRXä3aìA *ýv=A)%Þ(¢‘¯âj*5ybf?ª¾¦7÷¯ Ñ+è@\´åMHƒ@‰2è ¾ü ÑÔPôã7€á¸]÷™ÇAPëeêýB<Þ*#ýæëÂËáq¿ -ràì|ôh%‰x…\Né/bÒzð|VVÎièø-¨¡ -5(n@éÅ -Œ^$͉°¡k˜ -÷­$»€ub»î -wk ¶ÔßdÎßz×ý ÉÝf¡ÍËîú¬ãóî0×û]1>“­ç£F•é)Ò\d7KheR«¨UŠr:[Dáòx%2Q5I%euÊè¤aYI+£‘tÒJ’^ü%Ÿ(G-÷¸i÷þl \~NúSíyäU¿ý“þ0Çß.FyëÛa‘ÃM𔋵d»CPq d»”&¢Ó؜ʳL,¢Q¤ådµœJÖÈ)­ŒBÖJ)d„BÖ‹Í$SCÙ ÀwØN»Êyß§6ׯ®Ó6/úýŽ> -q˜êôëJˆhMIîlÈÊm–£ðµ"YÄ+¦qªËÙ &‹WQ%‘+ë¤Å•mò¢ÊËòÂʲ¢ -­”T®“Êõbs@±©¡ÂÖ¶‚º»@³ÓžEµËoûÜ­Ÿùº-Ð~b0üÌÕÞøð‹ê䤶挥2'‘ç—rÄ…• >•UÎepKªyÂB–B‚c^å3ûäX–VVÀÐIqUz1þ >7Ðþ±ˆí·åO;Aë‰ïºö¾tûþé¨Ï÷¿Žùÿ¨†zöÇB;»›~ICÈ›¼†¼F-¡¢LZÊ,Ó8G€åKÅ(^“4—×#ËáJ‘]­cØz‘9 ÄÔ@·Y¤ö›A£ýÐî°ë}ßO»_\;¹û‘ÁÓê–ÞÏnäz˜GßPLhÛ%¬®ž%lÏÆ². Iå*\Y±²˜Ž¯«¨Æ(ؼ™X˜%m§K»$i²k’ ‰^œ-!øãBs@©i뱯 -Ó?Õl·u©ëØö·ûíÏô?ovµÔßô9ªÑ†¸wŽD«†Î%HúS2ª{31ÈÂâö| -®µˆ†n)c!š˜5é*!/µQ)Hjì&ª†…I AªÂ €Ë |sÙÔPsp¨3ÝF›í¦…>›M/Gl|tûĺκì¾>ímÛw3È­U¨¸ÇN‚ÓÓ‘¥ý98üżbt±,»‹Bƒw2˜I‚j˜ºž¯Vsã:†L&9çZ&9°&óÕÔÀ·^ MïÑa½áÝ•¿ÿíɤõêé‡vë´Oî¼ëeÓq'ÐEy+Ü_hˆbhÏ'“GáÙøDzCȺB(kÈÔÄAzE¿*f Ž5ÐÎŒÔ0"4ÓŒ¨Þ)ft÷ËPnjø´Xû±o]+o?سBû쨅手e÷ã36Mœ$³Poö(u -vŽ0™š†žÈÎÉ2`òRõüy=¹0^G/‰Öñɺz*T×N Ók¨¡ã·©a#3åÐá™ -sÝr%¨Û¿ -¨­V¾»ºeùÌÝ Ë´Ï÷¬œ?²¥sÎiŸê™Ç1ñß“¬ÇA¾Ô‡a„ñqèûÉI™w3ÓSY*v†œ5Ã(€Îð¡³õ„àY51讆toŠ4ý¨0xòQ‘9€ûírÐlµ|±ÓWÓ†uís‹¯^ïY§~m»³á•ãÑKwæ¿|NQ^ùáç#B‘óqÑés‰ç’æÒ“æi1s¸Ìˆ¹’ìÐ9ZnÐ0/GûÏ·`üæ{ò|ç®åùÎßÏó{úcn[:6-›º¶2vk-äò»oV´÷ZÔm-…FÇC £ûq²Ñ÷Þâƒ4F¥cÉÑqÆ´ø(c jÄ&†‹Rß•¥ž}ËLó{#Èð}£Èôù½9Ëû÷®,¯×W²Ï¼3 ¨, Sý«!×ÇVCúהּ}ؼªîþMüG¬è‹N‡K?z8à–üOæ.{—Âý`Ÿbc?ø[BD/b¢>býÿSç»P™àýoö9¯¿„°3)Î{þ©J<ý¾#Éã}Yw:W@F4« ÇWAZï­Y¦Û¾æ‚ý[hÀΪ8,žvàë˜ -]Ô#ÄxAÌ7¤ú‚Ì€³èòƒ}@aˆ ‡zZ˜`CÝ? ÂO-É"Ü–#Ý>6›5Ú·Ò;²Ò2µ"{·þ+øvM%°Þ\ ì-±Àyp·I^vqàìñˆÿ2_gQM¦gÇ9Ž=Ç¥=Ggè¨ã>â(*(¨Èެ²;²H„„„ìd%| ä ,심²‰ê@EEœRµVOU–‚ ‚0Ž‚l*Žöéw¼o®{ñ»ÿ_¼ïó¾„;‡CŒÛi zC¢g нýíã|_Hõóù)OÈô÷m€;˜Ý 0Èʃ]¡Ê¬Ž›_Y4õ®²(ùÊ"6¬ÒÀö5p`ƒì63Àq' Ü­ðàc‡~„3‡ƒ!Ê>Gý þ¸P¼€~ÂØŽÀwr±³+ ..€º:ÊÍrÜN@¾»šuᎅEùc ãô*‹lظ -­àÕzÜHûMQà´õ xì¿Ýþ²ÇÂözAÔ¾“€ßïDkW p†¤ƒNÀ8t8‡@`s$¶Ç@fkèa;P±­Ylµèöë€én "¿b HäQƺˆêù¡…±Œoc.᮳cºÙ‹Ñ÷9 Ñýܹè1î¯1“ܸ—ÜÑØ³þ?ö` vë€5ˆ}w²äG!¸Bj¢/ðY¡À‘DÅ}¦éˆ¿S -È+¤³Ô÷ñ5ôwqŒåØæ¡½ˆ»ÁYˆéæ.Äôðæ£‡x¯cÆy/q“¼1Â4o(v†7÷¿kHÆx®– õÞAûAn8ìx‘|Aœ -‘õ“Æe=ä1ùíıô.êóŒŽ$ó€³{5p°¹Ã× qÝ -&+ȶü0È‹9 †ø€55l eãæÔ„ßÄJÚtJ{’UÈK®?Mj>¡´"ƒäkò>ÒíŒG¤>EOâsE7åÚ™ôý+í©ò2Ý<àb )‡×ƒâØ×ãòä{ÅA¡øÌ1(Šrû˜ë·hH<=«fDÏ ÂøH:í™XÃNÉô±KS2jd¿Ðš2îSBïQº”w)½™ÝÔÑÌë´‘¬6úHV+c$«‰ið°í:ÈÄþÕFç-Pä½Ê¬¡,ÔþSY„Ër Þg¶€2m¤GŽkøq#(BíC²8Äzáý”Bé]NEz7³½•Ü’ÕI¿¦ºN¿£î ©/3†ÕÍÌau#kX]Ëy¢2x»VƒèÐ:ȶßyΛ¡Ôk”ŸÚ•Á‡?T†9¾­ˆ>ùk1`¢0)쉑Kx¤’P{ -Î]D#ú›Ø„t -J2:xÕ™íœ&ÕV›¦•ù³ö"ó_ºóìëê8CÚjî¶‚7¬1R°ÉÁuØÓ² -œ6ÁYÏPå»÷sM ÍÒ¹½>÷×Éj‚ïèYÊéÇù,BOŽz;[ÆéRd‰:½¬MR h–g7óë5¼V]=÷†¾–{__ÍЗósʃºbÁÖàc ÒkA‡í½ENßBv?k¼÷|¬ó?0ßj7Óá8ÞH89PE -í-aàoç¦Poꤜ«Y¨ø²B#k–å¢ ©¥ÙçÄ5Ú*a³¾\pÕP&¸k,ö -Eú|ñ€>O<¨3çKb½ôXC‰ãŸ¡Êm+Ôyî~Þoß›æ`›©–°££b<Ö&„ü£œŽ»UÈ¥\5Š9—´é¢¦¬lY€VÉ‹TgÓ*uÅ©ç ’6cž¤ÛdJí3¥ýFƒt@Ÿ“6˜cv®ùþµ`°Åö^GK¨qÝ;—š}¬^] -´žh;c;ØåÞÛH é®NÂ]/eS®ä Ù ˆ¨V«”UfëÐRe¾ª ã¬Î$¯7èe—MZYW®Fþ0W-ï3ªåý•|@oΗ† lÇ1a ØœªuÞü©ÉmÛü%¯]ÓíþVÏ;B=ºévïB\pW-%¦½‚™Ø\Ìg×åKE•R¢S£y*“ÊUªÓ(ϲѓíÌEÑ_0}&í7¢Š½9 ÄÐÖ@žÍŸ  -›SõŽß®\tùn¶ÍcûÔu¿O:ƒ>hpý{+!èZ#9úRM2¹¡œÇª* KòåHž)ÍÑT*m‘N©®6¤«šM2ÕµÜ4Õý\DÕgB²û²¬½9_2÷®›?Bõñ pÞÁòÝ%ÇM¿u¸mžøÙwËÀ­€ÿ¼îÒuØÖLŠºÐ@#ÕÔp˜¥åBA^ ’–S¨Tü—Ï:§:ßã8þ³·iQªi£mŠŠlY"ŽÝYÇYœÅ9Ž}Ï^ãPd9(RÒ6¡Å ê–D©)L¥í†I3ƒ %Í8‡îÜ)Ûûž¿îüñüûóúãóø|¿'r2$E9)yçó¥W âro•¶?(}S—Ó‘ŸÕ) âÖ© CO§¶ÏÇ¥]‹ðãî%c7M–5Y,íi±Yñê!iËãF²yÃ-_RM-Ï»¢RÌ?{9,¤èRlŒô|RRF©$5õtÖÑäâYqE¥ÒèÂËù…7  )¼É<Þ‘×) â Y4P¢¸SFš“5;Êî/x´W³û©åòg-ŽÍÍ^f7ï2œ®ÝâÒ.ÔŠ¸ÅU!AyW££2*R/}8éBfzì¹üc¥%9¡gÊ¥AgjóÄ¥:ó‚NwJC‹ºrgƒECîzuœÛ6W·ÏûzsëüáæmsûÚw©¿~a®ÝòäÀ¦†Ç¦5ÍôeJÉmNÁ qæõȈԪ¸¸Äk‡Rb®‘„WH•Ÿ:&*»”í_V“Ë/»ŸË+ÿwŽÿÅ_rDgßfÏIkU¯«†‹[4Pe 1vGO}°MOµ»Ó@íÙ›½K›_Øo¼ùÔmÏ•Úþ³Yž'î ˜Yw…’úðÐÄÅFÇÜHN«M?x=Gâ_sò·úb§º:“Uݔɬy•É®|—É­˜¯RA‘b/+ôÕ¦êtUÿ|¸J¥÷Í -¥W¿mR}Üm²øN§ÝúªW®ÆÛ)6'Ÿøºå´ð|Òð’„ŠcîÇ„…6%ňÓâw³“ýŠ3ï\HeÜ©’Ð%Þw_Jè·{Òµ¿§1®Ï GV(£d2*uTþnÐVîy¾€xÕ«E´ôé«5öš.¬ýÝvmy·³áéN²¥ô5ƒ”þÒ’Ü.bÆ< á‡>‰üœÊo“DrZ³c}[‹âè-¨­U‰”ÖÆ$rÛËDòãþ$jÓû”Ù {™.¬TBí2¥·æ/^Ï#.SjПS3°giù{Ý’>'ÃüOó£¿Ñ’ßqÜbº…´Ð_B˜]Ñ\~g¢Ý‘&ftä{wœ £t\ ÷ꨎðèlŠtïzáÞ9éÑþ)z68±D W–oæÏZç?uÎ#ꇗ«T ,¸ôiÏÊÓCÖzÒNFé<,’iöQL— ¾—°?ЛÛéËìO`ÓûS¹Ôþ,W¡Ð}àœÈuàšØeð¶Øy°ULúÐ+véûSìÒ;3œÕ$~­S'žÞÓ šŸj·ÞÍ#*eß©]”o^T,7Y+·ÑO“;'Ê=Ì#e4Û@Ó‘/ã¹rdbO†,œB“ô&ËùxÈúºÊóY¤Ñ¶ãh™ŸÃØu®ýX#wÿ—vžÝ— -C3ê¹¢L<»­FÜ{¨FÜx©A\ÖT:÷Ï¢¯Û´²¿š®‘|³ÕO'íŒw7S­xã [Ö×>!r¢L„:{NF»ºN&º“&%ž¦²ÉöS…»é³T›éË´}P<4ÓMt«éç -/fô¢V™¸wW•¨kS%*º4ˆÒ©ÅÊÇ¡;÷ µRa¾:6Ãp`«®F~ ›0àcFÇÒþÖn´uF¸#ÚÛ#ÅÁG÷!×É -E$Kœs¶@…‹9ª]ÍÐ0«û·”‰ÚÊDù âT‘eÊØ8çv,ŽÉÊ`X­`¿ ¤ÍÞðØîÚN70Iàî>¡É~›Ú rï>Ä™["ÅÂé–fȱ2Eá>œ±Þƒ¬w£Òf6uw•ˆ²Ÿ ¢¸‹ rþ3W)Ë•¡«Í Ä0Òâbï -¬WSà ã— $xé9À[ßLkp·XB¸ÕAÛ̾ݱ†{´c7$;ŸC#äí@±±!ÎïšMùOŠù/ âX/A¤bÒAh)c¥Šë5˜Ø2Ÿ -ãEî0Ó"ÁZÛ+làò xÿÈ­Žj=”ûœir›ß$ù5w«ûÕë÷ ù/îU΢rèc3úƒÀ\¡¶Ë嬃hòD1w!Ìßav%8Ÿî?)¬b|ÏùÀ§ÜôJ¼ëØ Õs¦©Í~S”6î$ù oÜë=ï¿äO¼QÊï3MƤòþð›øŠ¶¢Ad¦‰pmÄ:éà ×fÄ2ŒÉ·@H°Äq$øKÜÁË¡€Sä æYeLøT²§éµ~S´zîµ™7I}§t ¾’_(¿h#‚t™ !üÊõŸ ŠÎNuˆM5e­xÇuHðØŒxº1b¹´Cp ‡= È¢‚{‚v)ÌKìiÆ5¿)ŸZÞ¤w=‚ö@0A}êÿúNø7åƒPFû,¢ÿ`ȄݾrѦ<`&è©ÂOÑl¢‰X+m$9¬CŠ›’iFˆg›#Zd‡Ð“= Ì ‚WÀ§„5ÍúÁoÊ÷*o’Q+˜ ßñ÷~(üF{.ú‡Ú0Fû0LÿðÞw$àsDüš% lgËgEw‡:Âv/@¼Å2Ú¿©.úøžb„Öÿø®Ï°¦î=€ãl.R«¨-Õ:¨{R¥U¬¸p#V†$BBÈÞ Y$9Y’$Œ0BŒaod‚ˆ¢ (PW+^!,EŠ^íµÖëÕûïyú>÷Åç9/Ï÷yþçwÎù}øq“zPù!€ q©ˆO±Æè( úCTö=¢÷gd þóW o#ˆoŸ_F¼ M"æˆ#Q/IÃѯȷP¯(7bì¸5.³Í 0w~áýB~`9PXT?ú9â; Æ<ÒqÀä„ü— -…¿'jïp1ÿƘ0oÐE¸×¨*ü«¨Fⲓ<‹è£Ì ~¥L!f(#Q¿Q‡P/iý1sôkè9ú•Ø9†= nÀmqœíŸ©ÿbø5Ð_ 4!Û€ú¼?PÄìÿ(Æ}ÏcžyËJ MKB¾$¥¡gãsâf°ü³Ø2ÒdLeÕNµE÷ÒF£ïÓ£žÑc~cÜDϲº1³¬ì vKœ}76¹¾¯'Pì^4û—Ôc«Aê™-àB¸ßÇ”è} 7bjðŸ6Ö#§èÚØqr&î)ÞDÁQa«é÷c›™ÃènÖ ú.{=ÁéÅÌpº°3ÜVÜsnCüsnÞ>@XíÈ\þ¯Vïò©?,† ¼ñ“!tÇ»4äÞ×Ì¡Y%éÔ´”jH#uìz:~˜’C¾C´ÐoáËX}¸:No\{Â5ìMÞUì¿÷Œß?ͯÅOó+‰Óürâ3»þn ­w’Íó€f×B`Øç ²áÙÈ9µþ½ñœï›¬HÿY}Là”–pü©Šuö¡Dˆ(0·9ZÂMFõ5ŸÙM.ät+yí„&A þª° ?,ª'LŠªˆ“¢2ò¤¨ˆ2%´þ€ø `¬u²Mà‚ßð%È;¼ü“éÄš·¦Ísyá~ÓÆè€QC|Ð íì%Ù'‘bzjb7ÖÆ2²›éæ„zj© –R—XMþI\Iº-)'KЍb mB\@ŸḨOÚHß8ÖW ØèÒw~r–€‚CËþsñ¨ÏkËé 3–s¾63ÒÿQ6æÈÝ4rÈÍ6¢[!¶KÄ&¡–~™—Á©æäóË™…¢F•¤Þ"½D»]¤?L ›4—9.5²Æ%Y¬ ±=7páóHÚø`Ø1äï],ÞY1W|rÍTñÙMO¬çwå£÷eCº/0mÉ|Lƒ"ÖHÕô -Q§˜o\J¸˜hæ”KóÙ²ÖU¹‘}_žÉ“¸6HϵIÓ¹ã{nàù¸€ä î žOó¯O…?|ñ{ÉÁe/Ê­²U¯{P¶ë¶u¨''þL‡Ù KÀT«ÅÄ2…’^é¸fq¦ OhgóK  ^\ÏëR¤ñ©üQ¹Ž?&Ó -lLjüÉwxÇѬwÆížÀòÝ‚Å{ÿ«"ÐûYMвÖž\;TêwÓ}°Ë„ nʦDÖ¤³cËtB¢5YN7)S¸92½ Cš'NÃE¢EЍC™,PªGI‰£ò¤Ä1YR¢ ²PàÑJ [ïr¿¬»çÿYöý¹š}‹'}ùKý±5Uçvö” ´Y°§/ç‘Ηg1c­é|‚I'¥SÔCRšà‚Â(N‘Yd*¨R!‡Z•2¨_Éž*!hTAc2™ÔÙ¨pƒx¹3H]çò}=@‘ŸçÛÊ]ó_ÔïY0Ö¸è^ÓÑÕ}µgwt•# cOU™ E¹ttAVÁ¨Óô©JŽN«¨S²ÄrµY&U•+UÍJ¡ê†Jô¾ŽÊEÊ1™Haƒì´UÎ@ú5¼÷ÂóiÞêþ¿Òí¯kwxN·ìö|Ò¹Çk°5ÈçzCÈö¶êóûëÊÐ'K­ñáÍÔ˜œ<^Ÿ-¢j3äì$½†/K5ˆu&ˆ¯-Qp5 J¶æš’­Qr4£rnò˜,AmƒìŒ•Î@7dÀïìK[Ü>TluÙ°Õ}¢s‡ûëþ oýtxÕ•–`ߦËû*«P'¬¥¸°¼B2Êp‘¯5 (ª\ˆÕåR' ÏGÑ&×w5\gZ×»Œ^ßì<|}÷ç½ÝW´wœÞVÛPr9꘩{.³œ¥+aÄ%òÈR‹˜!(Pq9ù„Œ<£˜’c…ˆ952|N§<>÷gÞ83mR²aÜ.°Ì $åráç¢tóï >ÎSWV:ýrkµÓí>¿ùW®XÞØ}rKEGèK 2ÈØ€9›ZG@$UÓb¥\‚ TDc+Ø´B-hÍâ.YÄK•}©6(E[Æ%XÓ¸$.wBlÀ÷×y;ó -§OU+æÚ¼Gû½†î®r꽳ݳ­ÿÒšÇ7õüèŸÛy(½¬n‰€)(A=Ç®’¨52:¡ZÃÆVf$ +Ì‚èŠJ²¢]„¨¼#Ь˜EO‰PÖ)¡=@ò…Ð/qÖ¯ßÕ/qxÜãépoÈÓ¡ÿÞrÇΟ}=ê¼KŽ®+èñ3ôF¦ô NȺãÎ :ÉVMiçãð­ Ó’LC5˜ˆævDS7¬©Ú<›â†]~žQmP.rF/Pîåð¸mžÃ`¯‡Cß½yÝ—:6ýºÅ½âÑž%–ûGVg «½ ¿uDÐ fõ‘B)7ˆø^^Lìu)6úZ2>²ÇðÝuÕä†üc Ъh](VED$ -È*›¬²dý’/ûFI¡ -¢ ÖâÂŒb­Ö#ngT-.uGÜ·ºÎ0µn€ -£BŠ€„ê39½˜+rñ»þ?ïù¿ï“%ºRC]9œË»R·„¼zÇêU.y¡;w®;ßl˜`ýWâñqâþyâú-g¢á?cˆS­_Ùiç¼ïyÄÄíÏ=*³|Ë BKZä±ÿJO6>Ìbë›ó)MS‘XÑT*—4V¨Uj^cu:·ñZvc½–ÕtWËnêÈ`>èÓ³~íË´[ÇÏ9N;W/9çî¹'ž%j:f8ìéð³­#Âmã«ϲ—Lßâ¼àüviTv›:^Û–™œÖšÇ”·.çŠ[Wó©Öïdë1§u—„ÕV#e´‘¥¶ß´j•¥´¾“%?PØbªv$šk‰«õ4¢î¨mv!˜&Øí2yÒ¶šü]7tGºÛ8«¨›é›×Í Ìì/Ôt)£]º8IWN¢ «0™×µ’Áé^ËbvWrR»·s“Lûy‰¦cü„?øñ=÷¨øž*ÎÔË·åß âú/ÄÙâèm±ï‘ ±ãý$»ÍÞÎãKÍQîËÌ ž¹æTºÞLú¥ ŠeƒŠPÑ`F80+ŠcÉ_Ä´ŦXJãU$& U%Å ïJŽ>”=r25jä"#òC##âÃK›nÕD]=qäš=±§É‘øáågDåGwûï>ÑW!p|!Âݲ7=É^Jp|ÄøRùq¡^À„>(9!‹Q’…±( ÆÆÈHüŽÝÑa¨YŠ“1!¸Œ;±¶ÔÿBG.Äî;QõÈžX?2În ¦ÚÁÛ)~c3:Qè/¥Hœ&à r"ïd(|¡¡Ç!ó/1ÈñBá¼”ø…¡Ì?­ÇÈTúã`ŽÏGMGë bÏ ‚Øò Ö¶ÄjŒ·+„›}<ÓAw‘#`¬ 'p3‰ÅnI`ºÇƒœÑ´(ȦG@=# :dÏ ÂRÏÏòïy¨˜=[¼éØ9Çû}æ Æ¦½—­óß·¾ÿ„ V˜í‰|Œ³Óa¢½S„ðtä‚—D„}‹è±ÑHp GÊøP°'ƒÿň'úC>i>Ô“}¡Ÿ<9S|Pà6%_z¡ÌÝÜ=P5uvØ1Ýü Ä¡ÎP/r….É ™Ü™ÐIéHÏ@Z^(ä%Q| áæDðJ¹/Ü&8ÇX`ŸaƒÕÀó:Œ$ÏI¤¾á!µ÷aæ ²†x½ìa²‹;Â{KŽîÿ¤!ÎȈr…a±²93aÓ¡Õ@“ -eq¤åqýu1ÛSÀÛËyˆn-œÓ\°H°nðÀläƒÑfÕÅÿÄ裆XƒT?gˆo"‡ù¼aªÃHè i9Cé -c‚rY3aÒaH @FVÒ–ECþm<$›’ ü1Ôn&xÙ k¹àž&Áiàƒ}ƒV3æ ëêš#¬~Á{Pði¼æ Ú©aa5, ¤>. Aè C¸+–Ĺ!Ÿá<Š£2úÌ0¤DCµ:²õÉoe@XÍu€ ÞQÈS|pÏSà\€Ó(üÄn°»Dö{Ñ;rPô†o½ ,â'Â!Éï6@fÍ ñ§Aà cØ8ÄLÁש(äû _î£6 ú¼h¤¯L€j] -ä›™üÄhÂÃÔ'ê¸à#¿NôwYŸŸ3ò‚Çaù¢)X‘<Åä,“úa©&Fc42‹Q–ò)mkD±’í¦,’B³øgñ€ð´¤_Ð íÜ’½£ZäÝT§ü OÖ.Pü&6+%ƒª_eÕ- Ÿã•/ _'E‰µë}“4ßp¼±R4ËÓBoˆþ`,LÊ\jÖV°ûÕ[x½ª‚Å~±IvXÚ%=!+9§x-¾¦ì7+_Š_)[ÅïT-’õ=™YsSnÖ\V˜Ó/*G…5ƒf. 9sPlí8¥‘“P–øÊY^X#ðÅ*EðP‘.r`i^|onIª)k-ç­®’ÿßôEêÝÒ—ªƒòvE­ò¹ülÚSÙeõcÙM‹ì…¦YÖ“~Gþ>ãªò½¶Aõ^W—Ö¯;«”ÞÐú8"߇†UcPñ¾wGÃëøs-å²}«5á¦bcÜëÂå)¯òÖpÚ³×SÏ2«$¿kwÊ[4ûUÕ‡5ª“÷•çµw•·t·•Oô7T]úKª¾Ìzu¯á”¦×p<½×p,ctPY3dz;¢ÐÚ/Jý?GÅ Øë†Ê”C›È9}ëÅ~ÝkUa¥†˜¶ÉO¾^ÅiÉ[+h2VJï¶©në«57µ×^KÿYEsÆpI}%낺)ûŸšÎì3é=Æã=Æ£Úc®'ûþݨ6ÛÙ^Ž(²ö‹òùŸaS˜+6ÇLþX•4í}ÛËô7oçFyPë:}Ôo¥K’šVqî.[#¼¹´B~5wsÚ¥ìç {õõúš¬³ºÆSÚÿÑ]_QM¦iÇßPŽÊXPÇÆ2*:Š‚ -‚Ô Ò›R éÉ—’ ÐIh’¡%t!€Q"Ê‚h„ãx”wÇ#IY –fçÛoö:^ü®Ÿç¼åâßOèŽ×»âçˆí˜ERSâ"±»H¬ÃýN¨ý -8ÙáïæLAöȲ5ƒ…Ç,á2§õ_*\7ÿ»Â{û|ù޳%a?<Ë=õˆ?ÿ òJI ¾ÃäEÞ¢Ĩȥ˜¢Û…oÄwà:ˆ­Øäfì0E¦4à(²¤7”*ü²„°HÅÿw #­•jcç\ —Ø[À•§¾Y’ºX-Tylý­Ê×fº"Øn¢(êäƒ<¬Û=Å·?|#åjd7+'æT”ÐJ­À5‘k‰Är¡‡ZC¸K«&<¦K‰:Z%IO‘¨¥ä7Ù8é+ê6¤9‘³(8°.?b¾\å°ú­ì̺ù:·M/ê¼·ý, °Õ”]r`\ûsH¾×3A]iœÈ¶+üØ&vAB£,IF¯"I©rj%¥‹^N€Ê(ãP)õ5TLÕÑ…4=M@[ ~̦#½™¾Ãî3…+™}‘·Xlp\óJá¼~ZîþÝ#™ÿÁ‘ʈê’¸sªB<º+‡Ôš™)¿šSËÉM¨J)ÆW²Å¤2f=µê€P£zÈÌg¼dä1´P.SGÏaêiª!p2›ôo;Ò:ȕڭXª?f®k:a1ÛvzÍ“f7ë‡õ~îV…ï+9§,Æù´PsX2^ZŒ$=+AÄà‹S+È…)µ´¼äV(›­bòÙ#,^ò &/YËÈLÖA™ÉzÚ×ÀXäØHóf}g—!ÿ´ÆÖôüðŠ×mGÍžwž°œhsÝ:¢¸° 6ôÇ^I´K»(Ñ[^L -”0"Ĺ©1eY¼af>?½ŒœVMËä63®rz™\Î?Yî BËàr´—££s9zš!p2;iÎk#¸|·ñrÝ“ß[þaò[×!Óg=Ç,Æ®¹l¹×‚Þw³1øXWÍeçf Æ«VDÓÃË)ÑÂütL^Nn?«„”ΓÒ8™rFJF7‹q—ÅÊxŠ˜g°3´PrºŽž|UO3& ³S¿5‚ó·¢`±Ñ—ƽÆúŽ}Æ/®0~¬²_5ªtÞ<Ðá³··9ðh[C䙆šxO©$É_TN –²£r‹Ò0µ¡ó–»\åg'í9YÚåšßŽAóZð-œÕ˜M«¿O¬ÍÁád%LM59¦º…U}ƒv¹ú!â5Jº@©X Ä–¿1f¯GÁyëP°døÐ²pÄ.O8öûó0vï9˜´ßf< smOÁÙvÿc»ÎÃbÞ÷8€fZˆpµ(%-Ú$TÔ,ÍÚ,5ÓìK3Í4íëHDuQ¤P"KÙ—déZN<¸9–ÇÅå\—ãv²updi“ä}Ïý{žïóú>¿ç÷Ïçýûþþù¾QãІ€ œ– `´á>á7«ZN‘HM‰ùí$RåC©¸Ã†´ãGdaªî”øÙ+2Z‚ˆ±BÐ'pÁ™Ä‚p2ñS£ š¾úáHr]ŒŒ™¡°Ì -Áj· ¬›€yhðôCÓ_œšãƒK^Þh³êàybþ5bþ=©è‰´lО”†±# p¶QÁ•,Á[üí8XàG*èc–‚íÁ¸E_ùOó¡ãD¢NòC®³VNöš)ž¨šêúi³Ñ8ÍG§»á„U;Ûˆù·‰ùÿ&‘²_’HfPHZŒ!IñÓ!qÌ"ÓáE‰„¿mBì"Ì~>"íÁpðg¤#½!åù(OhgÃèè†ôÑ3aã‚ÕNÓQî4›ÇNÁBÃ8kãC{¾-„!v,t€’ê5×J‰ d ¤ù@T~Ù|p7‡‚µc1˜?‡#úHÍKAoí*áV¨© ¾"tÓ@í' Ó@ìƒ P{™ }dY¾7Ü` -bƒí u€:Ò ZŽ34b(µ¥ø@¼,¢¿…€¿i!¸ÛÃÀÞÖ¡%`žŒDô¹(0~¡‚ÑNýáô¿}„ïôï â‰úG6ï9Öý?CL ’ [(B [â=Ë q.Ш= 4ûBšqq„‹ÀßÞî%àˆûxØg¨`]¢yæ=¢;£{¢‡ˆ5Àã‡xÃó5,k 2ý)ùÛBlÄp'˜˜Î0‰\ Wx@cô…2;ÒU ._ QM;#ÁßÞ¸Ítp[à\û6s˜ýŒùÝÅúÊþÂêcc~æ€õŽöËpþË×ç’çGÒÏú{˜ÃœÊ˜„TÁ ˜e0è}¡Í‚ª ²5alZ‚¸º(ˆöÐ~›ß'¢‡øgYßb.³cn²ûy8=¼×œO¼Oœ1ƒì·1àv -À{*DÌ¿Dà[‘!Þ‡·-Ló쑾h ²h‘Å›ôøÙHÖú 1%:Kè°º8|H±!rPº…6 ÙÉèïcöÆa‰=Íù,ºÀý(¼Îëþ“÷^ø‚×%ìæ½~åwÇBx/¢[bëëE†Ü‹=!ÕÏ9¡£a‰š grãÜ‘©òN6š²Cûõ…á=ÚµQŸTUônEó/Ùö»øƒÜ?%'bºÄçø¯ÅW/ÅwâÁsñᓸØâá¸Û’âRˆ¯È¬C‘AåI†Éƒ‚L;ä‡8bå’ñXÉœ‚á¬!‹|n–>àsZzès~ÄÛÄ’¨7 ѯ4[تÜÅ>þsùQáSY‹è‰ôrìãø_ãÆ?ß“¾ß‘öÇ·K‡¥WäÃÒ‹ŠaÙy¥uÏ!CçNF -Ñ;óæÚ¢0xŠÃÇ¡„á™Ø|Ѹ#ùŽi{òKcmJqkjobMZŸ¡:­Oo tÄ÷gw%Ñ;×yª¼È}µþ¶ïw,pèÜ>ú·zºóÝ­B÷›äþ¿”ë/M¡6å°­\!8´¼D¼ß²^¾7§ZÕU¯«ÏüÙP›q̸%½5©:½=iszGRUÆgÓÆÌcefobEf¯Áâ -bƒ,g¬":ïz×ÄÕÃæË_Jמ Ûç¡#ì¢Nº¹ïv¥F:¯u£náérsÔ±ÒLvSQ¾ ±pµxWÁZy}þ&õ6K­®&w¡*ç°©2û¬yCöõäòìgæu9ÝIër¿˜ÖæöËr{­A"ñrˆ¾YLtÞŠ#¾os³éÞåeórŸùÉ`‡»{£&^ßÉ›u±6ÞïLµfáñJSdSy:«qM¿¡¤P\·ºT¶ee…ºª`KBE~Cbù²¦¤2Ëis©åjr‰åqòÿؤϷ&Ó,ãç%!Tª€€ ; (]@Š”Ð{ï:!PŠRƒB( R'ÒDD H‘*" ˆ °ŠºŽîêŽqwöšPagë™÷ȇßçû9ç9¼œß’‹s×ÙÅ9I$–(˜¤BÞãVK” ¬S%>žÐ ~íÒ"^õéˆ=ê7‘¸yÆ^a¡ÃsÇÔÉ`ýÑ&¦Eÿ÷‰Œ®šT·S•Y¾ÍåùAü’âðÚâʨcGùqå…'yÜ3좂sœÂ‚ùnÁÔî/.wÍ-XOâl°DÁde1Ì%ßP®D _™øý”*ñs:ñbhqИv£ÏN~Và¡qþt Þ`ë~óî¦xûv>ǵ¥6Ç_•TSq4¼¢üïÌÒÒºØ"Þ‰Dn±€W4ÌÉ9z)åpÑ}Òÿ8ÙEïÙÙGדrŽl°DÁ²¯@`åVÀFExß¡³JðdT¸=bH»2`#7Õë¦>"ØÝÛaÖÑk{¢‰íÂ?~л¦>;°¢¶0¬¤ªŒy¤²&6¿¢)!»¼3)«l09£l:å`ÙÒ9éeëìŒÒõ¤Ì’ –(˜Fî Pž¼yÀyXí–…—#Òð`\ÇôÅ/XËœpQèõÓí„™ž<cÓÐÆr®9‘æUÑt(€×PZø=/2¯®*æPmC|zu;+­êlrJÕ'¹ê&I˜Ì©Zg§Tn$¥Vl°DÁt²_$ X' Ú¤áE¿¬Œm‚Û“Jper7ujÜRzdÔYµgÐg穾P“Æ3QÖµ]‰Ž§SÀÝN€…A&&Èþ¼t.‹CË#I¨.Ç^h‹ñž›Òó´—Ízæ±-õ;XO#vÆ>‰Õc>N6œn¼’k°Rlí³rÌÆk¥ÉÞ}¥‹áúxÔÁåñ‚£ó“g.+¿98=úÃÉñá7Q’;¸Þ pa`` ãº4Ý£AõRP.ÜF}£CÉycJOÚÊ$ ]ã…¾jQÂPÍpa´vˆ¥ð:MÏ÷õaC/áa…©«oî,lßëøfÀÂáÍ´%ã?wHo, »×Ÿ­DY\œ$û—o‰Á±Ç4(^“†‚U5"kU—’ºfJg­Ùl‰YsVˆXóV^ R x»_Ãçmœ¦çÛd-÷·;]Þåé:¾ãíf¼«Ö³{ßl`ó^`h½>jdµ~ÙØrãégc‹÷"Íô t^hX¨x@@á¿Ä!ûƒü°`ÿ©C‰ýÓ˜ùÑJ*䣃tÀ'w9ŸO~[=>‡*¹~fnsúœ êð…³ÝîK¦ú¾¯ù;¬¾–jZ|­ûÎü[«–é·í=8¾ÓtŒñ#Š42Bö/’ýkdÿ@Ás€Ìu:pPâQ•ˆD-JêSýÑŒæ…6t7tÚ䌞[Ð_ÚCe÷!SÎ -ãö"g«f(š`¾’–*`ŠžTÝ=ª»ðœš.În×Áe‘ä4.ýÛùO¼Hü"Q(¡¨Dø£†˜'êP\Ðˆê€æâ¶hC³FG t£›¡}Ja¸”FKé!kÓ.<°y'ælÑÂbé¿aµŒ&6Ëj`鬜:Ž‹Ô:7¢Óictì5A‡á=Ș0EƬÙ_l×WXÓið?M±1Š2¶qäÑ€‘J$¤Þˆ„:&$ôé*eQyPÔEY+:긺ë ì¢(«¢#ŠŠ Î~×ÊÅïúœ÷|ßÍ „G ÜE†ÀíòÁ 3N:8ͺƒã´;8OzîýÀí@¶5š­!8ÇÕøÞ&Àc˜G´ÂÍ®´ªÚ -|5;ÀGk ¤:{ðnq¯ã»Áó´#xö9Çgð¸í Äã€8†ÓßãfˆÓ¸/à<…æ ù.o½ÀuÜû[@²1?´s‡!píŒ@ˆ3‰§ ˆif ào^ˆ9°â-€‘j þy;Rf¾5@nrŸv' tR/ÊÒï2ï}ÃeÎû¡ë_^£®SÞï\?zqý€fLxÞ7$À¿ð½Ï>hš•p¬ A¸Ã¤N‹AF\2Êr6€hújÑ–ÀI²†€,[ íš÷¯tœ£Ö;ÍRZq3~.Ó¾g\¿ø^Ø3EÀ&ÿŠŸ$àß‘ßâßøNá_“Áí%Ï|Áý -Ÿ,ÈÖèÛ @`iRkCÙ½ÂÝV@8y5„l€ ±9Hä–:¡Òf†§¶›fìž -(wúĬÁMÒ›\?ÐÚñþ'÷¾ó?ëö–zÙí uðš:Lów¥~v¡Î{ SÀó‘?xýJ¯;ôoe;ºêºÔ5C¶B„ý"ˆÁ/ƒoSˆ¢¯ŸlþKlñYk3)N¶Ÿd;¾åáÆ9•®¯Øuøÿ´ºtž3{ÜŸ1þN|ʸIü/ã‰ÇïŒWŸ¼ÐçI÷èàs‹ >@þe@³Ð>ê™AængkJ—¥ ôX©ÛO]û%–³i2BúÓŸ¡‘Ö¯eìǤ鎣â—§Â2üøÕ„!nñ §Ýó1»Ûë!«Ïûk€tõÀçkÌçkÒ÷:Kç7ÀJ?(}\ œ[0·êƒøG}EbÑ=l Aåd<ŸL0™R‘×¼?ÀÜ8/Úò":Ìê©<Þn($Åé_AÙ{w…µ^ƒ‚fÒ-þqò ^ß5^?e€{›z…;Bíç½ó¿À›¡õñ€Ö#Z·h] ¶¹>HQçŒ@[ô!½MÚ®ÅÓé{–½W{­zê¿n4‰¿yX)Ûþ(6Úî~äç;ajüÍà<âµ ¯«Ò*Ÿ+õ¾ýâ6êEÑIÿóÂ>ú9á?½ÂÇŒ3¢qf·è ³KÌ10Û$H 0¾<”_†ú^ ê‰hŸ4 ]–­ÑÇ\Ü’ñ\w“Ñ,_³!5{ÓoªÀmw”rûqûqQÉ{/Ë3=.†iH}!å¾½²jϾ&Ú)iãd`OÀ‰Àˬã÷Øícì6é'v‹ØMû€U¬º€åE]sÿz=HÙ¤™[õ§ó­ þ<ä°èE!~é°†dú[6ó‡AµÈòZrˆÝå„Ü…øngcÒ<{¢r}ºåÅ”a´ŽÐ:Æß‚ÛX­²nN³ì·Iv‹× {Æ« þÀ=¬ãV‡§*8•¡ÀþˆP~¹™$ =ÔQï4×ûXh©ÿªÔÖp¤Üyñ£bUw -h¯eó-/©ƒìú’#{(ܺÉžq™äö ¥5ªŒ~4¢& A~”]ÞÉ­ ;˯ -»&¨ hÃ'ø‡Ãçøeòy^©|ž[ñ-¬ÓƒHÔ7“PïÌ\¯§ÓlÒ›(Ý¢÷¢ÂJÿÊ]‹î•»w£ˆºár>×¢/Kj{FîÜ•·÷XR¢gk‚šÜ´?—ZWL¯‰©dUE7p´QÇøå‘=Â’È_DÅ‘E…Qo……Q3‚CÑ:~A´ŽW=ÿ ¢ü1¨ï%£Þ™³û«höF»IïiÍV½ÖØ Vº™\-ó[¡ýSO¾xgWV¨S{zôÞæT¥gƒ*•|$1‹Z•pˆqXq˜U_Ë-ŠkhbO‰òc/‰scˆsâ^‹r⦅Ùñs‚ìx!„Þ!n꽫QßZƒM•}½üy6Tÿv¿n§áõjüŠþ -òºÞÒ€­' …;ŽåË›³#ñõû=ªÓT>)éÔ2U>£(±”¥I¨ææ*›ÙŠ.Q†â¢$]qW¢V¾«•S¢4åœá§)u߀`”_vP¯Â@cŠMjM±çµ¦Øã¦õØ`£áÕ:×åç«IkOk[Ž— -lZ -÷í®Ï—ï©Î‰#j3I%éi”Ci¹ô¼ÔbVvr/]Õ$8˜Ô)NIC5ŽÞCǼ†/: ‡;Ã/‰î1êÈìn&ýK¤ `Å‹ÌéX4jFt¢è‘M@-ýè - aÈPÁ” ÆBŒzVÆYúa²åLÌçƒYã…˜kÅÇ|kOÜbÃÁ¶X3ÁÙ±±Í~Þ&î±çÀ6r6ÜÈ»OúO’žG˜…6 ÅÉ DWŠ9Ôô¢¡Íƒi"”Ó½QE ŽÁÃh˜î˜Æœ ,Ü0Ûb*®e9ã·,'ÜaéˆÕ–S°ÎÒ›ˆ3c”’3°ñùÿ{¤ÿÀDúQï4H…0´)Ú§PüqE„! -¨<Q=0€ÊƪÊi®¨¢¹`Í 4L OÂtº=.¦Ûa.ÃÿʰÁ"¢œaÕDíìCE©ˆ†Š™4TùÓQÂDU8 Ãc¬P™f‡Š¬É([åˆÒ .RìŠÁn(®žŽA‡ØÔÀÆÀSîxÖÛÉX·9ðøxG r09£Äçÿã¢ç—þ˜AæCú„Ú—ŽZ1µJj Ö¨I±Gբɖç„ò‚©8«hJ¿›Ž’*²µî\ïÁ'8(>M\䢸ñ£'ŠŸotŒ b`”‚…‘‘Ö8ÛdÚLTíŒÊõ®¨ø;YúR6Ê*ÝqÖ~œu˜ƒÒF.J›=Qrއ’ˆNÞ¨äþ°¤›?(éãõKGy½Rä” àƒ…ï$èõ%”zQQÅ£¢ŽGC½Ž1þ Œ•±0VgÑñö•်¯œQ»f†Î@e‰;†íä b/CxbèQÞ¨ü$DÞ*ø,¿,’ß È ûä]Âß彂wòá9zõÈЧ[†¢—ò±0”OE-‡Šz cÉ ¾ L”Z`¢Æ -ãcì16Í£³\0j…ê -Ø#Ú­œaM™çzo0|?@uDЧjö*[¼>*/x}PÞô~¯|èÝ£|éÝ¥úèõBùYôß0œù< }Ÿ…¡ßc%ú •\*ÎfS1–¼wM\¦’ýH ¶Àt¥¦èíÐdrÏtŠÍqˆþƽ7j÷Cd ï½n§à]į7¼{´G}^kNútiÚD/5×D/4D¿j=Õ|ðýfØÿ‘~Rcà5ÝýÛuÕÔ™ÇqüIDÅQ‹¸âŠˆȾCHrsH Ä’„„}‹ì;‚ - -#x Z¤Ê´êiëÒZëÔ±u´ÕZÛ>sûxñ9ç¾|Îïß|ƒ–ƒûñеnø.<ŒÙoã±{°`‚÷ZÈ$™ýÎÛúkT¬åÿ"¹»:-´~*?ô"Xuô9µâØ"Zü{J«ÝS¤çÄ·ÈûÇ䱓É3_“î:Þ'/:._;}Núèæ5YöM–AJŠ"KA¶ €Â5à©î© Áí*#p£ÑL6¯#-»pͶ†z7Ó¦úóÚÔJWn]Rc£Ô&ØËÕÜSÙ¥î"•Ä;£HáÏ+,%p jHœüVJ¢be)®Q™Ša^bß(,Dò y)˜¼|ÌZ)qà®n˜­3×› ÁX›)¸Øa zÚmVµ´º¬­oöÛR¡G,KëÃö)k£È«™vÙ•ÉŽÂò³®|­Ø3­LV&–V™%ÍäXU?…¡š@£UwÐÕ(1Å¿!1ÅÌ(Z¦‚eÜW0Wi¦ Àx«ê6}=ÛA{ÏaƒÆ.'ãÊŸMê6ÒeKÈžÜ&ú!‰>Î6³!é$¯.Ý™[+tOªÎñfUùÅU”FWœ#ÒËûÈ‘åW‘ˆòyÌäÝ{R„’"µ¸äãÀwyÜÕp£þÑŒÃôõ‚ös 8€¯>ï°ZÓﵡ°7È"·›f•Õi-hgMoK´KiIuHl¸Ä7IÝz¥7½QëÑÐÖÐCi -n˜ -®\÷+‚k—{'à«B>«àº®´p¾€6¬?‡7‚êË{qeÃv†…C·d]@?Éß“v>ú §e“Лb×Ãwˆî–8Gvå»îÔxwÔ{S;º|)WüŽ[˜E?rÛ/~äVè¿’g9Øjfjk`°€Ž¿ûs€òñõ@=a…SN3Èw1_õ3ã"Ü‘Ðìÿ³]§QM^iÀïû†@Aö’…H ,!! „  aÈ¢ˆµŠ ÇÑÓѶÚN­£ŽÓjkkGfܵ"¨hE . ˆâq´­ŠÊj«²ŠÊâ3·Ç/sпÏÿç½ÿ{Ï{ž“¹ü¢sfW/Î=¾4Ìrl•4óØ:¹ùè&eÊÑ¢ŒUûÔ‰U'bª±µ¡r(ÆPñV_ÚÉî}‰P#>ëN„ŽâöWüùý8ÿ,Ö×9¢uõ ´º>üK½Ü~i]ìÔ…“Ü‹/¤ÓçœÏaåŸÃy®DU»D˜^»R”jý<ÔhÝžhÝ.·î•é­Õ2µA®·>‘éÎ ÉãNODÆÖÀ{Zñ\ø¡êÝü¡¸ƒoO#´þ"‰V7ÑЊw´ìŸXÔN)iQÓæ¶ÄOÍ¿jr˹jñʺZà“Þ\Ì25/æ$7¯à'6}&ˆoÚ Ô7}×´G¤m:¬i¾Œ=iƒc®Œ‡ª l²ËÛ:…;øwP^…ÐÖœ¡Õ´ì Ϳ㌊Û|Q¡MDäÛ”[œ}–Íè”fËpNµåºÛŠ<ÚzÇ·•1tmk|cm_³blÛüÔ¶Ýœ¨»Ç¸ª»—°v®ª­«üõ ?ò6øOvwP;øÏ„¶á;ðõyœ¡e­ôÑ*èpA¹LdéRÒÔ©¶3vΰOèJuˆïÊvÒuåOí*vÑt/vî^é®ê^ëÙ³ÙKÞ³Ã[Ösˆ.í=Íö^cHz»’îa†¤kü=Uøì>ŒðýGhùwùK[šw‡@9©(ë…+Jïc¢Ô¾"©_LÆ÷+)qZ;Í@U=h²̦Eæ;ȇŠ#†J$릊‡¿pþ§KðËòi¢‘ -× ‘Z×À‘V7á«n7áË÷ìÅïp;¾ƒ¬8¿á]~QB–‡™úiÈ8êŠ c>H7Æ'´ã""zBJ*'T¤ü­–"}k „CŠ]dRƒ!—EöBXd/€•4øÂß8p¡Ü‘Ž~pÖ‘ ÍŽ,xðžñÜx¡5—qþuœoC(ç'u"4cŒŠ´à‚¢Á )…d $L„‚„‚ÄË.9t¤?$’\0Qü ›Â‚ŠÌ·£C™|nç ›©ðÕPÝ «}Ïfü>­Çù×Þågµ#”Ð…f!8w - -WÞ(Ï|Ä!Á‚…gñÁ5áq„žÅ ̤+Ì$a.9“Nð éë)°•BƒrŠ=ìH$$(ÂHˆ“-¥@´Ê¢õTP™i ÌwÅg­péZwlò„ðï¼!ü_tïÁ1 쨄ý‚Õb ¾v{„ý cã¾ãbðƒïh01Öd³U!$Ä„’  §€VaÚ8*ĤÒ@=Ë ¢>re™(>õù/}K‡ˆ ˆØåÒØ_œÀÎ2AR]Ã`O™’!æ¨tŒùZ -ÌWR`H€ýR~“ç«E$ÄbºP -èev0CK‘q'й@Ì7ˆþ«'¨þæ Ê- ˆÜîŠr\Ã>&ȳ@~ «aƒüÖÌ—ÛØ£òö+ù{X>Ê”ƒß€8ýràöÉ€7(q¶VH‚ ’pIj*$&ÐÀ9fÌ™ºÝ!v¥hÖâê7ú€z+¢bMDíbGd©*ýFU'9oTµœ×ª+œÕ-Πª“Ó§êç<‹åü®ÞSø÷ª@ÐóÎôÿê?óýIHä“‚ç0‹)¦¢‚IOƒó0æMƒÄˆ_î=®_ã3ª[Ï|·…5»Ý略œ3¤ÝËÔæhŽóú5gx/4—xÏ57x¿kžðºµ}¼ÍÿÇ10½=„c ðþ€ÖŸ€.)~¤ HÈ¡€EnÙZ{È4:½M³L5yŒ$—Ò‡’Vùö'¬c½0lô{¿•ûÇŒ¼§ú]ü^ýAÿn}•—î” SwQðDMЮ$øMÿ|úÝëÀ[:ÝÔAð8nƒÉ@ó,Ò˜ÌäHB>î#/Ú~|–ÁqÄ’î2Uàñ<}ý©¹Ì·;u »3ù+îãÞ£¤ïý&îÜOØp/¡Bh3œüÕPxËÐtÓp_t=ᙨÙð*¤Ñ€ÿK_Â걺IÀÀ&ÀÄ À‚åã³(œNBQ(e¼(’:\çð¢ Ź77ǽ#g½={ ó·ŒU[ÚZþmóß7S¿ÞHù!ðzÊ¿E-É‚›•!ÆšÐ+Æú°ËÆ›âºäñ9ã°Äjéi¬&"N}} ÈÀ{o®7sñY”ðˆ· ‚ÈáRʳùjû®âÄ)ís³\ïΞMÿoÞBVkN·Å²ZДõ¥°!c“èRúÖº´aÌ»Åç̇$VÓqéSmDéªìÓcYµi@~Ô<¡¨4ƒâVñJ' ïœs<”0,b¯KÄó%!dW©ÂîÁ"½Ãíùf×ëŹô¦Âböå‚R~]ÞÊ€s³> ²Îü*ä´åâSÙßINf패ÎÜ';–Q©¨Ì¨‰<’Ñ <œqOy0ã¹jæ¨jo&¨ö`»³@9˜= ˜…÷Ýyî>Æs,õAƒeÔ½"€x°BBÞZ®uh)MžÖ°ÐB¿XRȶÎ[ȯ)\.<9û“ÿq]§QMÝiÀß$ì((H•* ‚"›ì@V,¸Êf„5@ !LK„@Ø÷°Ëæ(¢´¨Œöh5x˜9Óöˆ2Χ™NGǶsçöØȇ߹ç~zî}ÿï¹÷<‡gS*}.1UGfÎ5L%w]HÒ‡Œ%MFçÆo‡ëŸû_{“Þ{’bw2ÞõNØfÈt)hßc£ÏQho…;à;±<—ì…5‰7öˆh~·8Úf™Öq‘›òÑ|^ŽÛåœB™¬ÒÓRßq–Â$M4”Ú2ÒKècކw3.Gt2–";ImŒoI-Ì#uL$²9‰@›¶@ìa¡}/} ü(þþrÞ¾”9Á#™'vå<ÁlYes½$Îa¶èœóÅÂL· N¾ÇXžÀkˆ]æ;]ЛYÜ•ÑÚ‘ÞÞÊŠÐ¥M“šÒÉi÷)ê´ Jër= !Õ§#‘ªw"6C’lÉÄR€‡7%xxQއõ -<<©r€ûU˜Ûò`³E)Õz®ì¤ÃLiâî Å,·a>Ûc €çÕË)õëÌ“´å*‚u9‚6»¨Éˆ¬Ïœ ×e^£*3W¨ŠÌuZMÖ?©ÕY¿Pª³2Š´r}ÿìm€ð¬àob øZfkÕVðPaå~ÌRM é|%Qz|Ǹ$~÷(ŵO˜íÞ%ànãûê -%ZnU°šSOP嵕¹½¤jö8¥’=G«`ß¡ËØ_Ò¥¹¯hÒÜŸ)Ò<„üÒfA®%¼˜Ãz¹)¬UšÃ#¥%Ü«³ƒe•f¡îˆÉeE䶉ª˜íò3»úÊ.â ÷ÖÒ¼ÃMB¾¯Z Pñ+‚•¼Ú°ê¦9·›$åŽP%œ+´2Î-ºˆ³%âþ&âþDqЍýól¤Z¡»`ß—àà+© kÌàs•9lá¦Ú3×àk2­"Z*?¶ï¯Šû°«"yo«”u@+a{6ˆ }jKKü«K¤ÁòbA*hŒuEü!j x÷iTo5ªˆÿ½ˆÿ–&à#YPô>„e~L`C°&ÇÁƒZSXQ›Ár£5\kÚ—´Þ¸qM˜¥¾>Ê®»ö”SkMâmeêþúŠìCJ)×»ª¼Ø_V& *WÄ"5QXÚN”è©<á ½@¸Å>B½ s…oh\!B-"y3$ÃÞò0ðT`¬ÆÂ½z¸­5:<̶ì†É–øáæP‹ÞFšm[ÉZUüGõJæ>EMæÁʪ|o©¼èHY…8¨T*œWyå-$®¤Ÿ’W6Ec—-ÒsÊFå”ý½¾Fï*Š‚"o†dãà¥í½R€J,48XÒá`¾Íf:œ`¬ã ¶¿-ؼ£…bÓÜtÌ¡¡ñŒ³BÍp“×§{œ¯Ëõ+y~šÒ@~µ,¤ ª6<_ÞÉ®è¥dULÐ2d ôtÙçt–ì[Zºì?ÔtBÉ!ä­\€oJžÈÑÞ©ÂÀrÚ1p©Ë Æ{@ßãŽíê0Óu’ðêöèÊÖ¸]r]²Ky뀨1dzX]àÃkúsTçƒÙu -BV­–˜®ì&¥*Æ)LÅU*CqÆP|CeÔü›Â¬AÈ¿!m†pÖ˾¨øLpí~WÐþy¡ ƒz{èÖïô1Õô·){ÚË»O9•w&î)íHÝWÔ–u° …ã•§øå4K3´Õ!iš0†¦3"Y3JJTÏ‘ãÕ+¨ r|䄆ÿ‘ê‘È­^ñþ€žÃý:€OuWÑü)´ÿ ¢ý«{ôhsÁhF}pÊ‚¥|˜f+<áX¢?»›ßÏpáôe`÷äÊêæ{³ºÄ~ÌÎÊÀ䎆„öv™öáð¸¶+±mÔ3bl뿈±-¿ b¬î}FÏáqÕ»,v\DóG†ºÐþÕi˜íèÐÍÔÍZ@Õœ#HçÝ1¢¹Á\„%wöcö•Øí™—Ò.¥:3.f»$Íp÷ÅÏÝ㦥‡NNÕz›jñžÔû¼è5yËŸ>õ•?}â•?íÂO´q$€6†nfDgpG p =ƒI=ÀÀ@+š¯B;¨ôD7í xÉxK>ؼ¥0³ì›4+ÖÍ6Ìg퓎ñ‹ž¾žï|êº`ïñ…r·èŨ…fÚµþC”k3žä…eOÒÂ=IW_zFοõŠ˜C¼#fßw¯ÝÃv€™~€¡Q€Ži´¢ùr´ -oá€s¹+» kå †µ„c®L“V¢-ãïÆáãî&Ûž¼Ë²?f`;Dx;£ e»¨†jg²A»'Òз—h˜v 7|âfø½køg/] ·ÿëz Ù¿Õ¯{x¥`ÝÁ®)Í,@å €Ò[œß™@ÚC 8÷Ø’Œ®`ôÁœ6°§Œ4“ãÆf1ƳGÌm4c–5Åȵ4–ÚW+·‡­jv„®ö8¯Nî Z]ÚøäÉÎ ã÷Ž_;{  zàÙ ¤ à†LpA8"—4`ƒö`…Î`"à’½pÐÌP, ÔRFC` -ÅÀlŠÂ¥àzb'±¨&jÆA©'…^R -½¥4TxÑP®¤£—†ŽžQ ôHÕGÉ</c¢h­ºm6F·Ý&èVjŠÂrâ -µlž&š‰âñœèe -‡Ù#nÈ#DÎÐÿ™ -e¤¯SèëNC¥áCCß@:*"è=S½æ ,ߥkŒPºÉ%;LQRÌB÷ýlt¯$jÌÐý„Šˆs_%nsFÅœ÷ÎûÙ€ÍÞ»£9ÁísG^Ÿx<”“¾RH¡?1YLÃ)Þ„šŽþÓõÐ/A}u¨øÜ½W™ ×W¦èµ…žElôÜk†²”UGÉ·Ô›ÊΘ”]2‘ÝàÉþkÞ/ë6ë9hÞã‰Ü72´è–!¿K†–¡ÏXß™B51UHC'áOÇÀªcõqJ:ý¡ß2ST®e¡o¡ÙGŸœ?}ŠÍ?(ös‡•Ü!E oPq’7 hä½W4óú­¼^Å#^·â5ï•πŠZ>W ÕSoÛ-¸Tj}'¨Ü润Êö–æ¸Ý M£ýuÍyûVÍM‡– g—ƒú/hF›5èÒDœÓ ëDdEa0–˜$L!g‘*¢S½i=ÉjÆËÄ0ƒŽøD£G3t¬Ñ9æ¿G.çߌøÒêzØF›ÖéߨþZd-t¯Ãm…ã%mÓ…Zçó!?9ÿÒârNÛîú£¶GØ q;¥EQ=QGÔN€Z À(òîM4LãfÚÀ°Î™êÑI©ç™Jz{z°Þý”X£[3ÓØ­ Ù¼kqyV—cWÙ\ŒYg×ýµCSÔÇŸ"÷8Ÿ‰(s9~PØ~ÔíTxƒ¨>좸6ì®øxx§û‘ð!IM8JE ¤úo`iÇ’7gŠ`YÏáûl;x‘í -æÊ©ßgêý[at5#‰}!UgÑ”¼Pp6i‰íé„Õ ñëêã6»ÔÆîžˆ-‹)‰>$©‰®“VG7yTE_—UF?—UÄôËÊcPö=±LìxIÚ ¦€dsØ0<Ÿ¯Z@{Ž-ÜΑR¿.˜Â¸œjøóì8ö]ºECF¶ .-×îDÊ -Ç£É_:NÚ ¬NÜ&ú!¡È½2~Ÿ´"¾RVwÌsÜ^ûâZä¥qOä%ñïäÅñ(ß“€òoÐk"Œ!ídc@1ŒÌ3ÞE¦ð4—wóøÐºXD]Êõcœ[lظ †]—lq|ö,ëú…öÕ™ùŽ3 -\+ÒÖ¹•§º—¥ìîM.‘}7ó€×ž¤ÃÞEIÿRìNºì³+©ÍgÇÌ^Ÿí3?*¾IÆ1Þ„üS8ÄÌ!9&¼Î1€¶<&Ü_b7–ráÊ2Wªi©½1_ìÍdÍIäUÏÏTfgÛ˜“ëT6k¹ë^ÝQIæÉ·Û<þ™^äµ3­Ì{{j•϶Ô:ß-©ç•…©w•_§u+7¥ønJGŸé¨˜ãÈ=èCÿ<´çÒáÎR}¸¹ÂZVr y•œ^)§×.ŸjpdI«*/ŽWñyš`ÿÂÙv¥ósœŠç-q-š[ Ú5gdû¬BÙVÝ.¯ÍY¥ŠMY•¾2O(×g6©þ‘yKµ.«ÓomÖåZú>a"“Ü^.ø#ŸNúzðK\ü‚ g×8@ýž´£U+´¦Ëb¹eùÉV%y:»¢Eówåä¹n_°B´uþÿÈ.Ϩ¨Î<ÿîÌPDš ½H)#LQA£, "  t¤ÃІFpFš ‚RDŠŠ *‚1XÝQp]÷¸fƒ%'nìf‹‰=Gß}ýæÃsîÇçþÛ=÷WâR™¤à*êxåñ-^òønAIܰ8î¼·4î†wQüSQQü;aQPø_B6ª€Ä1xüCÂÂTßÉT0^ª…¯Ë,p¢Ì•Õ_â­Ú-]©ÙQ¬×šnÜ”m^Ÿ`U“™fW™žë¤H+v)O­à–m«áÉRš½Š’»I¢¼¤³Þ’¤)ŸÜ¤ÇÞ¹ÉoE¹ÉD˜›BþLH$ oA÷€æ­26.Ê98»]æ¨pfõnªt–.ŸÛ*[;¯©h½Q]Á&³ê¼8+eî6»òœlDz¬BY¦œ[”QÅ+Hoò’¤u²SûE™©g|2R¯ù¤§>ôÉH{-ÊH#BŠàKÈ&àE20\—ß–±p¾‚QÅ )qXéÄt)ø*û+ü4öÈWëÖ•†VË"ÍÒ­ ¶&Û–äg:Jóò $¥\IîN^vNƒWFv» -ëˆh[ÖiŸ”¬ Ÿä¬Q^‰R²ˆ"øKž³0½T꺭¤bQ’¬Ö#¾¸•¿UÚ+Œ‘‹¢¥ñÞ,½ï-})Š.ú$Œ)" -&¯€{ÙÀd1pqð5õ¯£¹k7ÐÙ¨‹Ö&+46rÙ5 ÞjŠúšò:±^qí:£üê(³œªX«Ì)¶©Ê,ÇdE¡Kü9wkE5/º¼ÙsÓönA¤ü„p£ü¢h£ü}þOQöQQFø_ò,…ÞÕ2ÚƒJàõ÷Óüw ™fÏm4¶š£¶Õ…¥lªÈ›ý5¤{Vëæ5…d7D˜¤ïÞb‘RŸd¸+Ã>¶6Ï)¦¦ÔuSu墈ª&Þ†Ê.Ïu•Cü°Êq~hå÷üÐÿá‡)÷ -S’ÏxÎägz“7è.^ú܃Zàõ÷ìZiÜÝ>5ÆPt:2e^iûRuIÛ*­¬ýÒKÝn”Ôm¿7ÞrKsªÍæ=¹ö‘M2§ J—°ÆnHC§{pà OÜpÁCÜpËC¼ûßâúâ:ÂïšÍ=zW?÷  -¦þÃ-4ûµ ]@e*Ê{磤ϖ)ìsgçö.VÍèY97¥[¬“pp½þÖ®(£è±f‘)–áÙÖë:¤v!í;ƒÚê®ikwýªí¨ÛªýçÜÛnºî{áØúžØBþÀßé.^RÐÔÓ=¤õlöRMPz”æ¿A]H-‘uÌ•IrýÕbVÏ>ªu4BcÿÃuG’LBŽdš.X°æP¹õW‡vÙÚï°¢¯ßqyß7Nþ}7ý{Ÿ;úõ¼sòë& ýÎæíÁ9:ƒtþ½´þ}ºC@ÅHÑĶQc$Ž:2±£ž¬˜Ñ%œ¨Ñ@µð‘`u#´BF6ëŠ×_s*ÍpÕpžqÀp™éò“5~'[-—žÇˆíL.Ø¡óï õ7Çhþ2h½ ‚è1=D[!bÜY?.b…Ž/ç¯Q‡©¯Ôߪ¹r#áØicÁؤ±ðÂ#þù7&üsÄÔë,1›É7ôö]Ô¿‡ú+©_6dâLjË„\ÕDÐU#ˆ¯ÙcÍ$«&3“+Y+&ÅlÿÉõœ¥“›U}'Ô|¦2爦Š4SJM¯©&-©mÞÔ)÷ë:îS´]{­Ã½úqw‚èÍäx+Ð}€îíõP2 -dŸ.‘W€à)6nªcémm,¹m -ßï°øŸiÀšèb1„91‡ ±q‚áBŸð¡G|¡KV@›ˆ¡EÂ1—ÄaÉ‚:‘3j¤Q!9N¹À°ÉÄ,vP>õ'SÔߨŸÖ¾ŒÖîùXø°þ°ü˜êÔ¤N]ê›O}&ÔgI}¶ÔçD]\¨Òwa“% ¤„R¢)i…~=>BOŽÍ‚8º2ÄÙ™!.g.C¼XÄ~‹Ø®eë6±JàËl±Q”*Äâÿ\×iTS×ð“@H.!y`bÈ ´FdA@A™ 4 aJ˜"cD'(Š‚ -Š(‚
P눕¢Ï±´Úê+•ꇧ€â¸ß±ë}`ñá·Îþ¶ï:{¯uî¿«ÁêµÀ²I ,cÇé`ñ#Ö]ÅîÐÁ²{I·|OÿdùEëƒÐ?XcÔˆ©€{“< -8b¯µ3þ7*ðý5€®3â5Á.ClU4°-¥§B 8Û°::pöaÍØQpÚ±³X'v“1Îée|â 2F9ï#¶ôa[ †lA{ˆÌ·SÉ¥À,œsì0\;‹ð¹€ -"_ †h€@ª |9 fæÑ€W¬¼2:p·b5 àîa|áî'&¸Gˆqî bŒ{šøÌ½D|ä^'F¹‰aîñ†7B¼æMh¿äóXƒØt'î=çÜo¬) Æõ<<›yb*Ìõ¢Âœ p–h¬$8*µ¾ éãÂuŒÏä&â#¹ø@îÒìÓ~'8¨="he N2‡Ì7‚næÉ{Ìäsæsr˜ù79ÎzJ´'$°c}$èM³¿ö6§À3 -¸áoqŸ‰áy¸-¤‚ žÇüpÍ1q¼Ö‡oÒéïçä3†go7j¿qÞÂ|í\Ã|åT¯ó©IgÐéˆÎ€Së¹Ó9Ö3§«¬~çVŸó3^ç¡iœÆõî;þ¯N0½Ç ~™âŸþ.ÆÜ,²À+çâ‹wÓ{>uÜË[cÔ}™æ[ŒÖk×ú —•ÄÀµö³ùk™ýó*tžÎ«f=ïbõ‰tÿÔýS|lÚù§ÙÌídߟ{‡Ý#î×»-š~C§Ü;çÞP‘,ôIÂF/£ PŸÄÝ“Ð-‰ µ;j1­3"”¸Ã:’¤wjY†ÁÉà<ã¶ U¦Ç—®3k]RaÑXmu8 Îú`À~ÎÿVÛýþgíý»íÙï xk¿;`bÆ®@°ÿjç°÷ƹ7‚ÞGhPªþ–²ÐýXCt3އºbçR/IÑÎF?®ˆÒm‹Œ×o —¶„)M…ªÌšCŠ-š–o°Ú·¬Òfopm}Pƒ]]Ðá;ƒNrkƒ~âíú·-è¯*xŒ·5x[—wË@à; ¡Mô4Vý;A Ý“è¶Lu%Ù¡ IÎÔÓ2Z{| Ñ* Ó=ó­~st’QãŠt“†¨óúµU]x©Íΰïmw„UÛo ÝÍ­ -iæm ião¹ä°)ä‡ïCÊC?ñË€¿1 fNKhø(h?/½2*êI¡¡[©tÔÊF6è´\DiKuÕlIò#šeËYñý=±ñFuR¹imŒÒbGtUµd5gëŠ v›£*gTDÖòÊ#ù"Ž ÖGœ'×FÜ"K#Ÿ“k"?J¢Àáÿø“A0M¬@èiB÷SpîUh îtº’®‹ÎdX¢¶L’Ò’>_³YáÍØ—ÄÚ©W›(5ÜždZ—nQ›cU!-ä”[j¿!¦‚».zûÌ5Ñ{Š%-d‘äŒp•ä_ŽjI¿£:ú½Päw1 À§Ãd_ŸØáh„áÌwWеL*º¢ÔDç•:臕fèèJ>¥Y)ÖhÈô¢×¥êÔ(ÂôªSc +“e&Iró2™Òj}‚ŠS_l_WÆ-Š­âÆÖ TÒCÂ|é)Ç3I¹] Ü'ŒË:&ŠÍº(’fÝÃ^ãzL›dÜ/ñ.<Às¸–ý?¶ë=Ê|ø÷¹ ƸŽËLÌŒË3bf0F‘QœPI…’.Š“­])’ˆ)¤èB[(%]ä²Û•êìn‰v·­Dm¯¶vŽT[ÑUJ—ßù:ûê¼´¯óÇû5ó×|žßç÷{žy¾ÿLÅ2°ƒ,œ9Ðîl#(ÒJ¡@«¢r³=Ùì,½u™!©SM’Ó£ÌW¬/I[`°æ²…©Iö RÒbWç8Î[µ]9'y¯ó¬äj×è¤sê™É­ê™IO\g& ¸D'g¤êþ|€ÖÁ³ˆœIDz±œÿÊPQ®ä ƒ¼<'*;o$³.gŒnêÆ AÒ†0ãeÚéf‰YsD‹ÖÇYÅg.–Æe¬°·n 7'];äXBV¡œJ/ÔÐ)£y+·ò—nžd˜°)Âô˼h‹¿çÎÏÏYh=wã2Ù¬ )vQÚ,y¤vËðiÙ%Š)Y‡•aYõªÉYWÐCÕäõo”a™db¨»ƒÏ„d€³¸ß`þ¡ÍØΠ…Û6îàAf‘ÒŠí`u± µ¢Ø‹M,§»hÇÁ‚íSb·E çn±˜U/Ž*H´Ž,X%›¶%Ó.ls>š¿ËabþAÇüSNÁù—‚7=p Éëw -É%Ž!9ŸkųxÏá)-v€ùû¶bE8{⺮`u©¬,“ÀÒ= -*¡ÌƒŽ/óãÅ–éÅ”„ f—L7ŽÚ=[¹+Î"|W‚hÊW+­Bw¦K'çÙÛý­¸’ ,:Áýˆº¹€í}ò€mD°õs—±ƒsë±ì¾×^RŒà(’shRÀ’X\)‚øƒwPCÅTŽffUò¢*'êEL;mV1ß$´b¡Ù„ýË-‚ö¥‰÷m´W¾Câ_^!S~\æ[~ uI}÷¼’ù–ŸÒÿ²ý¤q-v P…ëß»`G)@N9@ΡKÄÕ°Sk -³km ºN3ê<©ˆ:fjm0orm˜îÄÚüÚ¹‚ñ5ñ†5KŒÇÖ¤˜úUkÍ|ª·YxWï³ü¢ú‘WõE‘×ÑNѨª—âQ‡?óJw0ï%»™w’rf@Rɼ•eú%ǘג3LŸ´‘y)½Æ<“v2O¤½Ì²·ìCáÝ—î?éET6@\$Ȉ+^‡ZFRÄÙ"ʉ4qšA“á±ô‡Df@¾šyÃe2¯¹\¦+d^rÅì ®”}ÎígŸrGØ^®ŽíáN³¹ïÙGÜö×ÁvÉ{Øù[v9ÑýMNôîÊ ÿÎ_WÌÖˆð(YñÀkñt@¸#q?4ã©wêit¿Ëú¥óBæ™jÓ«LcŸ(µì#e>ûP¹÷@±‹w_±—wOq×¥¨áu*Nò:”ßòÚ•—yw•í¼[ÊÝ6å~‹Šè_UÁ%1øù/ˆ;æ{x¡Ñb ¾Ø…>Žö ú¿K=÷ ¥ŸŒŒ¢ŽˆcºÝÙ.÷Ul‡&ƒ×®ÉáýæV sÇ­Xç¶[©î-u…îMu•^›ú˜ÞuõY~‹Û%þU·ÛüŸ4ôÿÃu¥yæqÿx"ˆŠ*Ê¡™ív²“lC5Þ¨x €€ˆ‚ŠxѼˆJs`Vm\Ícl®ÑT“ÉÕf3iwšN»Ùl¦×¶™Ý´M»›nûîc·™µùã3ïûßwæ÷<Ïû>ß›¾÷]Ù„‘¯mÂü–‘¥ÿ¡¼„qÛîØN -`ÉTÀRi€¥°àyòFxºsî³.îq|þãr·‡Ûµ„÷¶ÕyÜßÖêyo«Õë­}Þw8Ã>·9û|nrŽol™ö½¾å„ïÊ–Òµ-ˤeÎ=òUΧ䋜o)‹Ìƒœç`ó¯À¶£ì"Ê&¡#Þ3©ð<#¾äÆÂ“´ÍðQZ2î½Ô\ü½äbÂ$•ÇÍD½×õÞ+ mÄk ݾKñvÒÕxùrü~¿Kq”·âf(âÎø/Ä]˜»p6âŸQÇÿ4ÿñ3ë` (;Å eû–í /rüà«*<æEŸy¯Ã»Ùq¸[¼Lüjfa9£Ôó -Wã})­†¸˜j&-¤ZÈó)=”³Éýþg’GN% <‘4IK:4›t!x&i5äXÒé䧡G“HÆÂ\È‘5)XèKX’7`\wÀrÜáß>xÃ> ÞçÁ]á¸.äà–©øKü<÷ ¹Å^ó9e>gx:ÒÉ,£ßñÌfÿÙŒŽ€™ õMî`Ð×ü‡tWèDúL˜+ýípúí`úýðé_„ïãþ+ÂÉÅÂÇÑ5ÿ‡¥ |¾ ¾Uˆƒ‡"wxPä w‹`EÄ„+â͸EQ"þ\!ÏýdA¡×œ°”8#P“§ó ”É܆€‰œVê‘kð¡ì=!ã¼½aûyã4gÖTøhÖ©È‘¬ËtGÖ]úïú ï;z6¶&Òþ -,}²sQçDŸö‡%¨óIÝà¶ÔV%~pYJ‡ÙFÜii~NÂu?V,ðš•'Š”äÃZÿñüÚÀýBsS` ô„9øƒ´¡¼}yt{Þ\T_Þbôî¼›ŒÞ¼¿1zøßDwó±5Q]‚ŸÑ_Zûµ|#x„~s÷Kn)ñ°¢$ÀU9 ”48UöÌ*·â§å©„‰Ò\¯CÒ"â’R²³Xí?"֋ꃋZBû ­´¾‚=¶‚zOþᨮüFgþyfGþ*Ë’ÿV{Á3f{ÁOŒ¶BlMôzà 4ƒe¨ë Îµ¢FO‡ å>pZ³`JýÞUžD/ãy:ùĽr y¨´Ì¿_ª Ü#© ¶•˜C{Š-4k±-¢CFûàêË(±õ^Ô½fjpp´–Q0f|7\»ÍÍ^âa3d{wUåûîÒ•øµi•M Õ¤© ©¯0ÑŒê]‘Õå}Qúò¦NåbWªŽÇT¨.ŪU÷¿ÇV¨^°+Ê1Â\ïË"€”hÐü¯ üù:Ôùеg=Õ“ÁÙŽÆ×pö¾·>ÉÝjÌô²Ô -ˆ-Õb²Ù ÷oЫ©Æ*CHµ®¦×µGhµ¶(M¥ƒ¡®<ÌRifc”š‹± -Í;Èç±JÍ¿ÙJ ÆZS¦Á˜/}ŽÎÃtÝZE3¸ˆrÏ î7ƒ®€.ôÜg&£) ú›7€­é÷x«9Ðnâz67äMõEäº:™±œª¯­ -ÖÕÔ…ijZ#Ê«»ée†!†BUªŸ‰‘êc%úÛÈgèý{¶´ -cɪ0æzO¤ï¢«ç2šÁ…F´Z¦Ú §£•ö¶`èmgƒµ}®½-ŽÐÔšæÑÐ’ãcl. š$¹,°Ò¤ V7Ö†–54‡Ëë­‘²ºhIÝf±ñ[d\ˆ)2ÞD>A¾c‹j1¯ó‡Û:´õçšQï´LtŒì°wxBOg tZÐf݈kêÜîVß™â^ÓÁóÒïµ–b²º]á¯jÓP­Õ!²SXIsG„¸ÙN/lÚÇÈ7O3…æy–À|ƒ-0?a LÏYBÆ6bŒõ>@ëp½à-3ê(ÿÍ.Ôûº‘ž7°ôR ÅFÓîßâêvsðÕ¶D‚®7ó¢‡ï£ê‘Ý2?YWy€Äªw6„vXhÂŽ¾þ®±¨Ü]“ÑÙ–³ že•ɳјúJ‘gý‘g]§È³fPìQÍ%Ãí§üLÊÿË^`}Åa`a­ÁQ íPDþ0÷Q ã)¼›Fal“7¼šLs:ž§ãàq&†3sàÞœ·æ0ªe \[¶À¥%ú³%ÌùìI8·´Ã¹ù s>ý‚páÙ´ï¾úðiíû¢ -à]ÊžQKù @Ä Ê?›ÜÏI¡oUÂ鲎Wôp¼ê‡«ã »6DC{=ö7fÁþf:47—BsëÏPßÞ uÛ~¨Úª`ÛvÊÛ?ÁöÖ lo>månV•¿¥±¿GãžAÙ 'ð&À¿ð:Œnœ®1Øß’BÓ¡‚ºC U§¶(»| è„¼Û YO"$=3!î™QÏ2°ž @÷ׄBzèæ=HÐÛ÷Úfº´ºXò0¿˜IÙñ”FÙã)Ûó_€Ëu@wPßlP ( PC2 …hÀèM¼ˆ? âÈtBô" ‡Ï¸A -¤ §äÜkSþRÚsïÓš¿Esž@Ùa”=î2`¸8·öí”ÝȨlñ3€q}ÛðJCè÷Hb >$˜L$)„^Šœ>Š8ͧÍÇéÐqZt^ñwKÆÐ-¨×uô·÷×D‚«'3®šÅ¸2qŲ’¬'›Év’A²—ç‘BpùaRNªH^É›ñB~ CòôËðXÎñHÁñPÁÙCåÿãnžànðÑDOµŒð£ZÂÀu TÏ Æ5ï2®^H–“ÉF²•ì"™$—°Wêì…º„=WÅ3užª›Ð¯¾ŽGênj6{é’ΆF.fƒ#W°'úµì‘~#{¨ßÊúô;ÙýÖ«Ïe=úÖ¥?Ä:õå¬CœÝןd÷ô­ìöÈNvÃ¥_¸âÂ…VW.ºD.ºrñ…a¸åûêh+iÁýœ ­‰ß8ú†—ÞñôJc=ßa} X¯aë2¬b†õì¾ak7lcwÝ¿fwܳØm÷ЗH=_2p5Y‚‹Ir4'jqb²j“Q•<‰ULž*”%Í'¾'9”°@f‰ÿȦ0n…¢ n2?öÛ¼Ø/T{cvØåÄdj²bòí3cе{bª´1§t¹£ÛûØaG,wøŠlãºáx0”´O¥~‹ñgÓ4¥JñCŠªSGáèôñ(ɧ& –iiâý)s¥ùSß·Ù;e¡<'y™2kòJUfÒZõî¤ÏþCu™5}¦qüûK¸o„\¨µõ Z­ŠŠ€€ÜH!„ @‘„+€îû–Ã9¼T]Ä«µžuÜ{»ÝÙît§Û§ÛîÑm×µûÛÇÙ±Ò?>3oòÏ7Ïó>ÏäýxŽDöxGŽð#ûôGžóí¼â×yßù1¿3êK~{Ô·Ëo‹fý–ÆÒ_Õÿ»$òÞTà¹ßíT.n¤¸á²TˆwR7à\êvæ¤4œ3•g7‘œæp$Iå4–˜ë2’ wŠ7y ÄY<ûâ¬Þ=±¼®ØAߎ˜£~m1§ý[b~(h޹-hŠýHhý\Ð÷œ`ýëÿÿ%lði<ðå?"ï»AïìtWäΰ)ø8“¾3éAÌqÅ^Îaù»QYŠÃpZºÓ€4Û¥WªsëN)ñèL®ðjKªãµ$µú4%öùYÇýëgu ç…µ‰·D–ÄŸ‹ªŸŠª“ž «’Øÿ—|ü1ø™‚œSÜP“kÐ_îy¥Îdú`ZµÇT[0žÂVFr2zÒeN -•K»<×­E¦÷hJ3y5¤YxS}k¥]|‹tDP%VHçEféu±Iúc‰Qú™Ø˜úo‘1•}¼ä‹ºêÁc%õ€¼ïª–ÞÙ9ôÎUs0“í…cšÓlÆfÓ§çveÅÙ·©¤ŽÍ™.J[}FG]z©—%½’W¥¨÷­PtðÍò!A™|BT*ŸË—$ùã½üS‰^ñµX¯`E„аŒ§t¿–Pý7)ÿr>0Oœ¤ó„Öc¹ åm@oî6¦SÊmÍ9`ߨIv¬ÏV8ת³\«³òÜ+U¯r•™g̬ó-Ílõ/VôÊ£¢BåY‰N¹¯ü€xBüK’¯dÅ„ˆ¾ä3šÅ_Ò Ü£Ú¯é€‹…ÀÙ"`’~ǸÎCBô®GGAÓ¢ÛñæGÙÕå%8XrÓœ+µ™®æœwcN‘g‰ÆÈ3d×øe7ó Ô}Â|õaQ®ú´$G}%@£~Hü‰øJ¢Q³bB”³Œ'RÚšƒÛyÀ"eÛèÉs’8Bça½+zõ|tÖ Ù°•iÐïâÔEØUÆ9”HŒ.%:»!_çY˜Wê­Ë«òÍËmäks{í¸H­=%Qi/djï þ)ÉÔ²b•–-ç÷´‹in—({ÖHÎWFÎGçÞ'´—ú ¹ì5Ô—25e;8•¥a\sIŒ}iq²£Á p)2¨Ýtú|¼¢b -ŸìÂ~VA— ³`T”¡;!Vè.J亻rÝ'Ä?$ -Kß±¢åü–îá!Ýýu=Í"åŸ!ÿ¤{¸Ks°HÏ_[p¢«"çª$ï¬bP[í‰*‹æšõ(« b –ÝœBË~»üêxmuªSv•Ò%«2ÇMYYè™^aô–•×ø¤–·òS̃‚$ó¤0ÁôŽ(Þôž8Îôq¼éKQ¼ñ[Q‚‘&”½â'à]êÁ%3í£…¼³¬Z‰Z:WtCYƒ?Š֠Ⱥ…ÑYwr´ÖpnvCŒ}V}²£²^á¬8¨v•Ìw—Ö•z&×Vók›}ãjúù15þ,ó‚(Ë-a”åW¨ê/ÑUÏ V]Éú¿äÍÿuºÿóTÿ©zò>+ÐC4å¤Å-Î(lõA~Û*hÛ7#»};£jåd´Eqm‰öim2Gi«Ê9©%×5¡Åà×\áy ÙêÕÔ볿é¨_xã,?¬ñ:_ã/øaÖÏùa ÿá‡Õ³ßãõ`ú>GùSÍ´ -ä]­ä=mä]]@^7š^/dõ‰‘Ù¿éýAõ‡0©ýœäþXnbŸÔ>¾/Ã!¦WãÝ[èÙcv ï©÷Ø×Ýå¹·û°÷îî³¼î%Þ®îŸòB:ŸòB:žù„´³ß㦉f‘z~š²tÐ.tVÊ.ïòÉ=ŠQ7¤ñ![äñ@$ŒoGÜx(3͉OäFŽÉì"ƲìÃÆòöŽ–9í­uÞ5ÚáºctÔmûÈi÷m#‹Á#?r>ô÷àáo<‚YÏå,Ð ÌQý“”}¨hï§Pv e猲c@¤b¦¼=%FÔô:ìŸÞŠˆ™]› gBgb˜=3)œ™ ÎΙîÛÓ»mÓUö?˜nqxkzØqëô §7§/œÞœú³Sàñ¯'X—Àc¯¸@ -v’ê§ì>ʵ’šº sH:D’&îuÃî9_ìš[‰s°c>oχ`Û|‚mq²¥2oÙTÌ›Žh3q6٬ܶ~î¶I»õ¶‹vël÷íÖÍ?á®ýÊní9Ö~íÙWœ¥ücÀÐíÂÀ2 NÙg€Ô9à©áž‹@ðe.¶,x pÍ «°iq6.aÃÕ]xãjÖ_Ǻ%Ö,eãõ%V/Y˜UKÌÊ¥#LÀµyFrí6#Yú„‘,þ/<爯°ß1Eù#T{'õ¼Žt´”²µó€œrcIICm×€Í75ï9`åwÜñ…äÎ -ˆï®ƒèîïí€à^üïÅ‚_¿û9ð}PŸVð Áûá)x=¼χÂëÁ_áuï¼î²ß1>F»H}o ÚM³´€ô+@ܰïå¿ lzxý> þ×õÕ¹Åüï¶°lˆe£ DË‚((`¤W%ÑÄØ 1`A,H @DņŠ(Š< âàC‰-ŠO1D!âŒODQDÄ‚ŠÑïgã0¿¹³;Ë=ç;{÷ûÎ9ÍÁ¢F Õ9ÌêÔPÔY“Þ0­s C ¯Y½Ò¿#ÉDHþN„øb:Dó ¼X -AC¸†FrŸ<û(g3Jßù,Zû”=ÀXŠ=æOZÕÿ8àðÐû `] hêÅ%Ò+"ˆ6ª!¸j°€&;àÚ@à:5¨Í£©1Šnþ@hóm¡¾… -}k7¡Ýj æíöGK(þœ]À4ªû8Zw }Äë0ø`Wè.–Õe@~5ƒîGn™Ðÿ+€Ûj M Üé ´÷î:÷‡¾kÔRsôˆäG´ù?Î:©àô€=¡@ON}”DoÍØGû­=˜b{¦øç}Ðó`ј]$7®•b·“ïu)ŽŒb˜Ñ½5t?+àÕä…ðʼ¦fõ 5ŒêÁ(F?¶‘ÐCÇÊ?bÚ>`ÝhÎUÅ—`RšwÅ®`0„’oÉ4’H%$‹¬"ëH>)Äk£ »ðxŽCxŠjt¢©h误(möÖýÏ0k0«ž”QéÀÌìÁL‡€É¼ÁLÁDcÁ„“ÈO”WI%KÉr¼æW£‹ß€—|žóExÊÿŽN®¸ýèàþƒûÜyÜån¢ë@ß…ÛB†VÿëѬ»5˜¥Õ»<ÌûRMœ)OÊÇHùD‚É¿#3ðZ> ]ò…x)[‚ç²,<•­ÄÙ:tÊ6ᑬÒá¾t7îJ+Ñ&­B«ôZ¤7pCö×ä]h2ehT¼sEÁ¸˜Î¬—9å¡¡|(—î¶T—þ”åãƒ7š¼TÇâ©j -:U3ñP•„ªÅ¸§Ê@»ê7ÜQå¢M•‡Ven)wà¦rš•{q]yMÊüWÕŒõCÔkºp^øZsÆ{¯æ=fK±m•`½‰­éf£ë9/ºÇcë<°ŠB»åw¸­m"nhçãº6 MÚehÔ®ÀíZ\ÖnÂ%í6\Ô– ^»,¡Öò4j¬špÊúWýEwü ÆëÎøªÏ°~f`}å`v¦`ö*ºjñ¦Ÿ ž|ùîõvC«šu!¸ª‹Åå^“ÑÐ+õº9¸ [„ZÝÔ貸³º•ÜiÝzî”®€;©ÛÁUÛ”qÇmþàŽÙœàªz_âÛ¶ól_*m™`_&ØÛ‡ +>Áì)¾ƒÌQæDy P¡ó+K´ÙÙâz?'\îç‰z½?Îé#qÆnþ²›ÆUÛÍäŽë“¸cúî¨>;¢ÏæësùCú üýVþ}‰ Ò®B°×ÂܾUXfÿLXêÀD%L¼ó3lÅw¦Ÿé`φˆÑ1H†6Gû[¡ÎÑg\qÒÙÇœƒqÄy,wÈy"·ßy_é”ÀïsšÇW8-ìqÊ”;-ìv\#,sÌ–:‰Jœv‹v:;9ß:wšlqf’‚L²™äâm|ÚN:Üh{u® â’AŠÚ!æ8éb‹£nqÐm*Ýü¹ -·®Üõ[¾Ìu_êò£ Äe–p§K²°Ø**2,m7¬Ö›l1l5)0ü.É7TJ6ª¥ W¥ë\:dk\^ËV»2YîgØ`ÚÚ(vËšùFµ#9œ.Æ % ï‰}#úcÏw”ðåJFsÅÃcøíž…žS…[†Å‹6›-Î÷X Þè‘f’ç‘%Yç±ZºÖ=_¶Ú½X¾Êýßò•U¦9 ¦¿ »§ÈÖ¥ÈÆþÏtÙ'˜+m¿¿‘¶÷Z#PMŽúñ88Ê>Ö(óÕc§ŸE¾ÞÜVß~³O8¿qT¬ oÔDѺ‘ÓÅk¼6Éõž+Yé•"ÍñÊ-÷Ê‘g{å™fzmS,õÚmö«×aeº×yeš÷å/Þ/•©ÞL™:’™}ê•?>4sЬD}>ÍŸûÇP¯o”¢ÔØ ;ú`kÀ@äxryþ£ùµÆ`A®1Z¸òëñ¢œÑ“ÅÙ£ãL2ý¥Ëü’e¾iòtß,Ešï³TŸ-ÊŸRÕBßê¾gÕó}[ÕÉ~ÏÕóü˜ê=å©×híç(ö1:æöÓ‘»‡Ž™Ò Š‚Ô(¶Á†X2”Ë Ååd† 3lj2Æ|/N˜&I øIšê?WžâŸbºÐ¸Ôl¾q•rž1_5׸S3ÛX©™e°ÂâÇÀj‹¸ fòÄ<.ˆiÞSÐæGßCp" -80(§Ù¯˜®[èu^”¹ÑÖȉé‡Ì˜AȈñäÒ¢}ùÅQ‚…‘¢äˆXqRÄ÷’9áÓ¤³Â–'„ÍSÌ ûE–­Š ]¯™ºÝ|jè‹)¡Ç»Mk"-&‡1s¢ùT‹?pžjPEq÷§Ðì·®ÇÒ¼3NŽå±Z,û¦Ò¿qBj¬;·(vŸ<.@0wl˜pvLŒ8!f‚ÉÌè)Òøèxy\ÔÅô¨åÔ¨Lõäȵš" ÿÇuyGEu¦qøwgfd`€™ÁÅ.誈Ši*e60ƒ U‚RUZbì2ÇXYKˆ(nŒQ@M\)GOÔ-–h²®kÁ²nŒQ¹û“ã9ùã9÷Þ9s¿ç{ßû}÷¾¯ÖÙìbˆ<Õ31òŸ=uO\u]ÚD¨1èDõ[~:s[<ס='ËyÜÄëµú¨ÖkPž0e £P”0I(ÐûHòôÒœøy2sœÎ&3.Nž›Ô#56CiŠÉuXSìhŒ©vNŒÞ ÖGïÔÆGp‰>Ñ36úoäÏ_kã¢E Q¿åŸÃYÆ|Œñ7³ìjdÿYÏc¯?4Øb¥Ñ¥IýQ˜4ùI„\ãtÁlœ-É4„ÈÒ 6)‰1òäDƒ")!ÍÎcŸ /TÅë+bõëÔÑñÛµ ã›\¢âÛzêâ/“Nž¿ÒFÅ‹¢~Ë>‡Ó\Ÿ3þ¦T`'Ù”¬âuy²%&G,5õE^ŠrRÆ!+ešaò—¤š‚¤ÉÉa6IÉѶ†E -ý¢»¸$³}LÒRÕ¤r'±Vaܦ 7îw 3wY`¼ä²ÀðÐ%ÌðRf5áQý–‹|'€Ã&`_°Ô¥³ïå;€I 2ŒÛ­‚çî^Û8cGbtã8Œ²NÅŸ¬þi ÂkÜ­z ·¦a˜5_b­[7 ÿ¼W`=.¸Z¯ -®׆—×]âï4T[è]MoÉ:À¼‘ë`+×ÀÇ@È.Àß -LÞ Œýp? ÇÐf' iî…A̓0ðà 8è ׃SÑÿú -Æ{‡tè{È€>-fôn)EÏ–:¸´4@Ûrš–o¡9tšæÐ…·XÖr0ߌy ÛÑTÆ»˜Oï¬&`ÛRÏÀí3`àQ ÷19´­*hZ] nu…sÛp2Nm“àÔî U{0‰‚C{2ìOäAy¢ -víõP´7AÞþ%¹ÉóÇP´ýJ^u³qW×ó}@w&cNÜ„7sÞGè§×í80 èu -p>-ÀþœŠó*ÈÏka{¾lÎ…ìÂ(H/xArÁ‡A$üupÓupÃ}ÍDwPÐÁA;Îñú6yDžt³Š±3çæ=€ñSîþ-€ÞiôŽ¥w8½ýO.¼Õ±°û]’°è°#ŽÀ÷Zàr_àÊ`àêHD€¼)PY^ãKø:_À×KôõFà»ÁÁnÜ ·»)kàûp°ˆ9×}Î5ØÊøOô= ¼G¯æ;Àá ¿ÿ„·ã¦ø¡pKŦ@ üØ‹…©+pÇ ¸ëÉBy:ð€óxÀy<äK°“¾“Iïd²;ÿB¾$gºÉcîM|Þ ¿‚èžþWúÏÓÿ Ðç"sO¯’^éï-ò/r—ü‡Ü#÷méP²AaNk€§Ìɳ!Àó1À Ì/Y$½Š^óãóšóèâ†ëbà]LzWs7¢Óˆªþíˆ0"ÜÉDâKBI I&Y¤€”¡ Ux…5x‰õx-øÛð ø{ð?|Šÿâ3¹÷DM?ˆÎ} :[žK‡ñWO2ƒ1 ¼F.~EG+§ë#þÉ—áK¾Ÿó q_Š¿ò«p›oÂ-~#nðÛð™ª×T]¸¢:†Ëª^ô©nâbÿ§èðç -8Kœ!N˜×>ÞC”ÇðøÙ¾<O}'ãá iøÇ dÜ”Ž[ƒ¸á[„k¾å¸â;—ü¡Ï¯Ÿø­ÆE¿f\¼ ½ƒwâüàNœrg†œÅ©!×pò/Ñ3ô¿Ì94L`º½ô"Œ"}*/¿–â§@%žSüßÂçþãpkØû¸6\‹Kø8†ÞNœQ‚³³p:  -§jp2`9N¬Å±€õè ØÂÙÎy€éy’9ØÇt>`:G}ËvŒúƒm›Þ(/»½cH?ˆ®5•¸'ã©ôIp?P;¾¸2z4.Ž™„óc§á´: 'Õi8¦¶ã¨ÚÃêèVW0Õó™.õbf¿z³WÝÈtª[Ùõ¶=h/ÛÔÃî -ºÀî ºÇn{ç¹h˸_E›Æ â¼´zy¥ÿ’ÊÉ“ÉTb5À­É ®OàÐ7®?ÎO‚ßEOH…hq`âtì ÉDgˆƒé)`ÚCʘÝ!³Ù]ÁÕìŽàZv{ð*vkp³hsðѦàÅ­!‡ÄCΉׇÜ7OüZÒ4ñgIÃ{ÂÿYûÂ*w¤}? ¸Aåµ/‚zýPg&óè™2ÝSÕØ:¡Qh MÂÎP3³}ªÙ:5Ù<µˆý@SÎnÔTŠ6hŠZ4uâušq“¦UÒ Ù%Y£éâVkNq+5W¥õSŸH—OýQZ*H—yYêå‡ITꩤމ&ý8šùbiÞ¡'çH¸ûÃ}Ñ1 -»"'b{d86GjÑi`6D¤3-vv]„‹m /­ Ÿ%^^%^¶DR¶’[ÖÂ- Û&­ Û+[v\^Þ'_þH¾ â{BWG²7ù&”ž3Ò¿|œœ º)Þ-F{ôlM±ã±16ͱÑhŒIbÖÆ¤2«¢­l}´C´<º@´,ªL\5G²$jW3­NºpZ£¬zÚfyÕ´ŸyQG}*£.*æD=PÌŽþNQ-øx‘¿æyO |JOÜiÍ:D'Åm”ÇÖxZüѬS£Q7«u‘¨Oˆgê ÌÒx »$>[T£uŠj‹ÅÕZ¤*®Š›W+›·F^×êã‰kS”ÇRÎŒëU–i¿àgh¿U–jÿP”Æ ¯ðyÍj9nÒÚ/Lާ]ÄnŠ·Òs·!I‰¦ä!X“2õ)!¨K CmJ,S“œÌ,HNeç'eŠæ%9Ä•‰’Ù‰eܬÄJiyb¬L·R^ª[¯(ÑíTéò…‰çø‚Ä»ªüÄ|~ÒïÊü$Añ&é \ÓÓ9 í£4{î±ÐÌIñFò£Ñ Ǫé¾Xn‰Z㻨1†b1Š©2ê˜ÊéÓÙÙ‹h–![\npIÊô%\©¾BZ¬_ +Ô/÷É×7+Ü)Û•N}Ÿ§?£ÊÕß&ž¿ñ¹zAI(^s?¸dN‘~7µ_m™ÔçS¼Î¬Jå°Ì<‹Íñ íT¥MAeZ$SaÖ2s -[–jfKS­¢âÔ\q¡©Ë7•K]¦*™Ó´Ì'×Ô¤È1nåíÆ}ª,Ó)âF?›é?ôý•Ï2 J/ŠWü<¿h¦³Hú]ÙÔçSÛ³žâ5iÔ禋P“ÞÕþ˜›1³3߃'# e1LIF"[”nd Ò3Dîô±Ó’/ɵ”Is,sevK­O–¥AaMÛÂg¦uªÒ-õK·\'žQü ŸnøŒ4AùšÛ´½´îÒï̶Ø&ŠW‹)—ù6s²†`VÖÛ˜™ŒÒ¬PeE1ùY ŒËf`ól‘Ö-¶Û\’l[)g³Î‘eZû¤[×(Ò¬›xsæUªõ„Êd½ÚÏdý·Ê”ù3Ÿš)¼BùšÏÈó³¤uØ´çÑÌ™KP\Kí×|ÊgvŽå9~(uŒD‘c< -r5påF2y-ãp¤°v‡Y”å°‰­9y’ŒœbÎ’S!3ç,’›ì«F{«Ò`ÿ×Û©Rì—‰§*½ý'^Ÿ-(_aðr…袙“h!êÔkS.s(§™N9Š]Qà—;yîIÈq‡#ÛËØÜIl¦ÛĦ»3Ein‡8ÕU(1º}‹ -$pöGn‘?ìÅcUkÉûÈ(‰b,% Œ¹ÄÀšJ,¢éÅv±¾Ø-I..㋪¤ Euòø¢Ÿ¸ÂÝŠØÂ#ʘÂO‰Çʘ‚—ÊØA›ÿ'“GHo­ÅtˆEÄ,¢þ9g0°—ñ°ÎŒŒò@X<ãaöh`òDÂèÑ2O -“â1³I›HçqŠãËK%qå•\LùRiÔÿÈ.ó°(¯+ÿ¾Ù„afØÔÔaQA¢â׺<ØTµ5Ú¨E£A¶ ¢ÀŒEE†qA&.TD0j]S4‰VmšX×Zc4DMòõ'á±>éï3ÌÂ}ï9÷ÞóÝ“Tà84ɬŒOÚOZ”qIÿPÆ/yªŒ_,*ã‰Ê·º8Âì¥o{PÄë÷z’NóýÜØ÷-gß‘¢ÄÔn˜’Ú“Òô˜ñé1—>cÓÇ £Ó§#Ó% iïJ‡¥-”¾•–,‹KÍRĦæw‹I­pˆ^±—4;D­¸éòÄ!:ùg‡èåâk˜{ëRž‡eÜ‹$“$Ó»0˜• -Lg«6)CŽ·×8cl¦7FgaTV?ŒÈ„„ìx Ï…¡Ù„øìéB\ö,IlÖ|ÉଥҨ¬Õ²Ye‘YÛ䙵ŠðÌ&rM¾æ±"<ã'"*ÂWýB[ +X—IYIoRðºÙ¢M^ËÞs=£ÂÐ\wÄçõD\žCŒáˆ5Æ`ã0DÇ"Ê83…ƹB„q±fL—ô7æJú·HCóvKCòŽviHÎ#iȆe!ëÅ×Ô¦°.e°.¯²I -ÿ^˜ ̦{êö~F`8[Ö˜B99c@±"‹uˆ(AxI$ %±è_2ýJÞF_ÓT„šf!Ø´zS²ÐÛ´Nèe*Kv -º»PrQÐ? -_H -Ä×ì\ la –›dð5i÷!݉tOÜÌø‹ØR r+ZáˆàJWô©ìŽÞ•èeE9æXèÌ 0‡¿eü,sÐò¾–Õð±äÃÛb—å ´–3КïB[Ñm¹(xuQù!ëbÏ#IÉåYØÄ}XLa[8º ˆ«lú5Nð±ºÂÛê /«ÚÚxÖFÀ£6îµ#àn›7ÛL¸ÚæÃÅ–-‡l…Ú¶*Û ¨joAe}uÍDì¤t#Ï#s½*Ÿg¡˜ó1÷á6®Jï Ýô×Ò¿ð­< pnPBÝèU£NþP6êáØ‡Æt³€Â>2ûï µ¿‰‹kgBíæC©žü•ÜfQú–ttR@ïZ°| 0Ÿ1ÏäO'0Þá6 z?Ðï ý €Ï'€ûQ@Ó86Ë!=¡æ¥Ã•x-~¤ ã0hå… •ãV>€ÚxèÚ¸¹Ú\[ 9ÆÏ/“‘Gä0ß+ÍÀ"Æ<ÛÊ5`¼#ÑßHÿa ÿÒý8àÚ¨ÚÅiðÂAÎu#œÇyÎã¼–âž¼ õâÅœ—äÏíœG;/"WXð¯ð _åB_-'{Ï9àÈN2¸ÖIŒ{¿šF÷(Æ;˜Þ¾ü™Ž^¯S€Ë@y\¢»|N¾ ×äÀu'^Ê5ÀMw6'ݯt¼$¿jV¢€;¼ ßáÅà. ñ=ø{\ôû\äû ôþr²“˜žyÌù Æ=†î˜fÆß -0^-½z»½ò~I8m|E¾&ߦwŽÍœŸ³>çáÁ½ñ‚‰ɤ¿¬ëDtò…Ø­;D¼¢éM"ÉP2ž$’ydIÆOÈÀK¬Ã äáä£Åøex†JVÒ“…;Ø€ÛØDW!-¥tUÐUÅñ­¸†Ü2Gq§ñ?¹Œãžã"G½@οèêÌ\¨!ªˆ“Û/ë"éÅo™ã8Æ9ŽÎiôͦë=z–ГÂQ3pk¹Mrpq«ÓUF™ŽjF¿g¹§ÑŠSÜÄmå$3Ù‘O"š%ÿCôpb.Ä‘h8®‰ÒÏ!x ‹Â×ÒÜ’NÂuY"®Êæào²…¸$[Š òTœ“¯ÆYù:œ‘çáSùfœ’›Ð*/G‹|š6üEÑ€&E Ž:´ãˆã=Ø~@ƒJDý¯½dœ j¥|å<\5x¢ñÄ]•7•\u‚Kª18§ú ΨÞÁ)õ<´ªßG‹zšÕi8®^ƒ&͇8¦Ùˆ#š"Ö”Á®ÙŽFõÎõ8è|\Ú±Ïõln/`uQã.b÷ˆ>ôûòØ<Þ·=¸Í||©ñÂE×>8í6­îÃpÜc<ŽyLÇaÏÙ°{þ ž‹Pï¹ =Óq@›‰ýÚ Ø§ý{´&ؼ*`õªÆnï:T{7a§÷ETùÜŧ•¾¢PÑEy"rËÊ]–¸½Y‚t.ú:ଗ+Z|p¬GØ{Æâß(ÔùMÆ^¿DØüÞ…Õÿ=Ôø/Fµ2vù¯ÄÿlTùçb{@,e¨ ¨ByÀa›î°P¦;+”~#˜‚ž ÅA¢PD -ß@ dü!ôXjYî?e©=¡—¢)PÆ ÔõÆž>QÓgvéÇ¡J?ÛõïÀ¬Ÿ‹ -ý”ë—`«>eú ¡T¿NøX¿I(Ñ›„¢`³Pl67HòCNI6…Ü’CŸHrCEIίx̲\fimc™?Æ×Æpöž}aëçêþ¨2„ÁlˆE¹a$ÊÂ&¢4lL†Y(6ÌC¡áOBa©oø/ÓåÕ}†ñç\öì)A55ŠJ ²Â.à®{]8ÀËîÂ.Ë.·]n r[‘ÛŠ(—­Â%Œ¦*X“èèÔ±mSÓ¦i3¶Î´vò!m¦Ó´“t2M3Mš¶il¶/2füð›ÿ9çËó¼ïùßž>f&m˜™J›`O¥fO¦-±'ö_æŽï¿É…÷¿É¥¿Ë¤ ©¿"¢Ü±Çø˜jÿ}6p/¸{ˆòVp¼\ÉàqéÀzœÏÜŠ5)˜Óhð¼FÓ+" ÓšjLjÜÌIMs"³•ÏìfÙ!v43ÌgNsC™óÜ æ"Ò\ç4wýY¿Sôe}¬èÉ~@DùÇùPCÇéÿÔܦ£î+äå;t/åÄám"Îh“ÉUc*7'sMÏέdFµNfXëa†´Íì ¶ƒ i{Ù~í0×§=Å÷hgùníKŠ.íUEgî…ŽÜ_ í¹ íyÿÚò¢ŠÇyŸ´î“îÝBÊàrpžŽ™e®CëÑmÀ¤nNèSÖgcD¯ÇÞ‚A}ÒÛ˜~‹éÕùØ#ºÛ¥ë悺£\‡nœoÓQ´êÎ ~Ý+B‹î¶²Y_Ù¤ÿ+ñ…Ш>B±Ê¨æ·I÷N1õ@¤œQDúäç,ù˜6ª0aL@Ø´ æ½4g"dÎGŸÙŒ“Èt›*™ ÉÁt˜UVG•–5„U~BµÞ$íåZÊœ5”·ˆ!òÓM^Zé{£“ƒ§&®ÚMpÖî€Ýµ6W&dw*Ý&”»‹É]Á”º¬è®g‹]Îêêá ]£ -³+"˜\ç”F×5¥ÁuWep½«2ÔþCi¨ýJi¬‰ -øÕzÃE{Rp†âÀqb€ÞÛÉS#uôÝé‰EuýS¨ònC…wÊ}j”ù¢Ôw¢Ï‚b_cõÙ˜BŸ›5ûšY“¯‹3ø†x½wZ¡ó. ‡¼¯ -ùÞוyÞw”ùõŸùõÿy¢_s‡j½J`‰˜&FˆÂOÔy]Ë妔7¯Giófˆ-»PìO…ÕŸ‹ÿ -f˜"Œ™1j½¿‘=äïdóüƒ\®’?è_Tä´¼¬Ènù"Ûÿ[ÿ®Èn~ ÈiŠ~Í-ªûJ#0GLÞQ;‰ºŠ;Z€ŠPÒÆÃڇŽ0wn‡)¸†àj¡ï2@×eE~W9òºŒ¶ËËätµ±YÁVœà2‚sÜàwyuð6Ÿü5¯îü¯îø’W·GyuÛ7H÷¢ŸÖé½ôÜJxZ)st~P@1Éлº¾ä÷oA^ÿnhRqp@ƒœP>²Cfd…J  U!#ä†:Ô¤‡z˜´Ðq65ô<»o`™Mø>qŸMéÿˆMéû‚Kér)=k¼Ju.µÓžÐFë‘Æ ÑDÚ5¤-õ–Êž[µÃ -dÄ!st#2ƶãÀؤ¥c8ia=RÃ|+,a_؉”pö„ƒH0»Ã§™]á ÌÎðM&)|IûIù7“4e“†Ö¸LZ³Äq¢h릶´Åc€qÈ=dL©“O ej=öN'â¹é$$O§`÷Œ»fbçŒI‘"ìˆÈØqãÙH+¶FBØ™Â3‘sHŒ\ÇÓ3oFâÔçHœŒ2‰§Ö8O1p¦Ö#ÑMÏ!Z¤-‘vÁ87 dRLL=ìžeñì\,¶Î'`Ëüf<³„Í )H\8€§µØ´hÄÆÅlX´ã©Å|sñÖ/Ž#~qžxq‹o ná=<9ÿ)âç þ…èCæ†h_$Í¢u˜Ö­ª¹h -П²fIH^¶6]àp)ñË D"ž\Þ¸•½„ßXÑ"vÅŒu+žX©EÌJÊ•AËß¿|ìò-p—~I|îâgÄ—‰PŸGÂ@ÕÜp°SÍ%giÎ9¤»Ÿâiò%Ò¿Lú/믱וPþŸë²«1Mãøÿ}ßSÑÇQQB_CŠRJ…SNéÛé“hª©¤¦¢”HER !„|ÖdB¾2Ñ9>¢¢eȲZfÇš±ƒ5š5Ë0ŒÏżó¯mwí\]¿ÎÛõžžÿÿ¾Ÿç¹ŸûÑš@ÒšCÔ€ eS¬eSªE؈éxèêxè¸Áuœ`E´ Dû994|Cݬ`®óóìµ@cž\ „0^_êºïœøo¶ €å!Àô(`ô ×$' ‰’ôNòRp’>šÙœ6³9m¡V†­<Z¹Ñ[9É­ ®u;ßáçyr“Ï÷»)ÚÀ½°˜É˜§ñ+Æ«f¬£´À0jÚRÓ¢ P6½‚ø€×76À -à}œ£óæ@»›R;àNlÌØ_¦ËlL:X;¸ù:8Ñ ²ƒu'íäF7yÛ€´Ü‹û€ou½ŽQÿ`Óô= ˜PSŸÖyä¸äÏä -¹*_õ¾6®™ßX°A¶æE—•Ûl–ïÐÇ÷<˜ï²ðßÍâŠsÒÉDwî' ®óL7Œ;áçà0Ìx½ï0êZSלV/Rõ¾"×ÉßÈ­þN¾'w97?ÐË=àG3àsò—Ǽ´üBOÙŒ<ãÁóœÅîÝ‹*à%…_꺑¥þaуBF’qd‰$ñ$o‘…×ÈÅ+â%–â9VâÖâ)6â *ñ5ø»ñõø Gp-ø'.âÍþƒoàéîï úò·Y]>ìˆ C‚ñ+"ð/Äà’¨•N­yÔYˆG(¢Î2ꬢÎ:êl¢Î§ÔØÁq÷á´¸&ÜÄ9¦î[þ<`_áGíâ¯äz²¡)s¡ä“ 1'‰#Þ`uý©Î¦Q/žZ)ÔʤN§`uŠ©SJÕÔYØÂ1·ákìâÔÕsÉ4rù´áŸ.Òáf­£ÿÙØ²~/È¢!ÿêÓ‹7°§î:WáG„P3‚ÙŒ¦V"uÒCþ‚¸Š.Ïbê,§Îêl N%Î3_¢gpˆšqŠoZ躙Y<Éù8I•ï +õ ÷á§¾dÁ”¹À¹JÝQŒÏŸ¹Ó0’©ÔŠåhIô>›žK\êP§§±­\Íœ““ÌE×Å140M\íts :á´Âk4ˆ2JÿCæ–’¹Åe.éçFžâŽ`†¢ ®J.¸$A»"gѦ˜ŽV½84ë%ã„^:Žëeã˜^.õãˆþRÖ/ƒN¿Zƒ*4؉zƒzìïu u½Ïa¯áwØcô3v¿E­‰Œ=ì 2·Ò–˜,wwÈ>®õ6Âå^ýX‚ã´±;šMÆá¸I(M"pX2 ÊqP™Žze6>Wæ£N¹ûú”bOŸrìî³µ¦Û±Ãt?¶›6â3³/QmvUæ±µ¯Œ-ýxNõ°‰È,+? bIa‰»J.ðùl?=´™*qÂ|Žö]?o°ý쵘‚ݖѨµœ–)Øn™ËyØf¹Õý‹QÕ%*ûW`‹U56YíÅÆ‡Q1  ë^Gù ‡Xmý«¬ånÊzxjÃ3”%ˆGÎyg–{GöúöÀÑFhh:ë!Øm3;m}PcŒjÛ÷Qe‰­v±Øl—ˆMv©Ø`7v9Xg¿kí—a}9VÙoEÙ{µXñ^–nAéà+Â'Cî %¯…bY(z‡Ô¿áÊø=þ}Übi«§Ÿ}zØ5Ä 56øt¨3¶8zc££?*ðÎ1åŽÓ±Úé#”9ÍÄJ§YXî”…R§<,u*B‰S™PÛ“÷ C¬ñ臣ì±ÌÓ%žÞ(òôC¡g( -<'b¡g$òµÒLŸCR¢Ïi†ïmE‚ï/R‚¯üÄ.®ø§©¯ a‚õÇóÎÇœ|â+¢ÐW‰…㬰@=óÕ®˜«Lµ?2Ô¡HWOÄ,õT¤ªc¢N’ÕiB’:[Hô+ü–‹ñ~¥üvH±þZEŒùNíÿ„ÈÒ»\bÜÍ¡\‡l{¶±«àóJza»ŽÅ|—7ÞóÇ[ +ÀΘà‰´_¤!90I“…ñB|àÇB\`¦˜/Æ.“> \/EÖ(¦TL :¥ˆ ú–<&¿J‘Aòig޳ÝÙÇV£2œ9àóRz)d>rIv°!æ›cvˆ RCœ⎙!c1#Ä !aˆ ™ˆØHÄ„Æ -Ñ¡IBTh†0=4WŒ -‘¦†®•¦„mSL -«'-Šˆ°kzaøù–ÈR“ÈYÎA#[Œ]dóû¼ëÐK½ä3Ùô’f€´ ¦Hž0‰$h\§ñF¬FhM0¢4ᘮ™ŒiáÑBdx¢0%|–09_#Müë2‹ºÌãøç73Š¢(à‘Bj‚+Š7&‚‚Š -"ǃ0#2L*Œ8©‰£­°©yð2×T¼2E¬Ìõ$³\³CtÝ2_Û®í¦f©­«™F»•m³o^R¯Ýýã=Ìïù|¯çy¾ß”-–´Ô}–ÔÔ–”Ô‹ÍRR¿âó¿-©)>óÏœ"3¥ä*¬’{ž$³±efªEim4%íå§wW^z¤éQÊM¦ é ʶ&ËfÍP¦ÕndX§VëL#Íê1¥Z—šÆ[W™“­›,ã2öZ’2ŽÃ¸cI²þ>ó¸&ÞÄ÷ý6j!‹<@<…MsùûLì)À.gf€ò2ÛËžÕU9YÊΨ,[Œ2m#eµ%)Ý–®´ì¥d;ñÙÓäìbSR¶×”˜½Â<&{£ytv%!»>€Û–Ûóh›ï!Y>óq4öM `,e,xа¥Ûòùì˜à¯ 9Á²å„*#·‡¬¹ý”f¢T{œRìc•lOÕ8‡MIŽïEà,‘²Ñö¨%ÒÐeÒÆÄF´î+¥ÕÍÕam€Ú­k§àu!¦ u‘ -¬Š‚áj[5Vm8ìªòպʭVU‹å_µZ-Öm—ߺCò[[7ä·¦A-Vÿôsèy¡\ÄÛ¾HJÇç1eRl¥4Ý^kЯBƒÔn£Ôfs µªn£–ÕíÔ¢:D~Õaj^)Kõ`™«ãeÚÊổÃf+]M"«Ë·ìƒ“4(…Ûð-ÒwZ^ÊÝ…Ä;ÿiöâr) Ÿãž“¢ÐínwFÔÎ[¥àèï’Zîi&sMk.:ƒšâ½B8ô‘jiÊj¹pk¹xjÙäµu-‹×âHíN~sÎñÜp¾ÑRb]ŒÏÓ05g9à§#ž§Ñ܆þ‹èïA¿V -xEò{M\0pȰãvÆŽÃ4äGÂhˆ±ã á1.ÿ:û:6zI®Ã¹:bQG,޽Á ¸¥…Ïr/¬’òÖ±Ë>¨¦Ðí[#…½Œ>šA‡¤VG%ËëhŸ€·š8Ùœ¨Íh[ém†“ÓiÊhÊßmlÒ‰GccPÏ¡_ONêIt=‰­ß.ýœâ󸤹ë9_ ñ9é%Îtz²ÎuR šþ¤Ðà½gàl€óf1Ñ3¨] &;Jw•>é%}Š—iB®PWf2ÄPäWIðU„®Ì«8rõ´f`ÖDböª4ò05€nt;¡xZjñ~“ÞyÀlý þ—€ÒÒ§p>³HŸûK×o“[ÔÈékòrfä>Ï7vßü’Ý@a5쇃L½í ¨‰Ð úB,$B&äé'fÝš¥ïåÑ¿´Hßêi5¨\÷µB÷˜u¿Öz}¥Mº£íº­=ºÅ¼û¥êtCïê:Æ~ή±Â5Vûìÿð)Ú@C(ô‚ÇÑMà©44sÑ›‚Ö ÝÕ4棱XׯuSè¬Dg-:Xs‹®êE]Ö>”®ßëc}¨‹Xô+]`åF>ü/j·h Ñ ã×ÐŽãð ½M@'_\h¸Ñ(Ac«{)ÓezO•è<‡ÆzÖ߬“ÄâM½¬7tT¯£~ kâÍaý¾ÿÁG)ùô3͉sþvÄÏ0ý™X\ ç©‹³²¢cgµÉh<†¶ê<õ¢S†Îot„Ú8DN‹ª¡*ê~½•ö’½D¸¥Fö4ác‹ûšQºl±/Ë[~øˆn(1ŒÀ¯Á¬0­dt²ðÅíSXy:n¢]¢Wµ%è,W-5ZCNv‹]zI;±bÑØfœ×VÓ U[´ÅâÓæfÙ>¶Ó=¶ÓlíK5äÈ9cnEžÚé„©›Žý‡ézjò>ãþý¥^¨TPnB„HÈ…’@B.@!BÂ- !"ŠBAQDTëýRu¶ÎºÖÖεgë™Ýéz¶³Óý³ûé™Ývvºn«Ý¥]öˆžýñ9oòþó|óæ=Ïïyžâîª2|kµ¯®1áæ;n¬mÆõµm¸¶¶W׆p%f.ÅŒâbÌ^œ™ÁÙçpæùeœ^w§ÖÝÂÉõ÷qbýpü…GXŒ}Œ£qQ,M¦¶B­ö!µ¹ïRÛ}›òÜ[‹;ë“q+67âD¸§Ä•8.n0ãüÎnpã̆vœŽïÂr|'ã‡p"~ŽÇïÇbü!MX‘„‹8œp‡ßÂÁÄ1»ñSÌlúÓ›£+¦žù,øeµ¡àj÷w¸´÷ѽW6­ÁµÄ\Ú”s›xi³§’ÔXJªÆñ$+“±Ü‚ùd'wc.¹“#˜Ý2™-³˜Þºˆý[Ïb_Ê7°7å ìI}ˆ‰´O0žþv¥G1öQÝŸP{WH{¯€Ž1jm—è„¶÷ ¸}=ÛÇÑ]8ƒÎÂc,PxuÝb¾¢Ì'úký…µ‰¾&Ñÿùˆj~GIÿ¿‚F²(£}‹²LÓs™1ì.ŠÃ¨( Qvˆ 0 .EŸ¸!q‚bzŠëÐ]ìBg±âNt÷ÁW2ж’)ÖZ²À¼¥çXKéMæ–¼C~Êš%Ÿ‘¯X“$º¢™|@5ï©—É)p¸œêS–=eÀå‹”®Ç d#ú$éJrÑ#¡K*C@Z¿ÔŸÔ‚v©­enxË:à) Â-‹°fÙ$sÉæ™Sþk”ßà4Èßæ8?&"ÿfE”9äO½O5_×Ð{HcÏq2KcØ^Ê2F9†)_¿<Ayºå)(²Ñ¡¢]QŠVE9¼Ê*´(Mp+ëФtÁ¥lƒ³¼ å;˜£|‚ÙUs¬^µÌlªë«úùˆü‘cQÿ‹®QfU=õÕ|ÆŒsdLÑè3NY†)GåëQ­F@Ÿ:­ê,xÔùp«ÅhRËáRW¢±Â€† - °WzPWÙ [å³jÆ™Es€ÕjN1“æǨ}ƒ|Hþ@þÉ1j¢O°'PÝ›zà49D&ÉNº7@9zˆ_ÃЦ‰…G³ ÍÚt¸´|4j áÐJQ¯UÁ¦­‚­ªÖ*;,:7Ì:?Lº>fÔ±ý «Ö/1½þ*Goø6Ggø!ù=G§ÿ‰rôO±û4â\§Q|‰Ì’ÝÕ´oѽ =?eiÕѬ©_§> †TÔx° -`5”À\­@mµ¦j#Œ5u¨©iBuc:ãNVeœfZãq¦1^áTïr*LÉ#N…ñKúåTÖ¬`o+&à™&£¤ŸîuÒóh¥<Í”«ÑƒzSyÃ(ôŽCèC÷ x¿IÞeùÞ_3ç¯LÐòVÐeî(»Ú%“dˆtSÍVÒ@uÍ´ªé[hæ§UIѶ -ÒöX”ú6¢¸# âD(ò£Ð¯Àv¿B¿ ‚€ù/ò½È ì?0‹œÀ2²¯€x€mŸƒçÿ¼Ž¯Y¶/ºâýsdœêõ?q»éð5­€¦êûi÷ë¢Ý§g„½ñô&#?ÈE^Ü`!ø! rB*d‡ à…lØnFf¸Üð2ÂSH/!-|©¡ûH }ŒÔàŸ‘ÖûÒz¢+NR­i2Bzé÷¶'Õ6û€ª@m’ ÕïòW7‹¬¡Dd§€;œ…Œá<¤GDH‹È©DJÄ„­‘$´#i¤›GöbÓÈ"6F®à¿\—yTd‡¢"\¶ \ÙT.j:Z™S:*抢¢‚ ²ª ‹+\Ü—Ä\REG'·r­IsLçè˜:ާÒFs§±´qî<z:ýñçpï÷{·ïýÞ×-{¿\³ÏÉ-ë®Ü2ŸÊ=Ãú3Ëð³ÒÑLVáh÷Gû5֢]2¤PÖÅ€Y’O~+y8«u»< -¼e* {a˜Ü -#åZØ~/cÑ`¹Å˹h²œŠfɱ¨L…52î–}Á'2|#ÜïåÿLyV-J¡! ’‰wìdb€ö›éR,)r¦–'¢?WòXÀXb''‹³-&ð–ƒ%PKGÙ[̲³ô–­¥¿ZZF¨…%Y6– 5³Ì'áÕRévvÌ’+4Ä»ðxªB✓ÐM˜JÌÞÎ&øl.@ÝÀ…è—¢¿ýå’c…­l+eSÉ0º’w¥/qÁƪxø«hòU4–jŠ«:•¿q¢jŸÙû>ç¼€ÍÍi06“»@¼ÿ0›{€Ï]1½ºK%ï’‰5Õe•dX+µ¨aXo/m` ßàÆ£ËP¼;61”Ö2üÔòð×Ñèë†@Ì:X»vò¹ãp ¾ã{”•KO"Þ£ó¥˜br€Ï¯.–ÂË¥ *ôÑ4­—œqÁ~³d³%dÔ³Õ³|à$í$;‰GƒñØ=€‡g~IÞƒs{ˆEc¿ÿÄïÓpnk*>'Í“†•|þ]%5ˆ¹!èúðqw4ê¥V èîBÉB%}lÀN:èÈPÎBpØ$aIø˜¡øvg9Îãs‚Ë~‚DŸDè$Á<É'ø¢ =óâóëïQµÜÁ÷%_BfjD¿d{=¾ÂÂÅYpNÁ'ð)|FLN“›3Ää,19ç%]`a¸ˆ—B.ó(}IN®PäWʤ«8x§®d±8¡DÊuè:ú1>¿JŒ;¡ëŸ‡Ð§„[oÒû ÎÂp¡‰‹p ¾„«p¥ñùùŠ…í&¹ù§¿t;î1(Þãá¹Ïå{@}> °nâñp/§œ›p_…®Ðbô?Åê'vÌ5Q5Mß+[4[ÿR‘jî³cÞÓr}Ç®{Gï±·ÖêEsS{õµŽ²Ã^Ð þó=Ñß9ñ×›°Ê `.àAèF¢ÛÝþhC+qè¤ê¶Òõ­rÐÈC£H_i!eœW®kªÖÕè2[ïEíBý ¡;­¿ðɳœt†Ó‹UvÐZ‚£þ+“~~vÔuGóMôéDç]쟀F3ÐÈA#bÃŽ³ZÌ™¤m e²‰î |öé??æ[±#(Fé×XIã ž¨ñ5µ¶h»HÒÜKS?´†àË(4ÑH¡ÓИÁé9häñb4qæR<_©ZÇõÙ‚»ÉÈ5bå.²Ô@dPÛù+^è?ƒûjFıؤóÄâsbñguCë ×|†Æh4’¸š)Ú$/{±£;vk.ç—rîrÕkµ¶‹müµ…O×qR-YÛ¤k#™Þˆê žë?†;@ÇÐ_e‹Ÿ.hza}þD Õ­¾è Bc¸>ÀŽر¼lÃŽ­Ø±;ê”Æ<Î-ÓzU‰­å«ùv5«¢ZV’áJ*­åçTÀ®Ó®)Wû ×êxËç­À€®+š>hv@ÏÌI½Ñy _bÐŽÆh4’¸ÐICg†V)BÎ^¤ŠfËUÞl–ÙlÖ›F-n~T––—Tj{O¥­žªÄŽ÷²‰\åë´·ÓhÓú÷Òîh{õ¶NÚÖÜCµ-ýµ¡U'­³ë¦5v¯k•}?UÙV¥ý­0īܤe†-5LÑbC¦Êf«ÔažJ–h¡ã*Íw¬Õ\§Ý*v:ª"§Ë*p~ 9.Ï”ïbU¾ñnñÜœoG‹åÉÙE»ßÚ†§{Ö[¨ÆÉE«Ûh¥s°V¸Dh¹±‡–û¨ÌØ_¥ÆwTb©…Æ-pMÖ<×I*v®"׸jŽ[©òÜVj–ÛFÍtoPŽûGÊ6]T–Ç}e´~ÖŸ™×üh¹¶¤DÙ(9ÊI‰Q&1·S¼9DqæpÅš»jdt/ î«aÑ44zˆ†tÓà®cÓ-Mƒºåh`÷yн\ÿgºÌ£¢:Ï0þŒ+*"àŽEP”E†a†eXT7Æ( -.Çšb´¥š&Zš¤q_cÆŒ5Ö¥1Ñx¬ñXÍÒ¨ÑZMÒäD§? æøÇï\†{ï÷¼ßû¾÷Þ÷É0¾¬tã^ƒÍø>|ß‚ûšñ3ÖÁs°æ1þTG9y™%ÑÝ41ÆW…1~¨ü˜PåÅŒV®1VÙÆ$Ù6es”[ ŒØ2¥ÇÍ”-îWJ3-Wª©AÖøMJ‰Ý`‰?f°˜¯r¼îŸ1¹ 6Ó Œk`TÃþ7ƒ8¦“bŽEqgò–ÃÔO9¦Á²›†+Ë®ŒøhÙâÍ`Uš9K©æq²&”(%a†,‰ó•Ìpœ”¸F‰IÍJHÚc0'…+ð ¸ ‰?ó:cx#Ô%S˜3ù=•|K!1å'tTN¢—쉽•‘è/[ÒP¥%”5)J)Iq²$[”œœ¡¤d‡-“”`™.sÊ\ŧ<+SJ½â¬Mеî2ÄZŒÖËïbS‚»•žIÆð•°ªÀ ¥ÄQÎg[Ú)#ÅSi)¾²Zýd±Q²5D‰©‘JH5Êœš¨ø4›Li¹ŠK+R¬mªŒ¶9б-QtzƦoÔ˜ôТ¨ôKïjŒí0­¥¹µÕÊ;1Uªe ¯† -˜Êï‰üßv~§Û˜7m]•œî­ÄŒ~2g V|F°â2Û-c¦Y1YVEgÙ56«@cì“e¯Ðh»K‘ö•аoPxöv…e† -³ÿGáö -Ïr+^eý†ti)̇Œâ¥ ÀÎß¶LUvyWMP*§¼ªªÕ½j4ʳrºUS·9×ä9û¾ºÏ~ înÕ¢µf@ ¶p¿‘¼WKžXÕN/`^‚õ¥ ¿Šÿ@‰£‘8šˆ£)‚¡a3/™fš«‘fl^˹-ð×~ä¢ñž**Éuvï4Ï{ެ•‚W¡nŸuè£Ù•-thF›t2àK¯ Æ` ùØÊP¾y;ÃèNâØÅ²‹ÍnþÝ{7‰ÝM.v7ñÿ7€\ìºÄµ7TN‹(WÖRjÀžÇü–g€p­GŸË½_–º¼&h'Œƒ?ìmãMØ×^Úï!½…A9€18ÈP~ˆ!ý0q¼ËÒÂG°…¾…BAè‹·l†ƒp.ª„|çžµžç‘ì7ݾ\æ½ýGšûà‚x¯£ð`K:Ž9:Aœ$'§©Íß1 -ÇG fó:GMÎÑhçIòù ÀâçÞ†ã* ß¿ç]ÐÈ{€Ý.ù¡ëCûvy¬ùHç8œZZgÚ8 ”Uçàø|J}®Ð'×|¤/ü¤¯‚¥ëFé„›ÔäkŠ~‹»E’o#v{n¯;tkÃü`Œ3¤ãóõ?M—NñÎÒ=üå]üå|îm-Ó-­ÐM<æ ­ÕW8Ë/ñ™Ÿãt?ÓºªÃºBà—9sIߦ»•‹m|Òêy= Câé­‡ -ÐÉFý¯{ùoèk•ê:þòKüåçøËkÄñOâ¸B—õ<õº u:¯ú˜æ=KÑ[Dp\§¹êÑŸDáÄœlAOЕýúpå@öÌÞF½=+ä¡1ɤÞI *ô!q|@#ŽÓÄq’8þªÕ´Ç‹”¯™–ÙB)÷’‰½Ão“±ƒdõJOòXÿÜWGt½Èaö€Þ(öƒV2:™hä³£‰:FG‰ãq¼K]k!íº„õ—³æ*íWm¼õWxŒvjgv‘…úBÛõ­¶¡ø˜­m1<„{ð/¸H.΋“äâ}ñž"Ñ1±++ÙYëO䱜LµËY–vÇNâØAÛTúuÚLMþ¤&¢Ø¢MÜñG2Ó¤OÕøžË<¦íóŒãŸJ‚‰pßW0`6Ø`À`6`ƒÁÜÀ -$¡&!Is±MÒ4I›&kÔf]zd=Uu«¢®«¶µÓŽvÕ6µÕ¤ýQ©ÒºNÝ:mk·UíŽvÞ·d­¬Þ÷÷Úz¿ß÷ðó{>ÔI}ÎRþ’/Öÿ‘xW¼%^Õ½ø¡îÅ‹º7ÈÓZÊ¥c×:š´ŸæïÖü\—Gåãù¸&ßdQû¤qXóžÔÍ<¯pz…ËúÕ}šá^íÖÝóÚés:ñóÒþ’DÁïv)^‰VˆY =·HÓ¨5¥K¯H:Ò©Õ »¥Ñ. fƨ4&¹$åã^íÇöKcEÿ’»ô¹Äi¹;¥:©Ûp\'zL»}Lçñ5iÉÞÞTx{Y<¯ðò”ž¯+ì='Í$éeK«D³Y¥ãäž5ίéàî¨ g£85Êé¨0w®½•“k8¾vÇ¢±}'G×ÝÃáuW9¸þIîXÿûc~ξ[Þc_ìÇ,oˆ°÷ÿ¼£ÐúS…ùçÅã)z­(Ä]Qxùº!šË·¸'&™s±yÜ[Æ© ÕœÜØÀñ-ÛØÉÊÆŽÆ p8nŒCqSÜ7ϸEöö³l8ÆÃyn7<ÈRüìŠÛâ_gaÓûì4~Êcä+ÞTh)çæ«æê_ÊÔë4î’·3 1œŠ7r|S+Æ"-4:8àbBË ~ö&ô²;aˆÛÆYLœfWâN÷²#é(Û“Î2—t…ÙÍ×™I¾Átò«L§¼Ç¶”O˜J|ÅkzýÂÃJA.©=«çyJ-ä爼J6p 9™å”\v§˜XJ­b1µŽÛR›YHõ²#-Àö´si#̦M2“>ÇtúSé gœb"ó㙲5ë;Œeý„Ñìß2’ó±ˆ|Å+ -çO›ôúçÄ =.VÊ%?{åewV,‹Y ,de°#»ùl3³ÙÕÌäÔ3ãa*§pN7“9Œçnekî4£y Œäíg(ÿƒù(¸FÁsôþˆPá;„Šþ."7)ŒðR¹î .‹ÓJÅŽ¨].ƒ%ùY—ë˜Ëg¦ …m¹„ K˜(¬d¼ÐÁX¡‹Ñ¢V†‹ü õ2X4Lq˜¾âí„¶ì¥GIP°äݦ‡è2=K ôeñ6þÒ¿‰ˆú7¹Q×¾H¿”úÔ_ÒØNy›•—™’5„Kâ/Ib¬$“SC¦rM6úMNúJÝô–úè)í&X:@wÙ8]e³øËo§³üæs´[®â³<ƒ·ââ7âCñ_Ák‰ðœÒ«6Ý¥Gı ±Yy™Ò¾Lh_Fͱ ™ ˜Óè3çÑk6´TÒmqÐeiÄoi¥Óâ§£"D{Å(¾Ê¼U»h­:D‹õ,ëxlOá¶}ŸfÛ¯Å_Õÿ\DVyFšW”fœ•/ªÛäk\û2,ªÖªŠ'X•L—5¿µ˜«™v« ŸÕI›ÕM«­[žêaÜÕS4×,Ðd¿—ý Ž+48ž/QïxKüEýÏDd•'¤{©VwQiÆ>± fô<¡ñaùë¯QÍS³–@M5‰øìxí´ÚKñØ+qÛ4;\49ÚpÕvÑX;HCÝ$õu;p:÷SçÑ©ñ6µn5z¢q¶¨mIÄÞšNuk>¶VÖ¶JªÚT¶¹¨ðz±xƒ˜½#”ûf(ó-Qê[Áä»—’öGÙÒ~C¼Îß(ñý“7²Êý_”eJ¿÷¶À¼˜ƒ"(¼¢YåRƒÒâZŸòþö X;ŒTv¤PÑ™ƒ¹³˜òN3eÕ”úë1ù[(ñؤ80EQ`…#.ßõ°xž¼®×È ¼O~àS -ü‘U.¶ê.HgIÜ*­1Ñ'üÒô´ký*•j;Á€Šîʃñ”“(éÉ`KO>Å=&Šzª(ì­¥ ·™üÞòzûÈ MÚIvè Y¡»É =DFèÛ¤‡~&~OFï'döDÈwKë ´Ä”ô†DP´K·IºN•®ÕAé÷‚©?Š¢ ÉL!w(‡œ¡"²‡Ìd U“9Ü@ÆpéÃAÒ†GI™#yd›GΈ«$ ?KâðÅ»$ }ÌæÁÈ*wJkY©ÿœôÆE¿H·M%ZcØU¶V¨ö‚¢QÕ[cȘ06‘HêD:)“y$O–°y²‚¤ÉZ'Ý$„ýbcxšMáÝćOb_O7ù²xÃÄGÆ?#~<Š´µÎi1"ÍÑ¡5»UžÕI»JåQÙ˜ôUžä†!}z I³±$ÌmÂ8·™Ms™ÄÏ`˜/6âæØ8ïeÃ|/±óÄÌ߯úù£¬›¿HôÜu¢æ^díìDÏ~@ô­ÿcºL ¢<¯0ü"‹"2  -*ƒ ²ˆ5®"(Ȧ((ˆ² (8* -Ž+¢ŽË©â‚©1n&ÑDŒQc“Ô%§iŒ[5±Ç¦­MjÔ¨IÛ4©Ñ4>99=žÇ™ÿgþûÞïÞû}ÿ½ßÁTÇ:+Ñ+„HÏ–s©ÃÍ'ì'- Å;øq?>Æ›øqƒ)öSè&ÅÕªtÞÓûüâ"Þ_Ðu•~ïuú Ÿy®®ú§zè±üYWzÑú+±øTXG¾OÃ~>á/Â~ö«°¿ûµØ·b-v7RÛ(“=z›B>C,Nóÿ)V|«íú^ÑÃÁ‰ÿóá)¶P+[©]GñðÙ9DF¡xÐÁ!‡úáNg‰uÓ%y³Öab QhŒ@#ûÉØÎ`›ä°5óõ2yi£>ZUýƃøq@ëÈÄVŽf*ã%Ž”cDåÇËu²ôX;õØ;Ùúà–£äM,Þ$Ç©‹WˆŽ™5Ä 1 -û㱟ŒýLV˜ý<½HîÁfüØ…;ñ£I+µÚøÿoå—[°²‰ˆm${•»ž -_v£ƒ'ý l©SŽ#§º8ÈiaìÓtÂÑŠÆüNÀr -ö3¨¾l4ò´?6ᇼl >UG$Öòo‹Vãá*V°‚Ì[Éèrª¹žª«cÇÕ£ßÁ_ºKïs¬œ4 Íg [{/Ç^³º£g¤ºÐ A' -áhŒEc)hd²²ÑÈcå³Ñ)E£ -»‹µŒ«ZV¾„È,""[Ã.¨vº­ê._iós°wr#í ÇkGý>>wp½•ãe³‡³l®­wòUƒS Vw ÕJçhYG¨Þ%Vu.‰ZꚢZ×L-qÍÑ"×|YÜŠTãV®j·ÍïZ¯ª®ªèÖ¤yî-*s?¦R÷wTÒýc•xJ1›”l×Dó%™G(1,V a‰–¦øAÙŠ /иðrÅF,ÖØˆ‰lÒèÈCuR#£~÷ùþ=Ø;iAo mÆ -Úá…1Ä -ùžÇ½lü˜Šé09ÂC“"ŒJŠè­ ý•ªøˆ(ÅEÓ¸ÈÑŠ¯±Q)5U£çkTt©FF[4bȽ0d»†Ç¼¤a1íó|Á÷§`×ð!víòS\7”yŠa&äpo -K#.Éø“ÝM Ñÿ£ºÌ£¢>¯0üdQv•MvD}†}GEepcÄ `Ôˆ\“Ƹ ¨‰5q‰¦iFk¢¤U“ظ/MjzbNsšÖ5MÚSÓÖTk7ãôIOûÇsf`fî{¿{ï÷ý¾×K9‰YƒeIŒPfR¬ÌIIÊHJWzr¶Ò’‹•–R%SJ½Œ©MJM]¢ãj%7)ÉôŠMG”`ºŸóþ¾’ŒŽA¶£õ,Wà}ÀžÌzÞ×@¹óy>¯9©ÎÊJõT¦q”ÌÆ¥Ãd2Ž—Ñ˜ T“I)&‹’Ó -`²’Ò§)1Ý®„ŒEŠÏX©‰gÞ­Xóš`>·xÿ7Åe8Ù†V7ºm\›3¤™0*ù»òùÌÊë¤ôÇ”‘á.S†R3Æ(Å;[Ñ9%ŠÊ©VdîLEä¶(}t Íè·à±j®Kœ4l)w)·‹W—že 7Úv2ílí {Emgíó¡6Ãk|÷m~snãžæ²Öé0Š(…z§²æ8»E B×°Hò~Rr_&¹,çÒK8­p.£+©Å*.]«¹ðtqáèæÒæ‡Ã­‡Aïaa=°§ èGϾÓÏw1«]_ð»¯5 Í©PFŸs([Ú\ö!kŽ\ˆ>ºZéõiÄJÉ©mì*si-ñµä±Ž<ÖS Qòè zt!êãaØGOú¸>jÑÇbúº`;Ÿ’6úÑ{[ èV‘fõ6S²DÖEÊÁèV¡¿Fr{Íõ° ¶À6xh¯¾ç$íà2þóÞÒ.æc}ÙËÅc³±žìcÈöÑÔ}ü¿¾ǹÀ}7TG½KH1‹±IaÍ1Ôyë5 ë¹‘ÚoÒz vÃ^xƒùá2 -ûá†à 3ò#jr˜Kú‘ yô3ý6ý Y+0›ý;Š“ég6~¨ÉK8“ÑN[Á °æÐuÌ?£ãEÉ\vih2@oÂÑ!Þ‚ŸÀ18ïÀIúó59ÍŒœ# ì• </²W/RôK Õ¥ Òe_&Øå“*F;íDjÁšÇ’¢7»¬ïàæ€ÖñøpjÀ”ÁY`›ë\‚Ÿ šUé -ü‚þ\eN>aF®Ñ—ß2£×éÉglø ÛM†ëæVéżu§õØ ïuÈ <ÀÆÁ=”wš'-Õ]UëŽêõ'üÔmüÔðu¿Ç×}›úœr Çuvxaù”Æ}‚kü˜‚}¤Ó¸Ý_ëC<ÞωüÁá <Òÿ74}t÷WEé/JD3½|t*ШÅ-6¿‘ø¿•øKˆßAüNâwë}üÝeâE|æy\ëY"ƒ·qyW(ãõ®Ð2Ç9ö?9<„{Î=õ;ô™Bõ¼î¯¨ÅDz¢QDüJÊ^GùtŽ<ÎÇ)òø)y¼‹³|‡XËû5î<Ò\‡iÕ0w´|ÑòG'X4’ÑHG# |4Jô’Ë=¶Í« ãOÓÜ»¶[;iì$Nb'Ž'±sqb×±sq“ØiRçÒÄMš‹Ó6½¥Zï:íÒum·U¥]K+T(L\¤Mh  B€6þá&B Ć m0Ú‰3?Ú?~:ßwü}~Î{ÎùÎû¼˜ýŒcûc/ßËÔVˆ0³î¬–Ö]ÔbÎ -ä|Qóë_‡75—ûŽæòþ©¹üO «op”}ãý©æœãú$Çì1Æs´4WG - :œkÑ¡\»öçÕi5Ï«½ù~íÎj%?¢LÁ€– F´X0¦…Âií*œ×\ÑŠv­i¶ø´fŠ/hGÉ‹š*¹§É’×5Qú&¼£”á>dò -éö³e¤E¬ÐãïÇi׸?€-Ye,«Æí-5j¥ÔªåÒJ-–ºµËЬyC»æ =ÚiˆjÖ׌!©Æ”¦iM—5a<¨íNjÌÄ®1]ר鞒¦×”0¿¥ó»ð‘F,YÚ¬^F÷ºƒz¯’ø«Ð§ÝË}†c~‘±,lZ§s±æLf¥Íåš1;5mnД¹E“æ¥Ìam·ôiÜ2¤1˘F-ÓJn\ÐÈÆU c>¶nzRqëU ZïjÀöuõÛ~¤þ²?ÂÈ>ä.š/8™lجÇ>Xæz{˜f,3ŒqÚ–¯)«Q)ë&Û³ÕiÔæUÒÖ¦„-¨‘²ˆ†Ê´µ,¡xù„Êwªónõm~LÑŠÇÕk¿¢ˆýsÚâxUaÇà -WÞ§ÍÒÇy…ÞEÒüi—tÈ-­À<×3ôM2¶s3îÈÑ6{©’f ÛË5dw*n¯× Ý§G»ú=Š9bŠV©·r\‘ªYm©Ê(T½¦žêO©Ûù¼‚5wÔUóŠ:k¾oÇÜgòZçIñǘìè"Ìr=Iß8ce^F˳HƒÕÔ_mUÌéPÔY§^g£¶8[®éR¨&¢žšAu׎*X;­®ºEuÔT»ëŒîËò»o«Í͉[ÿ=µÖÿ–ö¥?«kh=îRü˜‡io¤Þ¡˜yd,}®èTckDžÖ¸ÚÆTß––Û¿G.Ljÿ¼j7Tø2|[ÎÀ/ißW­ÿ?Õ³èœ@sµuÀ~NBâ£/ ]Ðmþ|ùü5ùÍj ”ɨRCÀ%w{äj©®c@µ£ªéœ‘³sEÕGTÕõ”*»^”£ëeÙ»Þ€_pý7úþ­Êάžæ¿øÚY‡jN~î#?÷„©¦ÎyºJäšä -ZU´«¦»VÎnª»ÛTÕTeOLŽžÙCSª-isè°ÊCO¨,| ¾$[ø[²†&[è=•…>Vy¹ý54–ИÁVˆA: …RÅÛ#Õ‡¨}Â…rn1ª*bQe¤\ŽHµì½nUôú´¹·Ce½Ù¢C²FSÚ]€CÚ;'KìªÌ±{ðM™b?‘9úWY¢ÿ’¥7«3hïÇzÏ£3N Að£Û„%nˆà»£RU_®*úKT>`RÙ€UÖA»6 Öjã`#øe É)>s2Æ÷Ãâ/À]•Æ_SIüÇ´ïÊ0ø²:ŽÎtfÃ|0„^ BÐ-½’'Fü”)ÕƒR¥›u¸P–„Aæ„E¦D¹6$«dLºeHú K¥É˜J’ 'w¨(¹[…Éã*H^T^ò¶r“¯*7ñCå%~(äcFg‰§ÐJ D HÌ~´½h»âè¡O™b¥l3åɘ*QIʤâÔ&¥ì*LÕª Õ¨¼T@¹a­ŸØªuL.…ä…ëELê%ø*P¤l§ÖÜŽ‹Üþ‘ö¡3×Ç% C„Ñe{ˇvý°äLJöm’Åϳ‡Ë?åÝ¿À}í"Î ô´JH†ï%æ:bvÌ ®™RѰ$®Hë÷®ã@ÁøíÃpíÇìÀhĈ²ó‘‘`Ö¼>¶5&ym xyõX{†g(b᜾âÝ5‹Þ s/w¦¤æ)ö1WΣ®‰0JW©AJëc6ƒ£Ô”G1¡ÇÇ ã#óuÊÆ¡KÂ?C"<Ëšœeße.Îî o-ëqæ3<ƒ{>MÑzêmx_ShŽB?óÝÍ´µ¦Ù |ƒôÑ5¡[ByÇÐ=g¿Â®’ìr¤'™§¨oϳ.Ï0Žg™KŒã2{ã9ÀçØà— ò2{ó2r‰õ¸„{¿øüœçÿ¬ †8Ä|oa¾<æ!æj¶ ]Óq©øŒ”ó8zlk =ï?4ݸWáÓ˜ñë…˜r ò öÇÍÿDöèöÆÖä›î6B·Ðÿ4-ëq‹õ¸EUsówJ2ß1æ»sgÔ‘ÊþÇt¹@5yžqü_!1&@€p 7ä"   ¼Vê}Z«V[;«S×nÖUë¥u®¶^ê”Z§­½¸YÙº^Ö9»ºº¶®gëtk×é¶sºZ§5ûa³çwø¾/Éóÿ¿Ïû¼ï÷>Œ×„nøZr¿1¨÷xvÀ·šщfK´0ß4&{C¥ýääYææÇÔi>ú¨>æ¤ÅÕÇ û~˜õqè°>ýJµ³%?ùÎAÛ¹B²>ˆþzôƸ%¨9 µöÝjÂø]çà…cÁFéõú2ur’¹9ÅA½ŸCa?/á~곟B;Øi÷ƒ8Ý'U¹„9@ÛõmÖcŽÂâ°íAÝg‚š|•FW´(ćWá$ü ^ƒ~øE°a{3ظž!'ïâã}òqžý=/…X€RlÝ $÷I¼pˆ.G·&°ê&ýÝ åÒúõ…jèK›t™>æoê¢Kšª‹š¥OèlþH·ó1ÝϺ¡éÆÎÓ£S{—Îô7$ïÖÛ˜~S¿Åâèõ §à¸ÝÃMúÌ«2Ò‘Zøv’>§×ýŒóϪ@§VÐOS;ñ»tF=ÄŸIü»õ–è |¼ŽŸÓÝž¦pOÑ©þ”¢y•ïe× \¼¨¿0]×¹ Üâ¶i"±'kº£ãÝL>ÕR¶ŽXÒÈÆfæ)®Ž°½ôÃ'Z‡ê:´ÿÇ%t,óe¶v+Œ-ÀHü|';í!›ø…Ä.'v5±ë‰ÝLìqTa'±&3òiÌB/±mt9.Öh¿XI´dn9³·LïÁ?à¦îE€³Á¥v(¸Ý ,ËÍäâr±‘5²^qÄOÖCr1—Ê/$v9Ûuñë˜ý&â·q'“éÌÆ-æï[|c!.©yds.3:— -ž£Oán1°¼Ã.¶þ̓¾ÙŠ×¼ -¨‹U2 E|ñÄÏ$¾—èEÄ/×"|,ÀÇ||ÌÃÇ\òq7õÑ˺½‹§³øÕLÜOgTÓáT2ÝCe÷èOð…ºÑà8[ÚS6óú[÷³Õ.çÙR^‹Ù/i8:4ðìD# <4ŠÐ(G£J3ð1y™J}ôP§Ý\M曓´„»ÕTÌ÷p÷$™:¤ª½ƒ¤ùè`vàá [Ù6¶÷ ¼jV˜Ïý^?½á!š=8-:qèØÑHCÃÍhóÐ(f…–£S­‰wÔ«óŽMÔ¡öAS4>ä. Y¤ÖÁ«Ô2øa Ý©æÐƒj -=©Æ!çÔv®A@{ÐÜÄql5[ê=0f±ÍOã5Ü3\ê6„ª;Ü IC¢tg˜UaÉšæR{X¶ÆõiÜе ­Tkøhµ„7ª9|œš†u©qØ 5D,P}ÄJÕEnÔèȪ‰<¨jÃIø\ëÐhn8zŒ`üÇfB7×]<ëÄK^:ŒCÕn0jœ!F­†x58ÔlÈP“!G†1”ªÞX¥:c½j­mœ¨êáÓUe𝑦ûTiÚ¨ -ó•™ÂI•šß‡ª4êkîÙ#Ð[c#;zyÕöÀ\·s,kã³¼4G‡¨Ù¡F“I æ8Õ™íª5§j´9KÕf¯ªÌÅU¡‘Q£UÕ¬òè•E÷È3G%1ËTlùŽŠ,ÛT{^‘/ö=¸Âõ hZ÷£»WìLޤ“ Zìœ1yÞ€¿:¼ÔƆ©ÆbT•%Z#-6UZ’Uaq©Ì’­ÒXŸü±¥*‰­RqÜÆSÁˆÉòYg+ßÊJ²­U®m«râ÷);þ%8 çþ:ô0Z+МËQt*¯ØNhåº1mž×ðy>GÚBTa‹T™Õ,¿5VŶDÙœ*´eªÀæ•/¾Hyñ•ò&Ô)7¡U9‰]ÊNœ%}±ÜIk”•ô˜2“ö*#ù„\Égàs®ÿó€Ö£³ÝÞTú=Ž€íÐ õÜWó|$^h)UŠ—b{¸ -íFùìÑʳÛäµ'+מ®œ¤le'ùäI.“;¹FY)ÍÊLéT†c†Ò‹”æ|P©ÎÍr:Ÿ–#õx>“3õÏzÅhÎL§9þµeHcø_#yV†—òRùŽ!ò: Êq˜åqÄ)Ë‘¨L‡SÎ,¹œyJO-QZZ•RÓåLï#}šR\ ”ìz@I®GeÏØÇ•˜ñ6|ÊõW<h5ú Ñ›–É<ÀXŽuPÅu9~Šù¬/^ÈN‘Û¡ —I.WŒÒ\6¥ºRäÌpÉ‘‘£”Ì"%gV*)«Aö¬ñJt÷(Á=OñîU²y¾/«çGáù ¼•Õ}U6w@+ј‹f·›yðPP•Üû¡€Ï¼à†LîÓÝCåôåðD)Ù3BI»ìÙiJÌö(!Û§øœrÙrjeÍmÓˆÜ)ŠË£Xï}²xQŒw7U´÷ ¸Äõ—²ä´ŒØ½hN¢%iãèÙUPE<˸ré{ Ū¤¼H%䙟o‘-?AÖ|‡Fäg*Η§XŸ__b -Æ*º KQ½2.‡ïÊT¸ ž—±ð—^pQ¦‚C@KП‰ÞDÚ‘±P •P>È…LÚÇÔ|ô}ô^…ƒd+ -W\±Q–âhÅ”X]’¤¨’tÈ‘Ù_$“4ÉèŸ(cé,J—*²t=ìT„ÿ°†ùOÃÇ\ÿK‘%7´±ME£F4ª¡ Š gžÿR]æAQßg¸Ø]w‘EЪ(* -È-,»¸‡«» ˆ -BÁŒmªÄƒÄD3iÔFƒ¹¦Ui;1&ͤšÄNk’NÇcšÌ´éL56iµÓÖ£­c³ý,îdÒ?žY–ýýÞç½¾ï÷}øÌF¦L+ƒ¿‚Ý©`«ŠWJµQ‡Ødv|dÉäÈ•ÑQ ìJvú”ä\*ƒ³C Î~Å;·+ιO±ÎcŠq¼§XÇïç¸þ¥^8V±„ÕÛœ`>(ù §Ý÷”*i¢ýådÿ_+³;IFYIžñJôdÈàÉT‚'[ñžÅy*ãu)Ú‹€õ®ˆ'/Ñ‹pð| <è<Ï_h¾Ûê&¾åðáq;(ó@1Ï„;³~$Jš ~d›Ù«äʼnJð›°**À@0àô ¨:3ˆž"$ƒ© ú*€¦ –ˆ!ÿyð¸­U•ÒRxP Jí­lÌL_€öqÿ~Lš‘lÉu1JXbPt|ïR.·Fz#ì‘CÜH  Ñ –‚N€˜jÜų#aÖðKðgpK-ðÔ/p€ -PHÌ9ÄœEÌ“}èO?üµð/¿QŠ_ÆÂµœEgKO ËF+—k+:w%—KƒµÃÖF.Ú(\/¶Q¶u`;ϰ-®D¸µþ°M·ÜT\~àvPBÌsˆy&1O Â_?¼æf4`‹‡9†‹V§AZM.ºð£?ÖP“Ãpxøs {ÈEÆz–êÑ3ÈïÏñr [d÷§àïªç¨!måÄ\@̳p}jƒ”Ϋ)ðšÚ$C‡Ý7¦XÓlàaüX‡ëñ£?X†6N`àÂÝLM6s°6“‹Íôæ¦U|RM»y†žØðSp‰w®+§TQç"Í¡|™ÄÌÕ«^3uK a·A?À ¹XWY<Á£Ôe0þþ":„ÛÈÇò1Ì%7ÌÀ¦é‡ p£;q|'õØq¥…žØöøL~xÔ¹”|çÑÂÓ‰9c5ü=’‘Æmˆp> -¶‚í‘Åûqð„î/âO=äärò, êwéÓ}øq€KùCðM~€Âî'™ûq~gôù£<÷>¸,ùžO¾ Ú™…äzb/üÄk„—u?#|ažgåD:a/€Caq0&ŽXÒÉÉkôêðãu¥7F‚£4Ú(¹8¶!Åù8ÊÃGÞâ¹ä ¿ŠàÎ&M“¤ñÀÿ˜æÝá s½A¯Æ ";`4"hu©ô&x›ú¼ƒ`x?NsfOÓ§8§)øûœÓ÷èÍw â/Ÿ:©r¸óà΄;˜Mä6îÙïH„óhD…9pù¦ÒɈH#¥ú88rúpüužÚ\ÀË,b—©É%ý% -~±O -y‰À.þ…¡¯!˜M÷ÐwÿAÏÜQ‰þ¡jÝ@Ç|ν†:¹Šª+¥OQ8¿ç|‚¢»Œ»HÓü–â§I~E?"ˆsp‡Ïè×hºë¸Bå„p;ÄC¤-ô>ÜU”n¢3o ï>×$}†ž¹ª|ýQúX5º€Î=¯:ì7j öÀ~ö{°¿û›°=Hj¶“¢=¤k? -óUxŽ“Â3z]WÀ]R¢tè+päk>ü\P\fýFi¤3®Ò[G¥P'Ø^„ÿuz›|¼…Â:êzö:~üùCüE©Õ0-³¥{/ŽÑJ'iÙ : ÓŒ‡Aøó ¸ï߯¸E¶¤Ÿ+Z§d$†üŸˆý,¢˜ƒß%ض㷋–\D›ÔêôåK¨ÉèË”à!ØAüøž6“­Ÿ]Täyï«xs‚cuåù9ú/Ÿ¡¯pÞ#­ôN¤õŽÈÀ[f|·a -Gböó±]Šm;V]Øöa;ˆíl/Ãn«vãÇSø±‹º<Φ7vÀ²·#‚­dr ´E·Aè+|2»ðòþ‘;ú1Ø7bÛŠítìf‚ÙØ.Àv#ÊŽíØ^ˆí¶ë±ÕDZéÊ=B]¾¥FÛ •yBðz€ÌõÓ}d»Èûô%á,œÇã%2vžŽŒ¿íäbˆ\lQ*¶'a{:¶sÏ…Ø.#ÛUŒíì{±ïÇ~=Ñ7Q‰•ŒõÕêåByˆ§ÄÓ5d«‹Œ®¦¢ôf'Þ©ƒÐ~á.Â=Ëß$8#ý²`; Û“±=äb¿ûeدÂ~ºñ£ ?:ñ£CÍZEŸ¶óKo·’‹Îê -êrz³™SÓLõ›9ñÍð‡q,ŠøãǬƒ`#£v£¿—ÿ÷pFØH±oÅ~:ö§b&'2ŽB".ƒ£ -Ž8¼p°Ù@w¬àÔtÒ±kµ„ªÔÓuÔ£–jéö §.¨¿‚»€3ÂHÝÃxßÂU32Ý_ Vq ¶ÇG©-*3VÇÑG&³àȃcŒeðTÁ³<µx²Œ¬´k1Qøèˆ…ÔÃK‡yèt7§Í͉w3 Üô¦öùsW;#µ—k·#¼ñw3×p¿5&Ū1>Y 1ÕGÙT5IÁ¨i -DeË=G‹£‹´(º\¾˜jycÜòÄøåŽm+¶U5qÝrÆ È?¤êø½²'¼¬ª„ªL8®€›ª4ÜÓ^ø¶°zô¥¿íþJÖÄßKXkñ'€/S¼üIF-6Xå3¤Ék˜,!K.Ãl-HÌWMb±œ‰óåHªQu’Oö¤zU&¯Ðüä.UûUfR©q¯JL/«ØtBE¦Àp |©'áú6¼½\³pÅ-õÀÏõïãÿ^~w[¢å6ä2™UcJ‘Ó”®jÓTÙM3TeÊÕ|S¡*Ìå*7W«ÌìU©¹VÅã–«hÜjͳô©À²Uù–½šk}Es¬o*Ïú!¸îð=¤pmÌ`­cmázk°ï.|q⛃¼ØSbUeMR¥Å¢ -‹Må–I*µNS‰5[ÅÖ¹*²«ÐZ©‚—òSš;¾IyãW)7urR¿£Ù©O+Ûö’fÙÞÐLÛ9pÜá{HCð÷ÁÙÁ -ØÌúU |ÀÅwä“*ñ¥_ÊñµÔ– ’T“ŠRST˜:A©“575Ksl¹Ê³*×V®DÐì´EÊž°T³ÒÛ5#}­²2˜.»5-cD™Ç55ã,øß! ¿Îö°4`õò$“|ÿÓeÔä}Çño…Jx  b I€ðä5AI*‘ˆˆà;–ùœ¢ÎVÛYKífµn¾”Vúb»›UQ«óÚy­UÛÎÎÛº›uÕ¶»]ï¶¹—ž]·›×énÝÖ9ö d½÷½'yÂóûþ^ÿÏïÛÀýz|©Á—*|­ÌW…%Ye–ty-Y*±ä©ÈR O®GîÜr¹òjU˜×(§µEk‡ -&-V¾­_vÛÙl»4É~PVû÷•g¿~«<ÛmYm£ÚÇj8! :œh0 LõÜ«æ·Jü)çÿJA±Í Í(·Í¤B[Ž6p©ÀîU¾½JöüÙò§kRA»¬Ž…Ês¬R®ã~Yœ;•ã|Vfç)e;ßוí¸%³ã?ÚÇ -øzXûÚ]ô ›p €îUò[(Áp9&ÊéHQ#]vG¶lN«¬N(V^¡O¹ˆS‹kšr\m2»{”í^¡,Ï&ezvÈäyFž“à¢Ò=¿V†û¯2¹ïhöï…¯‹•oh*¢ÿ¸ÖÉøSJøÝœ|ÎwÇÉæI’ÕcTn‘I–"‹rŠòe.r+»¸\YŵÊ, ÊTÒªŒ’ùÊðö)Ý»QiÞoÊè'”ê}üÜ”±ä ­ÅöRøæSp’ Àµø@)>”߬È8Ki‚Ì¥©Ê*ËPf™Y¦²IÊ(/Tz¹T) ±j¬hQjE'èUŠo@ɾA%ù†”è;ªÄŠs2T\åú©’*>W?öa;‚íæR©ÔƒÉ|ǤJ€ pÏŠ|³THÙ¾x™&')½Ê¨´ªLiØÔªP¤”*Ÿ’«ýJªnRbuD†êÅšX³Fw×lU|õ^ÅU¿¨ Õ¯+|þDñU·µûݬ¼mØo  ø¸W -<ÀÉw›íƒLÈF¾™jâ”V—¨ª’üJô›eðÛ”àwi¢¿LñþÅùƒš@ÀØ£+¢,€Xð#ü,ç~o?Ûcý-õb¿“Øfªä6WBQ9(.îåÃW -üõRZ N)S 24¦êî`ºâ¢‡Xƒ$HÓ)ZBh«B6„€ ¡)B›Ú&È–D$4¾>·´Ž±…ÁTP *1Q -<|.¨¡HF3ńٴF4P(N†iÅOç;J˜N3‡X3ÃÛ.ká¡–&€˜lAĵ )š³0‚(Ìö4ýà–æÃ1‹ø¦¨æ{ ½å$f[ƒ”3þüÓàK†–»7“E£•ü,^®m¼øf“‹v²Ã)¬‡#Ô#²€ÏˆÈvël¶µÙ/ñ dÛïxþ–:ˆ¯4‚z0^o€³¤Q&R1´&·J ȵ „¦¹è¨N–yÑ=/Ùù¼äº­ã‡MÔƒÁœîéä>‚²›žèÞËÿ!»góØæç}¦6blSà¬å|ös>iÌm†¦d„7yp.Ü]€6Ó­E,`‹ñc 5YŠ½Ñ„šôQ“>¾‚ö9€fêq/¢r9br9Ûc/[ü²yî¦fgÎ0™Ï%Äì æÜ.mÃùmÔã¡ýÒÖã,°ùÿëšo€:ûH—»Cš„»YÄk„7±f-Åÿ׿ØÒýPl,ƒ`Ø'í&'ß¡O÷Q—!ztˆ~ˆÁ"È'(â~ØÇ¿—sbÏ«üï5’ï*ò]ÜMsvü¤,!Ê{_Œ3Ê÷hlù<&Bh-ÖTlÇ„É0 ň2é»ää=r?FèÑâì(¹8 -Á‘­Òa:tüPu¸V·ƒŸrŸ4ò›°%Æû-°ì‹ñ|Éé4.ÎF©&‘1B¤Jg蓳øqº\àåƒèE?OÏAr–3óì!pF•p»áÎãvúRâ#<¿+Æ;ã<ãa¬u:ƇQôx\Œ›(±.ŽA]¦6ïáÇÌìûÔä}fõ‰¿J‘ß#È+¼{‚í^ÿ‡8¬£iþ‰¾û=syS~}ŠŽ¹¡™ú=*â:ú+”ÍG É/P]WiÔŸ£%.£«~Já~BÁ.éi\:¬·Ðop÷¼þ¤s0¼^/càü_€¿ƒ¿ ­>Q\f¸ -ô4æGªÖ5tÌ»jÂÒLý?~„ʹˆòy -óJòCá†ÑxЙOEµØö‚ÛðÞã±—öh­4¼7ÓjvP„}¶ëhÅ©øß„ÿ3ô,jñiÔÞ0Úî)á“(Ò'´ -ÛØÞL5¶ÓÒ{ðä #uŠÌ¼M›ß`”ïpEW€íàãhë€óàT¬ÕŸW<~§`;Û¹Øv0‚Å´i%ëômüx}¹ %ùêrǘªí¢ as9ÇÆjÆj=ÇÈ7ø{L`e º¬RÒ5ã:ú%.ÇZ;ÚöÏÅÆ}÷ØQ`ÀO#6³€ÕZˆm/®¤ÚuØž‚í{°ÝŒíVlE°Ý¥ø±º è«TeÇê Ÿöе/àÙi²ôŽVÒ›«àŽbõXŽø÷öØ1¸™\ÜG.¾®tìš±k.l{±]‰íZŽÐ쇰ÆV+¶#é]LÊb¼ø -YÇä:†ã1~6âžÍktv Ø5bׄE ÈgÜØ÷ª?–áÇRüX¢ aì¶2)sèÒnþ–áM?Ó³‰ŽÙ®:e•Ž05&9¢ÏÀ(÷F©ýøñ»•ãdè}¼{¹·”\,æ¼XH. &=ôFóÚóñc~tªŽ!ì…±Û¦vî¶á,¼n¥/fÙ:´™z43UaêïKý‹ï£âH}$AÚÈ+oX&²fp¯‹ß:9/:¨ÉjQ6ö­LC8Já¨dRÿËr¹À¶u–aøÄ‰c;Ç—ÄNì8NìÄvlDZs¿:MÛ$m×´ ½ÐKÖ­ô¶¶bíºBJ©ÖA·VëS·¡©ÝVº1D¥ëm0ÆØ€A h*hš6&.ZQÉÐPG‚'e=:Ççüç¿ÿþ½íèô 3HìÞ1VÎ*fížì —¦ÔÏ îc%õ±šûX½z>†ÿéA4§lsGÿX ã°”gKH —˜´¨°;µ;Ѩ$þ:uè4¢“ 2èt ÓÇo>õ.&¢ zg=;ì6Þì%ʯÒc³ìx/ÁUø|ªû8fw—“´Ÿcn–ÀÿËïç•iž¥L¹"‡Ì.õ™|ê5Õ¨Ç\¯nsL]æ”:ÍYuv«½0§¶¢µ-W¦x­ZŠ·(mÙ£fËA¥,Ç”´œVÂr ¦áÜдv¢¹ÁMß{æRÓaâÿG^ï{걫ÇjSW©¡ŽÚKªÔVZ«liD™Ò¸ZJÓJ[;ÔlíWʺPɲ1%ÊÖ(nÛ¬˜m¢¶ƒj´Sƒý´"öË -Û§áÜÐ:ÛÐ\G8N -6 -CÜ÷C7Ç]'±´c«aV«£DY»]-v§ÒöJ¥ì~%íuJØ£j²§w´)æèUÔ±@ Ž1EÊ×(llV±G!ã~Õ*h<§ãG -W¹^‡kú›Ð[í›KÍBzøßNUyŽÉç9-¯ç²¼îßÃuùÜ7µ ;Ð\MZLÊ3 ÿ¹v@–giÞ%‰§‰r1OÝ5¸m -» Õ»+rW«Ö]§ 'ªO³òWäT]¹HU•+äóÞ©Jï.UøØq}Èíû¶\¾Kry§áïr{oj'“蓎@.Èpmƒ4ñ$x#–FÊEˆ¥Þ[¨×ª ×¡€×¥j¯WU¾ Dä«JÊ[Õ¦Êê~UTÈãŸÛ¿A®Àçå |EFàpJå‹r®ÈáÿPåþOtkÑ#ÝZý!ú€kRÄç]„‰'DÙü~‹ªvy†*ªøå©©—»&.W0#g°GÎÚ…2j—«<4)Gh§ì¡/Ãò…žRYè¼–­öcm¦þÕè݆]‚hƒ44O#ïÂ"ž¨âYe¨PžP™\u9ëÜrÖWɨ¯Uy}£á4tÊž'[d©Ê"kdlUidJ%‘#²DNÀ *Žü\Åád ¤Ô¿½EسAè‚,$!†=ˆð®‚P •Ë€91B†4B¶6LÖºð"üog4ŠÆt@ mM@÷µm̆ÔÝ>º¶œTÂðš±®âÔib‹8d‰ã6âcL–ͼlìl&ôŘàãqÆc|-0'–3/—§<~wé4\×|tz¡ÓèF¹Öaɪ{Y´× 6t-#’‰ªHb¨Æá3ı‚8VÇjâXCëˆc’1™dÑOÒ“T¾ž -Ö3ë¶oíc”e}~–Œzõ‡ÊÑÎ.hE7Á5LØÚ\=5Ð-C·h,¯I5$sÔëo%{sIߌËF¯Mı…8¶17v0&;XÔÛØíTºàï¢ð¶½ÒVæÄæåfÖç¦ÔßÏô¤H3׆Aæ!sÜÈà+º…+óš³z ))"ßö[‰/:@ënúdw±t/óô ıƒv?›û~Þ—èÜ}ìS¬) -ñk”yRÚ{žòÓ꣯³ŒsŒ"!Úì¥ÍíµRÜt{^sk^ën¸h -Æ‚zóÉ8K!LŽt?Iñ!ÒÃÄq”8fe‘ep¢ò#Tx÷p˜lþöªC¯©ƒþN.a0ÆU´ÙX‡þRÁÖ|ûîÉëíÏë¼e€ø>oHº–Ô]Ø)aþ0K$æÇ›“¬—S¬•gX'Ï0ÙŸ¦“Ÿ¢qßÚ%=y„2¬ãçÕ²œ1@Û¶k#ãÏVR°+¯{ ¯ù@^ëëy3òxޔͺT'áé¼Y{.o¿?$Ž ôÇ%ýKŒÉE&Üúâ<{Å‹ÌÍsì™gŸWí Ú´mlé¦}y³õ`¾}ò{ÞÍš¢çó:Lmý`VKs†‘%OÊ,ý~¿€·ˆcš½cšú·l:Wè‹ß ú{Å›4ì“d÷ºÅ§pþƒ·úOó/yÉþCú/s ü/dì"oGñGçÛ,’ßá~®à§~ͤy“IúKÚëL–WéÀW°ã^/ë¡}_%Ô¿é,jgàœ€'à¿èÞ€ üUf½§yw?ó6sZ)ý -—ð:â§Äñ2q¼„¹„Óº€{‘Esx–ÉûqœÁ­~—Éó&̳ÿg¹ÌcÛ¼Ë8þõmÇ~í×v|Åñ;Ž׉8i§¹ê´Mš£é‘¶ëµµ¬´)ëF‡=¦±C¨¨Ý Ó$@ •c“ÇL †´ILHC‰Cô©ˆCêÚÁ„Ð$PøäPõéë÷}ó~Ÿç÷{žßï÷<$ÈËé%<ù¦þÈt~´nû+ký|>Äî¸ ¿Ýœ¾7å¡3 0†(_fôѯ¢=„öÚM´gP^ <ûÐ?ˆþQRã$úgH“GH—‹X†ô¹¡xú< rõ/0Òç°{ >OÃß°y Þ†×áûë!·ã»Ý$ð=‡~7óVÃÿ!Rd ÝíŒcºsŒe Ýe:Ê#hŸ$…Ïêª>‰+xñyÒëKÌÊ+Dègtž·á°Š—«ÌØ*s½‘B?ØLm* ô×Òу®_×éu¯ÑÛ]Sµ2º5t·âÿ¨ž¤›ü,Ýßôv“#WÈ‘Ëøq?>­2ãSü{Rý#Œâaø¼ÞÕCdÚCØþ¬À/6Óük›Ëà*< 3—™‹‹ô˜ÉÏ‹J³\;Ñ-£[Cw‘6Ð@wÍY4Ù:0 G‰ÈÇtš7òå)T 2÷ÅdÔ ýV×9ßÝûµÍ-hm[¸°¹=c¬0g‰ÉÅ!´ËX¨¢=ÀˆGÐG¿‰æ š‹x°Ìl'[Ïp¼<ÊÝSxö<üÙó:GÏŸá#~¯®Ã)Ê|¯{Ã6Û¾o 'äàhò¢€VtaEÐ.“‰UôÐF ½&™1KT–ðä0{Šl9O¤.óô:¾H½¿æøÿ€ëê:7°ÅŽ¥µ­ñãëv9Vá ì—]7Šº&DÐnƒ,úÍãÇnõ¢_G½q´§É9þßGÇ5Å,NÁ 2iœxŒù1öŠ1vœ1v¥q|X‹ÿc3g9ò޳¥-s]‚yŽãÝlû³äÅ.µ íG;Qô“ègYl”±QÅÆz t'ÉÖ]üÚC”îóYIÈâ§È ø«oÃp þͳU=½óspôôQÞÀÌZyÖ²QªM:,š°¸Ð÷¢ï×6rc”e­4Ô6ÊØ¨¡7ˆî6f¥©~f©HÕˆj•xô²2{Èúо?†ßÀ]ø¯>ƒ­3Ø=ÂѲ³Ð„q޼QžàϰǦ!‡[[­†-&vÂØ‰c'…vŠØ©`§;ÃüšÔf°L$»‰G‰S´\V—åY<þ:üÞ¿Ãô(¶Naó ÇÊí¦|íayÛjiÏÈ“*ʪʕ‘3Ý”=½$[愬™‡eÉpf(HÓiúÍÔàŽcc{S”àÃPƒ2(ÿ:xž†$¾Ä!š´(œ´*Øn—?í’/ãUKÖ/w6$gGLŽŽ”ì²æ*²äè)r“ „F%Oƒ’§9Ès*ä© r¯…gÇïá}-ck†¶lÍÑë@%·!“ƒ4ï’øçÚ -AÞhߌ‚M-]N9K-²u³Éma©°À*$MG+],ljèjùž@ÓÔÃiÜCeP¡–®P où)àCù®Ðê$PƒnèÄNÏS€÷!ž6Jô@e«œ‡¬½4U6ó]?MÅS'hu>ªÓ[Õéwë4quš˜:Õ@z¾ŸJ¥Ÿ†¤Ê±†µ{š-´hAz Äï<Ï2ØLrr®YÆ~û´°ÎšEÖ~ÖÚAœ!–aüh„66˜“×@¬A<ã0Ç€fq„&mø&Ð ýŽឦ ]ú` ta/Çç)®qîà ÇÄ®Q“óa×E{HY´a“¥Na·V䡇6 ¿ÿ3]®ÁQÕg6{ËîžÝì9»›½d7›l²’˜²„[ˆ Pb F -*Ãe(P*ˆS­S -J‘Ò/PÊ¥j-2È Ê´:ÓÚ±jõ 3öú¥H«m-Óéø¡ÛßÉžét˜‡äœÍ¾Ïûoÿ÷¡ýµ’el?VáÇ~¬å‚YÇpYKb×r 5ã c̉ջø»c,NÔÑÕÏY{àÄÏ&ΜâÌ&¡38¯Ïæ]ìp~ÙᣴX¢±á,¿kÀ:gÞHL6³˜}?¶Ñ¯Ò'ÒÔÛ ðv ?€±m÷I[ÉÇýÌË-¨š¯¿¥~x;g’rœ!Öq68¯w©sΕΒmó­QQ]·Ú˨#r–s[ ì 7»é—Çðc?}²&{Œ€ï‡€¹‡Ïî—v±ÍïdVí¸¬ÉÄ»H˜²Œ³Ø"òÏ8ñÜíœs­ÃIKiø&xÄA;Á.GÙâd¿#’#¶h¡^Ÿe~œ WN0?H“ç°ÇHæ3î(ýqe3~VípçáNÀÁEïj'®[œóÙœ;ÀnGˆØ<¶$”¬êضH9®ªX< žWU¸Ò~ºH<^£W^ef\¢.‹ ÷euîqDæIàNÁ…Û¿Á‰ïÃï^pÐá³¹ŽýÏU‰6×Kà<`ìˆ,®D$ô+ð5ò>õñµñ>ïÉ¿JŸ¾Kbß&¿>Âf-ý | -þþ‰¶º…Îü”ÝùïìïOl¿ÝúÛûïØð¯£.> I®Q¬¿Aa½M¡¾…2zõ÷ øüó2zâ’àÖQÜ{uõºÎ¢k~Šž8Ž‚Ãà p Þ¿‚àÏà÷àºz]uq-óÛù›lð¯£$.ãÇ%ü¸Èî~Åuž¦9Gñže‡m÷~~|?žDë~—bz‚":À©÷é'¤ö—ð~BiUhŸ -^VˆXe‚û]ðš“ÚSNÚŸ&ãhÝq4æ8Zæj%vآljÇ~†ö>ö^çôån†×.×NÝC)­¦u¾†Ž}€vÝ×!¸~D‰_Òvý·<¬ÐâmvÙ\p¸Ç²ß;Ñvâd`ÁIЈÝ"v;±;»eZu&v‡°;»#”óbl/£"VÒÎc(¼Íp<¤ 4ïz"µŽŒ­%³kЙcp¯_/;muÐi÷Gœ–Ü:ÑžµØŠ ¤@»ENØA´{°;»Ó±;ˆÝaªrX„íeŒÒQþ­Ã›­‘¹‹ì¬ “ËÉörݲW¡ª+b› ¶Õñf¼Î8›Sü a¯Ä@䩾"¶;°=Û}ØÀællÃ>B•~ ¥¹‚*YÅo›ðêa®šýTÎ1²v®ú|*8äŒ=¦çÄØ·¯{LÚcz™\ØócÏQlç‰z+¶Û±Ýí>l`o6 ;¨’¥š‡§s±:Ìé>GÅÜNU QuCtôº~»œ1lóEÕëо¦ìkÙ¾² Tc× Á~û­ØoÇ~öû°;@Vñd»ˆjY®dg:Yà¤ÓÈG™®-Óåe:²¬ÿ€ÊÄ5c_ £Œ÷;¹~‚σaWuM™C,‰Ålr2›Ú˜…³T2ØÏc¿ûíØïÆ^Ÿúy;u£õÅ)TE “ÉG7Y»èþ.ýü›wm…kŒUh9WÿÂÚêJ4fò<ÀûiøS&ýÔçTr2•Ú˜JöáG¯àh‚£ŽvìMÆnY·ám'±è ¢ídvQn£ÒKt\‰iUdò™~E&q 6qÝr½-e¤/`53ø½ z¹nzð§ÛW£n·ûAì‡AŽI8²p4ÁQÄ^'ÿ÷âÑtµ¡UR ÍTxý‘'óy&@#5ÑÈThÔG<¦õðßïBVÒaÖŸY  ¦ðÜ:ølþ´<*ùjUò„TtGÔꊂ¸ -®”š]9x -ðLÂf·]eå\s”u¨Áu§2®U`‹Ò®tøðyãƒò$î;±B5‰ur%X|Ü –±øE€ÎŠ}¤¥ṗs&ëo/èEÐÌ»,HãK}Ü¥xÌ#+æ“iÕªDbñ ‚‰°jë£òÕÇä­OË“ÌËlSMªG®;lq’fú¦¹Ò܆i–ßÔ €I|HÞÔbx†SÈGdQh- Ï»|Iñy"Q£XÂ+3îSD~õµ -&ƒò§ y2Õ4Xr5ðÇ 9œg—Ï¢)²ˆ¥K{¡caÍq+æX³ÜÔY–φ«$ú/ú"\C¬Þedj(fžsø‘I>'ݲê=Š&=Š#íU0ã—?;ÇðÈÓ¼MvÅ«_nFã5#\›O&~¨ÀmXàflÞØ’šð! |¬|e6’¬÷;A+hâ9‹/)>«O»&ˆd\2²5 -æÜòç½r73D ÓV£:HJW §ÛØãÛÐxmˆÇ¶A€ÖlcŸoC•¸%Kˆ"›K+>´|¢¹pNç+=`hi¤y×€)üˆóÓÞ…ÿË~™F5}faüX5 àZÜëZ«"ˆ -nUƱ®3hÝ:£ÕŠ­Z+neœ¶J;š=ˆ²HYB6BIH€@€°„PÙ !Ѻ/ÓÅNíT;Õ¶ÏK2çÌx:Ûɇù0pÎ=!üïóüÞû¾ï½ø?}fÀé^”÷LÌR³pÐÏ†Ž¹`@Vr¸¡€ñ…@¬GæÝ ôòAÞw"0<¢ŸŸ‡A!ÃÇ\¬ÅœÔª©X<71 1 ï'#×hñGŒÂïÃñw_à¥ã±C0" š 5m ṫŽ`ø Á",BÁ",Bñ P¬Gèb†ÈP ­!ÜBpS/ ¶³æt²Á¨3ðÏž‹˜‰ß§"×$ä‹÷Øî”/°Ò0ªá¦¼` %¾„Á¥»:–BÇrèXa`F6;X„Ap؆ٕæVb€\½ñr>i:ù¥_PKðüùˆÙøø ä›-ãñ:ïGÀ¯/òÒ÷9’c+.q|Äh"ð|èX… ~5.û5¨uбßFlª 0²a|x=†ÊuèÖÃç04¾‚.zõE4)÷¨àš‡x ù¦CË$¼úãýHl-¹ y½×±;'åjn€ i´\ W8tl-бk²Û°ØŽ‡nAà ÛpNlEMla¢Yã²6â{ר`ä -€ÔYð;¯ãày8ûÂï0ä´Ï_õw97º= Ec‰g»@Ò€î“=Ð Ñбõ¹¿QøR$Ή ´{P»1¨îj§æÃëÜù`€× ðBà΃í…VÝ•‹ ‰Y”k`Õˆö¢°õ(,7U -UØ+Ø'F|`—€… - ^›EMBîÑðÍ@n/âù›+iúQ¾ýÃ'B/ p¼aT‚/DŽ;—¡C!ÈàXލBÔ"È0‹cˆr¢>ºQ8ô8/l0ÝŠûÃMÄÐ?þxy~nÈ0áë7|ä¨ÑcüÇŽ›0qÒä)S§MŸùâ¬ÙsæÍ^¸(dñ’¥Ë^^±2lÕ/W¯Y»nýÆ_ýzÓæW·lÛ¾ãµßîܵ{Ïï"¢ÞˆÞûÖ¾ýo|çÐá#G½ûû?¼÷þ§â>üãé3L6‡ËÄŸ=w>1)åBêÇé™Y"±D–-ÏÉUæ©5ùڂ¢âCi¹±¢²ÚTSûÉź†Æ¦f‹µµÝfïpvv]êé½|åê§×nܼuçî½ûŸ}þÅ—_ýùëoþòí£ïþúý“~üOüÿŸû÷D%–}ày1ý<\'¶_ ¾gÀøKÄù"0X„8d$ D„‰”0QŠŠPÑ‹žp)#`ª@ÆLÐÔ6-€ÓFè8žnðé#€®ƒÐm‚èO„Ñ@zH(=&˜žþðc̾Ødݽã¬ôz¶~æýßxý °Fì‹ìßû—ÀžáEyy äí= † %€áãã ÃGŒ £Ç‡ÿXé'$`( ,„K?W-=C¦ ¸„o"[ ;éõ=QÑoî;p0æÈ±ØïŸúð4“Ã?—˜œš–!Idr¶ Ù-úbXìwøŒAWMô1 —° Ÿ0 -§°zûÌÞ»»Ÿ}Ã_>xðŠä뇿ïo=züø;xÿþÉ“§Oðßk¡M¼×ê·ü¾uDT_“_²¥ba~CÙ–sñªJÝÉ:CÏR¬àÚ ³¹Z ¯G#â÷©³â¯æe&\Wfœ¿©HK¾ãi hÐ0 a@À† 4ü·Ñ wÛ|—Ýk…†fßdKe°¦¡üUhØ_ݯAųès¹ö94H¡AÌïSe ®*…g¯) A–žxËÓ€† · án›_To‹O²¥*HSoÜTb.ÙW]¡û Þ âZŠ A ùР†%4(„ñײ3nˆ¡ÃÓ膆ëí>Ën·ûEõ´ø$YLó !Ü`*Ù_mÔ¬/Qs­…Р… 4ä‰y—"þy–àSIfüuaƹžÑÐgc,»ióê¶0’,5šºŠpCuñ~S9Ñ riȇ•”Û+á]–‹xW¤Yü«"¡àZ:êÂÓ¸d¥Mèµ3–^µûDv æ9ùŸ7•V0•éN6CC4¨åœN¥ŒÛ#áöÊÄÜ>1tdBG*êÂÓè²ÒÆ÷@C¯Ã'Òie$5›çhÍÆÍ¥Åo×t§ôРÍåØUrŽS!ã\Ê–rz$N¯:Ò #{ÄÓ脆îÆ’n#¢ÃÊHi¬ Кʷ”õkJ´§ -¡A“˱)sØNy6»K"cwgIÙ=éÐq:E‚+ž†³•6¾ ºœŒ›…žZg -ÔU•o-/Ó4CƒTРÈa;¤rV§(›Õ•©Ð‘„u9'æ_ö4­ôqÆb§“Ùf¡¥Õš‚ *ʶ—ô‡Ì…Ú¸Æ|5Ç¢T²Û³,»8‡åÈ„Ž¡#Eƾt^Ê鎗ò{<Ž6úX‡“jwУ-VZ†©zQQYÙkF}QL­V× ††œöÏ‹ÄIšÀh4ÐX¢ñ$›&’}4Éô¸9ß»µÄöwí?ÿ÷Èc‡Wƒ{YEË«¶¬ñ}£?‰éºãQ – -˱lpËûð’WDLÒ|rÊÍáV\„`Ɖ‰çÈмVÖêmÓ°iØ4l6 ÿ†m†Î{†Pdžºg0|ÇûÖ0þ­aÚÍåV]Ö‰ç¡F!ʺ[KnÛ¹–lï¼{ž`¬¿¥²¦?Ã)`èŠo”èh@†½l‚îǧÜ"¢êäsf\Þ¼-R¸tÉŒ)êíN²¹m-¹³ónü‘ëá=Àð -•5¿çeŒ'#1=0h±tH…ŽùhÉ#EËîA¬âìÇg"²F ¸‹V®hÅ•­èIÅb½mî0;;×6 ‘ŸÃËö¬ùÀð7`ø2>¬ÇÒA :æS¢ã´vI±‡¯QÄ¢UÌ]1‹„W Bé% O±Xo·™æ¶ÛL[çÄn`ø «`ËQ/c×ñˆÍµhÞ«F'ÜJ´â”cs”_´‘+æ!Þ£DtU3(½¤Ë—êí³µívjÃððáÕáÇYyÛ`xN+1™€-xµè¤[VJ¬fûÌ ¶ªAÆ»¢âÿC)º,•È—êífjkÛ­Tkçíä®Ã«Ñ³òÔKެõ/cþk$f¼˜ LÀˆÖGùz›(6lœjÜ;^i<”)7²m©}UðÜÿɰÌw>&õtÇ%4”»a†ï‚Sœ&œHs¢ĉe!–e;ˆl¯ƒÌÕ[±Ô°¥0ݸ/Wi<ê™kêQ•ž– %ߤCüç£ý¾®X¿Ž o¾Ñü¶üÆ®O„W÷}Ž/€áê>{âcI_ᜲkôsíÅ,Ëp!Ýmú,Õg9Ï@ÖOØv.‰Rg“u6AØÏ$Ⱥ[µ5¯‡éX÷=õÚšëWǖͯðsòé´ül€‘w‡rl$&ÅÑ"’Ù—;$øù&,úaqÉÇœðöJ'½=ʲ§[_vwî»MæaÓ°iøß7ìîX÷?ùÚšû¹cË–—9Å_è´â 0t…ãJddD†'‡%x:,&rA>'ïçðŠ>\0îEE“h`Šî“NÓ½ªŠ³GW±÷è§î·õpkózøáŽõÀ^`xöø’å€ §üà¿+WÁ±¨c"R,ÀÇB¢èãq&<$¯LcÂi7Ò_uAÒ[=këÓVÍ}ºéûí;ú—Ç—¬/ ï”§Œêb$®fÇ¢*Œ ˱\P‚åýýø¸W@–i.wÚEfœ˜xÎHçm°jÞi«HW¹ßÖ#-Íë‘]ëÁ`ø0¼(Ì©þ §Iõ…HLÓÖ`© H±¢w›¤EÄ´‹Ï™qrxsvB¼@áÒEª¬éÍŒÑý‡ý:ynò¾ã8~ ^°‡¤I3„¶¦mÂÒfÚ„6MmÒ”KÛ IJÙ q°CŒwIÖ£çy¤Gû¾ï»­åѾoÖbK¶lË–%[^ p(t™4u†U}\Ú?àÉ©ïûk¾¿ßå3·ÿöÕýûޝ»µ¯Xb†?<1¨{B1 )ÖÀ通ðÊ )—žu -¨e;©Ž²5+ƒ»j¦K×´4Å’’ª^S5óx{z¶µ~q_=°a8ˆÞ £ie‡/©éÅ´„xX§ýj(ïQ@Ó¨š³‹áʨ€V³ò«f÷ =[rSÉPÔ¤tuE‚hÊx{~¶õñ {¯»¶/¼ÉÍ©0ƒêª/©½Šé‡â!=”ñk¡¼[Í8åPÙ&…­ÚªYÌü âÝV $7¥yMÄRU„LMoþgn`†#˜á#4­n÷%uŸ…b†DÈ@ÉøtPÁ­f*haTI­™åÈš^ʸ¥’°þ,—Šo‰„òe>OUåqÕ xà -"ßÝ÷8¸çøºgÿƒ3h.ù’†ÎPÌÔ—™€ŒÏMºôЬ] U,*¸¦WPWÕ2ÚM©”~K(ãÝÈd+\‘²Ê¨+x{ÙòÄÚ^o_=ŒN£iÝoÒØŠYz!ËpÆg†'ئ³b£ -ZÒ(¡e™^Êik{£¬°dÊE–DUÁÛ†áaä…}B»Ž­{_k_¶ý”—ÓœB3†sÞ”åJ(6úy -¿à%‹¦<$iÑCPM»‡ôÓöAcqdÐ<‰§ºk[cÝœûÔGÞÄ ¿ê«ÈÛ¤Yѧ®´¤Û—’‚ 9J©‰°6ä"Y?“1ᣱ&=·è„3.’tÖET•ì]ÉJ0L ¦)<Õ݆˜áuÌp¤í®á—} ñ'®´ôº/% Æ”hT'Ãbj&À§ûØHÁCgÝ0g¥æœdé¼sX]¶’´sF’aFG2Má©îÞÚP÷`3Œn»c8Þ¿ ?)ËJ®¸Òò._JÑŒ«È±ˆN…¤pÖ/¤æ½\Ú”›IŸAi¬9Ä_°S¤Õ²ªb$kçµdýŒŠlœÂSݳax3¼öÄ`<Ö_Vœ”夗]iÅ5_JÕûovç+¸éÃ€ãøµ 5R!¬äÚƒBÒ¦nñu\¹#ô.Ò@33lpŒ1–%Y²þKÛÖ´¬½÷´öÖ_Ë[¶lË6˜$„Ù4é¥ Žö_1®}Eo}ÈÃ÷å÷ð»O(®ìˆE”"9_72èáÓò®NƘƒÉž°Ñx%+,™ÑCò’Tå ¶ õ#•„yž{hXûÈ`{ë‰a¿4'ùÄ–7øPeK(®"ÇÂ*$Ã}ÞxØ-B -N½hã°J&.¹ªFä%9¢*J`mA ëG*é‘ÁûâZÌýó‡Ü]ýŽÇi­;­8ëEUÍ¡˜škàt@ ÷{dðˆKŒŒÙ…´) 9kìä~¦a÷ÌÉ™ò醪(¢k Bš~¤’þgx¥lØ‚»kØN(Ê?åd'Üiåi/ªi -Å´„xX ¦ýjx W:epÑÚC›1u3¯éD¼r~Ïœ¸K6-ä('ø,ÍeS—¯$̻챡·l°o~lPì•åäÇÜuÕÅôøxXOÍøuð`¯.8”ð¤YN›ÕJ×dbö=ñ5P:Ãã)'»º4c]¾’|kˆ—ŒÛEåû²œâˆ+£ýØ‹êƒ1SKµ³"mnfˆâ¥:üp€~0HõA¡\^‘0ÙK “¼ôÑÃŒ<¬h%§«ž™\°|l|á†èç‹Þ5ýõ¹Zñõ—س52¾»ÊCõRj³²=wIM̶iñ¢¾5M6´¤(Æ‹(ÕÔŒæ IÈÒ”„-Ÿ&hÖÆ8ÃVIùlÕ÷G‡,M”üÂú%;%÷_<Äùúµ3ÐÍmÉŸï!‘®ÛJ§è-ÅÎ…±&nc¡…~/lÈEõ#íâ³Ãñ™ajÏé!Prj–Ö ÒdŸ 0+j oþ÷ÐËUËì7¼*ÖoíÂ6îï×¢|»ó$á«?6´Ü:ÒÜx½_ÿÙÒé¹ó”º«ÍÀÉÙK`íL|bš„Ÿ¦Ð>*QéÇJ ýØÌ82Eg™`±*)=Põ×ô3ËØ6r±µ[hØ/·w`¿}ð¯ßïoùç¾£ç¾=\[÷ÍñÓuªá£»çÞnj:|«¥ùÐͶ‹oZp7È—|ImýðKÿáu¿ÿ:£mßlB%aæ5ó0ó†˜as5¦Þ±ë¾ìµwDGI³¼^ž{I—åvxÒ\š/ÕÅ$Ù¡‰Ãüh ÆcâD”,IFIòd„¨B#mZ4‚7¦"­¶T Õ>mß¾3üÿV?1¼YiÞ¯½-:Bšá×óóÜ}†Kñ¤yˆ/Åe’N(ÁèŠÄ^, -ãÑŽîd„,A#$9Z6¤ÂmÚtoJ‡Zmio«=õ´a–²Áò³˜qS5¦Ý¾ëž|OííîÃíÓ‚S‚aþc†Oò¤0ƒÉ.v8ÎêŒÆhÜxâ'#TQ*LéI‡H²Lˆ¨Ê†ÚtÙ Þœõâ­YÞ–~Ú0˪²aý -Ìôаm×=ÅćÈÓ“Â!A£)# xÒBª1B 3ã°ãQFg2óR!@˜ RÄÙ`», ªú}¿oîw¶YrÖ6[æiì+çaÖuÏc¦7ª1ÝÖ'†ƒ”R÷ áðœ9#lõ¤D²›úãõÕä}Çq¼N¤Ýܱó‚­ëqZÛÓNÅU»zœuu«k«@ÕzW¼¡ÈEAAP ÷äIž\ÈBx $„ÜóäFî$@BÈ…„„pAEV×Ôº9;¢mý-ʶ¿óÇûÿ×ùœï?ß««»Žíõ°¹½ôú€‹*9ÉMý‚8bÇÉ¢v¬:jÂh"(VVãÐ@ªýß ÝøÂõTòeÁ´ði¢ù4EJtäªÕßLpø„°»avwñk}n§ÏÅä…œ4~¿ÜµÄ1+¾=nÆ*ã(NUã´ý -<Lµ—ý;såö¬YINÁ´èiBx‰6£æ -«_ˆsøZ¨îîfFGÀìíäÕœµÜ°®Z)HÜB':ò!¯TãUr‚¦_JDƒ©ôoÎt$ ŸÌÄH¢\$Ò\ˆ„åV¿¨ÆéQÜÝ"Faø]|VÈÁaG­ ^¼j2“Ä#(Q6¢"*r¢*&!i"­$]0Õæ kæ ª¤Aš]0ݺ4!>‚DZòÑ€è¢Õ/®rz[Iž®Vº×-¤÷9Œ~ë`q‡L4Á5Ôz]E–޶“åCmdULDÑD„](Õ€þÀðÂðû¤áOICVátÛ^ÒDÛA$"ÊCâ V[¥Ó+Á{º$°¯SL:šèQ Ÿ1hâ°GõŒ† 5M|CF•^k¥Ê‡Z¨Êx3UA¨ºPªÃi/ è ²¬ÂénÒ„di=‰ÛЬ~i¹Ó+ÃzºdÏ%C6ë@èÃ>k\ËáOÉX¢["†d¬™Þ>ŒÐ•q]i ëB©6gx;iذ¨·% » -gd9Ä Ù$*ÉMM¾Å//uxU‚Üë”Ãa«Ž›Äôkºfæ¤á×R×2ÙÈ•\o`·Ô×*ëXê(© §0,OƤAÿÂðqÖÙÎÂù.â„<§1*;¬ ÊO[úTů¦ÂãÑà{j8lQƒ¨ ¾.3&…Bö×aËí:AÛ8—/áð W=ÀæhûS ÿgÈÜ4ICû΢優/Š}Ú:·£=k÷JÝ]Æ*¯…ú *8¡l§]Khü¤ƒ-nHÖ6Îj–޲å &_cÖiúS 3Ò€iõr`xaØšõDþyÑ]õüMݧ 1M¶&¬ßo -Zr-½Žs.‡éj¯)ùkjT´A©AdÐGOÐÛ¸7`©p–HGáùܤŒÃM$Õ€é¥!ÖoÚ?fÍ*wÞÓmÇÝ2~\?hü‹2bÛ©z¾2»ln“©Ê¯Kî ×@1‘’’à'¬äTYíYÖ0F‘‹G)2ù¥U‡„êhªÓ²4`^•Œë2nKÖ¬f{Á}ãVÌmû&îpç‡ÒÞ-Zwç›ÉZÜ¥5UûUz(Ô¦…¢MjJŒ§‚ m’3GˆrÞA ”’A¡Œ%êhªóÒ4Ðñ› `Z› ÐÍÙÏЭùlVÝéyŸ1>°º©»g›Áä<âÔX.t+L5½2hA)á-áh ºš§¨ƒD{¯âbÕÂ8V#‹aUêhªŽ%iÀ²2˜ßÏú²4ÿ!ï_]¿+ÿ.¶Šx»Åìùk‡ÚqÂÕn¹Ø-1c|­FÈ ` -…juÔ~X G(ZF”¨­ÀixX­ Z£Eªuò”–ÅiÀúÖ2`y/7eÿäZ{ü~dUÑðÔb‚kà·"UW¶YâÈs‰­e]-Ø¡ò5˜ ?×H 0 ´ M‡((#DDYa<Ê cÑú0EÂÕúÖPªÛ¯æûŠeÀöîz`]·óQ`Õþ±É%y¾©¥$4–)lóîÖ ùÄVîn´b»”ž:3ÕÇ6Óz&ØO3Òý#£ddõ ìÎÀ ` cs_ªÇ¢ùÀ¹|)p¬^ÿ´gÕŸoM,ÞÑwÉ9óÔ›$Ib“ îiç{ÏèëÜe–:'ÆÉ³“Ý+µ‹e¡õÐ;`/­ƒá…ÌLÙÌòÍlÞÌíÅšë{1f/Õw.œÿ“{É’çî·Ö}›x}cøî뻬2 -dwÖàÇ6³¹ñÝMµáÓf_©’é«ÒÂ=8=ÔM4‘Û~ |ùyØŸ“Ž~uâùéƒGð¦? ß÷ÏÓ·mÿýHÓëè¾ozŽíýº÷øÞ¯'ö|%:ùÁWÒS»Ÿ¨Úë 8þ#à^½87Ѐí÷ñ`óSÍÉÓTç”]·=Ær‘{L&‡Êu”ë…~C*ßgNç{­i‚ë%8® Áòdp¦?ƒ3 §£ø¥x¥×׆ Ï\”aheøeh¤ šŸjOœy¤é`~¦î”ÜR²M×OU!ƒKrUœ”h“¡>•˜ÒD¯u”àÚ38ljâ,J0œÆpzÃè—ÑTÍ~¹¸Þ¡{ã&`jm™Óë˜Õµ³îkÎ˦Õt˸Šï«("¤R™(HÕɼX—&ÆQœoAq®ËqœÙË“Í2¹,Îaôh6EeãŒËX=7eð¼NÞ¦ ;63e0´uÌêO°îëÎɧ4]¶«jn ¬ÅHµ"Q«RÄ€vÐߌ帶l–ãÌa,/Ž1 £Gð#‚ÇÑÂŒaõ†žÜ”Áñ>ehi3~Ø1k<Êž1tȧ´±iØPQÓ'5²D^©LRMé±lŸ)‡ñl8Æq(Ë›G¡|šäã „ˆ0#x˜ËÖeX¼+)Ãz¤ –æÖ9ó¡ŽYs{ÆxJ1©?o¯é¡¢–'u’d^­Hã2šk³˜Àˆ£Ãq2,9Ê„‹qæ0‰0‘|˜Áìh®ž€ç™aheØ>o°옵fϘ+&;júîpQÇ‹ôâ$¡•â -%–Ðàh¿>Ÿéµi޳”fÊqf¨Œ°àbˆ5\ð³ÂÉæêéߊyƒsÛ&`Ý`ÎÖÚ1kû{ÆÚ¦˜4ŸvTá¢/û“„AšÉ©åYLª"2B™î5—“=Î+ v°:Ì‚*;\òq`r¨!œ=Ñ\=ï ËÏÚyƒ­éÀœ£¹cÖÑÊšq’OZO:ªæóᢉ/˜ù)Â,Édu²ªPÒbM9É7ÕFz\W¶ÿj¬z{Be7&¹HÞ΋æê ø(ƒÿÿ†­ À¾¯un°é̬³™ußyP>é8â¨Z?­]ñ‚µ'…[E(f”à£jY1)QUããµoh"Àõ^âjN^¨lï…‹Ö^$oæGsõ4o -ÞßÒ€‹28ö´>uí9óȽyh¿lÊuØ^s´‡KöOâ3…;(jçÓzIyD. “A¾sÚÅ÷\·ó5KT1÷ÁE£É¼žž7¼×w·>uï>ýÈûã3ßé”ç€mÌu *¹Î].¸»“¸›‡¡NQ!iWcZÉDXª»ë Þ²=7ŒBÿ˜^Ut¢pQ+.hD¼ž€Ÿ2–-¾·hÀMœ;[¾õìlÿ§'ýAp—d:Ðd¹ê;,{OÅHÿ…îg¢¨¯¯p‹jˆ]<í5êÿjT:îhåCj©ÿªJ -]QIÂ%¥d¸@­•¨§ç [€»±å[ߎöA;.}ï߆w™®…[|•pRBÎ\ÎG;ÓÙA¢š×-œÒÙ¤÷”û¹Þ}S¦ñKUÁªT*Kå0)“!D=eRÿoh`ˆ2xÞoþ.¸íäcxk×Ãè{ýwãÛt#;]µds¨š=cÉ?§bv> k6¯`BîÝ:·E÷M‘É{M¤ÔDZ¨"VÃE± -É×Ó¼áµg†w€oëþ‡¶œø2º¹óoÉMü{Ù Êir³e¬¸Ó‡¢Å¢#ç3P„S - ®ü}ãðfïjª×;8Áwy¯ñm±>3tE`—úuÃ…zAÊQ†Àðlnw›¾G6{’xç¹õœÏ«kE“7Öë³ù]ÁHúèH ~Šôäí° ¬5±_8Îóɯsü¦ëœ€g¼Ç¨q¡+®¬>~ïöŠ?Õ>].LŒm´ˆ–Ð`º=a¹ˆšcÜœ>"ÈË‘~R‹J½ai…VUXaS…–/Áþò%(\bxa²ž@è§ @øÕ… ô«e ´nH¼Ñø„\Ùtzù±‡K?ÉÜY)†Æ¶X ƒf´#fHv§uq¦ŽõãÒ¨FÄ…^DJö +‹¬a]‘‰XH:â$»?y.Ôÿd€_Y«–Èë¿ËѶ<¼ñêöñ‡/Æþ±ô"|gõÀàøv³±tÈ£ÅÏÂêL÷eUŠ›”'™¸ë‰sü¨”àF;ªÉ3£‚µô˜ ¿õõ†ü@^~ ¯\RËÖ|Q{iíõK¶æ¾|¹ ù;­ËywÈp£Q§ª~èÏz¥D74å ý(/Æí‹÷¤… vj ÉHÊR—’ÊtwB;Ú•0Ž^LØG;“žt=Èâ@ô¥A”öÚrɊəūˆÇKÿÃ~5ŸÇ­l;ëÎζëЙŽíÎtwfeíìvu¦V@D@9…°\"¹IH"$! ¹þä" I„r‘‘TÄ£ Ò]Ô­.¨ã®€þúÇ:}^öE_|ß^<ó<ó˜~ú$¯ýñgÕÂåoX7ãXMsÙÂËS¥Rê8JIÁj‚ç'éÐC”îšÁË=Õ r€Õ[ák1^ò Œ}bc™O¹£€å7»€õ£_my~{`aaßGÃOÞ?dÞú8Võê9Гך¿;Ž£-%PÈ·rÄ¥Ü|%¿öjMkõ VŒœ"H+&ImåTyÙx£¢dœ¡,c·òT…£ -1*Sï¤öÐ]ÀñÁ¾S¡ûwB?6oí?¬¿†¶>Oo~~´ú¯˜ŠúÕÔZÜJ^]ÍrùrjùªÆÒ[µôâEóÛ›bÌ.¸Aå\¸ÞÔ’wÉͽÆáåÌ xYóbþNºëÞ²8¼ww`î—{-CÃÔàÃpŒlI¤nFœ'¼LÈǬŸ/F>Í-/û,zT‚B¬^ªÍÿ¾“÷›ûO>{¥¾.ë™ù€Vþ~1ã“”v¯…tnYHÞI×½{Bc{C¬·ö„ªßü: >g‚ÏŽPÀ‘¨:}¦$%_z–^´‘UðS~nÞË¢üìeˆÌç…Ϫ‹ÒÖÑÅçÖq%©k„Ò”IeÉ?RË’ž6^L|Ê,OxÒri' î ±ÎîÞ¥^y/ïÿŽ >9Ø}Ǿ®Ñ+ñ‘2â2AnB:@$¥‚’”dPžšô™–ø•ž°…ɈßÄŸ?³Ǐ۠d~u9;æ3'úç–Üd}/Ds;d~±—>ÜO`Áá?T‚£.á@ìWYàìÑ4v,dG$‚ü¨xPt2\ŒŽ•11 ЧO|Ü @<(ñ‘ 1>0ŽnâN\x??Ý„GÃÿô€“ š)›ÍeÏUÈÇMu„•ËÔÆ¥ç6‘-Òk8‘b#SÍ¢š™•v¥íš®Ò馆î©J³~²Ò®Ÿ¬ðèÇ*õä`w°þoø2ð`ôi(€`ƒàô!Ð8)ìÄ:«²êWÿ€Þ@¿K£óØâëD¾|ŽÐªºŠ—j§±ò®)´ª{²¶£g¥3ŒWzÇ«ÌÆqd¿q é1ú«½½>”×,À‡ ­°A„°›¸y©-ß"Ö8åÕYhÒýfóNS£à%›oà¶_%BÚ)‚X7‰oëǶ÷Ž¡;L£µº¾‘ƒye¶ŒT;-þ—Åç¨ ìßD°A  ØÀƒ ¼œÔ ¢p[ŠZåT“ï± Í·˜4èzS>KkÑL7tD‘aŒ 3â•fø„Y¯`ºlÚ^{ Æjªí·{Ðýö~8 &x@Äï ­±‡?= -²R7 -×øE¨UneÃ2Ç^dQDóL†r¦‘Ó9Iå÷Œ‘[M#D©%@P؆ñš~?Vçô£N­ÍéÁØ8 ÆÞoÂö @°A$o á@ ŒÔ (§pMˆ@­ò/6,qkÙ‹¢xŽÕ¤šf°uã¼Þ*d %v½ÂéÇk\CØn׺ÏåÅØ\ý›Ó‚µ9X»£gïh… Ò?…)lÅ„(9 -ˆÒR7D™…k­P«PqÃ¿Š³ÀÅKf9Tõ$«Y?Êàö. íñcˆ(w á5îA¬Þ3€¶x‹ÛŒµº{±6—gsváíŽ`ý× ;òƒèl¤¤lHÓk’lÔª¨ a *ç,ðÑ’Y.I3Á¡÷Œ°8–aº ßO¹‰rϾÃ7€íõ91fŸköpouiñ6§¦ÎîÁÙ¶á›0 Žâ„( KJÙlK-Xo;_½*Í%/‰K8 P•ä*¿®c‚K3\iaY‡™<Ç rÚ|^|Çk2ãLƒ=ø>_Þâí¨³ºU›SI°;‚İ¡ 6´mN…é™H OHÞT$¬+ÏU=Td’–Ûì›’rÉl+Z3! ®ð6?»Å9Ð(ôzI²!7¾3`Áz¼Ñ¯­ëPÌ^e½Õ-'Úœ2¢Ý¬·ù¶á¯a@d§#€òLÒ–*1ÿ™:ùHF¼×žÓ¼(/ÍJ‘ê ÞpEH³úy,—Áp‘¥Wúëtãú:ý¨†Ð;¬¬7 ¶Í^)Éâ“m.Ùî¼3È· '¿ò˜ Š=ûZs&ï¹6±âqg -á¾6q[“Í·—µO¶ÕèG$$‹_ÄpyØ¿"žì!hgTÄîñ6’! !›E f_+ÅâRl.ÅîÖ[ƒbÛðuÂÅ©ã@ø¦36÷….®ü}"~¥'¥ñ®>‹]WØ6£­èSãLà -ªÃ 1¦fñŒ’¢™Su<*šqˆOëóñh—fuqi6g°€ôA±m8ñ%h?ñw =™ºOe¿4Ä”=1Åa¾·$P–­©ì[¶ÜÖk¶匭¶sH_g7)hv!oFÀVÏ·0»&9Œž6ÃègÓûØt³—Ýdu³›lÎ`½5(· _…YT8PG]‘ñÀ•ùs߉â5{4ê‘;®~eðlãòH&{a¬L0âEjmŒ­£‹<,P°¦é2Õ|£ sª‰«¥sz‡élÓ eö1š-Óê -Á?Nû¿Ù­ó·¤<€ãø´ÛèöT3ÍîÎT¶ÏTNóÔÔØ”ŽfšWZi[¢y噡r‰x Š((¨x¡à‰J¹š ¨¥!x$š)¦™æef5­•5M§Ÿýî>ósüûÃû÷×oÄP‰Ê~™Ã¨w8 Jÿ÷W1¯4G£—u'¨‹CžôÙñ0ÆàVЮ#ÕÔ«š%JFWV]ÖãRÙ-fEí`º¸¾%lèa*º2 -”œ¼& 7·Ym((C Uÿ5XšB¥ý~¸dgr[7h±õ~¯± }ÑëùhÄ87w*A?åËÕ‡‰ä:’LÚž¤6³4œ^or]ÑÍÄúÚþäªú¾ÔÒ†¦XÑ&jìL/RiY‚¦vCAù©…)TÙþu6‡Ae}ì£Ú -½¢³ñ{4â8=ç„Ó?p¥kï¢ùòÁéõ¨ÚbÄPÐÂVg(3»éò]¢¼BGi¸¬K”É{h•ŠnzicgªX©eˆTCýÏPý½ T!Ù¡} ·´†ÖƒN¯¯t[¶>59wèìÀ#;’vÞ‰)¿ã‘/)+F š$ev+«•ÕÌÓ¤(ó»’”’.ŠRÖ«PtSj]ÔêÆÎ¤ -eGr©Jc(¨D 5f&P}Àj-~„&s‹wûlŸèÍ&çÌÝn.Y„hžÚÄ)æYÒQtÁ…вüÒE~­£Nkb´r[iWrÔÔa;¥¥¢=¶¹VCjRic.«´ñ—TÚ„j•ÆP E ²&Pó³)4üô´í6_¾±ëàÔÌ.«›K{OkVœW<>D‘Î:±Ew¼òóôa’¬>rç:­–ÙÁlHÖd(ÔÙÍqm‚–˜6Ér[uKT[ý|ks+Au¥•tÙpP.í0™ùVhúaÇëîí»f'¶™ ,í´Õ¬ìñR¼:!]:œ œseåŒûäp‡1"ö@L)ãF¢”¦K•%t³þÛ•YÝ‘×@ìÉ Ú29¾ã¢Û!—‡kZÃÛÔb¨ýÎ.ïúæ£zÛ–…‘¯7.nÞ­YÙá¨ø´Ç[úÊ"BôÄ>žß‘1íÏcŽEäÑG¢‹¨C â¸z)¹?­"êWŠïãWc{5:± £«Dª•ïUÕ„ö¨/ -j¿DAÝ?Œ¡iÛWÏú7lZØð7íÊ·û«;œ¥°Ï[ôú†ÿÌ%š½ˆNJd&ÍDrâ'IYäñø\âhRî6£0r˜-ÄÜʺ6”WªŠCô¥’`}UI¾¾$Pß,1ÔoDAÖ/V:×þe­öņ-ŠÕÍû¥`æ,„žÙoíƒY/ݱ)Ï|ÈÔÇ! 1ÃiÄûÄTܽشˆÙDöù™ιét^È73h’Ÿ0!àûß-Îñ/Ïñ—åúŒ]6(Ö£àê†5ú~c#턉‰üÓ—¦R05Ânûl°:™¾êâCçLyí!½ Áa—1Q˜_ñäsOÉq!)” %5à#Ñ‘ä÷03Ùg!—îý 0å̃âTÏûå©èû2†¡^6£†:×¢4C_Éç¿XW ›L‹à»³ÀÜ: ìœipÂ=<ÑÄU_ßðAçÞþð[T˜ßëXŒÏ+j„÷Jr¤×K&ý‚óx‘‰?ý<—pj¹xrYåþï -’¡FÚÖ ÚuF5Œ¯1ªx»n]!üýÛL03c‚…y8Z‘áø,œvïcApÖÝBÿéá^€G£äubÏœ‚ï“«É>'>±ýŽäùûsÖõCa€Ëûâ@Ci4kP z#Tù=”‘Ösaó¦Tؽ•–;‰`»ÎæÁàfáVgÀÇ ¶§ ôˆ;`ÜëxˆN®@vvø£G!ÑÅ R\!í˜pŽÛÿ„¸J®C¡Êï¢Py¿¡Œ8°ñOtغ.Ì¾Š„½ß„€¥©?Øm?.»Nƒûî“à¹÷8øš»BÐþ£pî€Dt¼¥=~±ƒ8+[ Zú!H³±Žðÿ[Cëk#`ïX ––«0¼- -Ø Ò"<€A -|G§F¼¢1£—©<êSJ^ÊR¬0ý!Y’±@*çÝ'VeÏ.åÜÃÕçÎaù³‘Í³× -f»3áý‚éðaÁíÈÛCØÏ÷Ã6bÈ@ ÄÀF LÄÀr†Œp`E½eR°+)©1Ïhœ¤ÇÔÆ"¥0ãAlqæ<¹ŒT•;K¼˜?C¨LãESØáT¤Z8q]41(ÅÞ ân oào‰>dl2.bà"† ÄÀ>c ¼@gàaЫ\Bðï¬XÜK&=î×”Œä%?}*àÎÇ_àÏÅ–æÍ¥‚iÒÅ¢IbhßX<»"¾‹Õ õJF±ƒ’Aü ¸—0(é&~>à ÞöµÀC ×ïëe ÙÎÀC¯fáƒßpcð/Ø´ø§LVÊbJû-?k.A”7_"˜Š©NDËŠÇ£ê%c„Æ’1üÕÒ;¸Ž²;¸þ²AÂͲ^bYgT™†ôù€‹2Cbà!†,OKÈ=ë yçЫ¹Øà7ÙÑøçÜDÊvZêC&3OÏãÏ&L%H„qâ1²¬ä©®|4JYq›ÐZy›Ð-Õû¤½¤>i'’:ºOÚJþ|ÀCÞ> 1d#†Lă¶Ÿ3‚Ñ«ùáÁor‰„çÙ”„ÇÜTæ›Ã›cääM'M$ŠÅc”òÒÑØšŠr]Õ0IU=u­fˆØ#ëî©é ÷È®ÅôÈZTÿ!¼Î¢š<Ð0ŽãŒZO¶sj­Óžq©kuÜQj-Š‚¸€"eA‚‚€` @XB$d%!@VI ûö%$lj@A¨˜A‹ VªÖGíè;ß™éuzñ¿ÿݼç‹Ù¦$ˆDbR«žf©äÖw9Wi­ÁQ^c²UT›-U3…f5URmÆJ*¢÷0P 5p¶Ì‡ÆK@¾ý Þ± »üCö?ï -ê ;61|âøµþ„Œ®î3$½-ƒ!ÓášxÊUF1‰xöÜ&µó‚Àà(â‘b¦ÙJfX,¥t«©´Úf(«FôÞ‚ÚOg5p}çƒÈo1(¶­­ßf°îØùºÓÏãþý÷|Sv/¹%½³V}Æ)Ö$;ÍšÓF».MeדÛuçEmvC‡'èh÷°QCÝ¢Yаésh]÷×,šî^½trxÝ77nû®í›úÎß9µ7Lwÿ@¢ìî‘,ÞDdu,BºžÆÌ ²Ü%â³kä§{êÉ=ê–Äîe’Õ¥:­u©S•.uºÜ¥Êt*½ôß ÉÚ¿BÛ·ó³/ûÛÔÀ’7&V,tOmÜäxº}Ÿöñîéà o*ä<í^DAñxbyÞôì0“>P,8ÓOkĸ¹¢D·Tœà6ˆã.÷HO:{剿9FÛ#OV{x¨AˆšW~Æ%Ÿ=½´à3Ï­/?uÿ²l©ýåúmšWÛ‚%/¾æ=J¦=:’U|?šHœÀ”deP2<øê”ëELÌ…“pY?ØÀhœ´ñ¸1û…±} ñÖ¾Foà/>дp¨Ïyãœ7gÔ3w¦{jþ<äÕÒUíïÖm—¼ÛvûÎ?š6œTòäXq*6;‰ÉϸAJ¹•S†+¤$Œ–Óâ<ôê˜uôè #j¤½6rÄÁˆ`D 3£ÜÃ,op®HÎÓçéûøOî±¹³m¯¾\ †ekİ~'üÐ ¢äÍá“Ä‘g²ŸÄg¤=Ä`O?H'œšÄOÞ½P{‡\3A+‰ú‰Eޏ%,ûa\^~l\ƒf/¿ZvllÌk úØÚæÍéœãsipŽíÞÜ9*XðU,_Æ۩°#°‚Ž߇EcßDǧMÇ'%¿HNIxšž÷+ö\Ì£¼¬¨‡$lÄ/¸ptl…ýÌ%ý¹17ô~K^È} 1dÒN<d8⇃ȀXˆ ‰<&(G¾‡dl±Â1À?’È ʨÀ÷¹1òù¿3?ðÎë÷Ö÷ÝÛ3vë¼à€jjqBÙn8 J¦–:j`ÙG'`Ý21|»š ;7Ð`ß&øo‹ƒPÏhß1ÞG a}C )Èeðž¿dD#O«/¨‚ö6ØŠƒ½Áâ ÖP{]¸äˆªéF¡ Ã(”þä €åKù°öc¸.#»Kq¤ÀgK±°æ£hpuþÜ?ŀ׊@Ø¿òtACèê}¶Æ"×îc_zAÂW;°Þ(v@ò×Ûõ;ð\·B†ÛPnÜ ¹ß :»w…#°×-ÖöyÀ˜a®~ÌÒ ¾À¦C'’Øz6þ UC™§Ñ_‘ŒŒ¹Ä -æ B û9þtú ®‘;“ÐÄ{–p™?ß*˜ŽëNÅÞNÄߎ&Œ nã -nÙéÿÇðåàlw–Ÿ pޏ?Æ8_àQC€Í< -LqÜ;FVâkú)ê+ª>eŽRÆœ%Y8ωÕÜg„zþ4¾Q0…kMa?M¸–ñ4¾7ãqüŒ1콌!Ü}ñ ÂXÆu;1pC:b`û­Þ!7Ey‚‹!%x©‘ØÂ„·Liž¡¦¿¤¤ÍRKØ33ošT-˜"Ö‹ž2žà›$“¸+ÒIl›l2¡_þ;$»‹¿'»I•õ$ŽÊ»‰‹ü厎¸ˆƒ„7Fz‚, „Œ¨\ö GNþ-M•ò"EÇš¡¸ÓT“`ŠR%~Bª“L&6È.dNà¯(&pÊGØå~H98¢ø…8¬l' +Ûì‚åÀ]ë¼mÎÀE ’P7ÈŒðE2‰&G½rp¿ó¤I/Ù9©³iγ”"ÁSz¹x2É*}L®Ë|DjPŽ'^81NhÎ~ˆïʾ¿•}‹8”ÝKÊé ßÉi¡ÜÉi¶1ÿ»U vƒ¬pÈ9ކlÔè ÿZ$¢ÎñN0gØy¼©4½x2¥L6A«TŒ'Õf=$7ä< þ¨KlÉ#ôåÞ&æö’sÛ)ƒê' ª/SÕ—ì¢Ï@°Æ „ˆAì» -N¹‚*ÌÔ1hÈÅb ‡½ dàç¥|Ú Q&ëO%xÂΗL¤–(Æ“+²ÐjU÷) êQÒÅS÷ˆ×4w‰75}”M{Ò€¶™6 ½DÐ6%hÏ'h ĈAˆDˆAºœ pÍá ‹òm|(œ"F-œ¤ãç•lú¬Tže '¹Zù8«8kŒaVÒm§F’µÃäËùwIÝýI7 -Úiýú«ô~ýÅä~ý¹”~}ãFÁY;AÆ2ýÍ ÄîΠðq¼ƒß@Á¡íP±ôÇB ù^“„›W¥Ñf•ΔT!žªÒ sF™¦¼‘›î.õýJsѯ”¾âz¯áJr¯á£×ИÚk8“ÖW\Çì+ªe^_4 1b †,oÐøŠB·!Ì ‘APwô}A"öµ†NUqØSJ©xBrR9ÆÏÏaµCŒšÂÛ´s%ƒIme]ôž²+ŒnãùÔnãYf·±žÕSVÃî)µ²)©äô ¤ˆ!cµHÃɽ_€Þo”ºCÆŒáP%Øøß ÉI/´ Ö”J z¤P*ï‹5êaniÁíÔš’›ô¦ò>Z§ù*£Ó|ŽÙa9Ãê°Ø8fkz—ÉÂí.7q{Êʹ=¥‹²O@‚dˆA½û (F¯£ÿ0ïËa°DY0ÅÿÍH ÍÓR'uÁ˜Jª¸£P©¯‹ - ;Ó-ÆÖ´³WS›­çXmÖzN›µŠÛfµðÚ+Êù–2~§©DÐ]ntëƒ1ȃÆk%”ú~f¿o¡2À ª0~PŽy]ýÜŠÅ?¶é÷K™é· -ÄòÕ‰¼ÖLMÑeQ©©‰k«jä\´Õr¶Uð[kÊתK…mVƒ¨½²HÔaÑ‹ºLzqWùb1ÈCæÖO@·ës0ú¬ƒ -ô&¨ö÷„Úß·uaÁsõQáOëããÔ‘Éÿ©Leõ”ðå-ZyÞÅUqƒ¼ÐR+®¬± -ÿYg¶Ô•ˆ[j‹2Zkô’kUù’6«NÒQ¡•tšµ’.ÓbA&b#åVgÐïüL{×BÕþPë·ÎzÏ7òöÃÑCÎÅF7’ð}µÉÌV3Gv±HœwV“e¨ÎÑU”+ÊmŲœ.5ŸÖÉ[ê4òŸm§ä­Õyò6kž¼½R-ï4Ûë†UNµÅŠ#rH½¹äŽñü€¶ñ|jÓx>­A2࣠9Û1PÞRíÊë¦îoÆ<œÝ‚x­.ßñNWóÖ@øÆÆ²è5Î…ÿʃ˜ü‘vz!0$lÅœŠ œKHð›ÉH"N¥â_Ô¤y=ïH÷zñgþá—04™Aìy’éÛñŸ,¿›’ÁEyŠÐÝÙ¬,õ´G} ‹ô¿Þ"}ó³ºbãg}ÊOX£Âe«¬eœKò²'!v™D _¦0YïÙÇ©o£"È‹ñÑÄ…ôÓø¿sã¼^–Ÿõ˜kJ86×zšà99“àõ`æ¼÷Ðt2~@2È“C ByvK¹ß¿éEÚ?)ÈÔ¯ìÞ&XÕ׺¼Š5Î\µµL^sv:³æå±F"±¾ÐþeùŠd?œ Æ¿K õ\⇻¿)Št{]uÂe±5Úyñn4nq2Úuîe´ûäüI÷Ç 1’@á&dº ƒŒt®Gº‡e¶ÉRµðÓÆRÐØžZ`n˜væ±àbžÎ, ºS!OþÁ"ú¬†“½¿Ÿ -ðøšHuû rùœKÇ}*e:.×°Ž~¼Ávøx—cÿñOÎÑ¥Žãë$Z"ÈØoäöi¤eLMoºʲ—@[™ F{áˆ^ Ø™„ΜîÖð±÷Š˜.žzìD{¸@œ—ú…,‚ýËDÛµr’ÍZ¯õê ?«Õ²ÕÊ8Ùrå™DÍÒÝA® bÊǤðËfL6¨lLÅ0V;fšÁ`¥Kƒ£†dp3%ë d 7 ýê l+G·q@Ÿ#[8ko ©G-ïx -œ, =‚j]Ìຠî¸b¡O¢žrí‚TŒ!HÞ4‚d€¼T2¨­-¹pÐSb‚±j˜©û€¦'àtÜÀC>Àÿ ZÇØÂMŽÀISsˆ?„…”Ç k -—ÍL ÔÜDFÐrÄÚ$jí@ç#Hî$‚¤½C´!×±AU&46Aw«'˜(¹‚ÅvG°UµÜNkpßõ+àÕÍÁo5SÓB´Œ!JÛb÷„D}àîÓƒœýºP¤»*ôöC•D•}èüqI™E3€ a°QŠòÚá*ëq°GÆt7Zƒñ¦#`&gV[ÃVpV0wEÀÿ¤¾Jz@Ù¦ å}²}/DîІØš¨¢.£Ý£¶r%‚`U 0µ¤j(6 -ÀpUa/S Í1f´-ÐÏá€ÆuÊ%oð/&¹Ü|Eþ@j ü ¶R×|Úi«„ÛA«„ú -~ŒñÍû9ã_ïyÆü"ã á-cžøŽ>CzÏBT0ÀBh2@EØÎjp¿BŒ „eœ(;`žuzºP/€Rè þeþ@®¢¬ùÖSWI-A+ÄvúwŸÛÌï>¬o„¬/øìeü<{Ég‘½@|Ëšõ]b=ó{Ç–Ž£ lMi2Ø4kÁí„p/ˆð7‚0¦„DØ'Θ©^Ä'µ€ ”RÊš¿¶B®§÷ka~ómg}#Ýæ|%ñù#ø3a&ä½Ïüÿè®Ï°&ï5Žãñˆ==-œ:+ZÇQÔ£uTD+ÔÊ…¨uáBf!„ì„ ²HŒ,’'$!ÈÒ ("e¨«(U(‚L©x@eˆ¢(λO}Ÿß÷ŸÏsý7ùiÈ ©/l˜Ô>B¶G/`‡¡ŸXŒ/Ш[ÌÛ DÙˆÕ„.3ò#6'ú=¦0æ]Ä ÂÛð3ĉ°jÒ›Ð:òëk”ñÃ÷¨cÁiÃ!iÿ{J펢¶FÓš0#Nê¬I@Zè -øïÝ€èçñžÀÝë¼µÀö…xr Ð9{€"=qéáŸðHÔ{\vÌ[laSBzYF¯¢½ -»Hz1ÒÊ|ÒÇ`öD>eØ1ƒÌ¦¨¡øëXç 5Q5P7y7À„»½@¼„?Hˆ „øø½@O< ””ÈqzÜ»Ø,Â\>é5ÖF}…)£¿Œ¬bŽ…_d½»Á íä< Äy€`Û£žpnaŸp¯E?áÖãœtôݦ,p…8ÔÀÜèÿ9 Ù¹’¬i„$â·,~È'º<ê=E‹Ÿˆ3“Æcóh/qǘcØÓ¬ç˜*Îh䥄g·xƒ=ü?1xöè~þM\¿ !¦_ð;¾_ð[¬ó€¨¨´Ò 8> ùyÈ·/å¾5  õYt ˆÉAÀc‡|bI°oéé„×#åe\ó¾˜=Š;•0‚­ cê…Ñ͢G˜ÞÄV\_â͘>ñ•؇’:ÂCɹ¸>q%Ñy?Ãhó]ŒøÞî óûÒƒzÏjPo‚4L È A f„~ä ¢'ØJâ8=ƒþ‚leÆò†ñ§„CÑ5â§Q ÒÇQíIí¸^éM|or=áìñ¬Šô@v–Ü›|šÜ›ä,`¡:j ®pƒÄÜ!uÓlÐ,ý®ïAp#èÂ@³äbvô/‰øŠ¥f<§™¹#¤|á`l©ä î\ò¶QÞƒëRÜŽíV^ŽëN9OêJ© t¥”Q»•'i= -­Gî,`Ow&ºã˜ËÝ iÍ× Þ8 ÿ…`Ú¹Ìû} 3Ô Ñ»ÞkˆÁãJ&ö¹DDâ§0³ ¼Gôñ’MÖI¨T:ð ©wc;Óê‰éçÈíªrj»ª”Þ‘nct¦3:S ˜])ÎúlˆG ìen GïÞ Ÿ™`Þ2²w,‡ì o°o~g‰üå•1öàˆ–ŠAg[ÜÃ$³K åÛÙi½PyƒR¦úƒT«©'5kk¨ÝºCWÂlÓÇ·iòYíêÅäjl|©¬(9U˜¯Ñ±r³´œ“frÎ% 5»ÙÄ0Ý3²„£oÕ'è:ž¦Mã,þÇs&ö[W8ŠnºR¯)UK¦]^6õaÓ·Ö– Ó›—Ö6ýX~%dgñEl¨õ|AWIg)ÊE§¤ -Ö •šb³è ÇKŒ1¶‹F|q B̳(ÖV=ÝìÐ1ŒmÚxC»ÚYˆÄè†0 ¡;âÌ‚Écµó&÷ßüε³k±ëíŽõß\¶,®º³ÇçÄàí¹ ˜`ärlLj•žxžÃcW‹¥”Ê´|…Yuö¸6¢¢NQîÐaK:|a›6.÷¾†”Ý®¦X:ÒbMŸfôí*™7éCõÌIC×<\:»ÜÿÑôç’ÉW{}ÜÏwù/<íØµ®àÞÁSSø~ÕŒô™øÝuÕä•ÇqüƒuDPÊQTŒ(»¤böåIžl$$,DÀ*0Š:ÂŒZ;NµZµŽ:n•±ÇµZP*®ã¸á2í)¸UdKˆŠüæ9}m^|^ßïýŸûâþK.å7V¬Èú©z•¡aëZúÂÁõª†újêBK5}ª¹F_Û\c<Ô\“¹¯¥ÚòmËzG°Ši¨af±Ó›àØÒÜ0†Ü»=†Ü|îÅj|4ú쳨ñÇž$MýW?l{‹œ»ñ ê¡I]voqfÁÝ"köíò¢Œ[Õ¥ô­m+•·UÈnŸ¯”ÞzZ©h|¼š:Û^EŸh[£­m[£?ÔQÈè¡ITÁ”"ÅbžÅ‹x¨¦ Fœ„­R.öÉp\‡ËTÜÈ*þm?•Ôÿ;•Ò1¤t¤e!Wr¦Î‰¹îD¾ýyùjx,k#ü]×!Ô³Q>ňXŒ…Á&ðgk ™§€*ŠY>cùÈŽc–„d”%rQ•‡)ó±#5Ò¢q’…+éQhåG¢Ÿ3bwèF-!?œ&äH#!»î²¹EjàÍZÿQ¥q_‚°qYˆ™¤×_…ÔiR‚ÏHš #‡‹Ü°8΋Ŋˆh¬ŒÀÑóðMÌ\Œ ÃÉsp1nÄqÐïÈ9fGβó!›~%dÍ 3Y /ÖRLtÎF€kÓ¡Æ\ObÆ ÀõICªo2„“ ÷Ÿ: Æ©‘ÌÂ:K§‡ayUÁ¡Ø4c&¶Ïœý³Bp,4?ÎÆ Ž#GÏ2ç_%dãBþü”R¸+>aáí¤†³.|þ sÜ퇤xF€ï=’qs@Ÿ Ý„Y0ÿ)Ö‰AX戊ISQí€-~Ÿb÷äOqpŠ?Žû;²û"sÿ;„T¶R4HH6Ñ•%ƒ‹Ït¤0 ‹`—Hp\ç"b4óGÏ—‚vøn»M…Ò-Ú1þÈ3Vw_,ûÄ•Qí1_Ž€žŽ@?Å ê`gÈÂ]!ŽgCêØzzt–Ph -á.…r=3þ¿'CúM*${yæCô½ÂÓBêEXtY þm ø­R¤w2¤ïù6©MðVÚ/z/é’¼—vH†?êši3 ²lhS¼`ù¨DFf(ôЬ\õ_¡Üœùv¤ÿäCrHñ1D§%Ö3Oõ² ‚;r,j—ƒß¥ø°h@aدEvy·äü7é;ÅSÙÇÁ0Ù ¦A1ÏÊùlzÁ,ðƒ™ -„É8ÆüHè—ÇA³6 ªM©PnK‡|²ƒbHÿ-ä” âz9DWÞUB𘂠›z' ~Ù¨©]ùBf§ÚåoU‡ ¦Aä %Ó@ǰ‘™äl¾rÈ2p`^cI<ô«B³ÕW(w‹¡8 …¬Vé)%$uÄWT5©!zJ _Ñ6ñÝ+¤_(lt»rHó e×ý&ó¦fÐ|U7h¾ì2}`t†–ã‚ì06 -c½P’ì‹2A J•¢±$7‹?OCÖj!Lä0lSA»Gú°~DuÜøAYgV\7¿•?²¼Q¼´tR½–6º?«Iû:û?úœK†œ 9õÀÌ4d0 ³\`å°Qå‰rî$T¦OC…l6Vh£ñ¹%o eÏÙÄLp»ñqŸ¥__›Ó«ýÁÚ£¹’×I7ç?ÑþŸíúŒkòÚã~‚"Ô-­ÖÚj¹W‹u”ºDd‰ !’Iž 2 hˆìÈ‚•QP­(TëhI Š Ç­{{îóâ¾Ì‹ïçyy~ÏÿyÎùÿÏ$éAø¥'b*¡3êµ7EmŠžJ¨ÇO%\ÂOSÌx4Cšðí ¢ b'iÝ‘/éí¸Œfü F}ÌKF-á%½:v2±2v’fŒAçi:OÇ¡s=½_Hœì`úž0ãÈZ¨:±*¶J?³÷,Úm:™ìý‚Ë 20%áà Êè§qùqC„²„hmâݨ6foT?«'jœÝ†Ÿà4Æ9uÄ NUÜ»<ÞÄ*‰ÁÐ^š _b`4šâ0ò6YAù÷¶PõÓ2˜í²æß4—ã·í]fˆó”2Òe\w|DÌ xŒÃ˜Š˜~j&¹‡T˜x‹XÍú…p%©=æ6òsŒŽ×@4ðjâ ¼r’ÑÇ“ -)㜠[`b™cí1ï` ië,¡p£<»Õf;/êÃ_ý·à˜Ã -|§ÔA;ÇsÃ÷ëT!5஘‹íáI 7XçÚó˜- eI$-¿>¾CX?˜\Ij(c‚ªžŸO3 ¹4#7›6ÎÉNœ0 ÿŸ±ÆŠÑZdü{Ìß½\ù±ÄmÍl©×·¦ÿm#Å¡ÎC¹Ñîýä€.9 Û!ƶðÔËœLV£©¢]–Q[Åj¿¤6,ÎKeÓÇ„™ =ÿ<Àd0ŒÜ Æx’9hËÇ@ZöjK˜²Þf¡{£p—Ý?¥û–½«pùj²ú˜ƒ¾ÆwË“òS?Þ+ˆ<Ú•çß¡¤c›S‘X­8…Z-P²K¹j~!»B”Ïl’æ0{R2Y:©Š=,V²G“ÏrÆé=/mDP\s`z¿Š[k “ÐsBŽfÉýÎjv,œ«t^<{ñÀŠ ­Ûj]½×7ªwÿZ‚uë¼@ð¿šE מãÄVÊET$£æ²x¥b%RŸ’ν)S ¦È$¥"£B¢ç˸DÆ5rÍñhÚ·xèþHGû¸z³,߾ோ;í^5ìY¢ovYñ¸ÉcSÿ%ÿ]7+ÃŽ´jð~õjrXU“P¬Pó©l•,S.)’ÈDu2qrgªHô\&餢äa±H8š,Œ DOÄ7"æ@š‚Ö@¸Ò*Ñ,…ßXþSµÕúÖi©y—­®}ï’-îz.ûíh¯ um¬ÀùÖǟѨé1yÙH‚J%e+Îfð%Š b~jŒ›Ú–Ê‘ÿ!K’餈tXŒHF“yâ1_dàñ“ ˆ9´iè9%FŸçWa`ÉF‹¹ÚÍ󦛭ôíNÖoü¸¸¿ÝÍáÆUß®6œ>\w1Ò§¬"6T­¡âUŠ"OÄeå!ª<3£2%QÙ*£f<”%žÕIi#b–|TÈ– 8)GjDÌdtm†½”-ÇÀôÿ,_‡ù«ÞÁÂtmƒ…®k³Åƒ®]_t_w]×ÖæíÔÐ|ê`eöDAmLpf—VÆ"‰Š…tn‚KÏÏRòÊ%q¹Í)±y¿I㲇%dÕˆˆznLHK×óé -B—¹æ@ -º6¥X†êåÖ¬ÂÌ6Ûcô7W‚Gýë0}wvØ]ï:¼¦ùº×Ö‹íû5-až9ÑAéZR„¸–NäVñ¨ô -›TšÉ#””$ãJšÄ‘%ý"\ÑhrLþ˜˜­çÇŸ7ðÈJ#—rn<ɘ€®Ï[ŠÊ%/sÚ¥àY‡-xÔg ~»ÿ5æö=§…?÷X¥íñt,»éÿS~g¨»òZ”Ÿ´…x¹BÃÓ/sI$­4IÅŽ¨Õ gj/óCêúø¡5z¶ÔÀ‹*4 ÑùF.!gœCÈš0 ÒÐ÷-ÂÀ¬Eàm…-xÚl®Û€»÷l@÷З }pëüÆ}öU÷Ü7ôžÜ­ê>í"»áÍ¿{ŠÑIÅ’Ú81øk¶5ƒÒRÌji`¶ö²šMìàKìÐÊ vX©‰QdbF¼0 ÒÑ ©và³Ú<­µ÷[æƒ_oÍ7l@ûóå é™£UÍçeE¿»mÈzè½]~?h¿àn¸³/æ$ù%8¦‡Åv‹£Cº2ˆ]Å$¿®²ow/Åçö4åäµWÔ€ÆW´ K“´ÓÕ¯h!æAÎbô;,Ã¥` Á -t·YŽkÐ2´4ŒÙcªÇ­‹G—f»®Mûó„cò³Àݬ?ÂQáÅ ‘}#Ù!ƒâÀ‡á'Gz6⼆úpǧq^½o Þ7Þ}¯½Žó»ò:Îÿò¬Y0y!˜Î`°º¯X‚¶NKÐØg jŸ,e¦ULÿ²9or^!Ÿp]/?îÈ4úí$BöâõQ.X=Éã´žå0&òõÕ+¼ô…Ažm°‡±;ØÝð2Ø]÷>ÄãÁ‡0ÏÞØã·ÞGzýbLà©€;Z hkµ ·,AÕ}kPd´¹³k-•³[me³{Vòg]Ö1f=·ÄÏœtÂÍØ{j†xÈ&ÑÕ{–ïî9›æéþ:ÏëÈëjïÃoÚ|½}î{ðõ׉ϾGtŸýÜ~ÿ;àèÀßæŒåp·€ŽF:0 ¼×¨YócÒ>:Ì“|üÞ–ûÉy9íÓ¡¯‰ŸÜ7FÎy:¸-p.ìßÿÑ]gQMžyÀ? qµ -.,* ‹l!!!{ I „7Šk¬KGw©KUªŒ#-êX+:–uŽíA=zÎL[·ê8¸ ‚ -B|潚«ô|çwñ]=Ïû¿““ÿhi²l´*5{¬.]0ÖÀáºwp3ÜG²ØN RqU˜‚ç"Ö˜[”>aæs¢bOn¡¨ï¾"ùç)ªùŠÚ{ƒ¢>»G£6bÚ¸Õ¢-GÔx}íHŸYÁœ|HC4ЄÊaX(AQ”åÑ<ÔÆf`u|¶$$c_R"޳âñmJn¦Æ¢/-H‹'Ç<ë> ÚñóaœÛÄ@TNš•“ý±qÊ ìòýGý¦ãë©Óq‰¸2Í“ý$¿›äß¡(çŠ2»)*%`Ò8¦z¥ d±¥E!Ê; qÞó‘L›.}6DŒÈ3¡e|“Ï4Ø|üàòñEs262'aqˆhöê/È¢h²èÈf3 å2¡Ìñ…*Ϫ¢ (\¡ÖGAòi,Ä; <As -ø§R‘u– ÞÅtð:3À½ÆAæÝLd>Íç 1Êy™ïà -‘ùJ ®'ÿï J¦CšÊ€‚ÄFâ ­ÖKTaPÔ-‚´!’ÆDˆ›XK…à/lðÏd€ßÎAV'ɺÆï.ÜgÄ Ivg‘|ž›Þ[xýbd=“xM°ä‘4d'Ñ¡`1 IgB'öƒ^9Úòp¨—GCñI<¤Û’!ÙŸ -ñÑtˆNf@x&‚v.Yàwg!ë>Y½ð†·äƒß/ày„ÿ‘Bä ´¤ƒ‚t&xCÈ€. £À&EŒ†èí ‘[õÚD(¶¦@¶‡œ/8ÈnáBÜ–Ñ>„B»E<ÿ’¤¾Càˆ!îÏø™ ÙäÜQx†Ü /(#hPÆy#o1&žФ°èBPPCUòê“ ù4 ÊÈò =ÎGÎ×BH¾#»3â눒Óö‘“ KG³ÝÒ×Rä9ßË é&'}HNõJ1&QÈÆO”nÕ=µ[}KûAÝ ÍÕ<Ï #4¤ƒ>Ò–(:ìqLT°}áùÃ¥ -A¹1ö’Å(ªIy †-|èv‹‘{8šTgP^R¹WÕï¿hÞÊŸkï4OÔïµw´£yÿÌÓý¨wë:ó?è.Ù)´á4˜Â½a#*¢}P›2Ëø3±LŒ}*‹ÃQ™Š’ºÌÖáhA£dÄÐ$§?¦ÒµjÞäžÏ}­íÌëÓÜÔõjžèz´oõ¿æºõïW £¦Ë¦QÓ…‚Qã9³g0"7ŒËÊx£&ŠºäÉXÅ›UÒ@|œŽe渱jGʰs)w°tèuñVé+Ëne¯ù°ö©±E×c8“ÿ8ÿ²á‘¾Ûx_ÿØø³~° Ëð®ðï¦aK»yØr®pØÒf.lµŽ˜=‘tГ|Ù{]ÄŠ:Ö&NÄ'œéØ=ëÕ¡#«1ƒ+m¬WµU™Ï]õ¢žò²GöFÍýâ&ÝK³ñ_æ¯ -n´[nš®Z¯›ýd(î0ÙÎ[mmEC¶SÅCÅ-¶·Å_–¼³zÓl/çyÁA¾‹²o®"÷ñ‡øñØ”î‡-ÂY#›óô‹z×Y’þ[ïäü¶|¹è×%ëå·+¶æÞpì2\+9TøcqKÑ?¬gK:-ßÛ;,·J/Y_8þZ4àhµ 8NØJ•¾±),9ä²y‚ÒÁL~#œd+È·±.”†Í±LlK2ÚÈŸ9Ð( êý£váãÍ‹ïmp°o¯ª]_±JùSMƒîŠk{A‡s_Ñ%ÇQû{kÙ7%íγ¶®Š¶’žŠ“ö¾ŠfGŸópYùò×eMÎÇ>ç»'( »¦•d»È¾WOz4Ì£a[4;Y‡öp§½Ø›=ëñnÕü»†˜[›mì® .Ñ•Õ+”+×ëÛ—n)ü¦z§ítåAG«³¥âdÙ9׉²+ÕÍe¿Uá|YõyÅ+׾ʾŠÝ®~ç.×ë²®‡'0“ÓFfQCö½5¤Ï&2“‘Þï÷%Žï?1åÉAáŒd·÷袻¶[Ó~ØìýmC­êüšÕù§?Þh9µ|{ɉÚýåǪ›]‡]mK¸:jš\¿Ôî­ê]²³úEõŸ–¼tm_ÒW±íl—y8ÕyÆŸ³8–¥H%C¥E¡S‰²f_Ë–¥‡ã88‹­9v!Œ5d¡l!‘Ò´ÕôÖeFišìK4ÓLóÌï÷ýóüñùû¾¯ûþ^ßë¹ÃXYá‹A¢@ÂƒÐæ»3A…„™êÄîü‚²Z¾—6]M—ú©ÖDî»j+µ§¥.;øìÍfšw¥‡9´&G»7Æ%úÖÆžgTF².ò«8¸MÜ<îm^÷?›7ÅÍäO‡gðg9éü9v:>$·, -ô%tƒ”IIl¾b󿍒þ¾¨I^¬Ö¥NÔï{å ô‹:‹+vô—xÑo˜·f‡:4¤ ÜkR„¾e iŒ"anH^lYXVt/#ê¦à\Ôˆ 5ú=/%z*"9z&,9j649j>$)j%Š=°Ö“0æ¿{‹È"_…ô©Bƒ4{Y›üS“.u´EÍp£™ê@­Ãöî -Ãk%§ÍóYö5_qO–fÄúžKf|•”’‘Pžw™— ¼.ˆF -ã&ùBáWøåLxÜÙYN\ì;.v% -ô#<°ÿ¿{Ï+­‡•UÒäÕͤ·íÛHß´ï“zÔrL¥¯ÁNûú¥“M~ÇjK‚ìÊ -ÂOæDûdg%2Ò22YIçŠÂ„)—xÑÉí‚È䇑‚” ~dÒ7*a:<:~–7ÇŽ.°DþDaDñr€Ùò€¥ò°xY~nS‚Ñ›j¤á®½í&Ê·šmµZÜö_¾äs´¢2ÐæÂÅP×ì"wZ~Ü™„œôàØìŽ ³Š‘Ñ*àd 8çᇧOq#RgÂy)³~Ò[¸Àž&ý„ÕÞø `À㞣A  OžË_Bý¨'=¸Áöà®q/n&¸Êyàd,@؆%`mYŠ ºå`îY…€ƒn`u£Ûþg=áw¿»4Ð{½@ð‚ï¸7|I:¼H¾á5 ï?BHÃI ‚t`{RÁñ¥"‚n>Ó|¶¢bf/s/m!Âó?Ûì‚OV"x·‚¬F`‡'˜ÿ¤!à,É¹ê Æ0z}áßOX Ò‚AÁg,ô7¡ð{Ɇ_g"D.³ǃ -žQÞvˆñ·Glˆ„üYˆMtBtÊ"ðsÁ+]ð*W„5¬Fh+ !‡½À:æÖi__¤#è;?=öGÐ+b„ÀŸ¬¡ðà à¿á`>ã‚ù˜7ø¤ÃÒO=íà3â@$ñfAœà„õb³–"ºh%ø[WƒWG·Ùámtp:ýÀéf€}6a×™û™è DØ @+Ìþ5zÎëq$X?ñr'j"D‘­Éu§"Ö• -±›¤žS g8@> J¡dòÅHJ[Q¾+âÊ<³Í‚:ø­ D¶3q,¼ÓÁà]bûkœû,d„;2È{Çêç!´—ö8?Dƒs3œë“€ÀÙüU¶HXA…ŒP»ÛAKŸ -}Ø,èNÐ&-†*ÅòunH2Ñ ²Ð_Çx+l´Æ´Ew„ŒºÃ†ç؃Q7ÙýQ9/¯9}‚ñð§pïÅ€w3Wâq>ç&Ò!†ü?“È{¨ÉÖÔ¯²ÃZ{d±>@V¤#ÖŠA¯rAr†»UUà=,7ûJ¶$6°^‹ZÂ^%´s^Ä[Ó÷%÷¹ð*·7î!ïqÜ+ÞC¡•;Q× 8/†àËDN&AÐ-™±d_Æ“|ҶЙä-Ö‘{äÎ@w. Â…ÖlÙ²Át½[¿ÞàýR[Âøª"¸WQöT¶;ü±¤ûKRgÄñIþO¢‹‚»¢{‚[âÑ7Åc±—!ì‘ î„qÇe•#¶C1„$WLhÙb-‘»Œ -#¹G‘ÿ4³g æ_4®¿egy?Y»ñH_κŸ¼s[½+â–²%êßòö˜²ãÂkÒžø+’o.J{ÎI‡E§eW@Ü©‚è°¢ƒj$´©ÿ>Ä‘|)ÉÖ‘‘³Àä6¦UT”ùN-gÍì7G8õ•Æ/yZ¤\ùИN»³n}À÷™%¡7Ò,Ü«ºíQ—´ÿˆ=¯ÙVÝ!þJy2é´ò²ä„òɪ7Ò£ê·ÒvÍ[Éí»¤V-÷¥@¼gˆ_Bî°Àidg¬'=Š—ØÀ¼Üö­…6åM%sz_eøœ_-Ñ ~6K]n™t^7 -r˜— …ìó9戞ŒªèÓé;âOè÷Š?O9$éÒ—w&­8’|[Ù®ý]Ù–2¦hÑ[åÍ©ã²¦Ô·ÒÆ´wI “ø³ƒšdg’Q@zlZhËR›ájwêËZ¿©OkCf>¨áÿýVE¢Ëu³†v¡$ƒÙ³!}*¯$ò‹\KlWv­¨#£Ir8}¿ü`Z§jêMkê·š½©}šÝé#ꆵ£ªVE}Ƹ|{Æ[)!yD‹mìHî@6oñ<l&}ª–Ø Ô­°ým——Ý£Fæ´ëyNßÔ$,»P¡¤}UžÆì.Íå+*âw˜…‡ÖW‰äî”¶dïS6gÖ4ežÐ6d\KÙ™Ù«­ËN®ÍQ×dªªs¬ŠªœqYUöDH$¿[G² dë”Î¥ Â‘2V»€òr×G6Oš]mï5Ón6†;^ª.í©‘{vWèº6g³?Ûhä·™6 -[ -+›Ûey»U; Ÿjks?×Õ¬»¨¯Î}¢ûØ0”RiN®0Œª-†1åVƒU¾Õ0.{$ómJÞ ŸlÞ²ÙTÍ¡ îp¤<ß½€ò°ÕÙæV«ÏÔ«{ØóÎ5Æ8Ÿ¬—xtÕhWf°lYÏßWf6•nIÜY\#¯-lTWìO©0vé·äZn|”Z^0¨3 iÍÆM™qTU–?¦(Ë·Êßyûµ³ÿ¿{)ØL|2‹Òß8›òkËÊ݃‹(7xÙ_h {¦Yðáñ†ÄÕGêÕŒ¶šô°½çF6Z -…õåæÄš²*yeéNõfSkʦâÎÔ’âž4Sñý´bÓ:SñÖT4¢1ŽªLƦ Ö   ßa&É.&»×òå÷ú”‡{¦Sî·Í |ß>ŸråÇ”³m¬ÙÝ­ü%Í"·ƒJ¿};ô¡µÙuÕÆØêÊb‹¥BfÞ\¯.1ïM),û,Õ¸éLZ^ÙÄ€>ÓÖX:¬)(U˜Æ”LVÅû &ßBÙœ¥3(CUÓÈæ²§Üi™F¹uh:å›'Ê¿Ž¸Ûjú[W[Ä¢öÖx×Ö=rzSS -«nW&·º./fk­I´iÛViqU­ÊXÙ¬5XÚõ9–S©Y–ïˆ×ºœ-CÚuåÃyTeØ4¦\¿ÑªøÛuÕä™ÅüæMBXEA«„aIXÖE@0€qWëz”ŽZ´D¥ -RŠEAPAܵEÜ—#n=3Óv -cÇNUÔž‹,òŸg¦g¾À|øó~zþ÷yî}?ÜÑÃf¡À”Í‚1=ÛŧoéA­ˆî4ÓµV+ºØâÁok2?ÖmW{$QRYkð+;”£.þb‘‚¸Â}ëõë˶¤­ÞSœ±¢´"{qIÝüü’Óyy%÷˜×óówöç,Úñ>{IÑ@ÖÒmƒ˶\6‡«Eô~Ÿ¾ÛÃу*!Ý®3¢®ãFt¹mu´M原T™Õµh'lšéöy}ªOqÝÜàOjó# «—Ǭ;¸&aÕ…)Ë*>K_X¾?+wÿ᜜}móæî»Íôådï}Ÿ=×@V^ñ`æ‚O‡2òw Í YÖóéÙ§D÷ñèN€®6èâ uœ1§Ö³.ÜÑ3þ&Õí“öŸŒsÞÕ’"/jÊT6æ…®;ºT·ªnuÜÒÛfå×î˜=¯¦,=³úËÌôê“sÓªo2/³ Uƒ™åƒYeCs²w§ç”FÃRÖ‡ÍDßï"º_IÔUǧ Mµ·óèø%Sª¿,æÕ\RU\µ,=ã°½CïQØ>ÇwÝ©yÁ'G,iY5-¯yc\vSѬŒc{“SkR“N’oô / ú£Ã†”/?R«>¤*FÒÒËGRGC€žo#zTFt£šèb=ÚØî×t–G_]Ò¡k¶TÞ%”^ 1ßþM´má•D—µ—Òd+/dû/>¿08÷ìÊð¹g6èÒ;¶ÅÌ>½'^º:1¡½Uúº>¾ý…>î’f6#9ñ(’õ‡‘œTƒ¤Ñ°Žè‡Dw]ùЍ½‰èÛ½j/qTyÈÊîM¤’{î\Ñ]¥ÉæÛ‘×ÞŠ³_q3ÅmÑõ,Ùük |³ºV®®S§\Ý1«s·6¾³zÚŒÎÖé1×™Ó£¯ vÚyÄÅ´#>¶3ãšÇúåÏD÷]­!êh`÷?ÁòÏUtr´ë®ˆŠžšÓ–§Ž¼O}„«Ÿ„Ž[þ8Æ:ÿ‘Þ>çá—Œ‡¹©Ý˼’º×ú&<تŒ}°;húƒêèîV¶û†F×ýByÿCxø]DFvAu:íyDöÃgD7Ùœ«#jnfù§‰Ê¯•ÜâÓ–‡"ÚÐkNkzż•½rþâž ãù=Z‹¬žkCOš]JOŽxVÏ×øžÜcz>–F÷”ȵ=U޽͊ðt2ÿT„þ8èòw(CþŠ@u7‚5÷2Úýω.Õµ4>Åò/í¼NôñŽÖü͈V¾´¤Å/y¹/eü¹¯TF†WfɯfŒOx•d×—a3½/ÏN×·Â!²oƒcØëí.ê×ûÜ‚ßÔM |{F¢úõ‘Dùæ7‰ò$þ/!õÿò€Ÿà5Zç!¢“õDul+XvvÞ'úÓ_ˆ=7¢Üß-)«_Ì3ô{pÉý~‚„÷j£Z“èx³¨ÁÙáƒY4Cùƒ‡ -¬UC›lü‡wÚú~¨´óùÐdï5òµƒÏIJáA±lbY?ñhí¬GXœaùD›î|K”÷#Qæ;#JÅÒÃŽ7n:¼øZ(᪡3 -B¼H‰c?dšø`©ã¤Øl.A‰ÅTšàŠVK\ŸèŒ_¬œ0låü_õLj*ÙPü5Ë¿C´ò)Ñ<–?ûg¢D(¤ƒ5E@ÌSà „ç¾j¾Q)fÜ1Kè -ƒÐóŒÄX.š‚"[Ûà ñ$´˜X£ËÄ -Ϙ÷ LG«b3Pr™hó-¢Oˆrzˆ’žż#ŠQ(L(ˆ½…?&ñ|X2¸ñ$ðä¹BÁ9AÅ9@ÃÙAË·A,ß -I|KdðÇc¡ÀkfØ.0E…ÐBcœŠÐÍ<£ôëÿM–ÿø|=»¿¶HÍòUŒÉ`JŒ'WöŽ˜Â›'ž «Å -RÞxó,À‡ÎQœ±[9B6³œÙÌ'”2ÕÌq¦c „°=/ÀŸƒJÉGp £ P'D@¢~™&P,6‡÷:KÈ‹¬áYf󶱃´e -¤öð¸â[Ì1<~bÞ8Âc€Áÿˆ!eäìÛNʱ f5(ý8ûñ¡auhBPG /‚*Ýùðûh"['Á{÷dxWÚÁë°=¼š ocg_t„üšäÝNõ:CÖÇô3pf¹ÿá/F—a?¸öŒ„½/‚C8«#"P€ð0!Bgˆ I5CHîxXAU8Åvð/·‡_¾ Žð=áß³ÎP|ãÅ]6"ß3/Ü xÇŒ0ì|ßÿr -€û;ÜßA2BY !Þ"¼8è¼ùˆö@"„nšÚd3DæL@ø²IÐl°EÈv{íuDP•3ë\ jv…ªÝ ÊKS¡¼áåæ¹Ê_™! Xæˆ -’Á@H †ç+5<Ÿ‡B6ÂÜ9„ÉX¾”CŒ'±ÞÄ -ÅÆ+ab2,1m‘ ´k¦ r‹ỜVîŠÐš©ÐÔK iõ€ú¬êNæ¾'Ô½ž#êמCêÏ~ <WCþVï…Á§7>ßE…pVCÔ¿É.ó ¦Ï4Ž¿áY¨EjE­Ö­( Š"Aäø%¿œœ ®„#@¸#rÈ A(Ð"HêM׫­X­³VG+VW—ÑZë]Ýgßý/ûÇgæýïûÌó|gÞùà]Ðךß$žƒïi ¼VÀcØjDœ0•Ë!D³ -hzG¬[»ÚÖC€q nü@=âüžú­Ë_þ\ßøßr}As}F}íú„ -nsþàþ[xÜ -Ï«Aày9øÿ*Φ9â|ìºüÕfú…9D¸Y@„DÛB(Ï„ÑK€—´âßÕê÷Dñº·Ì*§×ŒFç—!.†ômz<âöŒvlóï´i÷'´k?~å1Kƒ­wƒÀëZl»Dïi&xŸbÁ6S À‘Œ•àaç [nb<ÄÅ$^ó@`1셢—¼ —®x!JsüC¿þ)Wï?2aÇ4;N°aÇ1vŒ“àk -ìZCç‹°o‰—R ï#ÑÉä– ÷³~—H·{/px*Ù½â_Ñ -LJ‘j§Ù°b×»¢ªÍ·û=oò:½~æöy_#¿ò™!§|ä\Üq™¼çwž|¾ó ÔÓ$|ÀQ>P‡@‚¿)ŒwÀÅÙØuâ°sÉW`ßÀ÷Qn2«ô¶ú#5hÁœ‚óéyÔò»R™ã/’ô×£5n3Qº-߇¼/†5ûžuûM üÏ -ÆN Îî:!¸x\ð$hBð6&ÚP(õ‡APo8ö„Ã.S€Ž3‹)mOÄEHÁód¬¦ü¥Ú`ö\íiùX½Óf6“iK¶ì'Eœã²dç Rµût¬vÛ阽¾'Äõ;'£Ú¾‰è :>|$l*äë°èÃáèƒáïý@ï‰zg„´‹!¸U 4S€µ ÷»V,ö …=vÅØû–S^jÖRæ -\Íg ·[ÝÔÛͨË.¦Ç¬=—"w9™”î9)ÓxOHKýŽÄU|%i¤ Åt… D0ûĬ^ñyÂ(žewE¿&:b€hÙ ÌF 0$@¯“@ˆ)ÀÁ=ˆÄ®%ÅΩÄs¨?Aï ÐïÅŸS~+[ov«l‹åLqàÇòÉ¥gr"§2¥.GÓRÃIZÿYEà¡„ºc|;³+î»#v”l•œá6Knscÿ$â€S„A -¬j)0«¤@7¸øbœ-Ç®“çȳC¯ŠíÑc½ºS¹ŠòSåf‹‹zê‚3%쥓aŽã¹±.#ª¤->½J µ;¹,¨3©†Þ*o&e=d}â0¯6áßp]PøŒW%n… 8år ôr`édÀ0ø¸»q¶Âʽ-ü=Ó-@÷«ìÐ}ËÐ÷†Mçªü>š*g./­.Œqî×È<ÙJŸÎ¬jkF­Q¹—Q—ÒÀ®I>À­L”'ê’®ËO¥ÉÀ+I²Xì"¦) Ä]ŒµEú7ô!Ç=*™nïµFÿØgƒfÐßœÍOÖúÚNTÓF*ø«ûËÄÎ=ERÏŽüŸæ\µ>»€fP陕µ}z;¯DÙ/Ô*'DÊË¡ùÊ9a~ð5iÀÕ(ƒakRe -„-D}/Í -=Ë·D·u–èzõ|œo.5-Bgš7˜ß¿Ýf´6xñ@ w•qoäÆ]œGSIÒö:m†u~­<¯”YšSÃѪ[xU¯0'k,Tu!T•õH¤R}¨²€§ÊRlu:¦@îB¢‚L tOKA7*,ЕÚy8>šn³Gß¶a6Þ²Íz¨1hQoge§!Ì©©Râ^[.÷®*Kó×—äk‹UìM/+Ï(LÏ Uæ~šš÷P”š÷^ Ì^Z6éj`§«€0¢pöX WÙØù°wÎT™£‹ –hºu:yÀMt­5éÜ2¿¯-à“®&bEKƒh}]mŒ[UMÂ6]eŠ_Q…*P£+¤«K+ˆŒ’njQ·@¡ɵgCeÚû"yÑ;ÁžBüÛä©Ð;9S w!™‚æ!tCÐå}fhºÙì´@ÇŒ ÐèÁÕ”£‡UO—ÿÂÖægõ­‚uUMbW]CüVmÂ7Ï ªÎN«Ô±u¤¬¼“/Õ ãô§D±ú{ÂXý[~|pŠLÔ[V„)‹» Dè×B¼ƒ½w‚Nµ›£c=fh´Ï þœr°ßͲýßÇûô¥Õ]¼5ºŽgm[¬GnËžíYéþʆܠ¤úRzB­ˆÝ×NÆùQ†üHÃ~TÍ®¸ -Ș -àHt@Ä–ËZ¢—Yý‚ïpÙ€p±{  ÑC |i…/CC._úØÖ¦-Ö÷‘+µ½aN¹ÆÝn™Ý‰[S¤úÊ;²©ñmÅ´Ý­ÕŒ¨–V"¬ù0GÔö)js23ñ²Ö½Ë^;B|–3$tÌoL>,u—õ'{ÅRùF÷j©+EÆæ^O?ƒì™drŒ7œî— N0¸­Àà7]X!¦@*Bw´]ªAèd BcÝ öãü‘ÿù_ݤªžt¤èŽ{Ì+<¶sAöÃ!ý(ÿsÅX亄#qΒѤÍQÿa»N£š:Ó8€?÷&l -‘°C ³DÂaI°)Te´Z=z¬Smµ:í±g\jíÇj­=޶.ŶÐZ7w‹‚ÖÁ:V2Ö±. ”©VD¨ úŸ×±ý ·߹÷Óý?ïó>÷ÃóÕkÆÊ/—¦–7¼›áªÿÐZT_ksÔ7å6¸mŸæäïDna-r[;~Óóúc³x|ÑþÍD_|J´íó§ûßÚŽÞ>êKËŽEÑ›ÇÔ‚EG->¯±‹çv…Í8T)ýÃÁ©ŠÌN˜Ð²@]Úò¦n|ó;)ŽææüæÚT{s£%·¹35§q0Ͷ¶ÝÈÌm@¦½îyW–µ³´Ô5°;ØþdÿdûßÚ&¢•GyúS»-<Dó¿çæž6zÌÊvcŠ_ÖYcÒo,°ô¼dîYbìÙjèm×ÿt"\ßÛžÜý8\Û…HM¤êkˆ©™ÝÁÎíD›Ù ¬ÛÃò²üDs;ˆ¦^ ª¸,¤ÒÛþ4þ¶„·ã¹¼~o»cdÞɦ 8=Æ Tx™îV{îÎöÑ .­\æ;vh½H5´mL¯ûý•÷.ˆ•CwÅñÇ÷C×À‘ê·ma3ð>›Á•ˆ^gùsX~µ›¨ôG¢Â[BÊ‘íQ(e>Ž¡´Ç*Î Ÿ ¯ƒM †C„ra¦xÄa–‡¯yFc…— xGa‡w$xKpÍ'÷ø„ÿÛØ l`3øvËoùg‰&_$*¾B”ÓM”ù# |É„@ÒCBZ(( *.É\,Ì\ ²xòøHóᨄ`† óX&cƒÐŸ Çà ‡W˜!ÏÙÈfpU3ë+Ñl–?颢«DÖ"ó±\" ¼Hų:ˆ ¢I‚x -C =€qœ?¬œœ/Ê8Tóž˜Ë ±”籎ç°'ìcÎ??g5û—|óÛùÿMä¼ÎÎþ˾Eì¼Är‰b˜(xSü(b -Dù³ZDÑhÄ’Tä ñ¬/Äj!8˜‰ÌKÌ"f5SÃìdZžm‡$=m2] -{¦ -”-€²HE•bfy"z¡¤Ë} ];Ò|!ÝêiÒ¯Æ ª‘9ê¨ÓÌbDõ2w˜a1¤xÂÿdì]†Ä òžÔ ÑñÐkyXz“ÚLVG¡'zB9à q ¼ûÖh(þâ ÅFä|‡?äõbÈ÷2 ?Éœgßìfn3ž~_ÁrŸ -B,‚Ç#dXù,èT’Õ<ŒI¬•ìiÒ b@o÷€¶Ìêi^;oToø"q• ëý‘P#F¶(wB¹+Ê&æx0”ÌUæ&s/ ø]”NDØPÂïŽ}’9¤0© <ÒT<Ò5<,¬©6˜]ž0Nñ†aÎh$¿.‚n¹?´ €æÃ h>†zGÔõÌÞP¨…A}йÄô1ƒa4{ÀÜW#‚‰Ð"òf2¢úôÏ‚å‹çËÁÇ#;‘‡Í @v¦²žÈ¨ôAÚK¾H?æ¥0­‚q}Œ5¡HÙŽ”ºpvEÀÐ,¡UòÈðOÉà ɯ)’ãcI -¢n »e@t 1W͈¹<îY0*Y¾œƒ-šCSÀêph(´‘Ÿç {™r¦ú!{®Y‹ƒ±<ôqúšðáôÓ6GÞO«ºg©—YöI-Gd–Èn[®Ëþ“Ö/ëµ<йa¢Ë‚¸«Œ;ñg3&óYHeçÏ–rÈ—pp2.VO)›R“®lO÷s’h¸pVÀ½üWCíKÃrÞ‘ôÛÖGýbû›ìföÖ蟳ëbú¬_Ë{­ä=ÖSŠ®ìW²‰½dVº­H<ŸU‡ª“6¨Žå@ud¤³³ÛÙÞë eÙÌD‡*%ªd*Ó=†'ø •Uˆî”¼x«x^h_ÑIc¹´»pMôµ‚ò+ù›c/çׯ]Êkˆ¿hoRºí'ÎÛ/%väÝR¶?T·Ù¡iµC{$Úæh÷B³gX£8²³$妰^LUp˜6–0Õ,¨¶yßœäò멚xmâì°Ëå¯F^,]}Áµ*ö\ñ{ʳã?JÃ1úҴʀ﫧‡Ÿ4OömåbEÛ„?+[+ÞM:Vö¾æHiîPÉv}‹«>¥ÉÕdÜïj3íq]3írÝ57”`ÜÌg¥0o/ƒiK9ŒŸŒ€<Öÿ’ÿr]žQMgyþ%DŠ -*2¢Ž2XP¤H€„TA¤D ‰@(¡„J¨¡†Þ”¢ AÇBÇ6™=»ãŠíìì–³sfçìκ»Þ½¿€žÏïÿÜ÷½÷üì{¦€b— OòøMbCx-µ'¾”I?&sÖ>›ßÄXߎMÜ9#ÛsãdÞþkQ¥Ž“5ÎáÍn—B»ÝÇN y\8¡ó ™£ …<¡œ;ñ µ?QûB¥+ yµ‡#ÏÖpä!¯ùãóÁ®u»NþñxŸ²ÞʶÀ« [ŸÒ] ÒX&w¤ÇÌg“£¬¿²™<%Ý«‹Ï:0Æ/<8Sî6|²ž¬nó:9@틼H뉸AïŠxÌh|K×D~ ·F!ZS4¢ª£¥.y} - -Àç¶|&€×âüõðt3øI¾ žäl}Ž#ñŽœfr+ãȦéÔ0k4Öf,9ÉnD$sÐ&äº JÜ{ãj<»b›©í1=ôVÞ³™7Åjä=`×ó~f×Åü—YËGŒj>¢Uñ‘wQ1”• Àå°k Œà½Äþ–n‹ÙÆð$o<.°„»öÄÅx:‡»érVÈ–QÏF›"´žvìI–»v$’5 -Js‚š¦t0kãµìš¸ NeÜNEüOUü{¶J€X¥Ä( Z±¿0D] -Ú(ÚïÀÞ¦ái–ü 0‚ÇE&p¿ØæJìˆ×”žFºߣyÇ· Ê£lú2v2±£&%ݵIª «Å¥”3ɵôªD K%ð)^òU -çý -…/ý -E¿s -ÃÌ!ºB„hï• `¼žü/ ॠ{o. -×Àýc¸­2‡ëå{WTî†KØ´EV}Šˆ9±{[äI ©®µ²rUª’ª:]M/‘6³ %}…ø¢_®ø7[¼ÈÍ–üÓ7[‚|äÄ’‹CžŒ_ÛdD[ :·ÈøU°˜…½7Ÿ÷JHðu¹!ܪÚ“5¶„ñ*·5ÃL³þ²ËNe莖˜=j…С&ç´K…<‹\šYH)ʨ +d ìì´nßÌÔQnzÊ ÿ´Ô1¿ù¥¥!Y*bÉRSvÑ1´• 0¼…x€¿¦ü{ç·JÜ.7€[Õ$˜®5ƒËõ; çëœ×œ­¡›vUþ¼¥<ä uéÉÝÕÊST…bçâüt÷‚<%7GEϒ׳dYœ”Ìa®$óš¿8ó̯~âLÄ‘d"¶41¥2Dÿ‰·ð -÷ðXp§;O¦ëˆ kX£MÛa°Ñ‰Ô­¦®ÓÔq-Ô5Ç·UWFíR•ÇïW–%T§ÊVæze–ÐRókYE;')Oë'ÌâžÊ{„ùÅ/!ùs[”˜‰Yˆþ)(šÿJúØÃÃ"ìØûj& p¡Õ´kèÕ0hkñZÛÐÄ1¯i8f­ª ÿ²è ß.¯Zä˜U™â*+—{H˔ԤÒj†°¸•¯<竼ê£|ˆù»/_‰|b +.1ãó]»°$Å[Ä=ÜÅgp³{W#vÏV€ÁôvYB{×>bc'Ù¸¦½Q¥9jUÔº#·‰·'³!á@j½ÄYR›é.:Sà%¨®¤ñ«š™'+|¢*'8•8ï|"*;R…˜Ñ%ˆÁS"¯h5Ë[øK€¾àãL«Æq¾¶ ûf`34ì&œéw3Tõ1L‹z–Ó²-£#Ê6¥-Þ.Y“ì(l‘¹Ä5+ȼ&•WdC#-¬¡¢¾Ì n¸Ç -V¿a×#Fp¢‡V#ZXþ+*_Í¿E{¸Wpã €®`ç÷ö´`ÿ©ÞÕ#6„²áƒ¤Â!êÚl­Ÿyú¹ +é@ĉý±»Nõ‰öñ{S£»s\»JÝC:ÕžA=”ÀŽqï€Î;Þ¯©mˆ Aԣ͈zL(Au«yƒ{øoq¾`w0Šý³¿ í@-vвñu ¼´—ì‰òq£´1¶©øâQ á…Ð-q£¼/xçl#GNï –Û*v:ª­s9¬í>ÄÕŽ¹ûi¿q÷\rçœý@æ ²o/"s;Ù¿}5¯2¿G×ëp€sÝgê—ó±æN‘ kÚdÓ» ÒiWRâÝX0åo3¼9êj”UØÕømÁWÄ;'2w™(ÚËÕÕîç躰t˜ºÛŽLÝ’cüƒ} 9Ó. Æra®æø 檮´ ut 4Œ”ã|Å$@êMHœ3áüvÌ;bæ=IQó>ÆasëƒçÂ7›å[™M²äΦ[sf -¶³fjvÒg:¾ôž¹`KýÚ–:³dëuóÃëÈŽüÚç1…ö{\Eö+¹W†w¸ÜA'Þ@?@ίXΟH» ¼mÑŒ!üáf[°…BÐÍਞ»Æ_lì«^ÇÖ'˜1ô)iú¼Í}ågžú6K²þ¼•û£y+wýÏVnÿ³ÕåÚî|í8xÙ8Ý]ÍMÜÁØòðh¨È_ο…ó¿Áú-‚¾3¿'ëÁ÷©pžîŸÅCæ"ƒHvÄÀûY(Éë9ßÐãy’Ñ¡çY&®/J×9¿hZïôrÐÔñåu3‡W/Ìžý¾Á~™ïŠ,ö=AŸÛ}‚nyø´á;Pó –ógîD.~àógЗLÁ{É(¯mÁóµ#¸¿ñ ¸½aœß!:½ 5pxÇ7ø?ÙõÕ™Æü¹Sa†20À ÌÐf`Fª°P¤ˆ"JQ,X"q%±žUc jtíE±¬=¢Á.Y{ÔuíÆƵG"%( -þ÷!çäØýð;ïOÿç}î}ï'´~¬$¸~²ÔØ0Ofh¨¼Ú#×ÿv^®õRîÿ+äþu°ó«ƒBW e{;ø ¬ÝF´è;¢mù§ˆ†_ x(ã6QÒ}¢Ø§D›ÈúÖ"Þj)ü]…5GPps¬`|Ÿ(|Hü?ô´-"ŸÖÑ"ïÖ)bÏ߈5X/vÇ~‰nHÜ>6JÔ-«ß³æŽ*ù .ÞC4“ó'œæü‹D¹¼÷´»DÝÙ8?¼‘( - -†Š Ð:ÒÁH>0Z¤ArCOÁY‚ -y‚JL”X R`ƒÈEv¸%’£‘áÿ,ÝM4‹óËÚò/qþ-¢TÞ{Âc¢ˆçDAõD òƒ„38Ó…<àAjt"ø“‚Èá¤@4ÉÑ$H%þ“ΊB+g•l»ÀjÙ›æðùû‚û?¢-ÿQ -ï=–³9ß§•È“kpg.\‡3”œéDö\‹ î$†7çù1# g1,‰e²|Æ/AÌb|ø±ƒ}Ï~èÌ‚"F‹à×Mï414¹b¸C=A×l[Á*¥pݪdpÝϾg?È ¾"‡ú«cM mdpãk7ØñµÜaß!\@ˆY@˜‰µ][ELA—,†o1: •À«TÍ4V.…f)«A³‰í”CSÍÛAsÒžØ=ö‚5²V;xáOöLO(áý?Œœo -,À*Àl!$F„ bf‹¡/ÀoŒÚIl¶ ÚErhW±õvÐnµ‡¶ŠígGОc7Ø3ö›ºÅG­:([tphÑ‘W§·þ!”s-FQ¬^ùw¤MË'b˜3ÄÍ“ x¤Æ 2¦Ëa˜oÃ2î_…†MJv(Xí€À#0œbWaxäØbhpl6¾wh2Âñw#œ P1—×F¸¾ êáœoÓ ˆñç/ –ëˆã^Äòýèœ"†u€‘Ce°Œ“#|Š=Ìs0/r€i•#L•l«S«©Ê¹ÅtÀùƒé˜s³é_ª&Ó}Õó¯Î æwÎ/Íp­3Á­Ö÷ÿ˜àñÌ ‡á!*€s}té$ Käz’Âxåg³[w1ñ¥ˆË—!v”]kt™òCçéï:Ïszk[êü»mêm£Ëkë×WÖj×kºÞzV]gûIýÜVëúÄöÖí‘ š[áõ³Þ÷ØM¼¯tîÑ:Î÷â\BO–¢$ 5J„ä®bôL“¾ïž+oJ,¶ýI©CC×)Î/»ÌVÕvYèú"a…úyÂ:·g [ÜŸÄïvÈãaÂi ?zÜMx¡¹ßä}->—㡽È.ÄCw&º“] mqm{w'ô♯·šåEÈæ^d›„Ö¬QSzÉ«´ly]jâyòhÇ'½¾P=ì9]}¿G¹û½îK4w’V{ÞJÜèu#qg§ë‰û|®&ó½œxÉ÷BÒSí¹ÄF¿Ó‰ÐŸHDÀ1öv( û»Cߺjxÿmù#ºTFtf’ˆNL‘PÍÉ®ó¨¨î+à÷ÍÀ( †}߆}fö‘ÊDTDDY‡aVePY”M* X´&F«Œ¶±M0.59îÑ´1iÐh’“ÛKsÚíŸóæøÎïÞûÞ¼[Ä…3r •{°ªõî ×îÊ2hËÝnÚ’½Ûª1Sb_›‘ǯ–ÈÝ*Òª=ËR›Dò”nïÂäa_YÒ9¿¼¤ýs“¿öËMFïÜô""ú,ÈMBÏÅ0Rå߯iç›ËPÒÞ÷QÀt &ËVÀX¹) —»²úÊü—w•¬Ñj+Š\Ù¼w›I½l—å¾¼T»Êœl~Yv‘›<³Òs¯´Q”ŸqØ;GrÜ/+}Ò_š~Ý?#ý¹_†äŸ Š¥IÓQ(MCÏÅ0 -`~'Àý4€Tƒ?PþL)í<åã•j0RmÕ|Vw•ï²¶ŠnsÙzýº’­Æ5Å;-Ê “mK -2Šd®²<…gnn½(3§Ý[’=蛚uÆ?%kÖ?9ë™_röÏ>)Ù(NÉBQj& -S¥(Xla¾¦ûá®”ö^ªÁÅÚ;éÕë4í>#û80Pk=uŽL{­˜Ó¼/xE}õZÝêŠhžBg./Ûc³·$Ã1¯8ß%«¨Ô#£p¿0µ Uœ$ë÷M”öK]ñO=¡ëO>»d(NÌGQb. -wç `1ÜJ³D}ȸºP:ÿ™}'iÿh`AÏ=èh²cZUë‚4ªëÂuû£ ‹kbMeÕ‰V9•iöÒŠ~š¢Ø-¹¬FXÚâ_rÔ'N>î+¿ä+D×½·£8®E; -Q¸³‹ýB³ð`¡ù4Ôƒs5ÔƒzÚ{ÐÞÑÐvHš[­˜úCìêƒje-¡ZEM‘+ócŒ³â-$u)¶)µYŽ»÷º$ÔTzÄU7 ·Uõxm©õŽ®¼è]ùÀ;ºâ­xsŠ6—£pk -bJÐs±—ñŸS” 5¨¤PþH3@/í ­ô*ÜØÉ…Ú.3¨êre•vú.+ìÑÌk_§›Ù¶Ù0íÐÓ¤ƒ{,wµdØíh’9Å(wÙÒØè¾©¡KÙpB´¾ár_´¾þáú:l¨EÏÈôØXµÔ z.ÍÑ,^.£ì¥üV€Ã´6ôTõªƒ¢ò~G¦ ÏK5§÷=õŒ£Z)G¢ôv÷Äòâ»M·w¥[ní̳>\êÙQç¼®ý°kDûˆ{xû°ö¿¹‡µ¾v;„îaÑ=¼Ý"—z˜NϤB€ 4ƒMC”ßÝ Ðr f hˆ ²a]ȱéˆ';m$€³gxFÂЭ¸ã[õbŽÅF¦˜lÌ6_7 ·ŽèßoÚßîÒ7ä´ºšÜÏ)¸÷Õä¯êAçUÝ輺ù‹ÝÉ¡ûQA5h8Aù½”ßz v ”V’,ÚÓNs!eÂöL83 Þì¸ÓÁœ˜Óï«GŸÚÄÝx*NgÝøýˆñLÃбBã±³U£m -›²»kpò•µÿoÑÖoíü†È±¥>^˜êÁøAêåwP~Ã1Ú?OäŸHØ1ÅØi=ˆ™±Í3LÔL{ÃÌÕµ3–…OǨ¯™Þ¥¹zZ¢ýÞtnàT•¾ÿÔ!Ÿ©ACï©ßóÄÓ·yâóóF¢sh"œD3ÁïÐ\p-»Ds8Ió?Dýï¦ü¦ã´žM¤ŸØy`Ó%6¼U"®ò lÖBgE¢ b‚•áì e”J 2Žã§L^î£ÌU++„ÊfMOe?×ãÚY-÷k7µÜgçµ]¯ ®ËeÔw¾„+ù¡Ábç©G)¿…ò«Föž¡ü)€ø‹”O+aø5ÝX¾s\ð™3ï› ¾)¯Ï‚@x+‚ÜŠfÅÖŸ æbãtÿõÑüµRýk(¿j/™¡üË”? ú'€ÀOD·TÀå up~  ü‡æàøÈ ‹Àþñ»`÷$lžFƒõÓÆò™”±xVÊ2ûª™eúüËøù–Ñ‹‡,£§?³xOÍ{Œ*†Pu±AÊo§üý”_t–ò? ú_ˆú#Àš?øßð¼àô%€í7j`ý­X~Çóï¬ÀôŸ|0þ^¼ù@0˜ýWA÷õNxçµ”ÑùAÁh¿ie´Þž`¸ogÉ?Í×ÈhΓï—ê¤ù¯§|9åg|Hùtö7Bæ|î¸Ý°{`þÀä'ð ¨ zhï %h£pÑ4Ñ40Ôppè‡X%ÀB0H…Æ1B‡Âo þŸFš¿Ê—Rïã¯Q>Õ=ø€ø  šX=àQ¾þ[ L Ìå”É…¨Cy+)ÏØhEÿˉH #[½!ý`¡ƒ¢’|E~\BAùY”Ÿpò©îÁŸSï8R¶ÙBö7”û(èœË‰ -rèoÕ —è}bL¬ Ÿx‘`B/Hb¤Ò͇Cä<ùËhâÆ …3ƒ–ÄÌ•A#ƒzþ jý†AÍM,ÔH`¡Z&)&5¤™t’>2LÆÈ$¹À ÚUò)}~D^’· ªã°þKÙK˜»0híÈ  ±¢]×ÌACoúÁ êl`¡örÓI©$¤!ÇÈ 2A¦È%rƒ|IþNÞü•eýJµÿ‡µƒvv :Ø2hO»®õÂ÷ Z1È[Ë•ÛX¨—ÌBÝ\RJjÙ¨ÛB: ÕBw˜®ãä,ù\'÷þÅu}GEugq¿SF…A†"3Àu -DÊPAd¨2”¡30€ ½Š€"Š, ÅBKÀ5êZWc‹îšM¢=–hbY-ñæb'œýãsÞûýuß»÷÷Îû}9(zÀy'úóV„œ×&ïq_™ ðÿßûúN”q%]e´v ^Hi¶óØh­d£e-r8hQÂAó:²Š¬'›¹hÞÃE‹Ýä"§¹¿[|Ë}mqûBüœûLŒ¼§bä?ã¸Ç–( zŨ÷ëïëË-Ý,ˆ˜ØÚrwýÕØ( e£}¥”è¨o•Üw’Ü?$kxo%¼7’­¼W’ü—’½ü’Cüç’ügÒ‹ü_¥wø?Û=ãß·{'¸+Eá;ÛõoKQ˜Ü’¢Á(”[Ñ'e8s21œEÏ3‹fâNóPx³pz]£8oåÉÜW.Zî ¦„÷?¦–ÿ”YÉ̬÷ ³YðˆéüÌô 0÷˜/õî2çõ†]†7\ž -¿gÞéÿÛ'\eÐð -ƒ/“‹ä‚Ëßpú@w Ç$@/#@oZûP/æÐ^õšÍzãáÏ~>+ŒóÄ]Å}ôA&ïÁÌþÝ™‚;ŠÁ°¢Uï'E‡ð†b‹ð‡;ÆÿwÆ>ýë3ë_›qÖàŠâ¦ÁÅÃ)þ0úJƧ(:©@““3Ðä8]Ñz~@ïîI¹wŽ}Ò†€ô<4“{øí£é¬Ç~Þ¬>Áœ;ÞÑÜ›s’ùߨ\÷Z&¼êU£Ù³Ùà¢g›á7'žóødÒY>£¯=ŒÏxüÓø”Ç5Ñqχ&G<Þš~áf‡²ËÅ#z}Ñbûø!Õô©Ï\ ¯C áaˆ ÜZd×C¦ÁÅ_öÙE y§ÆèN68”=qh~¡ñ¡y&sL®ž²?°Ý¢?`«xOÀn«>ÿAë]þg¬wüd³-à¹MwN݈6]±ÞˆVÇ@êAå½`€Wal¸¯äÂ-¥†•pM9ÎG2p:Ò‹},b>H©.ŽŸp ,ݨ?$ÏäÓEËÌv/¬6ß¹ Ér{ðZëžàÍ6ÝA½¶[‚öK:ƒŽK7]·ÛôÄîã`”®'ë‚QÒŒ¶kàÔ±ÐOø×/&”²ýf~ŒeÁµ%<¸«ç–ˆà¤JGT³Øƒqüý±¡Â=ѱ†»¢’{#4¦=Êó­áå–]‹ë­7…µØnm—¶‡tÛ¯Ùã°6äˆlMÈ%YKÈ#ÙªÐwÍ¡hOìšÂPÚ†’c ÿ8ÚO”7c¾¥¼q>‘_'òádâ$8œ,…d»_íÇÛ¸P¸=>Ò°;.AÔµ$ÝlSl®EGt±Õú¨ê©m‘MÒÖˆ6ûe—¬Y¹Ë©1üsCø9çzåç:åïNµ(#5hWÒ±0¸àe¾ï¨þ7ÉgRYp"• ‡S'Àg6°7c{gº7¯'u¾pKJ¸áFuœ¨=1Ŭ-![ܪÒY·Ä•Û6/Yn·"¶UV³Ñ©6º—©ŠþÌ¥2ú+yyô]—²˜7Li :•Æ¢Œ8н}I Ú™ÃÛ0€Û”ù®¨)o¥Sæ£Ì1”ņ,Ø›m ;s䬞l/^gÖ\á†ÌPÃué1¢ÖÔ$³U)™â&u¾MCR‰¤.±Ö¾:a¥cE|»s©ªÇe™j¿kQÜ)Wj˜¼vÑÅ£3q$2 -íÇÃxêAe õµ¹ýZ!ìÌ3‡ž|gVgþlnG®¿^[΢ «³£DÍY f iâºt­MuZ‘¤<µÊ¾$¹É±H½ŽÑ©»åùIý®¹I'Ü´I7ÝrÔ/åZ52ĉȴIè0.x82êÁÙL€cTûe¿}» -ÆAO¡)tÉX…îÜ6Ÿ eið„Æ<¥q}nœYuNЏ"[c]ª)g–Ûd48楯erҺ䚴OÝ2Ó¾œ–‘öƒ[FÚ yF2Ä)3 3SQFFaí…‘½˜ -p:‡z@õôQþÛVÌÎt”Ú±ÚJœ–ŸqÅó ê -UébMË–&YçgZëòòmóµ¥öÚœzGMv+“¡Ù,OÕìvKÖ™¦Ö|ç¦ÖÊŸŸÔPýjÊ=µ†°ªÞVÔËÙµuž¼ŠZaIõBC]U„(¯Re–Sž*Î,˶I+-”¨Kª–5;Åw01E½ò¨¢Ï]#‹.“§ò¨BdˆS´c -PFFý ðŸ‘9P¾ £g?Õï­è\Nõšõay“Ô4;³Ê›fq‹ý+‚ ´ áFšúØÉéujóäÚ,«Äš‚©qÕåÒ˜ªFYdåÇNÊŠmLXÅ KhÅ—ÐòÇLX9:‡•¡ãâR”…— ÃX÷U—è[<^H¹“Žá}õÝíÍ”kW  ¢ÕJ×8° -×(8ù­ÞüìÕóÆg´„¦¬Š6NZ™`ªjN·ˆmú“ðzвNÃþü?f`˜afnÃMP¹ÄEES“²¼”¬k¹ÙºÖÉn–]6e”¦y¿¡‰Z(¸*ÈX® -Æ)J¶«Y†ŠY®šµÒØ»µÛΞ³‡ó;0Ïÿ}ßï›ùÞ'úMZðBäø×‹£Ç½¶2î®â-ñc‹÷$Œ)nJójGüó%þÎW¤?Å'±…=´vß¼pU¬ëB`ý›ìÅç¯àîµJÃì56<¹ÖYki3×dé^S`˜¶ºÐgꪉ~E+§øß·bzß + ·|NXá²ùc–.‹µtsôÈ¥Õ±KÄ,i)X,1‹$fÄ=bao§y-~À¼ËÙÿ…ù¥K€UÌ^Èõè¥îÜ)5cFi(Þ” ¦mJצ–æë‹JGîÛxiüÆÉæqoM³nx$p̆Ùö;ÖÏ .X¿$lhIix^IUDnIcDðܵ‘³Z"sVIdîÊÞŽ±5¼þ«^ç½Èüu+7×rÌ~vð'î¢l3`JyŠ*¢qE²šX‘­Ý[Q »»¼Ðslù$ïÑåS}Fn›a¾í)kþÖ¹9[÷ÉÞúVßÌ­•öŒ­Ú3ʮ؇¼-Áé›%$}“„¦—öÖ0‡=à5PÁü·¸’-gíÅ¥œ÷ÐÇÊi\Ñ&TwW›QX‚±®þãJÃ(W®á¥ wÝ£Ëwyæº2d»f3]/ø¤»šÓªKüR\;-ƒ]³ ®¾lI®úÉ6h— Ü)wHŸžp»8ÿÍÌ_Íü¬ýÏ\Éžà:½¸P¸—;`yµVäÖöCÎûNd¿²êò‘Q7F¥××Òê§j)õ3uÉõÏêÖ{&Õ¯ñrÖo7$1÷T½(cÿ×1ñf`î6àÉÀÃ.`2Wûùf6hô‘ŽX‘t$ ‰Gãá<šgSŽFÿcwüAÄŸ¥¢ÏUQÍË´Èæ2-âãZ-¢ù¢~¬Ë£ßQÑõ;"ú°Ä³§ræo`þRæ¿ÌÞ?ÍÚÿøPTÃúëYÿ@ƇÀÀ& îcÂOò¹!g‚|&Á_$#¨%ö–;`?;}ÏMCŸóO"ðü«h-ÿ…ݰ}Ù Ûùï`;+ÊÚBgDëi#ó—3~ðÌn`k/bÝ… ÀÐF ÙIÑ'Ð3@à9=.›a»k[,m±03æöLøvŒ€©ã^¯Nƒ÷·OÃй^×6Am?t¡uÞ‚vUàÑÞÛ*æ3N5ïýÌ?Œev³SO §€Hfl¿o|oaêòƒ÷ðú1:w4”;p§ÓPâ¡ûAàßüݶ›7—û µ‘ð÷½-`þs̟ɹÿ޵2;—Ùƒ?eï[€°VÖþ`þ'`ì ·½x@‰7BÍd£¾Nq”L9ćᇡðÂ7\á….,P.‘›äW/òÚ{”³Ÿr˜ùÍÌgÝÉÌŽ¹ X¯>̽þýoéÉØã,vŠ$'eÐhšB| öCØÙGÿ ³¿{"$(žúCË ˆiˆáJ0Ž~OÒz™ÑJÚÀcl¡rª¤÷è}j¤Oé]§[ô¾B˜ á9â þ<“9•çÈSb(T¢/âyf(Ñž¦¹ô:-£µTªD•AÔª¦ýÔ@'øŸ/R'¹áæ‰ÝyN”[ß›„3?’;nD$¤_ûÒ}öš­Ä-&ö¸ž¶P9UÒ^ªçxšè<~4^ÅÆ.Ü0‰ºá#ê;ߟi¿%æG‡÷\¿÷ãëÐîù ô)Pâça™I³‰½°°–¥´†6R™ºeÙ¡Ü—ê²Ôª›ÖÔ k‹ê´¶«ëMõM´6Ñ®ø‹ÇåÿAb™ÛßN} ±Ýø:–×Fç‘Ç9Ur¿’ éê§ Ç•Ûþ¼ºiEý`_¤¾·¯PßÙKÔuûfÕT®¾ ªÒ:‚ökíA‡µ+ÁŸi_·© !7µó!âq6Tt-¡¢ÿâ?Îô A$²þb@÷kö"q$>“空¢Ç«ï£Pר«ŽÙZ»ã%­ÍñšvÙ±D»äX­}娍]t”i;µÖ¨½Ú¹¨´–¨SÚéè+ŸDßÔ7Ljç±ñ:#†#Ý¢{“¼¥-Á¾T3$¯Ó8—”8t%§âúÀ|t$ªË‰“ÕW‰Óµ ‰kçœÏi-Η=>w¾áqÚ¹\wÒY¢û»s‹î„s»¾ÙéÒsÖy6%6{6&]òjHúÁû`’ë’ÄT›$>5ÝÅç@’ÌÚÓ| ·qïÌ4A²yž¬@ü+#WÓ¸t[ZÓFâ‹´ñêdÚÚ‰´™ÇSŸÒ5¥¾¨?’úªgcêb¯Ã)+ )¼¥”yLÙe¬KÙgªMi4Hm5íM½î»'UüvSUªX*iîIÒXûægk{³Öø¾›YjveVøíÎÜc©ÌÃÛ5ì SÕ°ç|w ç·cèËöüe¶òüÿmyï”åU¾WóoªË5¨é+ ão"á äJb­ZµŠµÅEŒ(á ˜„K€!@¸„k¸‚á¢@E‚(¥µÞ¸ÔjëÝuÛnk·n»³Ž³Û±Ýévv»îtÝíöì;Óad?üæü¿=sž÷9ÿsæô¾™§¤ß°NJbMH kì˜) ^ Ùƒ{x ð »ç“±s%¹Á' ð0!n%½ï&ï¦,%¤^JRÐ/È5Œ…Ä|Ϲ„RŸÙø¿3ñÍÓqS²£Á“²։ةñØùPGì2{4ö!çxìWœ!Ùs΀Œ°ûe$ ±ÿk²gƒõŸ¤|¢Â¾£¢À½Ct¸™+J\V卵WI©ó*9}V™Æ8šë=¥0úM¦TL$[‚Æ’Û˜£I=!ÇåC¡CòœÄYž=ñ2¿/ñ.ÿHâŸùÝòù]rÂ³É ×–Dظ†®…ìÃëT†àuûYêcç¹¥ÁΕAƒåt_¸˜É—fåMMõtf<ýT†ÒãDz–·#­ÀDmZLU< lbÙu²ûRíÜžT¿K1#èT¼Ö¡¸)´*ž[SkI%„‡pÂ^…àuÿ^oOqÿ¡öì<ïâ•»ˆëÅl/Xвá¬n38u‘Ô“¹é­Âc8;Ã{0Kço×öeš™=–[º•s8íÏš6,hUO ›ÔçD ªëâzõïÅõ÷¢º4†ÂE8«Ìá·˜?d`×AÝ÷°{. >vŸ…<œÉgÁTÁF˜0즎ÄЇò“ýyjï^]Žw®!°S[ÊlÏ© mÍná6guñ4Caͤ¨&s>¼*óIeæ#‰Yó¸BCDH˜YCøá®Bâ¾>„9ÌÂΉú×°{^.p!g é0e ‚‰b ŒGP‹ö¹5&0z -•Þ†,ÿö}`k~ ³I_ZŸ×È­Óuò«sû…f털\;')ÍY–”h?ÚP¬ý[x±–ˆJ´$  |„·ÊsœÃ—j€ß¢ÿ7 èA öÞR€7‘© -Œ›`¸Lƒå¯RúʢܺKeî‡M -¯¶’ ¿æ"]`½ÑȬ3V„VÖs͆v~YÁQaIþ˜¸H?+)Ô/n(Ѐ|+)Ð1"4è‰á#¼U¾—cpуëØû.—ã °{FÆñû¸ÙªÐ[µ•ÒUµ‡ÖQyнŜìÕXžæ[W¦]_]j6›ÊBÊJj9%Åm|cq¯ÐP4*ÖÏHtÆ+r¿Þ 5~#Ñç‰0·t…„¿–¿§`²î¡ï”aç«Bð 8Y0ŒO0{©ã‚Ͳ™ÒnyÖ\³®¾VîQS£ò©¬Î -(¯Ê2UšXEæj¶¡¢™§/ïË-唞×”^’d–Þ—d”þ×ÿŠ3MD¨1A–‰ð³J^ðW%fA‡ç=X¬ÄÔÌ4 ˆÝÐÝà‡›B ­y#45ï¢Zšöѫ㠩ޥõ™~E–¼@C]S_kÍ­iäfרšê!az•S¬®z;\Yuù:\Yù“HUIÂTf"P› _]Ax«k¹Š˜Cý“¨?4 -`Cíz'fà,@î€Òw -;ékW½ âj0ì\ÂŽÅ-ðêÒnؾ$…mˉ°uY [–u”MËe”—Wš¨/­Ø©’•)ZøÊšxåM¼ôƒ›è*¡ ¯uÂËÄ]xéçpþNÔAß{&q38ÔÎ; ¾p;è@Ä5€Í7h~ËD·‚@t[ÂÛ› ìNîDÿnðî¥÷^>pîWû¾ BïCȃóÀzð`Ýû…u‡P˜· •y“ÐÖ2ƒúcc}8óÜ»y3€Úi‹¨õtïuÔ`ÓÑ€ÐÜ ðcOxþØàÿ©ü>ݾ¿û>øñùÌÞë;x}~l‰Fຕ@¥öéCÂqÝ šè \íÙÖ¥)û§ÛÀö„±bðJî‡çr:žÉ£P-gã±< å<Ü—çáž\„;òJÜ–×ᦼU¶ûpÝö8®Ø^ÃE»'¨Ð 霽Î:é'¡8óáÅø^vMm ó-öø,Uîò)QîôÙ¬Úîó½j›ïiÕfßÛêM~/¬÷šµ´æ-ß¿-4ŒË©ÂŸS§ÙÛŸ*p£µ•-lQá犓­|PÞ:‡Ma(ó‘öú÷UìöOUìðϰØfÊRn1MPm6MW—šf«7™ -l0­Ô¬7m°\kÚeYbúÑêþ×­Vø×h—ù ë%µ´‹? Z+jãs¹¹Þ¸À©íl gÚXâØgzjûö~Ší:J[ÛEJ›ã,6PnLW­ ¡. «)˜l¹ÚœkµÒ\ ]n^b½Ô¼Æf‰y«\dþ·\h¾,Ï7WÛÛ9µäüaSßÛøâsæ\ NqÙ+çõH -eíuØìŽ-?Á?;µ—Öw -W”tбXÝ1Qµ¢cJƒe†j–tÈ´* -ÎÖO³Y4KžTh;7hµ.?¨Ô~vP™}^P…}nÐ#ûÁÂ~z°Ð‘]N-Û:â3.mµKîžûŽvʺ{:+°=TFiXC¬÷Eq„YZªX¥, -Sv–fU6ÌzΣåY_L²ý.4W—:Ï~FÈrÇœ ú©!{œ¦„œvšzßiB¨ÐŽdOºú„¸Ï%îãâ¹·Ï´•Ÿ7uÑbM7g¬ŠjŽ¥ÝÛH‹ºwP,ˆêª,ˆê©ÎL´Ìë–¢ÙuˆÍŒ®£lsºdë¦vÉq˜‘¯Ÿ±Ø);|­a\øNãØðÆÑwŒYo _G=9’Ù×7YƒŸ#Y.ù{{3~,°‘ŸK¢`E=÷ôÂÂ^&© W{Åì^•ßõŒVo™Óc€vJ4yRôp» ÝÇØÓ}²ãب<§¬ÈBcfd±óÈÈm.#"Ë]†EÞtɈzëÒ“ã°HáPç)Ç`e8p’¹ˆv$pŸÈøñÀŠÞ(еÇüxÌéÓJÊë¨ø¶O¨2'>R=9®·åÄØ$ëìØAòØÞ_ÙîõµCfÏ ú‘=s Ãcæ;³ÊeH¹~ÙãˆkZL½tN‹r"}zŒp¬ó˜ýp©pŒùïcü-µï4CJb¾ó€Äe®ý6¹%%pë›p…j\ú&#’„éëÜéÌù€}ˆõßÎø¸åYI ¹%Åû)Ö˜šêŒ‰ƒ›!;Í_3øï™ƒÃT#R£4ƒb­† J²IOd—úÝeÕô•†á7 1P’°„,,.¸V°:,õˆU±‚HÁA@ÙÙ*›‚`-‹k1X«b]Q´êQD˜Îè´#UëѱêèØrÔŠÖÝ:õÎk+Ú™süãá—@¸Ïw¿ïÞÜûÅ¤ÚÆ,ÈÕF/(ÖGFWÙÏÚä8;j—SxT‹ÓQWÈc‡ð(a>_è#æ ÑöÐÅ}xŠk°…ùßÏ+èö$ö|dmýŒ¥8Á…IZä%BNò(If²—4-y’YrR€"!1Ôbaâ\eLBŒUt|’Mä¢,»9‹–ê"VÂâ6:„Æíp ‰kv -Žûž Aˆ>0^舶‡K¼úgÝųô×f²ï]̼ Þ¹ù>-Û)9½‘˜;‹r=$±¹ãeѹ~òÈœŠ9Ùaّʰ¬…êÌ4› ÌÙ$Khzè˜Ç:°ö œw=}ÕlIVæ…|¦²ï(fïYb޹¥Ì.sFx™ ÂÊ<%!e¥3˦ÊË‚äÓKÃþ¥Q~Ë-'-ÏVy—[M(Ycó^I­­WÉÍØ’Sš±ËîÚŽ]*l½ŠH!)6=g°;Ø–m^¬!ËI.ÝI¥À¼•ì}*$®´BP•#«† `Õh¼¿ÊKâ¿ÊWê·jºlÒªP3Ÿªyò‰U ïUeXxUYŽ©¬RzVnQyTîW»W¶«Ý+î¨Ý>j·„•Û -aå^öšcÜ{™s½Æ2 |s@Ò鎭ÂWëÙÿU[`ò |7ôƒq8¼î˜P3ãk¦HÆÕÌŽ­ —Ž©Y ó¨I‘»ó{6–+Þ1n2w5î5w1¶™»l¸mîò‰xkÄ:²VXŽXóšƒ\s;Ø}FïZ:K+œ* ­ê<ºƒÙùmÆoÆÔªàY§‡G]¸×€[½þ\?£MþxÇ,iš+q1ÅKÞ6åH‡™VȆšjdCL»Í›NÈ×ß2T+䃶ˆ^7“Ï„¢‡=¬{ýÕtW¬ -èMÝDofm¦Õ·žÛ×]2 kPãO  Ý3CöŒÀུwîõÇ€ÆPôoŒ†scú6.CŸ}ë$Nû¶K÷ýEâØØ%qÜó\ê°[Hv ™ÃÎ×l£#ç½z÷ÛÒ,zÒaèõÙ ¼ËžÔu?0ø Ðç°†&5ôM:èöƒîè0h›Ý`×<šcþ°=Fâ`Ó’ë–rX·ÖBÝz„\…Uó3X°>"$d3ýë8÷2Î;÷sÖà `î. ¨ðý’ù§×õ00è(лв]µ:!‡e› -mv0oë EÛ2 -½NŽƒü¤?ÌÚÃ!k‡´½’vN®}'Ðñ5Ÿ·‰à) i{Mõ'Ü‹Ÿòû¨HÞDrÎAôúÒû.½.ô ×ñ iT€ù·€ä¬5/Zà\àüà/Ç_\Jy¸ÄCð2¿h/—ð`æ¯pB—/ðù˜ˆÿ¡‚ë­yOåÜç3ß3ÐO¯çWÀÛô:赦×ò;ÀìŸàXä_à*ã¸Æ8®kx!vº7\[cÛŒ£›‡@77þ€»,ð]Nêî¿ùþ)ŸâKéOgÍ£p4“þx0mÃÚ¾Ì=½jz/¼×Èä¹I~"ÝrŽ©~¶æÅT<ì<<ãEùWÊÏy ~ -n:ÁÅ-ZÉUòð‹€˜C@(ÿ4™nOÎyØiÖþ×/ÇqCù¹.× (Ï+ŽŸ÷Ý+»ì»7&‰·FÁ‹¨,(,ȲܖEvvå²ìrY–Ë‚+  "²ˆ.!xCƒEƒ—h¢&Fmc›Æ6ÓIf:i:MS§M[§vÚ´}zâ˜é‡ß<ï¼_þç9çÌyÎ>|e >X6·—ÍÂÍeà½å7àÆòpmÅï©Å•O©K«½€\@Îÿ$” dê‡âXÁqû·u,x¼FVÊà‹e«àÓ -øÙ*üøU-Ü]ï¯)ƒ[kkàݵMp=Øï{©«ÁýÔåàaj1d‚ºrŒºr–¾r…ž_w~kÝoè7CŸ°fC ë$r"”°ãùCÈkKãä©GmŽoµŸ‡³áWkðqðKp/4 n¯RàZ¸.G˜áR„•ZˆpPçÍÔ9E;}VÑCÏ)öÓg~Ö¬â(ë”b–}Bq‘}Lq›=£øŒ=ùWΑHÂØH¸ãÈë‘„óCHƉ£W‰Ï Ž×‡ÈG1÷7rá}…®G­ÅÍ›`!:Î)3aNi¤Î(-Ô)¥>¡¬§)w²f”ìiå^öQåg2z‚{8ú8w}Hca klœ!Mw0¹…7¼›ßŸ¼WЧ>(ìU ìQÏŠ¼ê·™.õ=¦CýÓžüOQ[2 ô$á÷<ê%žbÞ¿Æ{?Ì@ω¾ïlÔÇsc9•Ê…™49Lføv3ø3©áÌ ú@fk¿ÖÌî×–sú´5¼ÞŒF~OF«À›Þ-ìLß/jOñ¤ïJ[¸Ó>´¤})iNÿ‡Ø•ND„"BWÚO°þ_¦àz“p}ßbè·0–éL -&tRݱ†õ‘p@Gùô©tŸ>›Õ«/`wg[¸]Ù6^Çg@ÛŽÂÖ¬Î@wV?Ó’å»t3ÒFݼ¬^wKæÔýZV—õw©#‹ˆÆ¡#"$ð9põÁÜ7\Gß·`Â=¿` -c™Ðãži`à@îJ0†CŸ1ö“)¯QÇê0æ±Û󊸞<+ß[Ð’ëºrÚE 9{ÅNÈÄa˜’ÕÞ’WÞ•Û ŸÉm†'2›H1ÂØõD„>ãìÃO1wŒWÌè9KŽ¡ÿ›(D}ü7˜û -—A¯) ºÍÑÐiÞNµ›3X­&{§ÉÄm.,ã5VÔ4ë -ZE5ù=âªü!©Í8)³çäåÆëAeÆ_•¿•—‰´ÌHÄS–GDÏùÖá瘃[xÿ·qõz½ßž~\ñ__‘zŠ_‚Î’h·l‚V‹ŠÚiI£]–lvcI>ÇYbá9Jl5ÅN¡½È-ª,òŠ+̃ÒRóaY‰éLP±éZÙüIÙôXn6i‘‰H¦¨ˆžó;¬ÃÌû Ô\¨À>´bð<ˆ±ôaN¼¥<Ø]&OÅp[#¡Ùºj´jhg…Žå¨ÈãTWñìåÖ€Êr‡°¼¬ETZÚ)..š-ã²Bˬ<ßr%Èhùù‹Üh!Òü"A˜übÂÑ3¾À:ÜÇú_CÍyÖÁŽ~ ÏJÔÇXÚ¬,pÛ¤Ðlÿ4V…ƒ³:–rT'ÑÕUZ–½*‡mµ›¸åör¾ÅV-(¶5š+w3•û$Fë˜,×zRn°.ÊõÖŸ"–é+ˆ‘Ê ó=¢g|Ž}w뿈šsµèwj†«Ñï mO žµ 8+ ¶n=T;£ÁæL ¬Î4º¬.›m©+à9,<“ÃPPÛ Ì«õˆrjöгkü’¬êã2]õ%™¶ú'ÈŸdÚ*"ɬ"âL;atv"ÒÙ–xˆ}wW΋¨uºëàD¿Q‡9@ZzüWÓö¦—Áê -rW”ºâ¨—†6»t¬BW;¿©ˆ›Ûdåšê²ÝB]ã‘¶aXœÞ0#ImX¦4Ü—jêÿ(IqqJaRëˆ(Õñ‚Øw×1ó¨}­€¿ û  iÀo{ ú7,»dPÜú*˜=áPè‰|Ïv*Ï“Açxô,}k!;«µŒ“ÙZÃËØÕºË+Ô¸‡Õîif»û<“è¾Ë$¸1‰-ÿ%6Q¢‹nFÓw1—±s¨?åFß‹V q#µ¸Ž—¶˜:(0v2Ûµr¼¡ ÷n‚ÝñÕ­¡2»ut†×H§yKد£ö6q·wuðU]ûâº&ۺΠc»îc;¿Æîþ·0¶cÛˆ`«ç7±ÿðγ€qÔëGv#Mí¶Ôï0ôèú±/ÒûWCª/R|JÐøA=N% è©DŸ‰Vù*Xq>';Öçá(}ýÜ-¾Ã¼ÍýgyQ¾Û¼¨þ¯xQ}ßñ£z ?jÒó‚«Xó³xßc^€‘.€=È.üv v)jçõd¨Ò0Â@üÈ+w(¶RÀV,Äú“ Ư…èÑ\Ø2ZBm­¦6Žî¤þ^V¸ŒµÁ†µÞÿ{½ÿ·¬°‘±ÃNØrà1糨{¤{íi'҈ߕ>Ìjï@k¤ˆ›Ø<ɃȣP]S!> ¦·ÂkÓjX?­ƒ°7þÇvy@E}eaüûOpè}P,ˆ ƒ…±€ -(¢q]×äèÑÕ Æ‚»¸ADET,ŠNPXªÔh{,G$1®qu]{ÖXëÛâîêÙÃùñ¨ï{·¼ûî‰@óx˜g £y±ägΑڛ‹%_ó>É`þQ2lú]f(2C¾¾ü/•)€™šë©·4‹yÀõ³ÕÀ赌A.çÏ| b3õ‹8~¥Zø–9ÀPæ Ÿrx—ë¼;<+"áQ1îír\é\—Êp®4骎Up,§2!9ýµ’VŠ3y³™‹ÔLYÇ»HÝ?çÜÄR·O VFý* gSÝ€cµíjõ°¯u#`WDÂak‰„Î26–1ÐZ¦CcI…Ú²*K%”–£\ï@]+ ©!ÕÚ=­¬bÐ×ÔC݉ÔEݸr ß6À¸‹úÕÔ¯Ü÷_¶ßÈ ª×AVï©Áhð6#GØüádeáµò²Y™PV:ÒÊ ¬—ÉSþ^¼Åú ¼ ÀjO-ÆPwmä¿ë€.û߃€k= ?Øä§À†CIl3ÀYwÒ8ß•ÍÓF6eÙ„\dáiâ…kbR5Ñ™ß&·É+"þCV!ïâV`zïmþÈDQ7Œº+à|°; ¨ÎRûùžü@.óWtl†õlÈ]€ë>À 7Ù þLÜb¾Å|›I›I}g'á&w!¯‰h!…y6ƒ±·Ní(ÚF{;Q׋ºŽç]# »DÍÉuòOr“üLhîªûvÀ/ôÉCÆæ±?ð[7à›“çlŒžOåð ÷‚1yI#_ÒW÷Éã’èûñôûh÷Úv<xòÏôÔÕ¼ÑmÖ¼EîšÁxDžßÈ3‰àµ=‡—æ¡°i<‡àÃ,øðæ†`ÐÏ!Pq±¡k¡6@À‡ðkA‘?‘ $‘Ì#©dYM6RDJÉvRK¾!§Éuò/¼Æ+~ˆ·xIžµ!쨭ó„PpÎWø’®¤;H†“±ä32‹$“t²‚ädu¶rß -¼À.ãž2ižà.ó'øW¿’Gÿ¡§¶½ „Æ™ß5ãÑæ‹H?2„|B>¥N"uæâ9R¸kC°’:k©ñ%÷2S£„VoÃÔà>ê©~ŽéòCø„ŸÓG´¬7ßA8RW¯‡°m!ç -—¶˜¼Gz‘jŽ ÞXjMf|ÎtX@Tjdⲩ±žûçs¿"Ü@#° Ç~¦ðIü ×p‰'ûž4q7Fï \¨ëhK_h®ZGž¥9&ŒÛ‡ÔíGÍxê}L­ñÔ™Š0&ד«HÃæÆeæÆ%ärÿM¼ºÅ8O_œ…ߊSŒÇ žø8£t”»Z%#Rëúá®ã9”ôST¯‚hgGŸ8ã¥ÚåA´¯'5£qUŽË²Ñh’%à‚|:ÎÉçàŒbN+ÒqJ±'”98¦ÌƒUY„ê -4¨jpHÕ€ƒš&|­½‡½ºW°Ø2cíjÞAxQŸeN¸W^/'žëíñÀÖ7´~¸¬}6}pV7§lGâ¸ÝXµ›ŒÃö3Ñ`?‡ìã`»¥8Ðnöës±W¿uú2Ô:ìAµC=ö84a‡ÓT9¿F…‹@Y¥ÎBzƒð¥¶7ˈ¥†Üðn.yJ\µ·C£½;N;tÆQ§î¨w€®Ã°ÏíÔ¹GûT{$a·ÇìôLÃÏ娿•ƒ*¯|Tx£Ì{J½ Äç¶îHfÃKi“¯ -IA3!å·!üZËÉ=–Øk,÷»ð `™;å­ÕýýPgè†êö}°³Ã l÷ÊŽcPRÿé(ñŸbÿdl H—ŠVJæ€ Òæ³TX)åÖIy'e¹~’­ëôL–ÓYÈÖÕÞF¿R÷Kë…0੟3Oöv´Çî@olë„ò®=P4[ÞН‚Gasð8O–òƒgHy!s¥!)² !™²õ!9²µ!ò5!¥òU!Õòì£ò¬ÐëŠe¡OKC…"#TÈ›Yò?¼ þ]êÿÐ8iâÌG,=Ùc~ÀÙ3X‡²P7líó‡a(ë‡<ã`äG`½q´´Ö8AZcœ"[eL’eʳŒéò寕ŠLãFE†q«2ݸSù…±^•~E•þXµ(\(›IŠŒ­< æÔÏn_>ÿ|rë8nï”õŠÃUØÜÝ ù=ýÛ+ëLXcŠF¶i˜”e%-7•eš&Ê2L‰òtÓ\Eš)E‘jZ¦L1­S-Š0«FlSÏ8 ™gjÒÌ1=ÔÌ6 õ,“Pe ­<¤½W{ÓÔ>ÀÙs× Î:ÑÀW|j -{KÈí­ÇÚ¾>X„¬¨XÖ? -ýã¤ôþ#¤´þ£e‹£&Èÿ5E‘•¤\•¬š™¡ž¹F3+²Póyd…vfä>›ÄÈó6Ó#h§E ÍÔ(¡ž)Tm(›¹O|ǧÞJíºx Š òbØcòç«ûÛaÅ@OdFwÆ’˜0¤ÅöÅâØXiQìG²…±£dócÇÉçÆNRÌŽ™¡LŠ™§š“¦NŒÎÖN‹Î³™]ª›mÑMŠþV71úž.!Fh¢…†¨‰*a`+·"Ú}ˆÚÕÃÙc“‚¡œ·âØcòËbuX2Ø©qþHÒ Éñ&Ì(Í—ÍŽ)KŠ#Ÿÿ©"qÈ4å´!³ÕSâR4“âVhâþÍu™GEyaüùfav .D‹&¢è‘¨(Ú((Š"¢¬Ã0Ì ÌŒÀ° (‹Åq¡âh]\Q0.cMðhš›4rZ=&©Ic\kmŽmš¸&úõAÆäœþñ;ßý8Ã}îû¾÷Þï}ÚÔyI¯ir“ŽjIŸjíI·4ö¤Çj{’¨²'ŠJ¢xÊUîƒ?3ÝÔ~ƒíF[Ÿ6¶aëØþ>ð¤(Ñ ˆº´Ô¦‡£Ú…*C¬PnH ’Òt‹´$=WV”^PV©ÈO«Wæ¦5©úMß¡ÍÖÑYôgÈM­YÿHcÖ‹*‹^TZREÅS.qžaŽQû Ø‘läZVóÝÃuÔd¨ÍDµñ9TfŽC¹i*ÊL³—iPlJ“š²¤N“]–—Y(_˜Y®°eº•ÙÆ•j‹q£&˸K›™ñ†Î˜qZ—‘qC›‘ñ³&#CT ¢Ò¢—‹ñ}mðÛf`6½Ž•9àx%×RÏuÔ˜TfõÇbK0ʬcQb‚¢ì™BAö>dÝßbüûJxŠyhêI%)à{Ûrs™ãâ~0”AZy(R+‘R‰¤Ê9H¬LT¦ ñ•I\Ež4¶¢L6»Â-ŸUÑ]Þª˜^¾WYþ>ùJ¹ø®"r‘¨ˆ,¢J‰«ÌÁáB “­÷Æ2`y)PKÊ8Î]LßWAß³H^ªFBÍ@̯}ñµa˜çž‚¹îhĺã0Ç"ĸ3…™n»d†»Xå®–Ns/—½èn‘O©}M>¹ö¸<Âý…<¢æŽ<¢Z”G,!U¢|²Ÿw™óÔ•ÖÐK½REŠøžCíôZ ±ŽÞ§AŠ˜ßõÃÌÆ!ˆn…Ë&`ú²©ˆòÄ Ò³Sy_ôX…)§á©&z%<ë¥ã=Ò<ïHÃ<ç¤a?HÃDiØË¤þWÞd®÷Tó<Ôp/.¥ï$‹H.µ³¨L›6×ÌXEÿ¹Z…É͈hƤæÑ˜¸f"Â×Db‚wÆ{ñ‚׈0¯c½¥ã­B½^a”·]é}KYÛ#„¬ù^±Z”Œh"«DIˆŸ®*Þ/ë빩¹„ò=»‘5XÄ7Ñkè¿Öã7Òƒµhºy Fm†‘­cÒ:#Z#ñ|[,žkKÆð63†µ9ñ›-Õº¥ C¶ló[|¼åµÞFP‹(m"Ɇ>öP{Û -Þ Œµ~÷u´†ÆÕ@‚ˆÙÀø7hYGo†· ڥŠŽ$;Fc@G8;#É\<Ó©GÿÎôÛ] -Ýî² ÚÝû î<Mç hw=†n§];ÙÑG;õ65+¨¹„P×üÖ`0‡ºÓ^ÂÛÐN`Ø`ð~ — jŸŠCp(òC¡ùÂ!ñEAðÍãeÃïãaóqCùè1}ôº¾£ä›úÜC"? ¤«6j®e¬õ¤”º¶6Ö€ñÆí¢v÷1þ.êû€Aoý8•ê]@8¦âÅB{|a#üÞhBû>›Ÿn~p»és»yÀ»™èn±ûuòù󘈿°º+¶2Œ7Ÿº™ÔÏXg&Qs45ƒ©9à=@{Ÿ?0ä# pZM¸Ž¹Ž3ÁlF{›ápà3®ã,?ÆgyÙŸå!ïa±{(pöm>/’»‹¿Ð´÷Á. d/`=Èf½CýãÌ70”šÔ§É'ÔþŒü…ü•œ€óJà‚ø"¸H£ò5›ôKãË\Ç~Œ®ò¿Æ ¿Îb^?EnûD|B]kÀ4åR¨CÝIÔÅXŸ=ÃÚ -(züšŸ“¯É%ò-¹B®‘Rà&kóOæä»ÁÀm•ïÇ?rwØÜåÞ¸÷p¿…0Á>&üÇ?6ê”2«j4âhÇGìÇØLü¿AÜãšÜÅI¼Ã¾x×qï1’Gx“•»É¾ñPý¨ÜR*Çê+°GÇsMµÔΆPSÇÎ20›l<@ÿ±?F-5š©ÑAոͽŽÉÞ¸Á5¹ÆZ¼Á¾xWXwq‰+0"<Á°¨â¢¤â‚üMTŽ8UK¾<á¨yÌX>–Ʊ¦þÌsî ‹qW\†·¥ ¼%åá¦ìÀu¥W7®hZpYÓ‰QŸ\òéňïv ûâ¸C8?î οŒ3n§&<ĉIŸãØdGÈáo¡N¡~·:Gܧ¿ äøÓŠx0nîúâöøù¸11W&%atrF&Û0¬-Á­ 絜õóâŒ_Nù­ÇI¿~÷GýàHÀ) -øü^ø†?Ç` Š½STì!»Ç|†:ãlGæË¹0§§¦áä´›V„#Ó84£ߛш3Û±æj웹 C³v`pÖöÌ>†Ý³‡10ûvÍù%¶¿ô™°å%Uè'›ŸÓ÷œ¯8V? â¨_LýHjóȹÂ{ß<µ3&â̬)8>'‡çÆàà¼$¼ör&†æçcp¾{T`÷‚: 5cgP'v­Ã¶ ~akð¡?øÐ|^è ¾.l -ù©°>ä/ÂÚ…ªÐCÖ„|“/˜÷¨û@ÜŽFcy¤GÑßñØ9¤Á¡ ¼2ƒ¡áØ]‹Ò±cq6¶-.À–ðRlw¡/Ü#ô†· û… ½Âºˆ]bOÄ~quÄi±;âŠØñ¾ØùX\ù/±=R}FÄÿø4”ÇõßåX¿–Lk¡§6c9ì£ ×b r&¶G/Ä–ú–$£W—…º|¬×Ù±VW.ôèj…Õ±ÍÂ*]§Ø¥[/vê¶Iº!©-ö„Ô;*·ÄÞ•›bÿ$7ÆþSjˆUÇ=cèžñ˜õÿ·;Þ3Ò˜?Ÿ’¨Ío¬ÉvÝ$ô/ŠÞ¸ذ, -ëôX“n}ºô…Â+úRa¥Þ%´ë=¢Wß.¶ê×HÍ ›¥Æ„½²'á¨ìNQêô?Rjô”jýWrµ^•\ ÿEãkpŸùß4rýiÓŠ 2–)ô˜¬I¯~<Ö'¾€ž¤yèNGWJVRÑn0¡Í/´B³¡Bl4Ô‰C«ä6¬’ê ½ráUÅe8¬T.j* w4e†ß“(Nƒ*;STéë<Œ§ý¡æUÚ³fÖ€ög€±ô§Ó_±&k t§ú¡3m6:2BáÍСŘŒ&ãr4s±Hpb­±F¬66IUÆN¹Â¸Q.3îTJ3jJ2¾¯qoùØ5vãß•â U~Jº*ÙŸóëDÚ2j^¶'í9¶1–MŒ¥‡5é2ŠèX®Ekæ 4g£Á Y·9µæl¡ÚlªÌ+Ä -s•XnöHNs‡\bZ§8LÛ•bÓ~M¡éœOé¦Íô[«éKÕ¤*Ö,U&’5S•lä#æú5/åGòè±i763–µŒ¥+“w.Ö§Ù< 9Sá¶ÌG­%Õ¹ñ¨ÊMEe^–Pž—/8óìbIn…èÈuKŹmranbËݪX-û4y–3>ËuŸ˯Èš‹ªää¨ò–lUã'¬ù j^,à^°±hË71žnÆÓFÖȘÜyP“ÿ"\Ö¹¨´-B¹-΂d”,ÇŠ‹`/(Š -œbAA­dµµHy¶Õ²ÅÖ¯dÛ†4fÛiŸ,Û5ŸLÛÏ}2­ÓdZU%3_•³ÆÈS¥1î1׫Ô=W̽H;¾µXÇx:O#c©ã³ªÀåEþpÏFIñB8ì1°Ûõ(r¤£Ð‘-Ø6!ßQ"æÚ«Å{“d¶wÉYö>eyñ &£ø¤&½ø MšýCMZñMZ‘ª¤ªrúª4Æ;¬ùejŸ\ì¥ï³ó®A¼Œ©–ß+ùÛéáX¡EqÉ –Á挄Õü2r˲S–/d—ÙSY¥˜élŒÎN)ݹINuîVRœÇ5ÉÎ×5IÎ4‰¥Ÿ)I%ªœ´â)R’C•’ÉmÖ|„úGiÁJ%¬i"•¤„ß‹ÊkÅ$äUN¥jrª!Û¥ƒÉ•„,—Ë]]EBº«LHu¹ÅW»”äÚ é]ò²ª£r|Õ¨纯ÄUýUŽ«Tå¸ -UŠ+W¥ø²gÜ`í/”³*¸øì!mÄMœüVXEÏ] ˜kÇ#«ÎËݳáFz}Òêã‘êIEŠÇ„d‰žAï©–yZÅ8ÏZ1¶~‡´¤þ°SIŠªOŠrÿYŠªS¥¨ZUŠ®!ÕϸÂ\ÏR㻤ÏŽ@šH5ß5¼÷¹¬ ­IFJód$µLEbË<è[ð¬u ⽉ˆóf`©×‚X.ào¥ím¢¼ÝB„w«¸Ø{P ó‹¡Þ»bhë'bh‹*†6‘FU {ί'ê¹ÔZÏëa©'å|/¤¶¹™ú^@ß,í%¯ø#¦k&¢» jU8"W-EĪd„¯ÊÄ¢n+ºKÿÍu™G×|¦qüû»‰X²YÄ–•,–$–›&ni‡©žÃTUk_Šp’ -"‚ˆ$ÉDäÔ’PJ‹ ÌÔ2‡ŒUcÌLU«ÆÑÚjj‰:Jû›Ï•˜:óÇGr#y¾Ïò¾Ïû<ê’•¬È¬ …g³*°¬]FhÖI#tÞM#4Ó4B3`Ži M7-a° ­*tŠfpY Sa*ŒágCÑ~mŽÔw®·@Š^dQÔbOEäú*<7Ps#Ô)7Fayñ -Í민Á -Ρ üÉ -ÌŸ¥ù¹jŸ_®vù;\yß)`ñÏj›cmAvÕ3yfó.°f ™øüN5`={e!ñ/–bò¥ˆ¥Rpqµ/ñR»?µ- T@i„Ú”ÆÊ¿4A~¥äkS>ö±jmŸ¦Vöùò¶ÛÕÒ¾U^ö#ò´_‘WÉOjYÌQËx?‹¾„Vv&g¦ó¨ùìžÙRÿ\)¡ý"ôKÑ/“Ê¥Ök›ÈËá%O‡¯<åî‡XµpØÔÜ1PÍoËmÝ$5q¤ÉÕQ ‹cäøXFåç²TÞ•e­)× -SMÖ4PIŒ…¬Yh¦Àx>]B –I¶öÏ•Rä)ˆ51 JjµIòØb‘Û¶2¶1 oc­†¯êžR P ¾†ÆRC¡k(b ÎWovÌm7á)˜ÄÊ -é Äš‰äzørjÀŸôE·§ý èo–ÚTKÞ;$÷]’++«>vöÛ½ Æûðc~ìÇZ†Z}-Í¥–b×b¼–jÀWüÎ#0ÿG±çièŽ%Ïo ûêFɺUê‚ëÁhú$µÜ/5?(Ÿ ÿGøÔ ~Åcäã¸Ã0ü§é$~œb9E³¯ã¢×Qà:ꢮ¾‡_À|F^}ÝIÄ;l 5à×âwKÝö¡¦ÿaÉ M·ch’JV±ä0ÃøÌE:Û\:Ç¢tž%áB{é ôKøñuàñ¹Ìå¿L‘/¯—¾©…‹ðÌgds2yý¡4]Û!©ûòTò=AíOI.§õÎÁ?úH+‹˜X£¤«MXPÜ¥ëääË栌ȿq‡ù>gãA*K‡»ž×c¼þÀúzM#ßc©ï/ñv?Ž>º>Äé~Ûçá_zN­oá:p´tîÀp0©›JÉÉSjc² ˜ Ç&C’ÉÙ0¹ð&51I¶I1Í/àgÀ³×”_ã÷q0Þ„10fÀXùP+` ¬‡-°öë6ПÙPŸàðOl°ùésÂà&\wî¼ò…V´†6=  ‚0’a&ÌÅ~¶žj ö‹±[†ÊZ=ÒìW«^»u_Ÿš:Rt½ûúž¿zÎmø®Àe§.Þüëžø4æ¢+ÄÃkÄ4”xÆ “ˆF -)OÃþ<ì/Â~v—ë–VW%qmÒwÚ¡kÚ§ë:Gç6¥|"gÖ¿lä"\€óNš:u›ƒ[ã×–ÏjòDÑì‰æ+D1ˆ²¿ƒÎx4¦bº®j6ö³°Ÿƒýì–plVcwv·R…õ¹ø+—è4ñgý¨SX> ίu/`¶@Ûàhȉ+¸“ËÖÄ„f7rÞ}­!ø>wõ7%é,gãŒÒ¹¢YØÏÁn!öK¹¾ktœšÕvÚÈ>Ö <¹¤ƒº«Z¬€ÚÿäŘ® >üâ<ÖrÑ jr…sqQÐëAöKP_à ٤÷ùŸ4Ö*—3ZïzC·Çªljjm3S/`ÒRÁ _Z€-ö -¿N¸º§¯!Úë­ÝMúh§Ûoµ½Ùªn6\[›Óæ‰ÚÔbš6ºÏR•û\­wÏ‘Ãc™*=V©ÂcƒÊ=?ÐjÏCZéyF+¼n©Ôû©–{ó>¼@<¡½ÞiGK ¦†ÐþiùOó)r÷ÔN϶ªñ -×––½´Ñ»ŸªZ½.G«¡ZÛz¤Ö´ž rŸ©Zå3]+}fk…ïÙ}—¨ÄÏ®b?‡Šüj´Ô¿Vþ§•ßæºrž(§­©EÏ hà!úפϣxzº ήA{ÛÖgÕ¯™6ùúhˆ*ºkuÛx•µë/{ûÁ*é0LÅFkYljZÚ1I©Z˜©¼ å+'¨BÙÁ[´0x¯æŸÔ¼kÊ }¬ŒPº¤‡üÊÄ~©+OQéìˆfç×uø²6ÄPy ·V·WIH¤–…õRa§¾ZÒi ò:ÑâðáZ>NÙá‰Z‘¢ù³5/bæF*#r•Ò#7i‘{ŒYQÇÔ¨+ÆŒ¨GÆô(ÓHyNd7xòÏñ´‹gß{‰=Ê6ŸWvg¾"7Å*Œl£¼.aÊé£ìn ZЭ¿²ºÒÜè·”=Js¢'jvL’ÒbR53fž‘“g̈Ya¤ÄTïÅì2’c?5’b/ScSbM#ÑI̯\%î¿ ˜ñgûËŒ\}¤2|)â©)ˆe÷Œn®E1>š¤¹=»*£—Ué½ú)­÷@Íì=D©Öáša§éÖ)šfn$[çIÖcªu¹‘huX&Åí°LŒ;by7ñqÀ´Œ‹3qÖFz›ÆWhD{ÿo¤ÍŒåýˆÏù ìqÌÚ½]•iõVúK4+>B© =5#Á¦”>4­Ï`%Û†)É6ZSm“ŒDÛ{Æd[š1ѶИ`+²Œ³UXÆÚj,£m‡\FÙλŒ´Ý³Œ°™ ô1g$˜Æb>†þžùªÿå¹ÌãªÎ0þÞ{ KHe‰ KÃ"˜I&ËL2IfÂLB&Û$$C!² “"IT¤BR, ø@¥!µ(}¤Âc©ÐVª«V­Ëc] -B­ûÒÛ_Hê¿gî½3sÞïûÎ9÷|/>ƒ¶£Xî$–æEø êÒ®ºÅcU›8E5IsT¼@UÉ-wdªÒ‘« -G@åŽ2#èXa”:ÖÅŽ;ÌBG§YàØnæ;ö[yŽc–ßqÎò9.YK¶¹$¹_’môqgœìŽø.jÀõ]ÄÓL,k‰c ­a­c¸ªS®UUê -¥ÍTEÚ<•§'©,Ý© Ó£g¾Šœ¥F¡s¹Qà¬3òëL¿³Ýô¥w[¹éû,óq+Çù'+ÛyÑÊN·Í+¤ÙfNj?gÑ8žIËK+¼ÝMÍuñÜJ,«‰£†ÚT¥‡©ÒyÊ]̈QifœŠ3©È¦€;Kn¿òÝEFž»Âð¹k\÷m¦Ç½ÑÌvo³²Ü[™î£V†û¬år_°\™¶ÙGF¸ú9ƱÖlƒMÙÔ€xꉧ†º,'žr÷`•fE¨8;J…9SU3GK= ”çqÈïɔϛ«\o@^o¹‘ã­1²¼Mf¦gƒéò<`9={¬4ÏcVªçy+Å󱕒c›}¤fCV?§Ð8š+õÀ/žn¥-¿…xBÄRÆg±ÇT7\ù¹cå_-Ÿo–r}óäõ'Êãw*ÇïQ–?_nÐÈð¯0œþ#ͧ™âßb&ûw[Iþ^+ÑÚZìÿÈ\ìû¯¹x‰m&æàµÍä}$=é—î¥o…†%ô¹PF<…Üçó/o¸¼ù£•³t¢² ¦Ë]§Ì‚eRå -d)=àWZ X)å†#°ÚH -´˜‹›Í„À.sAà°x>0ã ¾7ã—Úf|>äÙæxŠü°–²hÃ×A,‡BðóeVVQ˜2Š#å*ŽRzÉT¥•ÌVjé|¥”&)¹Ô¥¤`®ƒ- -V(!x‹± ØlÌÞcÌ î4o2cƒ§ÌØÒ÷ÍØ’ïÌØbÛŒ-²Í¸"Ûˆ+´cèì/bOÒ~o€&ôj`×ù<÷–H™´ÄiåƒäX®¤Š1J¬˜¤E•Ó•P§…• ZJU|([óCùš -êæÐJņn3æ„ÚÙ¡Y¡ƒÆÌÐ÷Œ•ß3–ÙÆÌòÊlã1tzJٓЂÞ¨‚Rî}eø¾eèWJ‹«ð>ÕC5oå(Í]¥›k¦(®æFÅÖÌÓœU‰š½Ê¥WåjÖª"ͬ­ÒôÚFÅÔnÔ´ÚnM©= ÉµÏ(zÕÛš\óµ1¹Ú6&¯ègJ•m<ŠÆh‡ÛÊñ|PÁu<Ø´ôø¬ëüZé¦Õƒ4«>\3®Õô† Ši˜¦7ÎÑ´ÆxMmthJc–¢óuCÓ2MjªÓ„¦6ý¨i«¢šöi|ÓqkzCã¾Ôøz[Qk`µmDÕÙÆ#hl#ÇõËY‹°‚àçy&-ù)‹tS“4‹Ýsó!&áÍñEš²‹:ŸTK—XÜ—’.SÄË p™8.¦j–káaÖáãRº3нþ”AŽWŸeÜ4ûôÞ€·Rbc¥‡ð/¸L³>e~>g|…Aùƒ`Ïš›ÉfNìû€„mÄìáÜÞh5@ßõ8˜ -q ÙPË`%ÔÃ:Xí°¶ÁèÁ³>¢ïqßáB¿¡x_ë]}¥Ïô%ß~1À%øÞ…·úü&¾ÒVø#â˜7B¸À%B£–ñ›¿™ñ70vãÞ¯ÏÕ¥ÿh§>Åg^Æ}~¢£”æ$%zEs÷ÿü`@û}xÎËWbCa„ADA ÌzÙè,E£LÿÖ -F¬Ó­eü;{ãv0-[˜žnrÛÅtí#¿ÃLÝq½®³úßþ•j¼Ähÿç8§¯ÄЧm>Gçuäælê–€ž“iÏE£Pï0'o©šeX§×ˆã?Lýéåí…á¸0?gÊpj‘ÇÃJðê‹U˜^lÂÔb‡·àPx;†wã@xû#F°7b;&"öawÄQì\rã‘—±#ò¶G~-QÅX´„MÿÇ7ÔÿšÏÌg«Ø÷¦Q;…O-ý¼ÆñSQóp2"G#q$*‡¢óq`i9ö-­ÆÄ2 v/kÂΘVŒÇø°#¶Ûc×akì6ÇíÆXÜŒ.#Ë/a8þ6Äÿë¾Ã`¢„þÇCj~Éòãc>1ï1½ŸÍŽÓÏÇ_¦—Cq‹±y,ö$Ȱ31;VcÛÊJlYiÄæ$+6%90šìÁH²ÃÉA¬OÁPÊKL=ˆPêISßAŸì&ze?ÅZÙ#t§IÌ ›%UÂoWó9R2yÀ¹ÔWP›~Xža½ìJy;R¢±U–„±496®*Àpz9Ö§ë1´ÚŒÁÕv d¸Ìð¡?£}™ëÑ“¹Ý™ûC—üMøä7Ð!ÿíY†'K‚Gþ=2%|Å'å>KŸËäL!×Ïrl/ýŒç°ç¢—Í™s1š‰ ò e¥c [‰àšô¯©DoŽ=9 èV8PxáWt£S9ˆåÚ•{àQN£Uy^¸•ˆ–Ü g#÷?¡”ðÅ3¾¤Þm>óK,;&Y±ÆÍgßɸl`\á*cÑ—›ŠµyÙäŸ_΂jø -Ìè(´Ã[Ø -OaZ ƒp«6Â¥Ú‡jJ4«Î »êš°©>ª‡ÂªzB$a-œ¥@ŸSïf÷Ïü4™(c¯CO#ô²Ž± 1.½…aèV-EWQ|Å™h/΃·D¶’*´–á.m„«Ô§ºÍê^4©‡aS¿$¬ê—EƒúŒ°¨¯³ú¾0©ÿ êÔÿu¥Ò3J$a"?¤ÞõJžE>ñ‡ÉN–Å›èiˆ^ú—µ¤«t!:ÔQð”%¢µ,îr\ÅpVhЬ©E“¦v6VíZÔk‡`Ñn&íAaÔ¾& Ú÷EöQ­ý½¨Öü‹H¢ºâ5å’¸OÍ«zà4KáI²½Š='=é§›qñfÜš%pi—ÃQ)CSU6ìU…hԕêӣAgF½Þ³¾&}Fý ú­¢F@èõ§„Nÿž¨ÔßZýòO¡ÕIB[%‰Ê*%q—š—X'{Éæjîé¡}´ñ»‹¿›õá°UÇÀZ“Œ†šLXjó`®-…ÉP…:ƒFC#j -¨1tBo Ag•Æ}Bcr:#݃¬®•w¥#³K Vû5H÷æ·Að 5ЇäÀf$&±"p‰ëˆ|ÿwHìdÅê“°b† çÝ|—¨³• .â&7P[Ó³MÊó³÷`븪oRû#‘Ò‡äþ$3±2˜‹ÁR$uHYjA\¨±¡, íÅÒÐiD‡®"*ôcD¿AtŸ„¥½³ôH8ÅõMžEÒNÄLm]'{ÏÏé¿L—wXÕçÇ¿ *óÊpÊ%DqE¢ˆ×¸¨ /Bˆ«ÆE‰yj¢MlœµÆ`Ö“&­¶‰Ñ8ÒÖª16Mn?HõÏÃ}~ü~ï÷œóž÷=çô~ý¹’_¡¼ŠäY⦎%Þò( »)Dn¦(è/WS¢:˜ÆË`Ê–‹©@Φ%r2m”£é ìKÎʡ䶋–Ó|‹œ­2L—ÌhÂL˜cxöò,ö WŠžƒþ<ô‹$ßRÉk3`…\ÌÎr2»ËÑì#s ìÍ¡jgŽQ[ó Ù™“ekž,3Æ›ùÀ¼vsðZ+Ô*ŸPébË[¨åµ…OœÊï¤ÇΗú˜Ðgß -©S3`ä¼ÌVíjeSËŒ[Û‘CÕh€ê(Âuš:.–:».X¤n0g®<ÊûÌ™+úË[¨F«_sa -¿SðyH1g`^.õ@×÷UÉsú«$§µ’Ýzß 4Ãfp“'E—†g Ç -m=E¸ž‹­ž¤®'˜õ•°ÿ½Å{—xÿ€þÆÊK¸›Ñ4ÂbXÆ94sÐ퉮ïj©#š†M’C½dƒ;Œs¢‘ærÃŽ]ر;ö½Ø±;hŠ(|F‰u€l¨…]pšw¾år¦›ß‡ {é)ñ5Ò“‡Õœƒåä º½ÐõÝŒ>š4ÛïA³p…&ŸB‡á‚£4çÇhÎ{H¿c_N`G#I#ů‘KæÉuŠX4²`ã;ð1°'-ÍÌc³Ù®quÜë¸x-t»äÉžû%—CR[«Ö1Y~Ö‚÷à}8€³6Ò9‚ó4ã.ûHbÇUš(€Mö¦ém™&>ºv ~‹f³¿“ت¤×ÙâŽnÀÛäéc8!µ;ݪu.4@¬W¡ ®Á§ð9|ÉÐöé¹ú ù-ì¸M“ôÅçùyw¡t±{ñÞé>SÝý”½ƒûÓ†ã˜#äÀIÉ? {çY÷r«¦Uë ø -nGL_ÃMÀ%Ý{Ö 0´=&&OVþÛƒA’ý…ÂcáÐYØp ¶²ÜdÒrƒ­Xw‚@ˆ€þ0ÆCÌ„9P ePË`•~ašûYõÌ;õ¦ÁÙ¼'ú€ óK=Ö=â­‡­Ü¿Áð‰uÞ”œÀ±õ¯Õ_…8 £ l4rô”9÷ sîcìx„ÿÆŽiµþ© úž)ð>sæ]÷Žu› ~˯›|ù «| ×á\KÍ68€Ø´þµÚä Ýñ+’/û£—ˆýcÑHgýi¬?›µ Ø‚Ö.cÝ*üZÎÚk˜l7âùÒ£49ŠÖY}Äæ]ÅZ«æŸZ9ïÃéflAÿ‡›M¢ç‡O¡èÅ¢÷:#õWöäÏš¢ÏdÔÇØÑD<®²/W°ã2v\ÂŽ Z«sÚÂ.ì$•ßæB§‰ˆ|¯“Dò*VÞ…£p¸Ù†gúO›Sª-±3à“7û„V8~$`ûP]dOÎ)•ã˜FÖžÂ\Ö.fÍ2ײ VG´žµ·r…ì×!ž¼…eˆØ~²dJVö¶²ç9Ã]¸Þ|äñÉ_|ñ#ht¢‘Èú£Y"f°¾Q±£;öcÇ^ìØ£jí"GßÔ&½A,¶“[Y¥6ÝM¨YÙü¿êß¶i9æl¾숗Xu—üèƒNöÒnÎÉNaý‰¬ŸÁúF½Ž[ÈÍ2‘ •dæ2"ñ­Ó62äP¹YËá]Án¯@õyžråßáÊÿ”«ö,ßåŠ9lk½’ÐtŧÎdX´"ÑIÀ‡Áh$¡14429ÓÑÈÓJ²æB²b‰–¢ü*_T*öc1‘­´¹­ -Û§*oC²{Æ´¯SöÎÃ1ÊήÚ<ÛA ØÚÆß<8ù~Zg¢Õ¶ÑZÙf€VØ Õ²¶ÉZÚnœjÚMRuû©ªj?Sfû9ª´/Q…ýb•9Ôj¡Ã&-pÜ-“ãQ;]T‘ó-ºüÔˆçøŽ«¬ÉÒá‹>eo{gÊ)ÏÖQ~ÖÚh£AµN^Z椗0U¹Äi±áEUUn­E&h¡ë•ºerÍU±ë|Íw+W¡Ûr¸mз]Êw?¢<÷‹Êõ¸­Ÿ5«£¥n ký0¼I²!€vß™‹òWÕ_ín/³›»*Ü}µ¨c°J;öU‰ç *ê4Xó;Ð<¯1*ðJÓ¯,å{ÏTž÷\åú,PŽOfú¬ÓŒÎoÈØù·šÖùœ¦v¹©¬®?)³«E™]žñ×ùïC)Ë!´°*mžU2÷aËÂ.mTêÓAE½5¯kæú†)ß7Vy~•ë7L9þÉšå?^3º¥kz7£ŒÝò”P¬¬³2VkJà6¥Ò¤À3J ú»&=UjE©Ïø°7%ŸÖg¬‡¥a´z<+Å–¢žô7Äfn€³^ ôÔì Íê¢ÝûjzMë9XÙ=G(«çeöJSF¯,¥÷ÊÑäàB¥W(5x¥&„Ôk\ÈA yOcB®+%ä‰F‡XZ¶(.E²QÒ6XU´¥<+Ä–|lÉ%69Ášì¦i!]55´‡2{‡+£wœÒû Ôä>Ã4)l”&†MPjX†&„Íиð /SJø -ŠØ¬äˆ%EœÒˆˆ¯à  †C˜E#à\ ç0–6–C9ñ,{rhÉa”ÖV™á®šá­É‘J‹ì­‰}£5¡ï 5D㢒46j¬R¢'kt´QÉÑù½@#b–jxÌF½³OCcNjHÌçð, Q-œ‰§½ƒ× À\ZãZ#mP¶¤GÙjR´‹Rc<5>Ö_ãbƒ5&.R)qñÕo’û%jd¿%ŧix|¶ãó4,Þ¤¡ñ5œðš^JØ£Nh`Â'ðPâ-ø?ªË<*ÊóŠÃ?we1 $€’n "Ê"ë° Ã2£,Š0QˆNHAAÔ(бÖŒMªqµZ5qÁ4Õ¦‰M¢¡‰6mzš¦õĸÖ&©žÆš˜X§Ãüaç9óßÜß}ï½ïûÞï"ΡÓÉÔl‚åÐó’X?m‡ _fàK1ŸSÜU˜è£üÄ@Y’Æ(/i‚r’'+;Ù ³Á¤,ƒE&C‘2 2¦ÌUZJ£RSÚdHéPRÊ>%¦œRBÊÇŠOù·â %$»HrèíïÎTö#,…g¡–¿= 3ð§ -ùœb¬Üoe§Èœ6R¦´pe¦E+#=IÆt£ÒÓs•jœªãLŒÕJ26(Á¸\ñÆMšœ±G±'qAÑÆ;ŠNw(¦‡4‡bS:Fë» Ö@‹QšU<—Cqóäñl6)c¨22ŸPzfÒL¡J5M”Á¯ä¬T%e™•˜U ó Å™«4ÙlWŒy©¢Ì4ɼK‘æš`þnkB–C‘&Èìå0#ÑOa4AÌ‚é`…0eIFs¥f{È㣤œ@%æŽVBn„ârc59/Y±y™ŠÉ›¢è¼M²ÌÒDK½&XZaY§pË…YŽ)ÔÒ _+4Ï¡°\Èqh\¶CÌR,‡¨r(†)ŒJ¦æ¾5IVc’wU^56=VS¯¡5ËäYÓ!šýr«>—å>ç¾<ªò¬ìeëk…z˜¥P¦éÄíØ -)‚ilúŒu’Oýy-ðÔPû0yÚ‡ËÃ>ÆËÝ+7{ªÛó4È>]í5êo_¤¾öŸpðí‚S$œlþ7êSO7=Ï¡~°z&ûj¡Í"Èá9ÍFf3ûÍAŸñ(hú ÐŽ¬©ŸÜšÝ4 ÙK}ši~ši<š¹ü[¸dZ8ÐZØL-P kyÚág¼Ãຈ¡má ‹lêåyÖh‡*^/ƒ|ž3Xs"kžÄšCç£ß ù7IÚ%Ï%m’ú®,µ1ϵÑx­¤ÑXE#²Š‹¶ ¯C­ ÕN0Û1Ò¾¶ðÿC¼ËÙÆPØö"p8iFë™jòÀz§ñ™Íš õÔ î‡5JÁèúcÂ{¹ä±J¸†¦—Ðj}? ùÜ@ã·?6áÇfüè¥ à’éäp뤰;YT'†:×ñ¿½p†÷®óþ¾çpòzµÄyŸbF¬cIá¬7x…ä·ZòZ+¹oúw ½^B«mø±x¼BP\ûžáwŒíÝF³z‚w>…{¼ïpbGsÖØ ©ÃÅœCÄ9‚ô…°V¿Mè¿(¹¡×gš»áU8¿€ƒ@xu˜æüˆ»ôšÍõq?NÒxœäâ;ÉAÓEqu‹®ÀO¾/øš÷Nj[9——±W’â<‘×F¡ëË^”òW]ZG€’¢Mg€_Á›p¯Þb x›˜œ%7ïÒ ŸÃó ,\~ÝÔgw-ÐîíÀ—»/“M·CO¿À½HªŒÄ9†‡¢;b?ùÿ%ú=š].­ßÀ;Àz‡¡ßÇpÁ9œ‰ÁAe€ôW•Kä/ðã -MÐÕ éõy"»±ˆÅujóúMžï©”5çàZåÁzƒÐõa»Ÿqi¾çÒûÈ¥ÁöÖgð7×`x®Àµža naÖ”»ÔÈw4Æ÷©ÑïÙ'?PŸü_‚×à\cÒ -žàázö§ â òa:Ì‚¹`‡&h…6¦·5zÀ´õ=Ø}æ»o™­þÔx—$}£‹L™_éŽê6o÷pþ Û9oºÁ@èƒÁ ` DA -dcaª~P•º§:ì۱߄ýVì¶é_øñ“ß?™ÿÁ4zƒ"ºJ"¯¼Ë<}®ït K=|ÀYxËéC?çÜÛKWL|Y[0ëÏšâÐ2¢cA£û娝Âj¾ÀÏñãïøñ~|Ê´ù SèÙ¼™T?ÒQJæ zÒ9"ñ>–ßuqNÁ §ú?¾%wˆÅ—̺7‰Å5fÝ/”ŒN–þLN>V 1,Çv¶çb{%Ó¨ßáÇ;øñ[­¥|;Ñx…R>ÀêâçprWJU}ÑóÄs_ÖD¾ÂщÅ÷T½MNΨ@¿Æ7𣋼œÀãøñ:~ÅÃZ‰íu[±¿›Ê8¢}D|E¼› îBm'ô|î€í.85Égo‰ÿÁ¹ó-/Ö€Î(t"шÇZ:ös°_ˆíl—ñÙØ®Åæ|l6bs‰¶©ll¤2¶9=Ù‚ÇlâÍdsŠ¡ç÷ýÛ®­öëè"GÙ'Ù#ûõ$:¡èD¡‘„Õ ìçb¿]P‚ýrìÏÆþ\l.`w,$ -Ï“‘µTÇV½À·W“UD²Ì® ª—£»âîr¼]æê9Ç÷Æ Ž]®€Ÿ;åA¬é1ôü°B†Ãñ9 ™(䲬h”°êr4*ѨÃf,ÖRþºÏãy ZDd²+ÉG#ºr‹ëæ"Çü›pvrÔ¾ÄÑ¿…+`³Üµ^Þè Gk4:‘èıŽT-“éT—iL\ç…ƒ±YÌn0˰/ÌÃ2,ÃÀ3,3˜±1ŒÙ ãÝÁ$¶Çv°c¥ëxKÒÊ­k7é’Vê´ª*«R¥¶R¤U~µ•ªV­¢.Q*µJ—Ûc;RÛîÌÝÎ{¿ûÝ÷;G:½šƒÒ r‚qéÌè+YâȦc¬n:ÃJÄFÜd)ò‹‘ï±°ùÌG}ÄüVu™ÿáWjí´ì~U¼®eï³Zr6Ôf_T=/ÄFð|Tg"S8½%›S[K8]ͱèŽlsrx[‡¶÷±²}ˆå˜½,ÅLr f…ØÃ„cŸe.vƒ™¸LÅÝg2î=&â> ÿ1ãñÆÿñ¾,Ðwvi9Tk¿¦í%ý?k‚5Õt2Y$Dq"nGâÓ9oæàŽr–j8à`!ÁE8ÑÃ\¢™ÄÓIãL&Í1‘´B(i±ä‹ìK¾ÎÞä{SHù9#©3œjü—õJ³–Cq'_vDœÕïSyʪi%SKµ,'Çr %…pj6³iÅL§U1¹³ž‰­„ÒÝŒ§÷2–±›ÑŒQöfL4-0`ØtžÝ™¯2˜y_æ» dþŒþ¬èË2žÐÿ˜LƒG²_d}>'.Èz¬i{Xû–dçUËœj™ÉŒf*3‘ýY&Æwå³/»œÑìöä4Ìi'ÛÃp®¡Ü ƒyûñç-0w”þ¼sôš_Ácþ=æoÑmþ)]ùÆoà6t=&Ïà»ezå²X✖ûãÚ.kßœ,È”jÙ¯ZÆÍ›Ù—Ïsü† J*¨fwa=þÂV|EnŠúè+¦·x Oñ=Å«t?‡»ä*%oÒ^òÎ’ÓVòGa<ÁYü”we…ßй -ÖŪ˜×ÿIíW=£› Æf¸8–Ý%ÉøK³(-¤¿¬‚Þ²Z¼å<å.z*Öº•ýº"±tÇQÞ“DY‰Ò3%žRŠ=V -=xÚÉ÷z1{GÈõN’ã]a—÷92½ŸÁäýéÞï±ÓûkÒ=ŸÑc`ê~Ê5Ùî3ât¦ÅááRLrH»®[ú(ï…¢þm $`H#Ï·‹\_!9¾ -²}uìÒàeù»1ùÉð³Ó¿Hšÿ4)þ«$ûï’è(~I¢ïï$ È=÷Ë5Š«ÒXë‚E±_Z#¢_t ½^ꓬ}ÒWt,RTÉŠ$k$S ‘Œ@:;¹¤ŠI T“h$9è")ØObp/ ÁyvO¼L\ðMb‚ß&&ð¾ø+±#r®ÃròCõ|Ç¥ûÄ ÐpÒ!Ò®‘¶Zjä”öBêØVCq$„’…‰øY”ª!6ÔBL¨‡í¡a¢CSl a‹‚Tdè¡o1þñ'"Çää÷lg¥qHL‰ Ý¢Õ§9(íJi+"åJ_±1uBp:’ع¢Ã D…SÙÎbs¸€ˆ°…Ma}Ôa½Ü°nËBywîšx f•kf?T#”ƒ›‘“œVJÙ­ïaH=AÛaÑ+\úß8¢9°JöI_ñÈ4)³ÒŸ‡ØÅMD”ùZ‘áY‘ÙXI׃(ORS_US[ÕǶª\µª[UÎ\=%®êø=«ð¶¢@vP5,O8"Y=ç¨Ô%]Ú6ë™mzæ²I0ÏH_º)K¯¸¸ý0l>.³wR¹ò¤ ×)ÕñŒŒÆZ†šL®-vëj°ëú¨Ö5ë!¡‹Ö/êøçÅC«pøÌ'ºV5œ4X–Ö„†,(zõÛ©g®›Ö Cþ¢ô¥›|Dú' z "ž•þ9ñü&5x¾óªãÅZpTÇ%Õñ’ü -vz'—5Ñ7ô` BïcㆎSç} óÿ&TÃyéiê0(:5Ö :½ò H×tRúëw¢ÎK÷’¸Âc£©ÿ)¯j<®)ß^×x¼¦÷rSïå–긭…÷ŽšßMìÛz°Û¼[¯ˆ¯è9ø›ªá5ƒYi…Gcݲª9 S OkþI7é‚Þýe½.•¹Ö5âÎÓB ²í2â‹“{2å÷ceˆù×eõyÆñ¯(”K–kÁ]XXXv]`9DPEEE-â}Ö3±ÑªÕ68ƨÑÇš&5UcըʹMššúG϶&iLšLâ9šl?“qòÇg~¿eÙçû}Ÿ÷yG¯ã£ YµÑÆ&×Fa·‘ÔVÒÊ[Oó?Ÿr‰§&û4Ý‘ ©CÊÆ¹šäߢÑíÏûԩÕ~á?­pÞÒª·à¼ 'ºK§ÈÉi.Äg¹¾Ëè]¼ólç©‹ Lè…hƒ¿óÝ·àS cÊüº)+¥·úC7`?ú¯vj¾ Çá$üÎur.Â{ðgxþ—hV.37Ÿ«¬•kz×ÙànPÜ7ÖI7ÉÅMº©›Ôæ;*ßD2f;y6ñUÄ+Ô6ý޶«Só|§Î€}ý.¥¥ÂU¸„¤•’XzºM|;Ôé]jôsrŸEø€5ò‚zH]< ðƒ›t9} ô†¾Ð"Á vð@ Œ‚Z˜3a,ÒtSé|ÑÙ=¤»¯t—‚ùމû–ä}Mù•>¥Ó|¤Ûü¢«ð\„sOzÞîOúÞü BÁVȆB}¯24F£QG·Ö@ü™úÿÃÇ—øø‚û9 ç3ºÏÿЕþ‹b½Aá\ÕRu…”Ý!u>}³pŽ?ñ Ÿà^0zÝ£×½C.¾¡ÇüRCÐ('~nâã>®Ð_~¬fb/Ô%||ˆ÷µ¦î%Æù -Óx½¿âä6§Oïtò´Âk?ñÀÎÅøüS n)ŒqGŠ>¡ÇüH^òWBéÔŸðqçèsϨ‘سuŠîõÝçÛø8¦Mhl§”÷‘‰6´Îêu¢½F¡ô*üöÁÞ§<°k1N161´‚){:&Ê2lâÖQ|¼‰#ª&öxÆÇ!MgÄM:ˆäc¿Ö ñw’ƒÚÅ/wõEªcj/ÀvØÏwz¸×YΗ:—ÛIrq”\¡.+ vâ»Ù* -¨¸aÚƒÝøØIîÐ$bNgkbw%[éz\´h3n6i#‘7P›ë©ÍçP]ßIûû4ou.íw:·ö­h?¹ØK.viþðnEÃAü\âñG{4•XK¼IÄ›Nšõ¬–°RÖj5û*"¬ â/Èärfu)«f)ºOóUû’ïÊØáp—Ž-ql'¿eüZ!Œ#š1˜Ñ°¡‘ÉŒçi >Vác%>VåÌË2æe ëe‘摚ϯæil¦JfSQM¬¤Ÿ£û4×9öαͷÂháóf¶þ xú%{ÆZ1žp"Æ¡“„ï4-¦6²o,P¥š« -4jИ€ÆTÍâmnq<ÌMe>¨¢Éì“ô_x ¾ù­ýÇÌËy-°÷5Å+8Þ{wÑÒn}W0z‘h%¨¹KŠšºdhVW—fvËWc·!šÞ½LÓºRƒß8Mñ›¬I~³Tßc¡&ôX«ºž[UÛs¯Æõ<¢1=/ª¦÷gªñ¤jßjàb(GÇË‹lía5ï‹Ã¸öphÆßì?Íî Y½CÔè­iþf5ô±iJ_§&õÍU}ßM (Q]ÀH¨Ñ¸€‰8C5 T¸FU[U¸Wo¨<ð=ú¾ßœ2pôFqˆæZ#-á9‡Ï3#¹þágjõÒ”Ÿ©>(B‚ŒlQmpºÆöËÖ˜~Õô+Öè~eªê_¥ÊþãUÑšÊCæª,d¥†‡lÖ°ÝÚª!¡Tú¹ŠÃ~ï帧ýêÃq¿æÁ >Oáïõø©ÃËøð®ª Ð˜°U‡Ekt¸I£"©2¡‘9*P J4<²B¥‘cUÂåshäl–«Ðð+v*ßpX^Ã9y¢n)/ê1ON"Cm \e8æŸ3‘³Ô ¼OàoãðSƒ—Ñx©Šê£Ê¨~*ŽÔˆèx ±¨4&]Ãb³44Ö£!qÅ*Ž¡Â¸jÄÕ+ß8K^ãå×+ǸC.ã!eÇŸQVü¿áp;8„æ6Žøµ\E@#ÔÃØ$´ù®#ñRßS¥ñA*‰×ЄX4«h MlÊQ¾©P^S©òÌUÊ5×Émn”˼HY‰ëäLÜ.G"»\âi¥'Þ„‡²›}OÈ0ùt½­É\{­ÔLå8žg5G~/ÃK)^†&vWQb€ -’B48)J^K‚<–åYìÊMv)'9_®äe§T(3¥VΔiÊH™¯ô”g”fÝ&›õ ¬ÖSJ±^ãù@ÖŸ%w°½Mè.³Ñï¥rdž±PÉç2(áûbü ¯µò¬ÁÊ4@n›Q.[’²l©ÊLÍ”3Õ#Gj±2RË•Nc’–Ö [Ú\YÓÖ(9íyYÒ(1í„ÌiWà¾S}JjÇæÓntÖsõ\œN-Ú©¨†r>—@ßåã)—§;½·²Òƒ”i“Ó£Œ “ìV¥gd(Í‘£TG¡9ÊduÔ(Ù9Yg³«drþF ÎýŠwþAFçÇ2:î)ÞáSBØ}Úγ´$óô:PË´†ó^ ùàæû,žgÙ3•–Õ_©YÙ²â5(Û¢”ìt%ggËâÊW’k˜Ì®*™\õJp5Éèf‡uoQŒûeE¹Ëຬ(×]Egûž“åS ±Wg²Ñ’L†1P%P¹àÌæ¾é’RÝ]eÍé£äÜ`Yr#””+sžY¦<›æ9•àÉS¼gˆâ<ŠõÔ)Ú;Sï2Ez7+Âû;…y*ÔsIažïžçSDn[ÐXŽÆ,4&rý­‚áP ^pVÅæ‘,´oæÁ½4° Pñ!2W˜ ØÂdÅÚUè–¡¨P‘E#Q4NáE --Z¢þEÿ'»Ìƒ¢>Ï8þdY–]p9d¡j F¼¸äÞEv À‚Ç®" «!ˆfRÏzD§1j5Œ6É$ñHÓš£‰1‰ZsØLµ5‡GÚ´4u¬Svœ´Ù~·íLúLJ=øíó}žç}Þ÷}ž­J,}Z¶Ò—e-=/kÉMÙŠé\!iZP[O»ÑhÍgÀ€J(†<ÈE{,Ú#‹é»K¥!å‘ʨ4+ÝiSš3E©ÎL¥8³”ì#»+W]EJr¹dsÕËêZ W—,®ÍŠwýXf×Q™\gç¼.s%kÅ=~€FG!ç":õP eP“idzÑUFüè3ª8ª¤dwŒ’ ‹$ÙŒ4Y!J0FÈbŒW¼1Uñ5劫©‘©Æ§ØšvÅëe<¡HãyEgé¹ -_+ÊTtuP«ÐX\Â~ ÆZpA ŸóxÍA{ ÚÃ]è36:º~û°Ô0h.CM& l–XÑo‹–¶Óˆî`]~„»ñ£‡‹®‡5顸{ô Ù½ ¨‰]¿à¹k4NÁ>æ ébrK $ÏßéB]Ë)f:[`ììk¸i8`o¸g‰¤gÉÉ~rr€µ9H}¦;Â¥{„ÃïEv‘Ã$ðÐø9ÏP™,zé)YªJ\œBÌcÈóÐUì?t-ij=¬¹7¬³†çáø¼L0­cQÒqÖæ-öË)ü8Å¥|Šú<ÉBŸ$À$ïÄ!`=Þº!½ùÜÄ\DÌÙÄ|ÿf)x­»ÑR÷†æOàhXãux3<Òª_Â;ðœ L çY›ðãcöÊ'¬ÉE¸K÷e -é2A]ÁØ•KpSåë9 Èõ(bΠljÏP!ÝŸÂ+aÍp:¬ñ+ø5œ ‡ÂÇpH¯>¶?ctÍ$Ý`]¾¤9¼ÉÅs‹Mx›B»à@w~Ç„ ‘q`… -£a2”B5ÔÁXX¦±Q¾f“ÜeÂû…z‡¢¹E¡|©^&ª—õW½ÍTó¹þÂ_ð‹?ÃxNÂá™÷D„ýH„ x&@“Q³©G·Õ ¯ðãoLN×™ë®1É]e -ú?>Ç?±Y>c2ý”ºDÑ|„ÊoIÖ9Ýd¦ ê8Çá8ú>¨//w•@<)Ä3½Ñ̤‰£ûNìL® ú?>Äß0ÕãÐú?ÎâÇ{Ìwg˜RO³yNé9Êæ%¼x‡%½ªcdí5^…à ìÿ–œ\ä®ñ˜õ{%‡ƒ¼e¡3)Ø/&³.lØ®ÇöllÏÃn应Ìw±E¾ÏVÙˆÆv¶Ï“:Äß^ž<@ÁìG!¤¹ž†½Ðöá¸.§óð®b(÷Ê?ÁhŒDcy›Ší2ìĶÛuDÚ„]?Sm+Ù_ÊqÑ…ò*ì?J&v±µ÷ñ÷%ªä}í Â(nóCØ -ÿDó:\êÓfk÷m÷tÌä*tìÓ3zßs¨¶Þ7ò]ÿ›/µøRk3ɰZå¶¥jºm°\‰ÃåL£Ê¤ïª"iªÊ’ŠUšäTÉÀZM8K…[T`ïTž}¦Øwj’ý9M´¿®ûEå$ßQnr°‰vÎJôˤþ“®ÙùÐ3i“ ¾¯Æ—éøR•-g²E)v•¥8Tš:LÅ©#5-u¼ŠÒ&©0­PùiåÊäÑ”A^MÔ¬‰éíÊI_­ éÛ•¾OãÒ_ÓØô ë¸ÅkÏ÷x -­G‡Ò‚ÒŠ.¢ œ õ`ð¹Šï+ñ­œÜ”9"Tâ0kš#Q…i*È¢¼Ìáú7Ûeõy†ñG (—‚A–uY`wÙ]`e…]•]6ˆ¬xâM0žxD£†â ‰Æh´ä›hÔ¦:±­©M“š4¦‰=Ò$ÅΤÓvÚI›ÔÛ´¶;³ýÿ^3æ7{ðß÷y¿÷{¿ã©Íwi¬©J>S¼¦ªMMª,˜)OÁ"•tÊ]Ð-§ù)•šËn~]6óÇp¢²Då€o ³ã}=׿%؃¹Ð -M|žÌ÷!ò©§.ãÀoNP­9M¾1#U3&_Õ«ª,UVÈSèSEᕆ嶶Êi] ‡u¥ìÖ.•Xw«ÈzH…ÖÈb=·x•µpgÐÙŠælA»S¡‘Ï!®ƒãùŸŸ|Æòœ·(^UEÃUYœ©Šâ\•—ŒQY‰Mî’29m^•ÚÆÉa›,›-¢û<Ù—«Ð¾Yû.™íe²¿ª|û9¸)“-ª0—pv Óæ*½ˆ-˜M0 &ð]Ôðÿ*¨pĪ̑,Wiºœ¥Ù*-5Éî,’ÍéT‰³RÅ®:¹&ªÐ5UW›Ìî¥2¹7j´{§òÜ/h”û¤²Ý*Ûu]9®¨rƒìFc3×Îe.æKFB€ï|P eàä;GY¢låi*.ÏRQyž¬VØe©(—ÙãS§^&O“F{f)×Ó¡Q•”]ù¸FV>¯LÏ+áy®ñ>ª¬Š¨F–GµƒØˆ½¤¯ÍÔÆó¾ª¡©„«©µ*^–ꙫ3TàÍ‘É[ |o±F׸”[S­Q5ã”SÓ¨û|3”åkW¦o2|Û•îÛ¯4ß ¯ù\UZ ·FoTéÕQ=Füµh.&þ ¸Bà‡ð€³ -}/Þ«P06F£k“”[—¦–²ýyºÏoÑH¿CY2uÊLRz`šÒ •X£ámJ<«¤Àq%Î(ÑYIþ{J®‹*¥6ª-h¬¬–棘µPe`GÛZ‹>Ö-oþcB¼2ƒ)Êf(=˜ &¥…Š•rkxȧ”PPÉ¡f%…æ*!´BCCÝŠíS\ð¨b‚o+6ø¸«¸znŽ¢ÚŒ5]ŠÆ®ÞÍ0Pp¢]Œ¶›’W>ÖmĤX¥6&*%œªäp¦’¹J[4,ìÐÐp¥¾(.ܨ˜0/¼6Þ&|„fÃ,4b¹96Dx¨Ž5‰ÆŒ@=ïë Ü|¶¡mA{ôDô'£–R›b”45AC[Rá@‰pÀDØT#EƒMaP’†Ÿ˜†¯‰<¨Æ0‚·iáÖÔBS£¬Bcþþ“a<Ÿ}PÁ{{˜$å7 eÌhÆ6©uˆâgrјÅÁ?›v6ÊœœÁM¾¥ncÒÚš  0“mÛx¯9c8ûü™ßGXÂøfØHê¨â½“1[sþ)»ýVô)mâ)nœql( Ò".‹É£=‹ƒžÃ¥ƒM¾ƒÅÞÁ$w¨c:<_åÿO&µý¿¹ä°(ª…è´Ó§ ãõñê&ýâ©’iúXÕ tSæKÃ(kÌèRˆpÀp [Áeg%yt2'«9d×r¸¬cNÖõ/8&s-Å[»Ö³ïŒäjÌi'7ùUÑÚÐjá±ÆëçÕC­m¤^0}Æ›Žnr‡¿ ]¬"W4yØÃ&ê±™zt‘G7õØB=ôÆV6à­,ê­Ô¢‡öðãží\¹1oÁÀvÁaM]QÍD¯™:×óêåÑR¦o ig3Þtt“:¥Ø~ÍÐ[`+<„äªÆ(fð"ú$ý±‡<ö‘G/r/~/ «—~½°—[óÞ׸¸q›Þ}p±Ô¹ÝZjí^LRçZ(ÝD–TL¿æ£†Ë‹kòàÅ›©åò?x§Õ0ÒAjrˆ¹ùfªô-ò8FcNŽÑÜGgñÝZ.Ïxé;\ qS‡Y‡¸S¢;žZ{–Ó«¥\ê›ÞÍøûÇù„¡Ù¯·¿_XâbS«ã@H Ø Ax^ç‚þ—ÑÓäqš÷4Ý[4Ùz“õq -qê]øüC“˜&é9³©‡õÏx“Ðò¬1¶~Íc†ÎIÃ2è7e§á8c•~³ö!|D¿^ K¬ÙKŒ—Xð}4YÔ×+]$ØÅ>¸£À#œI̯e'{5N¡®±‡1¾bh¾ofì=Cc@~ áW†ad 0®ŸÁÉã*ór޸ΞqƒMæ&µ¸ÉäÞ:$ÝfϼýÉ€ç$`8dA>Ø ÆA´Îd,Á-­Ô=œÏßqBÃÝÅ~‰#¼M“ÜÀµ]Å[}®wuŸùO|ʯ~gá ¼ ¯Árøýy¤AXÀ…–zýEaÝÁ±Ü «äñNí -në2.ïSÎïY4¿£y­½”åy] ˜çõC4ÏëÝbš¢ú9¼ehŸ€ãÿ'‡{øÌ»Ja,#tZ\S ZŒ£–ø!}‚ŸêÃ5}Lá°Îâº>ÀU¾ÇÆñ3òø õxG;h“^Úåms·{†V½¬ï3šï¡ò]8 -‡áÀåÀJe|âIé·†NSÆ4hTÉO[„ˆ}?±#ÄžIìy´êbZg)±Wã*7êeõÐÆ»ôïj‡¨ÅAªr€™zÁÐ}ž†}FwûÛúŒ–ã„§ ShÇäžGîV4\Ô­šØ~b‡ˆ&öTbÏ n•_Äò}Ø«õ ýÑ«mÄŠ¿ÚM-vé§,õϵÅ'`| …/>g,³“Æ’8B-^T*ñG²™ˆ_L|7¹{Ù¢üÄ»‘m«™˜Ó‰9‡˜‹Ø:—w [i³ò¸ºÉ¨‹ åFµ™™Ü¤¿ò`< WÐ;k,÷—áExö²NžT2yg?‡øfâÛˆ_F¥½tŸŸøAâ7 -ñZ™…9#‹9N–³ZÖ“I:Y'«¨Ö -ª¹\ïÓ9×yþåÔØv÷Û2»È?œø™ÄÏ#¾…Èâ—ßKÕýIJBÐh&þtª0—Uûjg´‹ÁBF5ŸÎ×éžËp¢ÿ¦Ûù6ÇðþX¶f¶ûŽŸÍÆ±¸–ZtR‹•ú'ÓeÛæ]Æñoâæ¶;v.çpì8qœÄNlçpšØ9쎛&MÓôN¶v-m³vëJWšrT+[§v êh5­´¬[‘@ÝQZXA\š¦1& - þà‚ -!:Ì'©‘øã£×ïë÷ý}Ÿßù<_+ñ–£QËŒ7 ÑŠFQ4âìÔ$íMÁ,«cŽ]³O›‰x½™aö¦YEVWòÏá^e¾Nº{‰Ô{®ˆ´JÚ;ÂõQÒÎ>žï!®ÝÊctMèØè‡:^FÝNÅtúÑf6R´;ÍI¶ha†Ó:fmœÑM²‚ÇØE£¬¼QÝÓÚ£^C÷"Çú)RîQX€Ý¯s<ÛÎÛ -²µuM!}*F«jTêÑò¡ÓÆ tj"«Wë²5ž=¦dö¤Æ²7kÄð°† 7,jÈpV†Ëê7ÜPÌðE×,+šs_ÑÜ´bð%4—J˜{JÂCvJ.ØÁïYžm\)W‰eʘ£©£&ó¬šÈ+W*ß©d~ƒÆ -Z4ZÔHAD‰Â˜â…Ã*œÐ@#S4¯hÑ‚úŠŽk­ñŒ"ÆËê6ÞP§ñø“:MAZ]Æ´® wšRðIJãýôfaŠû žó’X’æ<™Ì1Ù”097»4dnÒ€9 ~s§bÅ}ŠÇÕkIi­eFËœº, ê°WÈrFAËeµY¾*¿å]ù­²´YÒzÿéõðJ9ZMÙ `÷£<ñĉeÈ–­Á’" X-Š•”«¯¤V½¶­µµ(b ©ÛÖ£.û€:ìc -Û7(hß¡öÒ} -”Ské³j.½¤¦Ò7ä-}G¥Ë\ÓÜ“á<:ŸBó %àC¤ØYXcÜÇ)Oø/Æ;Qbé-ÏSO™Y‘r»ºÊ«ÔYáV¸¢I¡Š6+»Ô^U rD~ǤZ[ÕìØ«&ÇÇÕèxFÇ‹r;^—Ëñ¶êäúîÉŠpãhî§ìÚáÆ’À8$¸ï‡¾:jâ‰0.ÕkÔQmR¨ºDí5j«q*PÓ m«ZjÃj®í•Ï—×9¡FçfyœÈí|BuÎÏÊé|A5ÎWUå| þqO…PËÙÆQ4÷` ¶z°=”^£0Èï>žE “ÿü×^—¥€«P­.‹Z\¥jvW«É]/¯Û§Æúv5ÔGT_?(·g\.Ï&9=«Æó¸ªÃA6Ã2Þ¡3|43 »#7ó96ÕÚ4Uëô_ø.½Ê4&ab1æðÛ¸zésV•£W6t‹Ñ-š”ri’bAÚL±³…"c+Ƕ•DObÛIò™cNæ9TæYPs46LJs;á0ÿc`w^ã@Æn¿ËwéUf 5…Vœ1!®¾aÉ5&9°f6¦ÔŒná&<àV´·Ã<»ˆcw.‡,ã±—8öÇî‡ÿ‡Éº@c ³ËÙ„nîÞŒæc+Zð$_-xI¸ð™L|Š1y†¹y–uz–8–ˆc‰}²Äæ:Ç`?G£giäÌs®×yïǼÿOéé´&èï ºôÙ·=0ÏþÛƒ>º9g4Od´NÁi8“)À—à<|.[O—“+Äq•8®‘ô¯±O^a¿ÌX\¥á«üÅ«¼ÃšøÎîRZ#Œu}öÓgã\Æøš¢"Ó¿Ó½ç3Æã0¥”¨+FHØa¾˜“Wá øÅðMÖê›Äq›Ä›Ã÷MÝ7èä-:uqóïýSu_ýÌqp?ûaª`ûÓßÜÓ™>^Èh^ÍëÿÓŽ; çsôÍŒIû.¼•1ŒïÂÖêûœï³_Ê^ý “}‡þùC~wõÞ=u³e|Šš“’•­“>£ûrÆô½,!݆ïÀŠ!yûÿtVLâX1KÀ/áWð[ø–»ì•eÓ2c±Ì¦Z^äÙ‹@'î~°êyyð_¦Ë=¶Í« ãOÒ8Çv?;NDZãØñ%‰Än;7çžzI›¤m’vݺ6[E‹Öª]ÛÑ ½±B«ªÒ(° Ê À*•kÇT.ÓØT`ã@“Æ`—¢mŒ–2¶™ŸsQôS>þü¼ïwÎ{Îy+8À!h…n† Xëõ‹ó_8žwp@7)Ö8£¿ã2ßÂ}¾ÁÄ]cÂ^eàþ̽¤?èøÌ«üò -<ß…oÃ7á9ü‡â|N¨ƒFb-C¡OoãcÞÀ¹\#Wp9/ã|^Âu½È¹Š|Ž<~K¿¡hM‘^Ñ#8Ìï0lO1|ÕeÔ.åc¾_ú¿Þƒwà&¼©2âØ‰ãÖ_ð3/ã©^P'ú=‹Cú%îéiœÎ/pZOà¾~†»Œ|œÍâ1òø!Åü} -ù"“zARBéLÎy½»û,|>ŸÎçðOø°séyxŸù4cñ$cñsyy‡0åÖÊ;¤ÐîG{í ´W¡=‡öíhϳ<¶â*wònûY6e6N“ÅY–ìâ]adÞZˆyNÀÇá8°Kð΋eôT¾¼/·df©¼… }?åedÛÐNëaÆãóA{í)tgÙ"Ö£=¯Säq’y9Á¦ò ¢Ü¯3ú -Ç¥èOÌTnƒðaدïwð“ü’ûj~ùŸ‘ m+ÕåÐ'ñº§`¹6¡@;vía´³hO²…Íè0y$ÌË>íf{;¢{ùÕn2¿‡ÅµSϰÝÞ€ÜÛa<—_ÖœêúB~Ëc7EÿE]ÁëÆëÂëTúÍÚK÷2/{¨Ó]B?‹æJŽ5h®g¥ÜIÛ8Zö±rŽsÌœáÎy²û)«éuþçþË&x"¿<”ß傽 ǃ‰Ü-hÛQtýú1ôè§Ðï£"‡ˆ‘Eo’•2K•n B>@6»X=Gµ–‘œ£Rf©Öf}†•6CìÙ<ßËo»'óGÛôÂqÈÖ‰JúeäkC¿}7ǶŸaŽð1’¬Œ1úˆ1ŒÞ-ŒÄ4+fÕ:O¥l#«¬¢¨à‡©ž <ñ+xr ŒÃù¶dŽ™û8òö”rD–.¶w-¶ -·2k‹9ês†9YÃÞµšÚXEN³f'Ù;VR+¨ ÖË8WYž\N¦cÔço5ÌÌRÁTù#ß¯× Çõ"g‰ùÛé~Žþm0ëa–{«i ¦Égª°”XVÔÄqÇÇ;‰ÓHœV"·«›ä/‹î4­gt6«—ù覲º¨ð4«+ÅúèdgëdGè$~ ->K¼c´@» ZûbK¶†ëIç~–ï—›—hy‰Y£E6Vh¨°Fƒ…~ , )³¤Y}E õ¥ÔS”Q·iT]¦I¥LkÕiºSíÅ;•,>ªDñƒj+>§VÓ%µ˜®ª¥øºâ%9µÀib"î6Ž·NÞ&!Ëç½ßË€µXf‹2e†z˪Ôc®U—9 ´9ªTy‹:Ë;ÔQÞ£eåÃJZV¨Í2§V˼â–j¶Q“åAE-ç±\RÈrU!ëu…­9Eàqös íèmo«hnáÿ0ôÓ*÷ò]7ÏtÙ Ôµ´Ti›U)›C¶jµÛê”´…”XSÛÒ¤ZnÅiþcÆ„šŒYEy…j0Ž*h|JõÆ×TgüX>ãEùìÿà:'?'ÆnâÝ…=XKÛ³’ö|”ÿÐý49u_;Ï-s)é(WÂa¨ÕQ©¸£V±Š€š+ÕTѦhEJg¿Âάœkpn”¿r»ê*Ë[yZžÊsrW^Ruå pSngN5pñwoc-u@[>CÐ )¶V­äÓB.1W©š«ljtU(êr+RíW¨:¬†ê¸‚Õí -¸ûäÇøÜ«äuo§æn¹kÊUsJ•5_–Óý#9ÜÏ«Â}ƒëœ*«s:„þÝÄ»v|ÚOýA?tñ¹´bq¾o&ŸFò‰xL -y,j¨µ+P[¥úZ¯üÞ ê¼Mòy“ªõuËã‘Û7)—ï6Uù>(gÝ9|'e÷="Ã÷Ù|¿×RßuÞœìpý-ĺ•V|e€:„^è„$÷Z ‰œ"ÐÀs¡ü~³|õKå­wª¶¾Fž@½Üˆª­rÒª Êd%×ÉÜ"#¸O¶à Yƒ_”%xQåÁgTxS–J}NBw3ñæhù&hšڡ•{Í…ðƒ—ûžP‰Üa«\a»ªÂ.UF|rFBªˆÄäˆtÈí——-:'kt³,Ñ=2GïWYô!•D.ȹ¢âÈ5•„ßWi8§{ˆ¿ Ý5سl„y€× ˆCcˆ~àå;7­zUS‘œÍårÄ ÙcN1d‹5ÊOÊïQy|Læøj•Æ7©8¾SEñc*ŒÓüÇhŠct ±×UÐü® -›9?ÑÞ€öڣس^è€V>7C„ï‚Pǵ§Irag+Z -e´•Ê–°ÊšpÈ’p©œÂ1“xi²E%É”L˜Æ%ÉI$1ILY’Ó(‰ IpB'žÄP½ø¬¶œ¶oÚ+h{Ñï‚$×qhä~ü\×rß…uc ÊHÊÖ^"sÊ¢’”!SÊ©%) -S jФñVé `Óø«ôVà4NaRRRlœÖäÐIÙ‘Ó]hϠņd “ë6hŽ3/÷«ø/¤LH/ÝS/9ôätï5þH’ÐÎu ÷‘ià¿{îvâw¿ Ø#™±±¦ÛA<ÌÊ0úˆsqsc,Ƹ1’|Írl7ßãmF1f#—ùÍ+ð>¿Ïiú<>]äsœ÷ óßÏçš4ñy%ƒ¸Ö©lH*åP_ŽŸËràsÀN0+8t&ÙܧØH¦(²)^bª˜)<Þäv ;[‰i[A×:A=Aã8Þm9d E¼–¬êùìA¢²ŸøÄµ·$+"ùo¦Ë<8ÊúŒãß;ÙݼÙl’Ýdw³»Ùì’“¡!¹ SAn -Š Gñ@e" -ÅJG‡CÀ)ãØ)R¨ÖZ§ÌСPflÇik©RA Te¬jÙ~ÞìÛNÿøÎ¾×>ßç÷¿ßóe`À.˜Éц³ñ£?:ñ£›œô˜…ÖMðº'òÑ…˜í¢.;qÄö+ü\ÌŽ©•õ ð 'Îü†)'/aÌåï¼™S‰}«É 0ÇPc7ñ!gÎ †Œyø±?QK9d–’“%Ö .ÁéÅó5±ˆ‰u!Óóüù}ñ0ókl‚¯žõVë’ÐO)å²^ÞŒ6)±®“`Š{`©5ô=Â0¶‚úXI}¬ÆuæaÌÆÞK}ö’Ðu\‹ÓkWr@nãzcÕy¾GY<ÓD^%ÎÃù-cÍiô ë5àM‡7áa‹ó»&X90ìÆOsðÝ6‚§Á&jä³>ê´Ÿd+ÁV곟Xl!y}KØŸ“6SÏŸ’žû’!ˆs=¼U|R<‹¤¹ð/²Öhr®ë­H(!0° XBŒÎqaðCð*5²?Ò+8pÐlûIì>’··—÷|ôê ¾ýLÚ}_cˆuM;9`Í…Ä9›ø¦¯°x7Zœ}—Éóò€ÂØk å-Ǹ(dD\,£FNàÇÛø?#'')ò3qË¥·ú¥£|xäßþCul¥åó©b³Šú3×»ÉZ§¹>¶W„]œËäyÃD&ÏQp°$½ ÞU\0±!V¥sÔÈ%êãµq‘ú¼@ÒÏ“ÜsÏ ,QTg~)¾®!Ä»î<òjgÍ Û¬˜š¢çÅÉ13 BMž÷[ ’IéŒÉ>—ÀïÁÁeðWð9ñ¸IÏÞàнÁFsƺFA}ÉpåC&ký’@&0@>‚r0Œ€Öuô-ys%ô ÊñkTåm’÷År‹Þ YWIδÕeý·þ‰{1Üé-S×€Cà¿üìXØ5‘ȵéG6(пјwUÇH}‰røúá:~\e£ø”†½ŒúúúMó! s?ΣiÎjéØOZŽÀ{šðÝ$U1Ò#¥1J ]v[>Ü_ƒ¯Àmpmu }÷)ºêôÌ'hªÐ—Q*çðã ñ8Ífy -Õõ>ºî=à»è™wðã$ñø) tŒ¦9Ãa|ƒ$ÆÓ×Ñ™¯Y¼;Á6ÐîÂù¸f¥ð‚•â_)ûÙú…ÜØb¿ Û5خǶ©Ú&b{¶gb»ƒÖxû µOË)¥5dâÚt;m´G/QÀß'*;ˆä÷àì›ÀÓà)p ¾¿Xåô¾Uæf{&‡ÐwѺИ¯¡íöP¯àÇnâñã.ÞN”ßíÔÈVjd ~ôÍêEá=×Kxó:\?דDtœO€Õ`X >¶Êú¤ÕÞû¬-`‡ˆU±2°›K«ú@ ¶+°\£g©§Q”Q´¨‘õÔH/5²?ÖàÇãz ûOR­ýz”æ^Nô–é·l·ß°ÅǨ Ùc~g­¶;hµ$»ñíi=±è• »ÙlYnlá þWÀPƒí:l7`{¶'Ñ!­ØnÇî-àn_Ï%sˆÅƒtAz·›Šî†×D—…w¬mÀÜ_P|û¬ت°”Ž]>ç`ÛtdûØJÔa¿ûã°7‰#u:ÕÑIDæQ±R-4ƒ,M§GZYm‹þþbÿÛVì͵›Çy,™GÛöÀ1ÙC,ºˆE'9é 'ÔF;5:KQµáÇLü˜Ž­ä¥…¼L£>¦ðf2ž™‘™HôšÉà6øñl°ãôðˆqÇ~+þë9ò–3’Ì=\·ss”°†AØOǾûÙšÊÞ5E^8BpDéÒJ8jà‰½ÑØnV_ÁÛÑD¬‘¬¢*ë©àïPé#©ü‘úĸc7|›8æV¦3‚püw1ŠÌäwGð䔸È6Q)pd‘Gnà‡§žRxªà©…§›MØŸLdÚ4‚HÖR1èÍ:g(UWÍ^1DwAŒû˜¶Ã½ÞÆxÀ÷ÇK›#>6sßÄó1ø3:m“ÒÔ`‡Ë€)®¸ð”°“—ÃU W\£ášˆýxÖC”–òö v—~¼ÝÇnwœè]·Al}¶øè³Ð`ÌãhiÍ`,÷ Y̹¼«ÃŸ™I‘–¡á)Õ&;U“”¯¡É^U'‡4$y°ªRªT™R«ò”•¥LPij«¢©ÝФ.V8uŠS_T(u)GÁ9%ßRQÊ}¥²_Á±ιŒ_mŒ>“@êxVË»ü©ÎJPµ=EÕ™™’a¨2Ã¥ŠLÊ2‹TšY¢Á¶rEm5ŠØê¶W±­EA{—öÅòÛ×Ékߢû^yìÇä¶U¾í¦òíßrÓF8–Á×ÃXÞÊ¸Ñ ÁHî‡jF¡JÞ—ãO™‘¨²¬4•:ììÈVÄ‘§’,ŸÂYÅ*Î*UЪ"ÄÏ î3¦©Ðè’ÇX¢|£W¹F¿\Æ~9ãÊ6>çú>`ßÄþb¸:ɧ2ó0ÛZ®«yVJñ)Ê7W‚JœI*vf*äÌRÐéRÀY N@¾œ¨¼9U*È!k¬ò]S”çê˵HN×Ze»^”áÚ+‡ë˜ì®³à¦9÷ALk°=¾YH¤IŒ;cÒQ5ÜW‚Rü‰ð> BøÈK?/M¾<» -ó*ÈÏ—'ß'·;¬ÀÈÙ¦A׃A˜çAà‡·à?L— p”å†ßÍewÉ^òg³¹’;¹ï†ìn² ¹,$„$Ð\ TÄ ‘’ -„‹­ãD¡8Vª^¬µ”BqÅ2¥…Z†N¥ö6¥#Œ2ÚÒ¢±í ØÙ>òÛq˜—Í;ïùÎ9ßùÎËo÷Òyn”'È[é«Ú%gÀ+{ ]Il% -e VPÈ!ÀüdŽ"Z‚ˆ¤ º"ø8Œá<À<]ÕLpUq-Ãî@™Ô‰håÜ›_!˜ -²¸öc> )ë $ÈUc—£Ö¥¤ &ÄSÜa -(ŒÁH w#Éâ)Â<a–s2…9¥C§>ÔÞq-ÁîüJò€íFP ª¸.ã~1¾äñ›Íµ¿~L{‘ ®MŽH²ëiô 4Ú©“ -JG l”¢šl+@È5¡¯¢c`œ÷*oð >40E70¥c»Û­ Ô +¹WŠ/üæpí¯¿ýdK©—,1!ÊÛÌa7“¥•fÞF3ÑÐbl¢‹ál m› ¨‰B&†xjG¸´ä›Ø„øÐ×¶;@ˆ€ |øRÄo.×ð¦ÕÁ”<“å)Æá> ?:ðc‡m9é&'½4´^6O/±èÅáòÑ3˜Jº™ÎºŠs™Üº˜bçàCg\½Øo3@¾jÊi¿y\g6À¯^g›”8 9¶@/˜}øÑoúø1DNÑØQÄCÄb¨…wDì&¶A„ÛÀù†zè‹kN#á ¬³·Ëá-à:‹²Nc½nx_âüÊÄ`ƒ=°Ü'Ðd6–âÇ2üá!'#ÓFW°?–óâr&ÄeÔåR¦çá‹Rô‡;ãš…›Í¬³žÔUÃ[Ìuv ~Öë&•v–À°6ÉIZFùPêŒIú`%XEnîE㮥>Öã‡yl¤>7’Ô ,f=FÖñâ5±–º\ý3KTÕh\íð5çéíÔ!©Ë%Î>Öëæ“¤!sÖ`¹ÂⵆÎ1kø½ÏD°†ñ- ^[ñcœ½²æêsœX|ÃÛ0´•>ñÐÞCMlFY} ˆs¡ª$ÖùóéA ‘n–¸ÔZã¨5d›||ÎXÆ÷`«5ˆ?b ¤ß%bŒ—ž&&ϰo_`¯¼Àa°ŸMö<~ãûÖH{xy7ÓôÎß1ÐßR”XûØÄ:ƒ8{¨WYëü†ÅIøňþ%@‰#_&…ÑAð’%”Ž€WLá‚§ðã$ûäuŠý8±x‚WXÄQ>8B>¬0uUJ É&Ö^¶pòKd˜ë|GÃ|Fw³UVÂ3¦]ö“ÚÎ6ÚKD“_PâŸLpo÷ƒûÀFpÕ*%“û„U~/N”£ ›)Ú‡®ÚGNö¢ívSOáÇøñ8õñjïQôåvšø85òmjd+~ÆŸ9 D¹®!žMǧš)‰ª±;Lr+ªj›OU¶,UÚòà*†«‚.V_½¦ÙZUbëR±mP…¶¥*°­Qží!ªy7^ÿœ'’q~ãú&÷À¹ˆ±§—£>š@¨å^gU ÂcS…;Y))*wzTæ0Tj÷«Äž£b{¡Š¥*tTàˆ(ßѬÏjž-Jõì”×sH÷OÁUð/×ZlÃÓÇ(ÜÁ¨Ñ " Tr¯ ”àOï¤%*ÏHV^ªSSSÝÊõÊIõ++5W™©ÅÊ0*å7BòMJ3:eòKå1VËml–ËxRSŒ—å4NËa\ŸÉ‘J߯öbøæ1Ç' ªA9÷J@!Ïóñ%7=A9¾$eùìÊô¥(Ãç•ßç“Ï—­´ôP&#}º¼éòøcrûçÉõ?¦Ë8Ê« -Ão6»Ù$›,›Ífw“ýI6›l~vIHBHè&@˜J$ -†$•AkJJÁJ¥´RZ•j¬Å¢¢ÌXµÕZdZQ¨µ t:0ÕVªv¤Ú–aèV;èúÜìçè0/ûåÛ½ç=÷œsÏ}o -|ŸÓ·Cy¾d÷}K¹¥Çe+½ ï{²y¹3°½¾ë‘ÀÝŒ‹sÁlP⼫¾¯À—`™Mþ2»|eyýù*ñËðÈ(!ªUh+ت‚`—œÁ>9‚ò7ÊœRN-àF Æ¢®‚Œ6`|K‘ZiÐ -’ Ä—¢|A|ñlòrå :ä)wÊr©(4K®P© -ùQ~¸RÎpBŽp³ìáNÙˆö/B×܆hD †Ñ²!|½ÊÆPNc%|½aÎ#hŽpøŒƒJF¥0ß•ãK Å¼›bxLq㤸Sˆ÷Ôn~ÇÐÈ Ð€ (ÙúŒ†ª¥%„¯“‘¬Ôóç]%\a8 ”gïÝ|ïJHùlÓÞH“Oq¹4d›i ­X´‹6´5òÑÆàÚÆ|Õ†žoexhE©´0WÌ!ÍøÐ”Ñ./Än{y೎¿«áŠÂY|<—ðÞ ¯ ³Îsc‚ZlØÅvüè0— ý:r’楉EšEéÀ0›¦&ÒÔåuÓüŽÙ¦ó0kþ@3¼ÆúŒúÓØnIžkñ%ÆòŸeð–ðÞ o!¼y˜Ìa|å²ÄHs©vãÇBüèÁÅæÂ!'KÍA#KY¸d>`˜]‚*YÌ ÕËÕƒr\tŽu¨úL-³1Gêšùl œkàŒ€ËKx_ o¼ö8I/9kAàè!Šh.\òËðcÀ•½øÉÉ <ÈFV²x%ùXÉP¹b“´œ³1ÀÀ¸ì4"sq}F (ßvxšØgœU œ¿½mìÞxm”7‚%ËÙg‰#rVZ‚Ç®5ÄdÂc?ÆÌ%DNÆ)¦q63†ã£äc= -qÅ´õ¼öyÖ¼# 3Á×6\/5ð†ø»”ð¹áÍï™Qÿã´øŒÐûˆ%úÆ,áiD e§ ÄÆ$~l¡6¶“[)°Íl&`“,Ø4Åoö!š˜$>öë2ꄯ•ýÖóMSÄÙÍ~†w9XeqrÄ‘ìÿÇ&Áf°ÕâÛÁíàNjänüø µq/õ¹‡Xì¦6ï,ºƒéaç“Òôo¤©©“ý¦øºš=puV?ü&¾kÁ¨Åy“ÅG«C&gð–åÈ3Üd…¹L(9!éQjägöÔÆêóky?Û‡ÁG>-}‘žýà)éþwÕžk)Û -öì!Îù#–¸°„î6ko,c\ÉŠðÿòAü(øª²f¥ï‚Cà~åÌå²y†Cv„Í&¨‡Ú“äã{OKß¹¨Fê*Jˆ¼¤ªÐìy“W³Ï=烀%¢ÅÍ †çqe‡#3 pÜHáÇå†\“΀³äåjô<9y™¢{‰@Ÿ&°/|N:‰gT ·î"bcö|'àkÖìðuÐâ4Ãà•Jg-®_€-¾__ƒßƒ?×Á[àmÎËrr™³z‰‚{Ò¿î”þr€ßGQ -•-¦@¡t¥÷˜%þ®"}€vĮ̂N£|S tÏLF×8$P¬ï3\¥PßE¿¿M/3y^b}ƒ¹ê"Éú3ɹ #LV¿Ô9¬ž˜™ë2l+C -3l3ÃÔ‘Á–Ä)e­X›Å%üx?þÁ<ó>jø*Šù - -þ-tûëL4¯áÇ&¹ßqXÏsh^¦x_¢xOãÇ‹øqJ»tR{õLÇ(Ž£ú™žÖßPõÊ&˽Üvƒ+p^¯Z!¤‹3ûNe–8ËqŽYæ ÚýÓÄóøñSâqŒ†u”Æñ êsÝašÅ(æCº™2ù”ž ˆ¿Mñ~“]äô -¥{mfß{ÌL¶ƒmà5øÎ:'“`6Í?š)³=Å|wˆœ|_APÅŽê±ÛL„籟.ìöÎL²ûi$_¦y=Bãz?"÷kŠ£zûÝDZ}‚§“p_!B%•‹}'ö]À Jà)ƒ§ž(<5ükÄn öçÅt”ª¤b£ä#BÅ…9!:Nˆ®¢w‡àƒ­pÜç’ôCÈŽnÐæ€$ïê‘îµ|_ã´)nÏSÜV êœ"xfÁã…ÇOž*xØlâ¯yœâ…x·ŒH­%k7ñ«xüxœSþspdø;£I쯇o9r£·8+Ñ[@ -Ô8ïc Ò•£h¡C‘ü|Eò\ -;ж{²ûTn‚¨‚ö¸ö¤üö6•Ù»ä³÷©4wXÞÜ*É’ÇöyyrÃûgÁEðo²šÑØ/óè(Ë3Š?³döoæ›%3“™¬$$`BH@¢¤ˆ ‹€U°d“­ì(Q¡"‹¨EED -TP‘M(ˆÈ–º€- âZµ"¢‚"K¦¿wæ;=§žþÑþ4œs3_æýîsï»Ýgü}àêÂß–6¥(M@ÏrAˆk&ÉЬõØ%êvJÄé^ ;’î KÈ— gœM@™ø ú³Z|ÎâuÍ9I<Ž9âv,—}“¸l‰+팸¬ O8:ê¬Câ_ ªmkÌ÷<bÔõ™$ìµJº×.!Í)AÍ-A&.~OHtOÈŸ§H¼ZsÑ´ÖâѪŭÝ$N­VÚ±i³%M["VϱxˆÙsRÌî„Ü¢Úx:À{u€>4ù ›gqéºYBzšt»ø}NÑ}.ѽñz½âõDóEÄ£gqé%âÔ+Ä¡·›~ƒ¤éÅ¢“NÔéy}d8/aÇKbñrgÀß¾*bNEˆ} -Aß3A ƒß"¿Mü~‡èºK¼º[4¿[<~MÜ¿¸éâ ÄÄÈ[°©¤[Š%ØVÌA²[ärÈNjð?‚„ôåý]༆–¤e˜y|Î1žG@(h’`À*þ€]ô€S¼@î Kœ!Mìé>±…ƒb GÅÎs˜Ì)4 N¼§_„(| §SCú¼ø,u%¤7á­$ú–Ñ’4øœÅ³ ¦–`‚6уvñ-dwºCœa§Ø¢±Ä¼bŠ1a1Äéñâ€æ1NS§aȤϋ„Õ8!5FÎ -EÏSw'Ã:À{Uû!Æ<€\>Çy¥–ô0¤§‰?Ý*> …­âޤ‰3Ã&¶˜CÌ™"Ù,ì\&57˜œK?‘GÓš× -´4J¹C96g! -dSCI23!Ýrmœ3™ò‹AÈæ{ŒZ"ÔŠ˜%ô¨I´ “¸cfqdZ$-Ë*æÒ<˜|6xcw!^"ª0›Ã…ù("ËqëÒkÖN寤”‚5Œ!96"Mç%¤3±¿ CÊs˜þo²¨#ƒ:ÂÔÁ2ÏÏÜüÖÁï¬È4ås¡4æ/¢Ž¦¦%žÔÁRLZšJk¢”¸M[³‰üŽf¥˜è -öESjh’ërÙ“¼³‚\¾gRG¾Ðùì…×Íßì¼ÖB Ë%ÁxPÂEWjI],-™“VÌI^T0‰ ªàG š¿‘FZÑœ^IRkA#VNŠ,c.š'äZ–Ï• À‘Wœ:ÂÀÏg/Ï\ðÚhÕÌL1—$ã’iêRU—kkuÑRG[ê¨bNÚ±ÙÚáE«ÊAΚh˺¼†ýÙ†äTI‚n}RÉ»Ë@S´å3$Î(õøìå¹ Þ4Å[f\âÈ"˜À h /\4 š¹éD×SGwêèΆê†Ýй+yÞC€F¶3‰­gDõv6gTû„T” Ÿa[>uÄá Áë…ׯµ¥¡µÒ4UFÀéh„®Fèºô4¥B@?ÖhöI| búaX_ôá¬êMbêõˆHŒù†Ð”–ð”°tЍ%Þ0>ëøìB¯¥ÒSí Î. ›¶z$Ãf*|Õ¨j@8‚:F³FÇ0'£YP£yùHŒ`ÐpΪ¡¬‰Á4°·ÐM LH9:K˜²Fðf°ý­áççæöÉ ™âT{|´«„j¡…HÀÛÀh#OSÀ4puÌdÎd¯ÞÃäÎàåÓq'óqém*i~R½Èø³RŽÞBæ7“ù à³›%lîjÛ› ÎZƒo¤ÁÅ£‰Iq©@>CRMÉ}’jPØ~D&š%êx’µ±˜õù8þ(“ššD˜ç¬šÇ|Ì9*Ŭ«Üvœ‡hÖ˜Ws/ƒw°¡QqN2ø×, ‚ðàAI5F¼Ž¨(´SBƒ)RV‰$›—M„ÂÍì×l²ux±’Uˆy–—¬d.ß/ù¬«(ǘn+Çí‘Д¥ÌЬBî݆Ö’j>—Ú×l[ ® Þà{Tœà3ð8Ø‚rš99Åú<Áäc¢Bv‚OUñÿùÏd±¦ÙN—[óúü`(‰Æâ™Y9¹y -6izEq³Òæe-Z^YqÕÕ­Õæš¶UíÚ_WݱS—ë»v¿á×7öèÙ«wŸ¾ýú8¨vð­C† 1rÔè1¿7~¤ÉSn¿cÚo§ß5ãž{gκoöýs~?ïù.xè‘?.|ôñEO<¹ä©¥Ë–¯xzå3Ï®Zýü k^Z»~ÃÆ—7ÿyËÖm¯nß±s×îú¿¼þÆ[{÷ýõoû¼óî{‡>øð£Oþþégÿøâ𑯎~ýí±ãß}òÔéÏœ=ÿÏŸ.\Jü¯ÿôÿÂõ›ÌJ±]Iö YW¢ÓQ¡dg+Ýù/RÊK”ôr´·Râ+•úk‘ßAéïŒÝ”7) nƃeÂ-Ê…¡Øp›òa¬2b"NLUV܉w+3~§Ü˜‹P~<¬ y G+Kþ¤<©Ã”ç”+/bË:åË&eÌ+8óš²fòæMÌy[¹sPÙó>þ|¬ ú‡¾T}£<:I?(—Î)›.^JŒ>¹\Í»%À~¾þË÷_¸_b2™Í‹lv»ÃáÄ·Ç£a…O×ý¸ áG8‚#1<Á\Á|ÁåLÒš”7?3'é Ötë®¶;©fÀ Ú[‡¿mÔØq&O6}ƽ³fÏ7Áà [´xÉÒe+êØ.j·¬[ȤƟILiLŠT*‘‰N„¢©hýòj~Þo¡ø»'¾Gô©~8î3gÏž;wí?]¸pñâ¥ý‰ºÆ¶ÄÓeÞÄÊÊhbm‡üÄÖ®-;zW_Ú5¨æÔîaãï3ûЖñ‹ö®›¼j×ê©›¶­œ¶sÓ²»ÞZ»dæÁÕOÌý¸î±…G. 54ÔÐPCC 54ÔÐPCC ÿw +ž¡†u%^¡†]½ª/Ô¬9ùúÐ _Ôºÿ½×Æ-~kóÄçw®¼eë ·ïÞðÜûV¯¸ûåOÝ÷Ñ¢'æ~¹HÖPG ÏVFë©áÕ®å‰=½ªÏ¿1 ÿ‰}C&|±wäœwëÇ>ùÆöñ/n{yâ– /MÙµzÕo.¯›¾Ѳ{ß_°dî'—‹×°Š6RÃk]Ë/¾Þ³ú̾þýïÿ{t–tžp|g¦™v§zf¦šfgÚi;f§ûiçéØîÌNÔìд2/AñoF)TLP.Áðå EÅ["-¯µ´ÌÌ)'-ûß¶gÞ/¼õÅ÷ýçy¾Þ˜áŽ€¤N"_£ -)“U£k„aê"a¸>WÑ~›Õ“Ä¢™¬ ð?J!ƒìÔ& vøeÞàzz¶ÓÃcòÁ-Ì£>ÿ$CkS®F–ŠkCe\ ¦)Æj3Jñ-)|’‘ʉ鱶d(;¼ÔØnZÔÚÿs®ÝõôË>w'C71}f¿DmäßäÄX4 >J´6 ÝüÙ3ÃæO;F·.×Ìþ^²h»ƒÎÊÎÜWã}®FÍ]ÇÏ…Þ -{óEÍü‘¯"‚f(/cƒ`Ó4„µ½Ñ¬ÿ¤çñ7Ÿh¦^!}wø>8¾5œÞKÇhàÊ™Hàí€AΡ Ô5à.-F\ |é{Gq÷{ëá»ïim=Æ/ÿ¤Zý©táçUpàûpdK*8¹+Øï#€K‡ÑÀëD0€Ÿ -¨3þ{ÎàÏû’7ˆ²÷1ö7ÕÁÚ@ÁæÏAþî•àÞÁoàäPa·È.57œd0ß~DˆK2qH9íyžþµBCO¬mHLUÖÅ1tÕ”lƒ„Ì2Š"8–¶dX2,– K†%Ãÿe(€ %6@d¨u= -ä×çëüý¦j„áò°”^A³µ€\¢ËŽ‘*´†ºÔ$,!­µ2.«KÃêãGv[ÚÃ=ÈÀú×ZP -$Aê=]æüüž+‚#†¤zwþ^ ‡X¦aFÕ*²b«ÓZ*SR»D ™&^\¾™Ëîµ4Àúh`C¡Í@v~'¨¿x¨<\æÕ¾þÏÕAÄGµ¡i]8¶^@(W±IŠÚ<Ê}IÕXžAï-ed›YIÌÜDV¿¥ýa(„ e¡úü „ —·Zÿg:8éQCHz§,¬¨I^ÕÀPÕQš%ìx£Iï-ÊÊ0ç2˜™¿˜,탡2Ad¨ j—£‹ºk.sú[°g-‘ƒH†Qæê$Xi}y„J&¤èE|š‘ËNîe2æÌæ@ZFÉÒû£ÊOü ÔA†F—# zw—×/Ø„9ÐŒ¸ÝÞˆâ©ë0RYe¸J($é9J;“CëÉ`ÑMô‚œ‡ÉÌ“¥}0° ïÀPäç·/69ùÝàæòªã&ìIÙl„ßÑë¼:y¨¤¬*LYT~?W@l»Í¥t%Æ÷SYŒÔBf¿¥ýaàC†*ÈPnû;½Ó‘×íW_ôÞ€™}ÈÝ]·ÕMÁœ*ª‚'Å(˜b¬öN)¾%…O2ÆqbzÈ…)}¤¢Ì^K…›þgX $'Öå¹mo N‡_u_qžð„ y“[»r=¢PØ"fU‡ÖdV†©è¢p­„Ø=!ð:qÜ´.Kû``ïZJ ƒìøú÷šsÛ^·9zñà²Óøè5ÓÈ-’®–&i *ઑ¥9u(Iº#O¬À©bDQHnÆ–P èâ”VKE¡2÷jÿðVwnëËÎ '†\G&ÜüÚǼˆõý°Ô²Ö ¼m0ÿNJœ\‹–ÆÉ°u‘Uø†ðŠH Z« %4Yà@†"È Þÿ5PÿëoÍgÿ1iºp`䉳ãÀôߦq/‚ÄìŸÄí€gÝÕ³é(M‰Eɱ•„Z¼,¬šT"V $´KÜMË2TíÿjA}ü»)ãÙÍÃö™ž9;g/û(žÞÄ–>ò£æ÷ÀÓíÈœ„f+Z‡á5Øb¬Š m ‰ŠèÊ@9µÊÒ2v~ jö­š½lí˜é̆¾q»ý†'GíÜ%¯Šç7BÙ#~ä̇ð„äÞ´Øt&© ›nÀ磛#XÈûäB¸.† ÓÒx–—ÑŽ?¿Sî]1a<úuÿ°í†–)»ýÊß/ØW/¸zòfnÀïNøcSG‚Èqƒ(j¤9, ßO ë%¦¡ºÉŒàÎèŒÀj–¿1!ÛÒ@ÉÆÏ€tû/õ¿,7î]iøûuÕä‡a\ÅŽUÇq\¦3:Z§¶:.gÆ -8gÜ: -ElA–,DBHHB@¾„- @„%1, { „E¬¥Š" -ZDÐÖÿ|§íñ>^õ‹çþwõžó>±ýL³pæP8:HÁå"ÿ•—Oêl@Hü4†@D E> E…ß§Ðñ÷h Ìx 3ø{ =–p—Ã~ß@¾ÃbI»ËbüÆÎ†)Ëõšy›µ`o%†³6…àâ”öÖË5~)ð -}çOztõGr(î ‹yÊÀ‡ÌĆ¡ŸÄ_ x ÷{œL|ß@¹mÅTÿæåÆÉí+5s7*àÄNØÈ3GRà¼]xž£Ú…øçŠÿ)Üó†|9x‰æ…^d\ \dyû¿bûø.pP¨…dß÷m®ký²Ûk—µÎm_¥ËÍ"8²ƒ'÷qÀÁ*œáò)Úc{&ÂÏB¤£?P|ñ5 -b} þüà¸\¤÷ÎhX½L3ùÇå5ðéj1|¹‰‡¶%À±/h`·?œbàÒ!4øöƒÐ£( ÷†ˆ^@9é ´S—aã±6îÀ¶½‰ïäïørö¯²Õ%'¶‚ä«ÝPíb 5—íAà¶(Å=‘)|FÂÌøìëIÉS|F¹!†'ÓÑòÝAcI¤n#V¶´ê4j¼Bkn    ¿{C.bÈE ¥ˆ¡ÒaÔž·…‡=Ôú»½’aƒg*"©÷tî­VîP*GhHL­ÒÅf)º¢sUíQ…ÍšH¡¶%¢²£‘PÝYµ¦ÝÜ~5ì[y‡6‚èøV!†zgkhpw€¿K -Lèy}\DMå3 û3Ù]WÑŸ¦ÖÆdkšijJ©®‘$ÕË#d=U$y—¹AÁŽ•‡ -¬7BÙñ-P¿¶Õ%P¡Ü_6†`×…3Æ$äô‘âè}N¬¬+5A¥ILÖ6±2ºUŒîóê`ì#%yWFÊSE½ E[V\Ks2§[•˜nh`çÔ±ÅCbšx¨˜.Õ è•½æ|Ä -Cű- °ÿÔNÖÐâæðVãí>ߊÆ=jÂÇÞVy& ¥¼»˜Þ Ée¶©³¹=™†š”Àή0šÛ¯†½k@ˆäGÿ -*ÄÐîh¹Ô÷ý\¿‡Ç̰O؃ì^LA›– m¸Ù(UÐ4Å2fw®„­O'p…¼¡xaé0³°|€YPa47(üt%ðƒ1Ô µÝΟ;Ï\0^°›vwŸºívgÔŸÝg ÉoBþ®¼™Ø l ´æÕÐ;Óe¬^Ž”cdUd˜¢Ë éâò~šPb07(úÍPf¹G?»Ï^÷œ;87ärzú¶›ûĸ~äŽ_|‡)8WÑ…­·„Õå5›Sä¶œÞS«’rûI’ì’TdŒ¬’èÍíC!b¨@ õˆAsú/õç<»qÞvòž«ÛÝ—q†ïýXMCAÙRFÄ×àªÓ¯Tìbkt-¹#²šÖsUÆêÃV%ë±Uù}8Y±ÎÜ@€Šö®©åŸ@ydóëŽÓ;fÎþûјó©û/¸ Oy`ÛðQ\Gg”êCÙ8 §•P£&*)ÊÈ&bµWÇl ©Mì@×fvÖæ˜#b\MG6Íël·OžÝ?þÀñäw3.®}ÏÜCU(šä&:©Àš›Ò‡+au*¨mDYD+¹6¬™ZQÇ(ƒ®±¯ù«ÒÔ~ÊŒ&sƒÄPºçcP|¹öMÛá 3¶[ÆÇ¿Ú3òØñ”þ¹ËEÍ‹KAòIT¤` —9ŠIK0ás¢ á…ä^RéÕŠ×E«î`JÛØ2_mŠ¥M«67n·€ò®ÕÕ/tÿ]wï¦ÍæáI‡}ºYÇSšÅóßÔ½tóN£Âxh*w ǼIHŠº‘N¦d… Òr0Œü #‹ïoH,BéSŠ}úÒÌĈA¾û£7šýarÐúã‘ûÇ>Ñ=sØß¼töDí[gçò×nžyÏQAÉÓAÖ$ŽL½N'3 cÔ8ìFBÈm}‹Íõÿ.)u#=Õg4Ãì b›4~nñ¬o·ÅèøÞUº™c[šíöËáÌQ8ý¯Ü¾MYByÅ͇ÐfÃBHOI8Âtû8š:K -šŒ |˜Hñ{…šÈ ùLdšTý}Å«¶íËoÝݸ\7½gMÓ«£[e`³·þÃG›p=ÇÔêÏ¡nÄ×Oü"ɳ@E¿dø¡çYþsì@¿\4êyj÷lf°÷lVˆ¹ê/Ëo˜Ö.Ó=Ù´Býjï:Þ*€»ÿÏn½Æ%}¨¯ã¶êl­ÎNmg[­µ¶ÖÉS›e—é²ÖJKÉÊnÞ/T^ð†¯ ‚ˆP@QQQ@@PA/]ì,«Yiåén­›múìÿéôÙûÿ»ó¢¿÷ßÏóâyžRؽ‘ -û·eƒ[ -„ìÁCä>ÄyE@²÷)H;ˆ…¬Ãaã”#Ás´£sEÇfKûÏ–¡î?æ…ó,£‹æµ?_öV3|ó~lú¶¯ÎEfAx8䌃ÀíáX·HÜ©?BÆîÈþÉròê_(Ø{Jîh3[ÌÓ^^0_¾S ë3`ãòp^A€¾ˆ½kO÷úðÝØoýçtâ7‡$磊”¾ådmñòÖ÷íÐ -P¥+ß²£Š6/… -äfÕì^ GŸpÖ„1~Ïù)‘Ø$ÂT1|RB¿’Í.»˜Î玦ÔTMª¯Iˆ†â僱š¦è.Yo„Aަ7†7†7†ÿSCÙkCɦ¥ÀC µ»?‰—#4wÆPO¨Çù?­NÆÝãfo0©”qZãbNYÅùLnõ¢ ~(¹VbOKmñ²k¬ZeÄu¨õ8MÀB ̵‹€‰üÕ•Û?„ú?©§#ÈŽº‚,ØkNøD”s§:#ã:'‡>V\À9OeÔ “ØâtžÌšR­2'‰ÚLøæŽž8µN£éhm× é•¡1”!?mb#ù~GPqEfVô¸ 7UO$ó²K.”R+‡é… ¶\f‹9³\cL­Ôu%×uë›MêXy¯ߪoNhkG°WüÏÀF 5ˆA²k(ö9‚ÚÇÔ˜ÙÖÓÁZâð·$)9—«3ËΕçíÅ4©™R¬éÉfôi¼ÞNb]Ÿ6µÙ*MZÄIÊnAÓ‰¦W†2ÄÀùv ·-‡&Ä Ú·Ú»€6ó[ÛÉiuLâ Ye¬>3ÂÏYKó”ÆºÎ@eõèòæ6r½U‘ÙÔ/"4õ SÆjb«MÊCb"5bè@ þ˜ØÐ‡í¸¤IUBþEI*w¨:SÒW‘«î.+Ô·•[iÜ>9EØß”+±U§Kør7Se@p^*6¾õˆA¶ó3Ðx¬Ý!—Yƒæ™!,ô!*y¢-žvANàÛEéM&AŽºƒCÓ«˜ £´cÓýuÔ†nv£C’›Ù$eš^X_/.bm[rÄ õøºüÞí‹yb ½gŒ \×ÅÑÏ)ÖÆ´&} I­æQõRV‘©Á²Ôñm•ôz;›ÒhgæÉúyÊn4½6,bhغ ;WB;bè>èòÒtóØv»/œðKWtá:A`jNmÒÖgªå‚\}CE±†Å´ð˜6N‰ÐΠ7Ò¤}…4Eš 1°1ˆƒ1t"“÷÷/,Ç1ÓýAa·úO¥\4FÙ´ñC A¢§·J„d°2ßÈ«(±°8ƒUm§3©ÅR µHaD”êðÊ Ø°$[ÿ­n+Aï¾î÷¾ß?³ÃÜ - ›°cSΙ# ͺØÊvU’DÖœÖZ×­ã )F– ¨¯˜Ïê/à -(ÜF;¹Lj!—¶Ñô§¡ -14!Û -èrÿf¦³ýס£^wÎû‡Ž„¦ YÃéÝ]1|•6¡Q¬HUU5gv²Å¹=Åuts¾°ÔšSͳe׈2xRKFE‹MP8ˆ¡1H·|Z·OgîkŸÙ1Ûž÷ñ¼5æzq4„`µŸ.èèÅñdz¼XØFPV(3ÚKdä®ü&Z/¹‘aÉsû‰b‘P+íK©VÑôÊPþÕB¨ÝðÈCÇŽOfÌ{¿~4ìµõÎåÃû¯]=|æRP²ñÌIZ«-ŠÛ`Šñ I-Ìö´¶|M¶ž¤¢ô¤)Šz“圾¹Ð‚—ÉÌx‰¢MÀE ˆ¡þßï‚Òùos†?éß»æî¨—óÍIo±ÉcA¶«‰?c)Ò‘ŽÀ]Ç쉗RtI­™ZbAeÀ«òº£•EÆHE¹)B)2âÔ*š€‡¸ˆAìøWÐ8/™1îøèÁðž/o\ñüî—©#ÿ=Øs#¯Ç’ëF#˜l{t%­/N”Õ“ %è ʸŽtM”–Ô~ºÚ¦aèB4|}˜¶¡MÀG ü5  Ùqtl^üØúÃò›ö¬º4éá4rãa¹ï ½ãÓ8–Á‹((>ÍÊŒç§õ' Ì) 1¦tIDIz²‹*5+‚ åÊ@}ª ò¨ùç; X¿p¦gÓ»·G\—^ººkåðm'ó#Œ»îñ!_Ù´_DÕTXréµõR,-s4A8K`ǧqqƒY•á9UX[~Mˆµ¸6Ю÷·ðQUˆAôåÛ ]÷ötÿw ®\ÞüÞðÔ®U¦Gî›Úg<÷´6º`**9{".#u<‰œp™HË,ˆú™\xú¥;Jg„œg”žå°üÏòÙháÇÙj‡Ý_9LŒ®q¾é´Ä4½kuÛ˽NÍsûwÕÎy(ÿÍÏ—þôdyI¼‡K¼’;•NÀÝ$Ã'sÓOMäg„]/Ì -¾Æ$^åýÇy9h‚:ÄжrþíÁæL~æ`švú@óÒí ìÞ(W6x»ÓÁÏ›<{òq&: ñYBHì“TlÔãÌÓáȧ¦)‘؇¸ÐÅÑÁ÷Kcî•ÇùßåÇ£ ¤ËçÿÚ»tÞÙK°_gaMßYÇ™ñq­ZGÛuœ©ÖºUjQ°µâV n ˆ¬²‚,Â!„²Bd! K „,dåD©ÛPZ´–qð©:ZÛZ¥´g~Õyæ:ÞÍE/¾÷Ÿ»óž7\ìß™§ûÙ}E;ìY+/Wþ¨ -ü÷CøA"ÄûfÞ?²Oâ /8áעг¿”…ÅÎVž:ósMxô #âôL]d䋯¨ˆç‚Ó¯ÓŒy©ËÈå….ýÿZòíìÚrp_Á‡OþF‡ýËàà6ß™ a»ñ·©^ñå ¹c àP4”Ž‚Š#‘@94ßp`ù…çX(ð^«û<ëØ|ͳ¥”º…<ضœ«JÀsmxoƒŸk¹ÅBŒûiÀ팄ôðëä}…»C¡Ä3ÈžÁPµ'¨{µ÷$°÷½N}ƒs]Ôÿ˜ëÒ -ËæðàÝ…TØòf¸½ »VãàÀ;±pd}l …ðÍÁûþIÀm €t×ý?än;$Tá‡Ç äC? »ùE}­€Šöeó {,æî· Ñk ˆ|7(Ô„±^ÀÃû›ùœ^–ø”BÍ|XÆÎ»_À+ù'QTy—ÐZs'³1™Þ]û%¾§~"ÕÜ0žâàÞÄ]á_‰¿,p¶ß ¿þ/ ÕÈPá¾Xè¿àþ× ñÑ/àãO'7ê'fiÒwÕÔ¬åuùӅܲ)¢ò5¡•6™ÕÎú2]ÉžHëiOíåÞJq>Ç 7 '8ÐÖÌj´');–m{Á§k åè&h ò€–ho'üÂ'DÿX_”úˆA!NW1K¿)áÔLæóY¹’†[ÿFf—èZº¶y on½š: Lõ㆚ èÈPƒ 5Û—@ÃÇ+@t௠=² ä ò†¶¤ÀYIVìS~AÆ·õ…ST*e’\W;^Äå^'‰ÄcçÚÚF²:Û/§«—ð¦n΢¶¤ôwñç¥Ît´£¨È@s[\d ƒüðFPœôE¤t$ÍH3Î>çeOsËJï0)ô/(ÌÆk¥Éh¾@>œÓ¬Ên× f¨ôv¼ÑhN1é{ÒúºÕövgÆo´'ÈÀÿh9´ì_ ÈÐàÝ>Ðu6øEgZücinÎ=aqÅWõu7h4á(¹N~ =ç‰"½ãœÜl'h0SF¦I7˜º21MG¶Mál/ 4d`¡ÿB€ mÈ 8´4'<@sÊgVü¬;5ñQç9âTså nyÃç¬êÖá*¦r°¬Qç(–˜¬íFR÷iÚ¾Ž,&#˜{Zs¬JgæKÃ|¨Ý¶„È Û·”‡6@¿èÂ|ftgB¾×&'=Te“îÊHÔ[ÂþH}¥ü®²UÕë{ËšÌúâ6‹¶°ËªÈÓØZs´}¢I/ÊëS9ÛKØÈ Úù'hß· -T7€á¸û¯ÆPŸŸŒ1!O I¸û=ù·;sécâáPc¹ÜƪQ™¨µm%·WY.éë(í°I‹ÔýMÈÁÏ7¹…˜ÚÙ€µú•¡þƒÅ A†dÐ|LÇÜg±Ÿ±èGX"nJŸVx«+›9ÜJÚ%rS}•ZÃd5LV%´¶THûÅd•½¡Hm¯/6˜Ø%˜ÖÙ^ÈÀA†fdPì] =ÈÐ{Ì}Æìó½-*ä[[|òSJј*“9(#6aâ"¹–[¡R°©)ƒ‰©|« ºÅÎ¥t9Xe*³\of’{{œí•ý8 ®o@ËÎeÐ… úÏÖƒÅoÇs{÷“Èé¸ä WtE›ÁìïÌi2´È•Me*icµAÄfa<&×ZO—Øki*¥ÛQCÑõÖPÌ:gƒZd`"Ú<–A72‘Áæ·ãÙ` ÷¿/F„L]8“|ÖT4dHc`JB“FF’É%%*± RÏmd`lÇʨÚkêäŽJF·ƒL×a4³ÎÙþgà!ƒÌãMPïù ˜}ÞµûnÿáÒI¯#§‚ï\ŠI¾êH(t˜SéM–@¡ ÊZ¤EÝ< Y_'¤öÒølkWÐOn”:JTŽâºKI­IïlP‡ ,d¸.92h÷ü0Ÿu/}ݾ øtúZXÐÄèiÜð…ø‚¾¾šÚÁ—ªs¤MJ¶¬LGk­6WJX}¥"ž­PÔbÏ©ìy¼ ©Ñ¤w6¨[õÊ Üº:ÑíÔïy{Öæ³ö‡‹G·=º~bÿÔxHàõkQIƒÃqùFŽÚ‰¥sÅB+GCê¢+K´UÆb9#É­¹2‰ SÚ²›uAdÒ;°‘¡ö½ù ÞººÜ—€Éó­çÞè¶/znß½ìÁ¨×ʯ'm¸yÏ×óòtÀ‰¾©SqªÛÑ„–ñøò†ëÉÌšÑt^ñÅìæsç‰ò4{¡g%wÇ÷Q5g,lm´EˆêÒÄÚ¬Jgƒdà­› ›çÙmÁ“‹»–Ü?°âÆÔg‡øzÚúû÷ܑ݋Nç“PÀø*µ’|3“I;ÇÉ! R‡‹Å‰Cä–¸ 5mÑçke‘çyòðÿ°_goMžiÇ#ÖŠNǺ_¶ÖÑŠ bÝZ…ÔŠ"ˆ(² Dö KØ! Ù€˜!!Á@BB Š€‚"ua¨Â(nXQ•–¢HU¼û^½æòhÞ? ßóÏÑóܿʪ€ê@ºÂÊ×þ¬-gº¶Î{òŸó~¿´oÜÙ¦ów7¬iòÈaݯ¾'Ê^â EãQ‰ÜG¤Ììû ÌäáTå¿Yü˜›Ì‚ÈŸ¹Ea|1¾¿Hx]&õ¿®’ù]«-E”"Íš¹`^?÷eŸõÜ¡û¶óúžaWtL´mšqu¨~ãöñ)~?™;I Ð_)©O©Ô„Çéi¤_hYÄ=âA.3ôž€…¿+âÝ‘q†+rýnW£äË- nµÅôù/çÜ^2§ï©í§ç¦°«gl´}'Ÿ=ì$šõqÏ›Åû2þ ¥½¦„&þž÷[ftôƒõ‚Cž2.LÀ?+N -z*£Œ)SüžhSѪesÀ¼ s«ß -óÓØ‹ö)[+ã,vU%8ZËÀy‡pßsÁû ð¸T zÄÅ;v6Ùø.3 ò-#0l†ò&Ÿ. ž‡¾. x¥ ÷›ÒD  êþ…êž¹|cæì«es`Ó"5ì^Y{×åƒÓ¸íJO‡DÞ'"EÕ5 2p!À8Œö‘ È;Bó©çq(÷òµ7ÚnŸ±Ä\ìýÓv{>F+,U°y‘v.Ïû5Ùภ-.›Ià±- -v…AÄî“@²†Dl ¤î €¬ü€¹ï8p}!ßÑ„û½¡ø€”:ê.uXbÌ×,15£óç(`Õ¼b°YÈ…­K2àÛUñ°w œ×‡‚»õ ðµõü_ ØyCÜ7^¸ÍR·ƒÌí@ßqX;o§;ðw†¢]8|‹6sÏ\Œî¦Fþ‡•…V[²aý‚4°ý,¶/û•A°õqp[ãžk=À;œ\ƒÈ¯Ý fƒ+7¸@¢õ!H±þ26:C6Óæ pmœ ßmƒlúÆO ù+8{ƒÀqˆ\רÇD!öP@r†üÔ£Àaù¿g -BÞdI¯RËâ&“Ô‰ñº”qõéã±MYÏ¢Ûhc„.Æ“¨ÞœGýì{a·87NsÑô·áoÃÿ5díX¼Ý‹¡`ßJ#†o;àíAã ‚”cÀcžxË⇿¦ÇL¦ŸN˜HV¥vL™žù „™7(àI¯±…½ô’ª åús©U g’ F3õLcåL£.¾S¯Iê©TSûÊÑ<ÄÀF |dãHwü*°Ë@çô4 £7vÆ„›lˆ ¯%Ç=T'ÓeY«…,ÙÅ<¾ªƒ%ªn£•Ö5gª †ôÚ¦º´–fmR[³Šz®A‘Ò­-KëU¢é£A°i”"µÃ2¨=°Œn›gM^ØiS n¢)<èq},ùŽ&‘q½,]xQÌux*3¯@gd•Ôëé -cuvu³&«Ñ¤Hom9vÖ(Ë8_-ͼ¤Bä"ΆyP€äÛÆa)Ô!†f·ÍïZ=¦Úpã­¡ÁÑ”: -ã²2EØ)Ë–™‹Ø*Ÿ¯«á×kXòF%³ÒTƨo‘ÑÌæâ¬3M"ZWMí¢M‹ì,Î×ó ÐöS(C UöK¡áÀ—Ðâºi¦ÝÃá·sþ¸±ö໦¨øëz2£»’*h“gÊŒ¦ª¦0O§æÖ—çÊeœ -“„]k±[ÌF[³ §³öTNM¸ˆA„ˆAg¿Œû¿€6WÛ鮣ö//wíÄ µERú $zGu¢ Y™.«-¥«*Å\]Y¡ ¾D i,ÊW˜„y:3?¯¹•Çi5ñ¸z·[‹&ÈûŸ¡1T oe­ýçмÿ‹÷g]l^uÙó¼××m¤;8h =‚ÜÓCo«?ÕP™"Õ–gW”—²ªK$üúBQq#¿Pnâ µf¶ÐØÊä›[˜üŽº~·MxˆAb3Ôˆ¡nÏçÐâ¸úm‡ËÆÉÞ#»Ç®ú¸Üí ¼ÚFî4iÍ -_§£–(ÕJ©‚ù'ûuÕäÆq<,2ˆ´ZµVP”\Q JUd—}‘=„5@ !a ;D‚HD‚Ȏ¬¢ìЂ VŠX«ˆ EtÄ>óžž¹'—s1ŸûïÅ{Þçÿ«Î.bŠX…Ü›Œ|þmz^y3-¯¾9ÛÜDÍ^é®’d L¤‡4T ÿëä~·îþÔm®¾8d{êå˜³Ùø‡Ç@_h[;žV'Í(«‹Ìç×PJ¸I•¬kiµ)%œFš€'¦\½ÖWTÛÍonŠåuÔÅåwWIØÈæe©É¹U'à–®Òz‡Á÷+ýfjoÆl´§Ç/ŒŽºcº}IâÎÀÄê6K ŽÈËmˆ+Ψ¥U\®f\§V²ëãÊónF••ˆÉe×o‡•·ŠÃ…wE‚î*IüÝÀ> ¤áºæfh:­¸Ö¥¿óݰ™ê˧֚Ï.šþünñ&Öõã(¥ÁéíáÜÌæè¢”[‰¥ÔÆËÕ1õ¬Z²(§>D$h ÕÜ$ˆšƒkîÕ+{*%™ßKGuÕå î¸ü×öS[—ÎoŸ{dª<5mq|ì…½q÷¤««ø‘¾jÄ?Ž?ˆgpzB9ŒÎ(µƒRÓF/oeV[¸×ƒš¯ÖâZªEþ-M¢€Û]5¸†¾rI@Ö.iÈÙ' å7Á­crk]:›_ß×SzöÜp÷Øœ…æÀœ­që´‹sí¤®dÅ ¤§2h¿DäÄ Æ†õÓ‹ƒû˜¥¸>n¹_¯ Ò§·ªÊ«W\åÝÓ[îÓ1(”\Ax*²P£& ­‡eµä¦~ûQaì¥Ñžþ·æšwÞØ5¼rr,›Å` ¦±¡ì)||òdh2e"š1žÈ!>LÎÁ¦øŽpøžÃW1CBzè†Àm¨Mà><\( àî”ò¦¬W•þØyPzæÑ¿dÆfµ·ö-ª´-›ŸhxomPñÎÁŽÿƒá¼ñÃ1^‡Rç#¢£gã¦iI„©””€IVšïD6Óë)/óD˜‰~Rå:.Îrï’äî†òÝÒЬ,õÇÐN©/T¤û´ÛV ÷Ö­]Ð,[µÒãÿyÑâÊ -Æ)uÅ߃ö仉'/RBHo“ƒþHôŸgÇø¾âÆy½äQ0³Å èÙ -ªëL=ÍùE‹D€·] -DßI­ô(¡>Ý‚ê]Ø+ÛòQë[ÑWý½¥_Mެ[èf­Û1ÖÝ-©ëþc×C\ÿDaˆŸ)Þø5:6`5Í»’‰óùäµ\ˆÇ,Ðï+ˆ®ïE$çEqˆ$ ìÔrëVÔýŸ7£zß‚jZÛ'W ÚÛ„pN%Œ4X`®E»3@D‚ß…P Y Ò&(öþäà ©NÞÀvö® æ/ž›ûWÚm½ÜÝåË Œó—FI,ÕË£FîýÕ9"/*J×À~ùÐú– º?¤þ*\8 ¶'CÁå|Ïà X d}ˆ5ôš1RLÜiêœ ®kæ|s'Z8B•¥ˆ¬$1Ú"‹ºÛ³ uó±ªò¯od®‚šü8®”:;ãáœr8˜ €•†8ñÏãÀDCˆ–+Dê8CüN@;å)§/Sײ~²ƒœŸl¡ðŒ ÏZC¥DîÝ‘A5ü"ƒ*{&#UÛd8p@>ý3Nl Ý]80Øã æ{Ñ`·ßÜÔÀç àÔm¤a äCVsØŽX@Òs`5æQd˜3…Üc&À?.‰Æn)TÙC*ï­Œ¶Ë$²\¨)àˆ¢/ò]¸ÃÙN`¼Ë,w[ƒÃ€V6/SðW1 ½F@Úgáû JUâTÏC¢ªÐ÷Ÿƒ4û€$€¢"1‡ä Bg $ê*AŠÁH·P ' `újAjÈ9HŽ7Zª $d;A, Q¥^ëäjì¿Cë>ÅAk„6Â*¾‹¸8òÞ$lû(|ÞgŠ<ã59åñjCÿoø_j@nE¬†DioK§• õüÈ0W, `{k“¨ŒX3 _¶*ÇâyžëÑBìçˆJÜZ˜ˆ°JºEZ n û€ï$/F.ŒD¿Æ>Ž™õy;é5?î5³HøoCìIH>¥,½À1S®½d{jCþ<°¢, 5Éè˜ujöSœ h5ª<äù:y)´!j‘Ø»@¸Ky4@ÇÝ¿4ã÷$iÂgŠ>æ3MöMØ$" qÈû…‚¼ã:Šyî;àš*C¾äa´‹Ó‡,²°¨.1Ò½?'qƒþLà‡.Å\‹|Q÷&¬Žúš$NzE¸sùePê äOøýÊÅN¤úM%÷úO_ÚP÷È@<Ò¨©éÈÖ»‚ì¬<eàÛh@!Zòý €jY·u&Ão5…C\¢D.ÄS^GU$Í…ß`Ìn1§ƒÛÙÏñýœßpïû=àŒ³»p“©SÉù»‚4\:¦ÈÆÉ9»xÆÊ °V«a ¡€dû…ƒYˤ~Hg‡/$çÆÏ'Ñg£KÓ'×d= iäNÛó%  õáît=ÌnÃ?e5&S74¤!i Ý ™HC²³ŠŒ÷€ÐJ®9ë@‰Ñ—«û‘^Ë\Zð;=zž‘4Cã1§â„ÙO#ª -†Ô FI­%ÃÄ>aOЀð~¨¨9x,÷&i<³ž4ÁÜÐÍ›€lÞËG6Ù8`Û…HÙ7KÝùŸ -¤Aï±êNmüÝta×Ls ç¤)û¤öJŒMŒï’ -îòòè¦ - -§¶„.ÒQY2%Y¨®")u"bŸßbää¶Øù] -q@À$Ù8ö@ášÿ50áöE Ú¿ª¬úØpÒõùü®—­þžæ0Ì¿b¢ûõ‰øv9¾ Y˜C¯ã°uå7DŠ’r™˜V©®,’é8d½I6…÷ªRr¿¨”<ĵÈH i(ß´ÄÛ‚™×µG>ÿÃtrÛVï/,Ø£O[C0LÑÑ]5×Òî¨SI ÒÌ=ŸÈV°¯ Eå ¯”«fKt šÖXBk¨¦ÑîUS©jµOL£~WiÏ_ €‰¼ é¶… Û÷ Ô^9k>ñåÌ=ï퓘#ÛƒýÍ‘Q–›q©Mz±ZA(VIòXb>YÈå”ÈÊ™,uIr- µ‘Rz³ºÑV]@·j -é½’Bú ß  dW'`#g!Ûæ ƽŸÀ­ÃŸ½o=¾îUǹ­½˜C;‚|{Z#"[o]M©¯IÊ×jñÅRE6‹')” oTWV¨(¾ŽÄVòX5ÆÖê¦U“[Ñ+Í+äÛ”Õh(Úà\¤A±Õjöº|l>´ümû±/^ôžýêé ï7¶îK>Ööðˆfs,ÎØ'¯N¥ñµYJdXÊ®KɆ2_ÄÓf dz‚ÀhÀ ÌF<ߪ!ðz¥ÜA=P„4ÐÖ;A%rê¯@ýžEsw¾]úºÓkÍäЙM? _pïï8·#,¼¾-:Ym¾–+jÀQYµåņ<YKçªèò G*—h“ez}¢¼IŸ$·jp’^iŠxo\GJÖ9‚ÐÕt[æƒi—ó»v÷O§ú=Wþ{ìÔúŽyè´ù{7÷†„­Q‰UmqYœ–d -Ý„/¥4ä°së -„šb)ÎÈR$Ī8ƒV}ÅpKsÅØ©ŠÓô‰ãUƒ•öu_8‚tƒ#TovúóöÎùÓÝý4â±tìÇã_öŸÛ×6Š=sӬꋈçw]É`X ŠÚÒŠsïdU¤·¸ÉÍ4a|S…$Ö$’EÝÒÈ#LòËM²ÈúaTí× ! LdN)Öσ†Mó~kßêøóÐîùcãîKž_o}vvoÓ8æ”~,è’x8<¶â»˜4jÿµ¼¼ž”¢ô®Ì’äbyœ•Ɖ¾_QyÙ"„Z4Âû¢`k§0¤mˆz{˜iŽÞ4èþ†þhÞ€þµ×ýèÉ&Çç—ÝqܵyòÌÞê ß²§9OÂ"n|@½F Œ¤ä'gR®> ݈²Ñèá6fY°M\dÓ2m&VÀp73pp¤,°g”nÐW¢A²ÊêÖ8¼µ¬vx2¶ 5ðÜÍÉòêàŠ¦éc/Oï‘O÷äýp>Dþ962{"1>m"—øS^úÕgEYQϹáϸħ҂ µä€ñzÊÅñ6 -v|˜‚yô˜l”¯@ƒj…ÃGórÔè€ j`b™Cû´ÛÓoî+ o=]«ÞœÚÅý÷áÒ×þ§È3a¾93WÓg’C“§3"㧉1±ÓÔ¸Èé²ká¯xI!/¥¸ )uJàTmªÿ söE7óËC»€¿Ôj?EX¢z‡¢Úf–£?l^¤Ÿs_)óØÀž;±­xöÜþÂًٳa'Óf¯œKšÅùÄÍf`cf‰‘³ÔK—çÊ‚Cgy!Á$a—~W‡¼¯Ž¸øÞ‰}g‰ô{×e(]P£æù¨®Ž¨;# Q7ÿølž¶¸ˆaÿÊ -8´Ž -ÇÜòáì`öá äÛxˆõˆ…d¯( œ¸ y§Br&ègƒ€í¢ó ¸pô>¨÷õƒ;~¾`ÅØó}ÕÕê„jéuBÕüàŒRÂ*G!lu)ƒÝË)à¾6<×§Ài·xðÝ A;/CÔžPHØ i‚ Ë=Hßøõ‡1À=â bPz\ÃQohô<-^öô˜Ð(³Åe´ÍCUM-tàÁZ':¸-"Áö%ä,áÈÚX8±.λÿ¦ÛŒ…Ø-~øµà·^€ìmç´ý\ßqJwžÖ®Ó Øud»O€~Ïq¨µË|2ô P’G( £‹a­c>lpNƒ¯Çý—ýòêj2[°Ke쎻8vGEÅ‚H "Š„BH)$$Ò{$!H‘*(( ŒX°À€¢.lˆ"(Ê3öÁÃ{¾å¹9W™?p.žëïY{ío¿ï›fFûœ0ðœþ Á¡Å dI_ê±Ë}³ÜRœ¼€¼b/ÐWx"xðWîéÊ] ^µ «<À²úŸ¨¸„|¿sÜ8ųqãø0}<æNÀÃbû8øÁ!œ§—‡ÀcVx}ï s¼àÐwª¢ƒ[‹1ñ,b]•Y¥Êä•HùÒ¡Leäéôúl‹QuÒ¬`ÕZdY-IV›Q¾£gu‹DYø¶@Æé< â‹üŸ…›¾ƒ -÷™_Îx:Ÿ÷[9pá Ë£ó!Þí5QÁ-eè¸ó'ð©Õ& -㤞É-Pq$F™D¥kô -É(á›…Üš|·9ŸÇ»eâñnkx¼n1—ûP`  #•±|"ÈœìÀ„ÌâŸà”ÛôçöÌjò]Ñwéà¦{MÁû®×E»P™[[’L(Ï'1¬F:רgK´j¡J®PêERƒ‘+.4³Å§ò3ÅM7óXâN-Kt_Â>Úâ›kéDP"ga^c¥'qö®q÷ÜÁK>Ëž^=ðSgsוúãGÏ×ÄÅœ*OJ)*J¥çåS9ZS¦XžËW -tr]¶:Ç¡,È£+*,4EC>UuÓœ®ìÐ¥ËïKÓeÝ"[q`/™jä, -·ªâÇI£g·M¹¸kö@«÷â‡76Þº|tïŦðÀÚ³1Ñ¥Õ|~y -M_LaË­L‘ÀÂQ°M-àͥæXL$}™9UÎBȹn&ê;t$í=)YÓ-²0Ç™ºe pÕD¨Þ`ÿ©ÑÅaèŠÇŒç¿{9výî¿áZk gÃoaGª£Q…uèdC Žª¬ g Kév1[F?!RS -Ôz‚ÅdH¶œ4%Yêò0ùÿa¾Nÿ©Îû8ŽÿŽý¢˜²u‰©)¦H ‘­dß9„sç8gáœ[ölIƒJÅ´\u]Iãšš¢G£1´h™h’êjs´P²„+]Õ¼¯ß\À¹}ÝxÞþ¾nü~ÇçÝsDrü^½ôûG{¥«TAÙPIþ ä=uzµ:Ú¬5>tÛk¿½ã¢7øØÍ¤ï¾¿Mw/ukÛ zDs7›u´#)yÿÏ)òª6YAÉO9e;Ïí‘·TÖ¦þP{@Üüý!AsS¿¹µ‘wæZCRsÿÁ?U O>©R…¦j¨&;Žš«áŸß¨}ºü­úäM[¡‡öZ -W£}×^îwÿ±76ôÄMãàU¾°ºK,ÛÕ‘–»³=«8ãRA…ôbyµ¨­¦–ßÚ°ŸsáÔ„Ö õÌÖ«õ ­uìŸTqZžV¨‚"²¡ÎD 'Í(¸`A™í^IyÝ¿’20d«uû•»Qç¯Uë“P—¦è Æßãi5w¸ü²["I^OjfƵÌ<É•üawY9¯«¦*¡³¡šÑyê;zç…Z×Õïè]ÿªŽk^wi°T”ï4¢ Ù„ò¥Ý”xwg 10dB¹õÖV»ó»IÛ°¯Õ™—!NG£|÷=‰‹ªxÈaåò~iªäw¹\p7?—ÛW^Àì­)Žëm,ím*‹îm+ßÞw4XºýÆ‹Âèk/ TA¥!G—Rpn ñôª.qï¾.qstå× [ÝÖqÓ3c>VÇF÷¿¡nÛ=RøŠõRÄL{‘Æ e‹øŠ"I‚bw:C±/ƒ¦8’­hÎŽº˜CºžñB‘ñh$3bà­\ÔPpJŸxÖ®Côõü…èy¨Kü2c¦~þßëžq_vdÚËrßT€]åûp·Â÷tŸìInpú¤˜šF¼"äSÜpè},t–66û!l"F©ÒÿMlàÛë ÅIéÈð3Ff„9²X«‘!±AúN{H+‘Rïñ߯QgèS‘“´ÙÈw´yª*’wŸlHÚ¨ƒTG=ÈÝ ícŒÜpsìdX"[´ò,GÈÊ\‘Zç É$7@t6ÂK¡Hê ¿‡ -î¨/ìûÑó¬Á˜ÙøáØiÆmœ>MKÿH{·­ -D6XkB´A2]d» ÏÛ¡æ(¤["O@~bN{@¶×i‡ 9‚ä–ˆZ£ ìˆFÒõØ?x¿Ñç9ýŒ Ï™ÓÌaÖc<áMÜ ûãcüóøyš*“·K"Ù²^rò¦ÎÛ¬"/c”›cWŒŠø—ü­È¬òƒ¬>iÇ©œŽFò9úÑÏñŸݬ…ÄÛœÜ{¼é„çü1æHÒHü„@Á˜æ?ŽŸåô³”ñªü¯!i­&R×i#gƒ.ŠœõQêi„ò@sTn·BÇÅäçz!§<èyõ“¬1v>õï eJ û?¢VÞ¬ #éCbhšw/eŒ=(}Íz›ú,~L:Àœõ%|H¼Åžc«‚d²A@6ÈÈ}“GnÞR§Å¨Üj„=˱—j…*–ÊÅn_J2}ò‹Ãærªcf32gÒŽñ¦$MÂIñÙ”qÁ¥´Q~÷Ž7ÜÞÌל¡ìGÌ᜻ ïvÜfOJ¯s¦E]¼Y¾*H±P‡¼éåk´P´N•äæÝëaˆZ¿åØn…ZÆÆÏÕwe…ÌÿCI~äT~%c"«Ž7*kHN¤¿ÉT$žßùŒ÷KácîÍ¢ܧEwØ/ ¯sG²»xïvtð'¥—g„ª@B6$“7ýù®Ï°&Ï5àoÂTÜÚ#¢G­¢Û:«âi)E¦ ‘ed“ÁY„„$oH I؄¡ld)C@+R´­´§Ç>}/¿Ã‡ßçç½Ï}¿×ó§Ö0Ò7…Hç•^Ú’0iöÆŸRÝO~ù™¾O$Ø,òi®³,ŽÏKš0ô)QN|BÈ¢`•ŒÁ¬Á zÎ@`·/àA\—ÿ$·%à%£!è5åvð›Èê_±kä]7Öt¹ Ò7S.l»€ÂÖø£Âõäût³9ÖnFåñ füÂà…=¤ˆÉ÷‰©ôÞ°œØ|ÜUǵ‡6ó[Bú!ã‚ÚgœªÐiznŽTŒ[$ãß®ês†°ƒÚ€‰ôÍx¤oÊÝÈ8¿dgøWžÍÁ¥—os0f3iAö“’p¯ÇZà‡Ö“@é¢ÊbÚ¢2ÙMq?…• nêÔø.a~DXŠÊ+"¼ˆU†ÍPóÂ"Ö ‘´ é›"d&R¾©0Ù òÍw~T]ùò·ÇãsJ/³)…¿Ãh -Þk0‘x—Ï ¿ÃæQšbF-•£&çð*ˆE ¥‘jQQDkbaÄ 8?RŸ9ÅQ_Ñ3£æ‰kỵ@Òó`„™‰ôãú ïܦOªË_,•Zí_¬t86]âaªÉ¿e?œõ¼'#¶‹èa <˜RÃN`V0epIt&¿ªæ‘+ÄÙ¤FI©7)ƒüD”F™ä¦P^2’)³$9enUH4 ïÓqé!uL(Ïnø_©ÙŽß+Ü;WcÿõT…û¥•]vˆgGjx@c5¬FÈ¢”óøLGçŦ 1¹¢ôèRI -­.INë–Ê迈“è< ýE¬˜þš²d  ÿþ^mŒdÈùF1øPaºí]í{fê펎«¯_.õ¶íÎòhÎ"Ô&“ÃÊ% J¡ËÌå‹àL8YÂVˆ¤±E1³F*dÞ‘ Y“bÇñ±S,Aì u-Ÿ3ÐýŒGr¤kƒü¯uAééõKê‹[~²0zÑd{d´îÚ…þ -ŒÍU€{}.ο2ƒHP%G“s%F†(– ¤1/S”ÀUJøpµŒ ·É`î—«‰‡á) ¿ŠFÐV"ЀŽÌ¥É‘Žì‡ò¨Îÿ+O­û½îÂæ¹–ïvN¶YzØàr¾G}ú¹ÔÏM­Äú•dGàóÒ©äŒäX†\ʇÅ@˜.âÆçKX‚ -CÐ,‹ Êñc LÁ3˜ÉEGD¯æs†‘#9²h¢ÿhÿ©>¡÷®ÉdëócVÆ÷[œL:ê<¯ÔWÞº^^r«  —™M&É31‰©\_.âs¤)"†$WB—I)‰ 2’¸_F‘Œ ©‰“0U8M_ "X»Ð ¹“ä=SvXk¥î˜Î›ö³ëž÷šn¹kyð^»ãÙ–F+u­kQEov1›¢ŒŠJÌ¥Óù -›‘À£§ÉÈ) -11¹8)<¹^JH¹'HFÈ'¹‘Ò阵€(ä|x'È‘,ùûРÊý¾é+í×=§t'†Înê·ø²³ÛáL}››eYƒ·K^m€wZ%.DRÉ/¢ÑXJVlt¾€KÊIŠÏÎá*qˆâ¶$XÑ- Íb3'a\ÚtÌZ ÉÀû Ò ‘Þ¹õ×íý¨·‡Ð/†¾Ò}tzcßÐ÷ûZúìOUw¹Z¶aœ2ý0ҺРAMx»ŠBŽ.gÆD•Ʊ Åb^HQz| ªP觪ùªºD~Åšxå3N`î4}-€bˆ ÛQ ë_(PfˆZiÚší3BMhö †Ÿœ4è1ÿwý°í‰Ò~óœ/yÇ-„Ö`?vÝ@Š$ÖÅPñµ\FpM"ÛOÆõ®.àcªÕ/u‡àFÍxÜÍÊç,ï’ÚZù’m(·5Û¡_;·@šÑ Ѓ©=¨»“'Ö5kÌŒª[SðÐéRê ‡¨ÏǾèÝ"vñ¸;tbP;L¹Õ*¢cZS™ž-,÷5ûzkûz›†åÖ4M÷¸=KFVØÈù)[ P¼šn2€Fz×CCÿ5€z¦÷¢›_žZ¯~nf¨š´:’©¹j"u³ä>ºéHàAÆúà‰÷£±¾pøѽ?ìÚ¯¤ºô«iÎ4§ûTçÞ¹(—Î…p—ŽÅUÁ&Pl„¦ªõ ŸÛô¡¾}¨kÂjœÛ¯U5{j£êµÙ®ÌW–‡%/í¿{îz9ææJÔ¤¿#î)Ö-pœˆñ§ûÞÐpÜ5‰Á×4éX'M!þª¦–à0ÞM°ŸxŠw[Ä^}ø[ðZ@’4¯Ò†ÕëB÷:t¡¶a}¨~~ªüÝå»o7¥¿½´K¼haÌ]°9óæÚ¹¨yÌeü|€eð<ÞÆoŽäà=Çpöšã¹ºÍIÜ®Ígx:Ïz]¯Á8¼¹ƒ±_¹i÷fþ¦íô˜µü™©V¢¡{ÚPsT3¢•¬lEå.ë¦.ŸÞ$\¾¸“³üý~Ú²õ‘ˆe§ãØ%¯3Kþç}—p¦7—IæžË ÷å8K×e±•óJºµãJ­ÃJ¥Ý‡f{Ûý6Ÿ;Xø±´ªñBê«ECM­ZPÕ€6T¨Ñƒ²>í@Ë?Ñü}b#óo“$`¾®ìWù÷£à{Ì„žtC -É5@;ã ØçA‚‰_°Ùm@ñ%kPkjÚÍ~Cf–àõeK0»ªÁJjn@A•Ý((ÿg4”:£I€á?|—kXÌyÇI*§xè ‡¢$%ÏçRtšæÔšfš™f¦fjjš¦3Q*jS$¬b…°Ž­ÇéZ\«ua÷bí®­'¤„q˜(Õ÷ùïîëñâóúþ\×}ÿîßý¶Ë Tp0ÊÄzS¼çs¼ˆŽ( 2â­ãA·Ù îòh¤¯ŒB–mT«Â‰c4Û탱Û!û× ÝÑ]Ž~è^ë‹§¯q¹‹D:ÞM"µÞ!‘ê#‘¶M)†ùÔXë‹ag˜ -'ãd¸›&Án,ÂD#fa$È‹ÂÀøO0¸K!´ðG¦¥”–>(¶òB¥µ'j—º£ÙÆ ‡m6 s™+.\ÔIç9¢þM©î‰´¥D*€ñ9æN`± Ë (°7Œ…‹ÑFx‡!À$áfþˆ™ãƒ„¹^ Íókžxó]‘¾Àòë 2_K,GT-t@ýB{´,Zö¯Òö_¢þ]¢þ$’ò‰$ÅÌ)\˜N¥aþ´8XèE= …ÃŒ¸zÃs¦ü\2Ë‘ÆNØl숄Ù Î^ ¦‰RMVBd²2ÓePšÚ ØÌ•fV¨ý*`ÿuÒ}P\¦ƒµÁ\cðB瀿Ù<æ¤JmÀQÛ‚µÝÌG0ÚÖ~Ì´Ó® ^ÚÊ 7Pz<ôØä^O$¾ðB°7âßyÇi}>Çúhc'|>ÄB'ÿ:8èƒæ<WCð¼‘2‚Mæ0,À—Ø€›oN¥X»×‚ÙêŒä£® w¹vÑ´kž þäʯÞHúËä¾H|å;‘ ñ×úÄúˆ›ð×ÄÁWH^: Âá4©.†x#=hÄ1æÓ, -GšrR·:"¥Öœ– `qó„'’ÏyƒqÅôÛ~ =õÏ@$=š &j‚µ Úw c!o&ƒ‡¨ 0 *áÀr4ßÙb÷YÈ0CÆFsÈ’,!M[t…=¥NàU»"µÉ)‡¼Áþά3`^DòÍ`0~í°Iê³ð±¤¡-Y©I‰|89Hž  #Dÿ:Øë#eDN3±aäþfÈŽ\E¢%ä©+‘!_q‘ „Uîà7xƒwÀÜŽ pN…‚}!Ìk‘Hþ) -Œ'Ñc´þ-uh“&éÝæW䑨¤/1Ï(ˆê£"B`÷pà÷´„È7Y®³ëke¸9òâ¬Ë¶E¶Ô™ù®TxAT€´æðGLroœàœùÂîŽcþ?šüK¢–ÞO~K¢ RÞSŸS?‘{i_â§cÓ#:¢u6á¼J‚UÙO‡ÒÅjoS„š£p³ -’í'r‚"DzR¿qÉÎ1QcÔç´ÖM#¼öø)'ÉØ¨ïXךäækF/û%m˜ÓG{Ï~B¡?dŒ%ÝKžˆ¿ËD¬.À±š¶­±võ¡X5êu3Qâiв`sl‰¶BÕE|çq•Üë³¢0H+«Œz/©‹}+ÜK~Í?Hâc½dŸæ¾`vóŸ1n ûèOÒÿ¤ ¥?dhø=L-ç6k”q“=N¹Æ™LÔRî -=d,×GžJœfb«‡ *`[”%¶‘íÆ¶¦¸h‹¥>U^ذ¢ló`æÊóô=Ì>þ¾Ô§ÜÃÂßÙ’G¬ó¿0odÞg>õ$Èn±Þ¤_ç¼ç_Iá\âŽ2ÎsÇ)º—pàÙFn£‡[lu4Äv·ÙØé?5–c5ñvw°Ö¿)ù eGõ+ ŸÊË™%5¼‡¢oD÷ÒHîò:2ïp»²~Léξɹ£¸ÆéÍéæÉΧ¾å}àâ°O¤2t.‘ñKõ °ÖC ‘7·™·†˜Ë:ßyã»Ã,´ ›ìÞÔÒ×Tñz·dÄüVG¹Ÿ[ʾ›µ=íVF½ø¦¸9óªðPÖiÇs.ñÏ)/ð®çãýšwšÿ"ç„`8ã˜P#ì~ä}bé©„C:1—J"ón!ú±“ȼõëÐè=çsSÈbMs´íËFêú¾])¶‰c~.SPnrn(ËW²«%3äç¤û§ÅíÊ“¢®üNáêcž‚Q¿ê[ñ ü øµø€ä=¿T›¢ ð,¦BBxäy³‚èÉ®Õhtž9Ñâeú±5hÑp[ÔŠþ²Ë“vÀ½jaÌ­J9åj©Šs© Tð}Þvi—¢.ë¸|oîÑÌCª#Òã‡$ -Û$·ŠZ¥½…-™н™¯2šdA£ìCª.þqÈ êþ{‰™¨'Þió:ÃÑ&šC ŽD,{Ú–°îÁ^¦ÿíú´è«;2(+r9gJ‹' -*¤GUÕY‡s”²[ó÷euî•-þFv½¤Aþ¸dOöse}ö¬Nñ6½Vñž¯ ð Ùâi(&ØAìÌ;}ì_;cä°›ñp‡ßüþï­‰sº»Ÿá£‘}¹V’t¦*›ÓY®´—–IVeï˯W6嵨÷ä~[T—sªd—âJiuÎýÒje¿j§òeÖåkq•ò@H[2YDÖ,%¨!æ¢i¥ÞäAGƒ®Fƒ>sÿ:jùàhìÚ[i~Wš¹¿ß#J:Q#g·Wå Ú*J¤-e•ÙÅ»”uMêjõÁâ*Ugi¥êrY…ª§¬Bý?u…z@Q®––«5B]üã ø;sÔýŸîúŠk:Ëâ~“#-‚€  £4)ÒA)¢Ô¡¦Zˆ ¡I -¤¡ -ˆ *HDPŠ¢]@?«®ŽÀ8*£ ~ÆYѳÿÝ}ÎÃ÷sÏïžsîÁvÞ:Â× {ˆ:,¥—{íþÙç²ý~'ÍøöÅ ×B½zj£Z«øÜƲԨ3ÅY Õù‚ò¼i%9Õ™Ùç²ÅÂö‘p0W(œ‹D/‹DË)¢ì·‰ÙÙïy’@4–!M?a{g6—-—KRï.›‘~°‘4à¤q·‡j4Úh¥™ëÙ^åß\“À­«:Y]ž_^š+(.*NË/¨Êå×g·ædˆûÅéâ)ñ±üÂŒ¼åÔŒ¼·ü ñŸ±’@Œ:ŽmÁC–ã–§yþSçnÂë~câó+™‡×Ôǯxï¹Þ`×Ýìq±é°_ýÙ8ΩZAdùÉô¸¢ -Ñ‘¼òÂTaiEfFñajÑÅœä¾Ü#Eã¹É%/„ÉÅKiG ß$¥¬ÄI±Ø;8®Œ‡Ò-88½ wà>\ÖÂ/é~½e*=3rpëÍAOƒþ^?Û¶¶[cK8£¶™Çþ¹!)¢èLj¬¸F˜”UŸ’^Y–‘\Q›Å/?/Š/¿œ[>–_ñL˜ðóRZbÙ›$þ‰•8I ›Á÷ÞrÔ«â¾¶oýØ{q{nvÒhã䘽Úõa½î†õ…^–k]g¨Oå¥hVñ…Ä0qÓј¬†ÌÄ´³âdÁ™Òô„ÚêL^M“0êTWöᚢõO„Ñ5Ki1Õo’xU+q’@6‡\ET)á I}îQFË£Êè×{*èÞ´!éæ¤Ý–þ17ݶaŸ½ ƒLçê¾jiO$3¯#>$«M•Úr,îÈ…œ#qÍÅ)ÑMUéç2CÎuç6Žd7Ím\N ;û–~æÏ8I «ý“jÐ÷ôû€TDôˆ×fl”»¦\µšoÓ,jo:” {åFøeõDzS{““zÒbb»²ã#; „¶Wå´×§jëHjNgv̦³Ú_ Ø-ï8çßó$t¬%dõòèi×F4;$îÝ‘FSÉh䑎Tïì^Å–8í¬›ö6©˜òÛ_p›í& £§ŽÆò‡ùÁ¼¡ÔðÃׄÑ!ƒqìŠDæÕú$ÿ«íß!c`Và7ð61 ÷=/°{5J’qP)‹^^ ¢Ù>º;JB·f6¢ágdt噡mÁB¾á±ƒFÕœ§AÑCƾìû,Ç´™POþÝhÞÄÀˆÉ6wBš(ˆ¯ˆò¯¡wðhãñԉ9mb%š~óc„ÏÈçPI _}8-…æÛ¤Ð«Ðè /οQÆÕý¡·¡òKrák{5áï.š©ËTƒÄå@ó˜%®uøR”=w1щµ˜ê°˜íá»XäE_¬¢Rè^K>KC ÷å˜× ÷Å/>î/×i’¼¬Eèþ%„Fûð¨o”€Zgˆ¨aAÕ¬©áË× HùkäÌU»-É«ÎÛãV½´"WýôCW9FœÕH³ Õ«€Õ”}¾«B[úZÁ~ÊZůµ³/9º}¼êôã§)g×OÿrvùôÁÙeíßN’ü‚õàfB½ƒ8Ô2ŽGu³R¨bE•|Õ ä|Õ#¥5“ã¯ÛlŽYwT [÷Ð`¯Ów®iù} ÓõùÆÓ£}xÏØãù=רý{©ÉPcæMæÎÐeáC–0cy^aÞ[I2Þ†P_B-7:=ƒPÙåÃ&\hŽ‚Î†80’‰€½dØÌWPÔhà¿ÍØØ'Ì bpÁN'ÈÔt„<­ƒP¦m§uì༮-tëÚÀõÝÖ0yª'I7Vÿ:V¡Òy„DË8t p|PÃGÁ."ôIL0“a€<7y€»¢+ДœÀ_ÙØ*ö¶ÅbTm€¯¶Ò·ZAÎV (Ùf§ÔM¡QÝÚ4L _ÃnHt©« ¡’ Ÿ#”Dd\(ãY Nðm" In`!í ¶2ÁQÖÜä¬"o¾d `n2ƒàM&¡`±Š† PÔ‡ÌÍ»!³”+iC­’4+kB»Du£Øý±d- Ä_F( -†ÎëTðn°àÚRûÁ¸Ì7X‚5Éì7óFp—ÖŠ´.0d´!PF8²;!\vÄÊj€@ndÊ©XNNÈ«ÀI‰€¡CЉø˜o€@+°öËÀ!W2°hJÄQf¼:düþ…šà{RçvƒO«>Ð/}Ðhc{€6mÔÇÆ@]Ĭ˜õ£ P¾˜eݼÁtó·7˜|‘|µÿŸÁÏ”A–$àØÊ×™ ÁT%à°ÔàOX黀™¯ »Áÿ¬>ø]4ß.#`\5ƨ øL™‚ϬÐ_šý­9ÐÖ,€ö—P×-¿RÁò ,?SÁü“ÿË@5–‚"°ÍIb-aŽdóV‚¦p£·'EåêBP™>0OïÿÐ]¦QM^[“T«§zYuQЪâÀ  Š ¢ Æ9! IÈ@¾ @ æ‘A@£‚(rUZëP´º¬½ºœE¯ -*Ȱïwû;ýñüÞï9û]笇ظmÞ@èXø  ªo#Dõûî‰/àÞúÁþa‹ÿ$vÒløî¿a,ø|ÂÂFk~¹`WÛeµ0½ã;xÛœ€·{!p .ÀáºB ²i+nü hkrx[6é¤Ïûá²?nnüÃÍ€QŸ'q–-–ý“[FöÖ8Äÿûhœ—=н€³Æøç€0È Dá Aåq1ßOö#pR=•³%€~ÈhÍþ@mß ä3@ê Òµ­@¼¦ez$‰|†eÐ[IZØ€ wÏsîü“˜*ÊCfýèVæ=zGÌÚ%v?íç&õçWú0ëæêeÖ ¡'öw³aO"¬ñw6êx’ïí ½ Ú Ã†9è<“³cÉXÎ^÷Oš×;-Ï÷U¢,øiBrÄâ3p÷Fòm^ýVì!æuv3û։ثÌó¼ËŒ>~/ãϸ‹Ì÷¼nÎólì$¥ƒ;C8ɬ™ û¬T7[àºÚ|©úõÙa•ä®› ÆÍN…!ßFº½Ë#{½4°ýiÅ!÷U‘¿!º¨_¥Ù”>‘‰ñoA%»‡WÏíæ‹;Ç9-<þ$ê`÷‹O²DfÞhl<º%nŠ|TQG€³ÐÐ ÿwNê::ô>rÐ~®uœ.ö›;ZºmÑ`Ù.×W&‚ç£<¦ßïAÈԄȾD ¾W‘Aí–ç3ÏJJb;DÕ|³ IØÎo·òÎKŽñ®I›yO¥ÍÂḑ%¦^×H>ˆJÿŠ­’Ž3*dSä -Ù4ÉÀpµ1:?õ­,Ôy‹Ð7«ÜËásõ†Ùïëœ_ÔïpyP]y«”êÛWÀÙ~1GyF¯ÀŸÔ¦ÐŽ'XGÔùÜE‰ 6¡&¾ZÖ,«”žL(“ô %Ò;HiÂ{I 2Â+F,,ò…jB&)Ö&:W‚ún*J.º“âe¶ÓWÙ×{;¾iÚ4ïIsÈ’ßëö®¸VIö¹T|Ö(ˆ0gËñ-ú$Z“.U—’íN2 +TUñ%ʹ iC -nE>rSiT½‘ÕŸâòÕŸÙyê zžú Õóíß¾§C7uÞ27Û‰Z»¡¦5/[|çüÙ²íÛ[‘Wjˆ»Ê™ÛN™ø»[ó¥QMÙ*jm†–U•–É-M-š4åcR<7±U‘¥>«4¨¯©2“_%di> -35£±™šñèLÍÝÀA»€,²tÔy ]l ÊÕÆrx¹í»OûgæõŽwÛ‚_;ñCO=aCçAÆÖ¶2îîfS<®Ö¨ TæhX%™ÜB}¾0/­D’¥=$×§UèR:U©)?«RµÏîƒH§û‹«ÓŽ1tÚñhkíuNšãÀ¨ùÎf¤ÙÍæõ ÛGkfõ›·,ºÚºkyWSÔ:s-=èhUì®úRÑþ*S¥¤ ‰Y˜—››#0d¤e”§è›Ié§Uêô+*•þ±"É0$NÊá%ëǘÉúq†5€ö@:g6š£t1f¦î_˜­Km^t¸ÛÜ?çép½#`á¥áË:á¼7R·4Ô²wVW °¥e2ra±š‘S¤Í(Èhó‹â“r+åªìF$!ˬ”eõª¤Ù”HîP<’3ÂSd[XŠì1¦5@€î ÙÙòæc bf¢qæÝ‰Å˜Ç]K0·»WÚ÷Û4¿ët˜›¹ »öÈQrÀ¡FVXY_aµ„˜S©ŒÖ—¥pRK q‰Œb¤¨\&-¬O´+„=JAÁ]¥Ø4_8Ì—-,‰qŒi £çOýÆ -œ1PíŒ=ö fàÌ<Ìý^gÌKv½üœ;φº¶žÚ»úp;iSe #ÔÔÌÝ“Û &èëZJMrŒú`:/¡2O_^*”ÕÊy¥ÇNi7Â.»ð*Åü²a^\‰…W<Æ´HÑ=¤;aà€f¤~.æaûl̽ós0ýW0}W–Ûuõú8™/l_Út.Ò³ºƒàWl¦ÿî:jòÊÃ~IH  ²("›ì ;ÃBBB’@aG(‹¢âVÔ©#.­u¨Sw -µ,ƒ0Et«¢VEq=žc= ¨£•bÁʎϼÇOýùð;ç~¹çýŸûÜóžû|}&G°³±P¶µn­jãéÍé¥'ÿ’Stbo~~Í¢œêêÕÕ¥šêÎҴ껥? eUåeÏÌ>6‘® ÖS9ìšG¦‘g?2ɃVéù™Env“KÝ´³7üŒ®r­k. Ü^H -Ø÷/uÄÎsYq[[ 7üsbMóFuaÓ¶Œ¼ÆÝ9Y ßækê« -Uõ EÉõÅÊúÞ┆¡uý9©§&ÓÓNMitÁæùû ÉÀ÷tò°‘InŸ3 ×.3ÉÅ^9w©^SǰöV¸Õ‘|§ýפœŠ®”Э3¢7\X%XÓQ"ù¼½L‘{~«:ãÜ®ôԶʬä¶ã¹ò³õy²³í«¤m½«dmC9ò³Š–éTeóŒZl7$chäI-Üif+í ÒqAÎÞ3$M‘SØÌc÷CVöÅÚïê{n»­ ØÐ­ _}#/¦àzq|εõâô«åIª«JÅ•J•¬«*-±«A#êêHO¸r—ò{š¸kJ•xñ£BrA7좑þ£„Ü­£‘«­tÒ~NZn2Iý##r¢ß’ëw×ÿöY°Éî§QÖÛž7<–{—7óú…”›{c··ôD¥­ÜÑfå†k”nNS¦2ØÓKÈ¿R2LH.$ &zrX艨³àÃY? -^Œø3C±Â \–?bY>.‡Ôˆd#hŒÝkìŒây˰q¾¾œo‡oLlqÄd)jMlpÆÔ-:¡2Ø{›-O)zIHÆ8!J"†‰Ã½HXÑ>ƒ--Ît?xÒ9ðÑg#ˆá†0† ¸Œeˆe:@À´ƒ„i¥5Ò !—e‰–96±Ì°Ãpõ\€C†¦8ªâÝhˆñÕ‡" `BÊ‚,ÚR±)ÓÌ!*²BÂk÷Ø þ;[ð«íÁ¯s@\‹#âÎ/ï’x7)÷Á¤üîÞ{Ê4æ<ǃ“.Ÿfàqèqô!ócB±‚E”1ä"SÈTæ,Bâ¦%WØ"á€=„UŽœ^Ájo›3âv¡›+øw)ýnà¿¡ü×ü) ->ùH™áÃmŠW]  fà{Ó!ñև‡‰” T\c¤M¡PšC¾j1de6î°Câß!>êQ­ D®Hhuƒ°Ã«Þñ„à)å5‚Qʤðš£L ÀžÀãƒîº@èJƒM‡Œ­”å ¤° ‰0†&Þj¹TÙ‹‘¼ÖŠíHúÚ ²C®Ö¸CRç‰Ä6Äç½ ¾ä Q÷rˆqðÒ ZŸÙ„qß鄾 ðý gLïQغ|šAìA‡ÒCil2} fŒlž)2¥HÏ ®Y‰Ô_8!e·+”< 8î…¤“Ë!û‡¤­¾vúCòKÀÇľÀ™Ä Iñpðñ‡1ñ\Èh"G$ð–ÂwH -Ž.Hp¡AêF‡Ú•ŽLwò8(Xa„‚hSä‹-‘§^‚ìdltf‡'Ôß,ÿ˜rØw6¹&`FQ4%o™L::!»öAz;ü½ô׈QÉ›•o%ï¹CÒ¹ˆ7I{-GÈKŸ+௠DÎ4ÈéÐPý*êy…ÞL”a5×%B )mPëø1o­ûLö6ïÉŒ=~ãi‚Þ«…Ž¥Ô†&7rG­Qoå1ÃI×yC²‡üWÒWñ/dcüùlL¿Üg)¢BèCBt˜š!Å‘Ž,ªï:飔Êc}€!Ê"LPÆ·À:™ÍÌš §ñ¢bϱüM¾ïrþü6s_øæ`äkõ÷ÿc»Nƒš¼Ö8€Ÿ„MEqk­K©ëXܪêŠ {HÈ’„ì @ bØ ›ÙB-@"D@ÙD– -A°^µ£VP¯ÖöÖ¶ö^µuãÜ×ïùð›9ßž3çù¿ïÌßç)­1à ÅŒyDêÆ>$ …< Ýù‘ø‹ô{“ú>pš}n0¡—• 1á[ n32Þ+øÔù»$lw€Ê}  êðb¨:¹ò»î•‚±ùX‘Û¯QòƒÏÄ)Gó²¼rŠüï…U`î0kq·è&üL¨…xƒÖO𤓭Ôû”1ê ò÷Œ·¸Q f#ôêç@O[ a#²ù’O}k½Tmµ‡ê=Ž0ÍÃù}Ú‰ÿÓ­}‘BÝüs"o×£X©Ç}i¢ç¿$ß6h’WŒ»ÆÑÆØõäѰVê0³+ô -sˆ~™1Íèe9•X”E«T2KãËØÅò^Qì9A~ÌE‘6fL”óH˜ÿŠ•¯ø@ËS@‚V±¶@ö((Cú^ò -dŸ!o5ê•Þõ¼n3úaÓNû™úÃKǪý]/—wtë˜ÍùüãÍ9Q~uqÁŠXžšN-Qå1‹’J8yŠj^NB³03¾Sœ?*ÖÄ?g$ý—“©zš©„$ÎŽä é{êåæ­°ôsðòìð羚P?¶l³›lúÎe¤Öw]O~{Gý€IÇ=VŸákȉŔe&ti§¨§r˜¹©:Nf²ž§Q…©J‹8Y9$Q)ïJRR^†«ÕïèêÔy²:âm<$ò¥H–X¸|¨\^Ô¯s­«Àæ­v-îK.O®íªÁ}ݪ§í7–rŽVëÄ>åÑVAÈÏN¥fgf1ÒÓ‹ØjMOuª^˜¨>/NPJâÔ·$Š´?¸IéïIéóeÄÛH\Ì@è\Àkƒ ø¥ÙÜëX -¦:¶ GÛ.î5y¯ioÀnm>KÙW£g®(žÔKƒò‹âñÙÉ”4m#%'Ÿ”]ÆÏ¬Äf´‰eýiÆ´$:û7ž<ç-3.û#%. lb$ ŸzoöðæÌb0[·Ü=·Üê\&:7¡;ö/êj;ñE« ³¥¾¼W_v¨¤Šï•_U*Ñ”(ÉɧÓèŠB-K^P•åDjωÄÚ^±H;)–äÿÊ‹*|Ë”|¤Jó Á‰d!ÙÉÂ"ð¤ÒÜ5.7Í ÁäEg0ri=ª·{ßÂvËñUæÀMÕ-ÄÝ¥ ‚:®gvÄ_cˆÁªô‰Ä„r5-¦4‡uFÇ—xüb“€«»$ä[…Ü’ç\Aé?LQÉGª¨l2d§‚¿ŠÀ=ƒ˜ivÖ'pµoèÿÕٻǩåâÑ•µü7”·ãwµ†Ì6qŽišD>ªYP|]>º&…Q*4…q«ô–¾™ËÔwóèú £ê‡mø›Á©š§„WB‚-P޼A†˜;ƒ?ÔÚk«#étƒö {h5h»âæÐ0ph¹¾×ÇUw·-§“ú¦ƒuHy^à×å/;,1©ðüæ -»±0”i¬dÒŒ,б‹M6޳ÉOÃhoht#$1êaˆ-0Ñ ¼Ôp·€ÉF;0rÞô]²Ãö mì3`Ûfouw)ö^£ÄlÑô“w+{™ßÊ/ñŽJ»#¼E]±þÜ J Ë’BïÈ'Q:*(Äv#-¤½3×>ŽxJ÷¿!ÛaÉ ƒm©Ì`ú,ß· AŸ ,ýhÐrÕÔO-†©Íè’ëûi¯ÿ~%æs0ɇv9-€t9ƒ(ÇaŒxÌ@!p`ñ9¿Á÷à l °åÏ,n—0a ßŒ¤û™†ì@Õ”Ývº;®(ííÝŽš[G–%Ýô];ƒÛ1MÛοÁÞÞ Oʾ£\O%ö¾(wà¡Æ‹¥¼°fÂdlà‰fGì§ÚmÚršÞŸ~‹nÐü^BL Ѿ"4› I\‰ðdLC÷pfb˜ˆcù!™Åƒ5R–lOhÙî0²ÝP¡E‘ój9Nhà8b#¶ØMÀN› ŠdBËB¥ˆgC™b…ÔrÍDd9CVéI½;Äö„hód¤ïðFúoÛ¸¶Oðõí®ú@p‹ê£žSc>âÿÑÿk“˜Î á³E©¦³¡N²C¶Øªì‰P¸@^ᆌE­œ égÞlçBòÕˆ÷ù@|Ø¢<ˆ:©ËS!ê¦RƒÔ¨/Doé/þG Þ[1|lùï ²h””fº;èÓ U:A=Ó*Ë$(j=‘õ'oÈ×s‘Ù背f2öN…ì /d~~C]ô‡ô'ê^¤O©aêMÀ)^ÉàÿR¿q|m$‚ y$ êr¢ÙÈ‹³ƒAàC–rr] 3»CSãõ2.TkyPþÅŠ~ÈÚ€¬¶@ÈA~Šú6˜¶ÛdþJõ‡¾Î -Ï|:’‰Ða9‚‡äz‘…[ gBÆ„>”c8¦i¦8 0à ù:W‹=7ß9õ>Эòƒvc4Û‚ nAöÞP¨†Au,â­òläååÈ—Š›Q£Š¾è!ÅïÑ¿)^E*ùT…ð'*„ d#d@ýGÑ4ÁL‚X( aaV4æD{˜%1K튢|OÌå"¿ÖÆßæ­ y³%ü¥~Gä¸nwô¨v?DsdÚúTì õ…¸ß³Š–ý`F¿êùŒ¾ì—ñ5ˆ} EÌ=¢zõˆ°òºƒ&L´ã•°`‰dcn¼=æ -'¢\áŠÙyž0—ù¼.©ö+\2lZñbæ†˜ç†ÆØg¹MñOs¾NÐÿ=ɪíHéÓžK{¤¹*¸¯¾+¼£y&¸­O¹™ƒ„î\Äýœ‡ØÄÜ0þTAt¾LÓÞiñ£½/Œ±v¨NuÄ‚ —7óužcżsæšëÂJ>æ?.\ÿ0sÒ}ãöÔ»y»ÒÍmßÎ9,¹©?-íÖwÉnèog\Ó?‘]Í}oDÚå|$u™0ãRâ.`ú» ¡ïÏ÷aÂÌ¥ùS™¨¡÷¨á .Éá͇瑲=ž/4ñúç[‚–/Œº[¶$þö¬•É¿¯Ü(Ü*ºnÚ)ý1wÆ÷ÆòËÆŽ¬K†Nåw†ëÊæ=RvÎÎ<[ñ7Eœ)FÊé$œ²zºƒ".Ú5«é,uL,¦÷Xo?²Tè4¸8˽o‘Á§÷ýÒžªù17*êfühiH½Rö‰èRéFé…’ÆÌóEMŠs…-ª3‡Ô§L§4'L]Úc¦;Ú£ϳۋßd)…øˆ‚”!…J~r}˜Eóçý§oÑY–Ð{|Æ_Ëy¾"űo¹Ìíî2ý”îú¢àkµsù—«k.Ì_œÖY±B|Ʋ.ãäìÍŠcæíªöÒ/5‡KöëÍ9P|>wñϹ­%úVóKeëd´Z n)‡€J{Œ4·Œæ/ ÷#/x ¬ -b}Íî_›`ß»FìܽRÍý¡Á|±~vLç‡U §kêÇ,“´W®–ªøTy |«zßœº–Ù_çì);”÷•ùŒq—ùc“ù‘¡É2ªi®@Vs%dM•}Q á»OsçÐüÚ·–ÐYVp¯Öø3? c>ØËéÙ tº¶Få}i¥1è\CiÌÉÅóÚëÞú`±´márùÞkU{*7iwÍû<狹_æí(o3~n9‘ßhé2mµôšþ:oHßXÕ¶ÿf»>£šLÓ0?Iè‚‚Ž8X€UA)Ò“`B(@¡(ˆ =j¨ÁjèAš@PÔ((²2눸zÖQÇ 3{fËŠ{vV|÷ýçÇõû>_îçûNnü…MF˜»*Ä1& 8¼µxxóæot~ |ªÚNøµÎŒð¦ÑŠ´Xç´áQuëTYÙ­î‘üÓö9É.}|¾gWF>UžVJoO©ò“&51›/×'ô…Ö&ÜàÔÄßçTÅ¿ -«NZ ’¤ ßÚ4D•¤#/ÌC -ÇÏ¿+2T¾Vk·Âró·ðRº‡¸ÐtLg¦–¼åveàî뢰%1Gz ;ry³r)R~1½)£Ü¿>½ŽY“ÚÆªLéa‹“¯…‰’&ÃK“—ÂE©¿±Ä¼u¿òsèæ->‡¼T¡ÜC"Þ{Ù˜p#¬WÀ¿7ÁÛ Æ°ØnAœ“:hO6zIüw V°­zE'í:JâÛ Ó9Uù¾ç*Τ‰%ÁIeÒÐQ'Nt=ü´hû‰/þÄL¬@Œ¤ -ä“TލªPî![ –˞ד`¡]tkÀô ¸5°®]µRëíwÒ—]&okîô7—ÈBŠÛ£ìK¤qÎù-IÙM”Œ†­7Þ9±'ÕýLW–÷©ÎBŸÈŽrß0y£¨L, -’M1e?0Y²~¡rDãÈ…#CdUˆðVð—€&z 0Œ·_ÿ@Ç]=h»»‹ÐxÇV³râ¸pÜÇD0`Æ¿ÎÞŸ:esöZœÃé¡dçè¾÷jûJ•¥h *d¾þŠAC1…½bø)VO(¾P˜ýÈ óTµŠ{økÀœànÀÞ~ýx{Éo¡eFêg·CåÌ5á='ݼioãs“~¦)wC,âïDì¹k5‘t$ìÖ9Ç›— ñ27ÿñzOß1™7ml€â36Eñÿ|blÕ‹6Š<è£È•~WõN°P0-¸Ñ  Âù7š§HP5¯ ¢#(^ØCÌ}l¯•ñgIó´­q‚LOÎ…›sž²d?8{ øÏ&`V`ǘ9Ðfê©32gòÌ ‹÷ì´‹×ìï™ÎÞ÷‘#ù:JžþÚR%Àì€ñn€+Wqþ œ÷gåC5(~¢Ù/õÿr!õ…µZ í˜çƒÈçÆœ%¶ ëYôÎÀgñf~ÏÒ÷Ðóöù,ž·"/Öò|&³öx6dã¶tÏÖméG·ÅU·§ëÖnߣCnO¾6ßp»`  sçãýY9P´@þ -HUÂYå.B¬ò )RyL“£ôØÀRÒ7*ƒ71”[hÊÓßP•)ÛÉÊ,SOeÉNweÍn×å6s—e……óÏw,œ~^´p\þ·…ãO¿›;¾CdòÀÐe€.|‡-¸ƒŠ{2ž!aYb3‚¨÷»œ÷V¤à÷öêþï]5é+ꊟ®÷J¨¾çJ”Ûê™MÇWÓŒœVóŒ}(Ûâð¡ñûÛì>o;¼6kb»öÎÄöãÊ6Û•/úÊî ßa+~*§pþ<@úÀéבk:Àù¼‚>ï ø}ÞK¤­ÛÈëŽêëî®_|4¿økC¡:(rƒ:£k‹Òô!þT¾Ñ -5ìCÝ–è†á^ôûÑpZÅ>}¥w ½Žóñ <H[ˆÅùì_˜HȨhÁ ™\Ñ~¢:L:ŠIvÈMÍQÔ"†º -Ö°Dáš{PŒ¦9JÖÚr´w¢2í¨IÇué|‹†u¶£© &è9öæ+møªïàü‡8ÿ)@ Îg)èk¤îHœ‘EÛ vÈœ`ö­ Ñòÿd×YTÓgðþˆŠì;Av‘5&@ $$$6!²)6Ù7Ù, à€ŠˆE*n8R†A[EQÇ}k=Å¥ã©ÓŠÖ­®Uq‘Ê3_Û™›xñ;¹ÈÅóæ}¿srpig0 b’òÉ´A$Ó -ñZHÕ2E¶¶1Ê´ ±JÛYèeéc€¥‡ƒÄÈ'6ŒPTÝ9’•¢ÒI~Ü’ý”¢‚¦(ʵ€ì“ÌáFæpÃö [83ؘǰ„'à hðiCÓÑzˆ¤u±ˆžŽ%ÌiÈcj¡’ÉÄ&v&½ÄÎO€çNÃ߃†Ðƒ‰`ÂO B JðÏ€_¶.xzà®ÖÇ‚6p¶‚³“0‚Ï~b˜8A\4†ÏuâñœxgÎÿ3üƒOü>CÀ|Áó™x\-ˆ…,‡é@˜0êYà—êï޾ë á»Ù¼íÆàõ{MÀ;H3÷œ)¸Wˆ;¦à=%ÞÉwøÃ”/Œ§x0þȃ‘&ø’|á<BæÎD¨d,Hå:ÄÏ„(}‚Š XiˆÀctš `«)ø»ÍÀ$˜ƒÄþ§‰ËÄ- ø?&^[€?iñæˆ >ÌÞÿÉTüÝhˆ\H>æÊD¸§”þ,„Ku ™ YªBò !®2†¨ÉÁmæî¶@Т߽V"þi áEkn°§Ù“ÂWì÷Âì·B°_ a5ËW²ÐÁ\R'’ïH#’ôݘùZXÈc!F¤ƒ¨](ë#,Ëò2S„Ö›C¶ÎÒMÖÙʆd·Í”dpöGñÛßÄ#vÄgí&ÄWì߉îÚ‹ux!žpx.†ÝS lŸH`ó8ìG!°Ö„`gr{Qv4bÉgÙ…Š£D¡)t±0Þ1&ˆ*4ŸŠ¨±š _c3ÖfûE·ý;ùv‡7¡{œ^‡îu~%;4ç¥ì„Ë é·sŸIÇ\IŸ¹>½Ÿ{7s~–Ãé¶c -ØÝRÀV$4”³ˆeÓPͦ‘2‡‰4/-¤ñ§!E:‹£ >&¦š¾Ïµ|3Soÿ"j­ÓóˆÎ9O•=®Ãw¹ý60ÿâ÷åG=ïÊ/xß‘ßôþIþÄûÅ;ïÃávM ×+˜3 çï"ᨠ¡¶ D[1`Á@*é»ä&™ä™äËD3>¤+õߤ©L_$«­ŸªŠì~YTã|?®Ñõç˜V÷±è.¯Ÿ¢z}nFþ•s#bˆ{=b˜wUyÚwTyÕï²ò¡ß¥ˆ7Ü Qð>³ áv&®§cᢠa$7ŽôÞdÒ;3,È!7É#÷È÷ÑžÈLÏ Õ{šgú c ûNÚr‡“ËçÞPÕ¹_Khößȹ×Íû&v‡ß……ýüs1ûÎD <}Ip"ú¶àXÌË€‘Øß|ăs8žÃ‰p'Ü4!â÷ι„t¾l2K %Îôd©s¼ÔoÚãbɬ»Ñ&?æ$³¯«3¾K/šw)­Úó|ò*Ι¤µ¼“ªÿã‰=GíŽ :PôuÜiñ¸ïÅû㟈ö&LîUÁw( œ¡dx ¥ÀC¢Iîb’¿Ì€B¾ …R2Ó -{Æ›*WúI5GûneÐŒ›eJãÑ¢Dö¥åéNg³óÜN¨Ë½Ž¦¯ä^Òäw0µ5ð@r—p_R¯hhñÉ j_Ȁ꘴?ñ²lwâÙî¤wÁ}©èKƒoßpú–Â[bÉ RH~6é[EFV˜Q“µlƯuNŒûõîÌ×èŒV+Œ.”ÇYŸ*Ns:šŸív(·Èû@VoŸºž?˜Ñ,øréÆà=Kº%}©;¥;RC·¥ Ë·¦œWô$ßVô¤‡ô¤CØ“ÿ/ÔàMˆ7&wУ°œ(#³ÔQoÌ©G³©±¿¸0®6úN»X'3åZõñˆõÕÈ–¬gaks&%ë–C¸>üõä_GTä·«Iß+&ªgR¯VéRZfQ·6èQ£mvŒó­>¬ã-"ýƒ–{ë9ôצÍÛU™åÕ[^ÈÛRZÁï*^ÔQØ$Þ¿A¶.o³¢9wGxSîß#VçŒD5ä\ŽjÈ}¤\?!m,DPSÖÃO’È;ÈÒ!oQ‡z½R‡[3º¹~:õ¯ö™Ô7³©S½´·éío³hŒ³ßUŸâÚ»RíÕ]Çí\QÊo+¯®/]%n.Y'k*Ú¤XU¸MYW0Y›?U“)ººàaDmñ{Ùg¥Õ•AP_¾&¤ýç²(T°¨ûõLêf ‹ºÖ¦CnšAÛlMÛä¡õU»`Ö`«Ü|wóB»mMIs»2<;?Ëån¨-ö_[])XSY/^]Ñ,­+oWÔ–~¡¬*é¬(9]Vrž¸UQþ.´rÄÕ•ÖT¯ iääiQo«hjl5M]_§M:Ï4êÒç:ÔÉ-–Ôp÷|æ¾®ÝþöP³í­Ñ¶[ZT.k–zlX½ ¥¡Ð¿©®Bаò¿l×iT“Wðç}" a‰Q‹(”B @Ø‘m( ‘‚¢€"Š‚"PYdYЍˆ(.PÅjg: eÚñLm 8§§ËÈ¢õTEg®Ç™3=a>üÎýð~øßsïÿžó>9 -½f–ûí?pfÃÞŒ¶ Ý=!)÷B’3&‚wüÝ7- •éÙèºï:«Ã­ÀT~!3ß·…Œ”1áó ײ ¿~\­·btžqÒj©ñZVWdx¢,Ò¬¬d‹°èX‚]~a²ãáüt—¬¼,÷ŒÃù^é¹Ç}w:œ}.()»;x{öÄìB’ržû%FeÊT¤A¹:Œ']Hxœ ðuE:À€áÓL8Ë„ëM|èj2§Û$ì†Z%ÿä©ÀÕåÕá&E•›­óËâŹ¥;¥™ÅiÎûtK+ÌóL)(ñI:Z㟘ןח7¼5ïQp|þo~ ¨Ü^ˆŠ¤B”«ÃD&¼Øð}€ê8™ûjh¸]Ï€Í4\nåAG« ÝÔb¯yºÑWYïoP|&lmþ©M–95q¢ƒU;öUì–í.ÏpÝu<×c{i‘÷GÅÕ~[‹bŠ.n.Š.zSüÔ7¶dAW‚Šm¥(W‡I?eü½àÏ•@:@ÁÍF -ºÛ:Îë@Ëycª¶C̪nsÕ-9竟ßj”s6Êü@}¬pomâúÔÓ)Ò¤“ûä 5‡q'>Vn©ªôÞTÕàUyÁ?²²? ¢òë€ÈªŸ«æ=¢«Ñus5:«Ã€‰r%÷jú®œ8OfŸæ.-¨»lHè²a–^tætz/ÏéZ} =b]Zëf«ä–xÛÍ;íã÷:ƞ͒G7(¢êË=Âëê½Âê:}Bëú|BêFÉ:íV7ç^‡òˆ:”©{–Nºxàó -€ÛµW›:?y›OæÏ^T]ׇÒ^+FÁ5);§Çƒ—ѽAO×ïﺴÉ4ñBœU\çÛ˜ó{ì>ìÈ”F|rTÖ^æÒ^çØvÞ= ­Ï# }Ô# mJØ6'jE§àV”ªû‘ÜÃHÀ]r×.’;h¹HæO2ÿ•õÓpl`)0¥²?µcíïWh§öù-KºjðѨ÷c¯ÇšD÷&Zn¼–*øàêQèÕ<»ÀžãÿžZGß+2ïž[2¯žQ™÷•)'Ÿî9‰O7Úû^F;uß‘.> gÐ_ÐEî µ“ÌŸdþ+¿?Ä€¬ûÚpàþØ{߆‘ü™L3ñž—NÜpóp„~ÔÝÃð; Æ¡wRL‚†2,†ŽXû– -½km•ƒb÷Á>±ûÐßÄî·§Eîó¶îŸ¢ !T§*Ö„ðñ!ô‰>±¦üž8Ð^“®LI Ådð—©lÙT,Çq*‰+™N×¶›>¬³~ºLW4Ó g3s‰'|:DŒñÓÏx‚Éžà ê©»ÔÐDÞa5é`áÉ¿°‹äÇþ | è9ü_òÁû•!(_™SŠW"ÚyÖ‘vœuc8¼öaÚ½Ö½‰Ô°y³…e=·c‰å\º¦ù\Ût¾‚m2ßÄY·ÐÍY»pOË'´ŒçãÍÍ-ÒBÞa éàÇýÿËßò-ÀŸÆ|P¢&(Pœq%HÑìÑ’¡ˆ¢”¶BWÚ=iS `¬Å0†F3 1¹ -Ó˜+ñ°Æ{X®±YËñ2kY|üu‘S—Žõ‘þ}ö.?æ@È€çOòßä2ÁµÁù``AöaŠæ”1 -©5¸žZ…ŽÔJTP+Л^†4#h=Œ¥upÍÅ,†–08XKt0ØØËÐÄ»‹“75LòU$ÿ;€àxü ™#€€0Ç%°uÀ—ÂjÔ}4„åd/KÑ ôP@é ˜ÒF)ÅAWJ½) ¦(Œ¢ˆt"¬ ê‰öEÐÆ–F;R!Ž"²Jh¯`¢8€…¶—  A­ÒÙhy„ƒǵÐâÑH´sÑüqè'î*.ZŒ‘o¿ω7rÐò,þÉ–XÑèD8ÛÐ(³g £œ‰?ÚE,Añ66Úîá 0G …Å\œ êµQÐJ\ ®·´ÑúY¿ ?ψ×r„Èÿ/j©ÃõÖ:YÐèB(È>\E t‘1QîÍB§0M”ƲÑ!Y í2¹¸¾PÅäèÏͺ(îÐEQÑK è¡èñ ñ£ŠgôijºoĨ;K¼£ÎËÿx¡,(”›RèfB£Òœ0ÐSÊD% Ý‚5Ñ%šƒÎ;¸è´_ótÉ߸Jkx(i Z—¢äÑÃG‡>b˜áÏ9<æÏJ¦ø/$/ùÏ%È&Á¥Oß’"oæ½?B§·ùÆzQèóo®Ë3ªÉ4 Ãoh""£‰ Ušt %„„ÞB 5@B ‘¦TYëX8¸VÔ;zгë¨cûXÈè¨<ûÎ?—×ù~ÞÏyïûûqY«Aè:5[¯¡tMÞ¨ ¬D`f/†ðð¯^ 4…>ÐÚ üz¾ù }¥Ž­ú‹zÈø3uÒøõŒñ<õ -qŽz—øŽú’øš:O|Iãç~`üÌV=¥Ñ.hÖ`™ t "ð7ÊN ¢qQTMˆ Ö†›—AH†ç¯ü$2øÄª3šl5žcî ~è[ý>`Äô-cŸéÆÑ5¯éSfÐÿeþ‚~Ëü ý¹Å#Æœù}Ƃٽ0½Ã“ÛL þ— Æ‹†B°çF cJ€øµjˆûHÀ}Ä1—@LIJÏQÉz#rô߇¯zVMü#¤Áôŕٳàn‹'A–¿³F­²­}xÜæ·ÀiÛ»×íþøÄn–õÁæzЂõL0X^ÛæW6ÀšË!`º`áÜðU8߉øŽ4|ï3¤þ-¦5Ÿªó.!qƒ§1ÄGQkîGÊ,î†7[ßÞ¸ÝæVØN»ÙÐa‡¡{×ýrÔq&ä¤ÓÕËΗBî;_ yë4öÕáìF°=Ö§#ÀòTX`Ì¿6àÜMØ{ãW"H7BÀÁdÛ²]Ôæ²¼5_g.}ÊŽÖ{šføk"Ïäf|±ùõØ*ëk1õvW¢Z~Þ´Ãñbä.çó‘#®ç"¸ Ÿt?>í1>Kúgø ÉÈO.Qàx,ìÅ€ÍѰ^ „`3ÎÿÛù8úxÆØ»,ó|Â+>Iýq®ÿ’»Üð峜DÃöÓK©–’ËmÎ%Ö8œŽopšŠks=±¹Ë}2f€43J>}ØópôIÊ¡¨«^¢~÷Ú3GÞ ®ãñà4žÛÅ@„‚xì{ìåØ;W`ç2@_ŠV£·BkÂc¡³Ú"_Íù!º—y±†ç³Ù¦§9<«ŸØB»É4±ã±™Ëáä&÷C‰íäý ½ž{ㇽÆâ÷{ô‰»HŽ»GŒç5˜¸@L×pH‡Å@~ƒ$ì{[0||GÑ -ô±Ô=/7A÷*l 7Ê)—ŠYºçQ†SüÓÉœ,«#Ùö9eNû2$ncìzÒ?ÒZ×ïNÝá5˜ÒïÓŸzw, –;XýȒpÖð>úIóÀ«–dY>¸-’–á-Ð\!B÷EÝ”j oÕBWÚhº‘ˆN(œÔŽÈ}µ÷É‚WŽÔDû+“,z*2m;ÊxŽª’B·a¹±°†"4øÖå«hR~/C’·‡)Î;ªțf•çÝc‰ò?ø‹à]Y뫊À]²HÑÆ[@èY1B¿Vaß©SG—šèB³:Ùj„&ZÔ4y/Ù#g­¨‹$öHÌ;$l›61×±¹¢Àµ¡¬”TW*¡H‹ë}$B%M\ÔÍ(/ÜÍ,üÈ -Î n½§—ƒOY x–—‚Çb íï- ô;ßÍj„®ÊÕp¾:£ÔDÇUè°ÊŽ0®ô\2ÔÌÔÛÙ¾ªckœY›,ͦ©&k\Âw‘U“ªEbÏÊŠ:Ÿòò¿’²NFQé³ ä ‹_r*(¯äVP~é[† |Á·°(B0ßl5ôg>B¿U t]†«‘€Î*Õщvut´c%Úß±–0ÒNÖêkc,ïl 3R5n^ÓÔb]¿•ãP+Ëu‘H‹ûd÷¥³³|&3ùeæûþÞ#3ï¾0ÞÊÊx«’=©öKvçH–å»ÏÛUì=kç¿i;m_’ë0õptò¡n.ðÈ>¸Ä'³n…_Zmé}íWŠÄÚ -¥®¶>H[Û4î@o¶Æ¤ÐÕ`€®ýGºLspf=À1Zƒýe_ïØLýRsŸî`6ó`aË(˜ßâËÌnQ±§5Çñ ›õ¢üSÙ≧òí²›Š3›æJÓ¾ýÐUÿí_Ü×zŽküÒ;¾qolc½oLcéó9aò‰9ŽÞ1 è5Rk)íC:Ì{ ÀPðõWžXÒÂÀÜó\(n·†¢vW(hdòÚDzsÛµÜñíé‚ ãDQª±Ð:Ù8[¢3.²O0–8ĵ}êݶYÙ¶G¦1Ö»Œ5]ƶõ¹D´š\"ΡKø÷Ô¼‰ÎÁ€Ê -€­Õ§þ*ê/= 0· ð"&\BÖ¿l!£ÃÒ:‚!¥#š•Ô‘ÄÖufq:'óâ:§ ¢;#;?²ŒèüØ:¬k£XݵÛ&´ë°$äF«$¤«WÜ1h|í‚~ú£zó =¸}?ÀúÃÔo¤þê¦þ çô? ±Û´ÝnÐ3b{˜¨ÞXFӛ̊èÏV÷åsBû¦sƒû>à)ûKøýk÷ þ÷«„~÷…ò×…òþÇByï ߎôyVüãÀÇæ÷?Kýv€‚Ë@ïx ö¡D?•€æ™ Ÿù€ú¹BžG0ÊqLà‹dÆ0‹%Ìcù Mc{ ½ÏöxùgŽûËu7Óv®‹©†+{uš+}u‹+5=åJ‡Þpñ_›ÏÀA€ÕÔ_Fý9¦\Èèw`l7@Ø0èÊJt€tôT€ªa4F1n¨ed˜Æ8ãÆ Yö8Ÿe‹bI°”eƒÛI Û›É52@¿csÀ'´ÿ?2÷ þ5€ôŸâîP»@ñ` ²ÀEÔ”PÓ\ÑœÑ Ðì1l1 l0¬1‘±ÄLFˆ“>Îb¸¸”aã§ ƒûÀcäú/ç£ßjÊ׃Êw‘JRCŽ“ä,¹Hn‘¯åƒ“ßï‚!? ’ÿ†šÛž Fz0åEüY¥f¡&žƒá\TOáaÈl>/ r¥*× Qù…ÛD¨('U¤Ž³DE³åï -£å°¢Óò¥¢Oô\ù\ôDù»è‘EÉùM‰Â‘0ŒúQn ƹ2ïÎà8_BëÍÆX=£&òP3]€ …^"°O,ߨ×[½VeõJ½ÓÚ¤ª°RU‹UGÄ/Tâgªs⧪+⇪nñ}õSq¯ú¸[â{j´¾öVwF@¹/ePëÄ`}¦Ð\¤Ðzèi=u\ÔŽçc|¡Åpì<Ñ˘¥VtS°~U*~µÑæQäVÉ@d™íoš}v÷5uvýšû^ÍûÍ¥Qw5¿Žº¥y2êFäkûŽH´»…¶WÉ•(”›·aŒŒú€z{ÀtG³h.ÆÓÉR±0#Ž3œšÊ{‘œgñ$i¦å€n‘u¿¶DÒ;nµÝ½„ÏíïÆovø%n‡ãí¸=N7c«oÄÖK;bOI¯Ç¶Ë®ÄÞ–]Š}$»7,5Æ£Sk<:žOÀQçÐþû0Á‘޳-õmèHÓ8&É󼜤d½ÌÕ°Ÿd'òdåXôdYÞI›gs3e©]§~…õ¤5NW7HL4¸\ÒírýA·×­][ëÞ¦m}^ûÇ9íU3Ú~fÝ{S"º6&¡ìd2:ŸHF'âø6ÔQ7úÙtïÌ“Мº©ÐyT bõæÇq~ÉËtåæ[]Í™i{iüB‡öÌåέé«\Î¥•ºMÝ8útêæ”2¯&}•w£þÏ }“ïqýùÑä_åGRžùJEϺ4t¯KG×Út”Õf ôm˜LÝ,+zºwN¥qÛ›éNðx†ôLdnG²*Òó/äZµN~Ïî»Ió[rËþMu¹G5}žqüI•K„@H &á8L¼  Ã5! !!!!BÂ-DÈý~A§uØm·γžÓMízº®ívqw—HEêÈÓ’ÛÕÔ«|­9×Â0ñú‚¸“¡µÜ;áFîzx5÷OÕ¼×ðQQ€ŒBägÌC$kPÊû¾|½sKM„gZ;ø¸Üé>€™°  ·›.=·w¬$Ñe@Éqï‘ó<;ebï6©‚tU¢!›Å•ÔFQ=­.¯•Q#쩌…U"t‚G¬rÁ V¹ðMˆNô¿“ú|D­#2ÆÏ”fƒoà+)À ì;Ÿi ðDok•vpßà‹a¶2Ôv\wzÏ`Y¼s·:íP{qŽG«2Ïˬù6“ëd:jÔH343ôâë!ZñHXiþ\„:ÿ!«$;²Dü0uÁÏt–Ê𯟠ù[ƒ8?^á¿Ûì|ØûWáAµ ,Õ€9£a²†i;d8åУ;Ð^žrðjiÖÑ&µÀ«¾¸À·F¥ô7•QõŠ*šVnbh -ÛCJdCáJé,K!½)—þ.R.û>¼Hþ_†R‚T -DQ!²5(à{îA‰½W °iÀÎe$ÀJæë÷Á/©§Ûô£wwT_ÜßZÉþ IŸq¤®<÷xu™Ø§B£ð/Wk(¥%•´’â†RÕ"W„K‹¦Y’¢{‘â¢O#ÅÊoà T?1eÅ(¨°Q0dkP6¾ î¡;Î`͈½»ß"vŸ™&'3{Â@x½1ÊþZ}ì¾&ãe·ºjÎá*÷˜¾BtB«/ôS——PTZ}¢¬Ž.+m –húÂDê©¡z™ÅW?ä«¿‰–þ›)*E´ü2D—¡kß~[¥OªpõKfì]-ã­{`°õt·R‰m-»Ì泎uñ.UõilÏR£Ð»¤ZJ*ªRÊ%•Æ“¢Š¦@ßÊÓM„çèîFdë>aeë¾çêÿÅäé-·Qù(Àšw€—ø=|¦ÃÔÜkXhÅÞƒýk°}twK™ÐÜfWßvzoUkœ³®%ù ÆœyTeâ—7J|¤ EþùueTam5-×hfäÔt…dÖŒ‡¥Wß ¿RýóUXzÍŒŒ”eDL€5Ïø÷ðÉû;h¸‹çÏ´Œ`íê!@[¯+˜{} õ=LÛª®_9”w^ܧîHr+²pËÚ¸ž’V‘wÞU9)·ECÎi6P3Í&§©“‘Ú4œlºÂ6m„&™þ’lúžbB©&fBdkþ*Ã=à[\ŸÎÿž?Þ…Ý;`ë@ãð¨öÃðI¢v0ʾd ÖQÑŸà\ЛvPÔ“}„ß-<Æí’zg]/&q:+È© Tv{;-±}„ß¾À¸Ô¾Îˆ³¼fÄ[Þ%X%± ‘1~Ö¼Äïái%ÎwpÏ¿Ñ;èèh˜0L9‚nê(h¦(åT¸máäiñxœ“p,Ù…7šq0k”8}Dâ™:¬ôbé|ëü. Z. QÎÌÆ<Ƽ¢Æö¿ 8߇ü/ô!Æ×š?”á êV®ÜÄûà°ƒ6OTÍbÿœ·ƒ¢7(\ðÉ“˜7mÇ»uÞ!kî²SúÜçÔ›\7ölþ¡ÄYÅ‘K3ZÏ‹3µ^±Ó×Nœô==}‹3½F:5ýŠtjê­OÌ$:ñŽ™@^Ö<Ãwð ß!îïß;Š;ÀjœÃþµ ]®îþª'pW ™«DÎê»”ÕK»“î¥ìM¸—µ/nExàŠÌõܲæà™å÷SË­G¢—<¢–ç<"—×ÍbðnV€¾Å>hÕÃÄÖÉ0¾-<Ûâm[ãñ8—u{\Àºrˆ#?’óZ_#É3úM/âeÈÈñîo ewbp‚š8S-Ô]ƒYÔÄr¨NçÐaU‹y¨,"_“M|Tn'U¤šÔZú¾Ž\&M¼aåSÞ€²›×¯àúTÈë%=ÿÓýOÙz{½U z9ÐgªIOë1!€EÝ4ª1…C×¹z. 銌ÞéÖý¥ÝhÔ¯-öiw {µû…=Úákí a—öœð¥öº°CÛ,üC÷RøD÷NئCãò -Gõgû§„s¯ùY‚g.¶û/1nó_nÚâ÷•è‘ﳇ¾ß˜ß7ìwϰ_Üd8"i4œ’Ü6Ô[4îX\3<³¸ìÛoqÉ%ýP|ÁÇ÷CósïÁÉ”;eàT3À(ª#–jšnC±îLo´7ÓÌ> 嵄¥ †Î3¾7%_Ôü…ù­ â›A_K®n”^ ,·¼PiõsÀëú€c² ?ÉÎ\—×´ÉÏtËN¢õ‰ ´¬ Béñ`”ñhD¹SE”oOuÌ&Ê¡'Q Ï<™Ö8æÁôH®1&ÉèFT–é•È…æ?G|&¹^(=7µØª.¬Dv6l³ütèv›S!{lO„VÔ†œ´û!ä’òûûªšÊÃaƒ¶ÃP~`*Z«êp”ކ¡æ€Ó„€qtîL¢:RÍ ¦þL•CsŠšR|˜I¡¼_ã„ÒEuq¹âÓ±KOÆ,±®^&?µÊöè´uŠšÈ-ÊѪƒûª#¾wü6¢N½/â–º*♺2ò­jw*vE¡ME4ʈõhn -ÃÒµ³ÐŸÆƒŽL#ø#ÓZ3Äp'S×2'2õ¼º´há©ÔÑñä,ÉÑÄ,¿›Q ;ÿ¹mu\‘ݾé«U{¦¯w¨ŒÝæ¸+¦ÊigÌaÍö˜Ë£¯¸lnsÙÛ§.›ŽöeqhW¶D>FŽô`  }À£lîåAÓ¸–mstÌÙl®6+Bx4s†èPz†dÚ\«½©yòÊ”ÅŠŠ¤BåŽÄb‡òÄõÖ›5e Λâ«]7Äw+¯w_ÿÈcmB·KI"ªK’PU’ŒvÄv4ŒæÑ tÒ6×Lç¦y 4,àÃõùB¸8ßÎ,pcŽÏ7p5¹aFrâD{çÌ”ìž=ÇjGæù¶Œ|Å–´ª6Ï\î¸!uµSiÊzçµ)å®k’÷º¯N>ê±*éœveò]]qÊ+÷âÔaMñLtX™†*b7Æ2€)Ô:óýF[\ÃB:ïäqP¿ˆ?åY@íÇÎP“çÃU/œbT5?F´37Y²-g–UYv®|cVž¢töbÕÚY…Žk2‹5ÿÊXë²2}‹Û—i•ËÓ¾Ó-K;ë¹4íÖøÂôNíÒŒA—¥™¨^6 í‰r´‘uè› ÐšÐHùW?¡wý|êò98™/†#j8¯g«> ìÌ›fúÍÂâM Ò-KçeËKr?R|•“¯Z•ý¹ãŠ9Eš¢¬5.Ëfoú/ÛuÕä™Åüÿ†²$‚FÙ!‚@èn”ª u lJA@ö5@Xö­YE#¨ ¢à.`Ç•xZÇjUl«ÝFë2©ŽZ;s¦nÔg.õœ8g>ü>åäüŸçÞû&ïu)ß1»$~¿»*þ¤‡2~DVÿÐ]™ðÊY•È$ÅILLl'báoûpk¼9Àù@[D;W‡"!ö+í°»HÆÛ^à««Q¶ç†Mm•G‹š²Ìê3Ó­>Êȵ­NSÚW¦V:–¥¬u.NÞà¢LÞæVÔï¡H:.ËKº$ËMºï‘—òr–"•9æ§1{b7‹P¾Ï.ÓÎõ‰8E¯Cä`‰{JmÐ]âÎiŠênT¶„«¦×åÅ›Õä¤XVʳmʲ ÅÅYå’¢LµSAF«K^z§›:kÍÐQ;‹k«ñÖi¬Z2E]±\PUnZV=CUœ`Q¨J·VåÚåª$YÕNéùM.)ù›g')v¹'(dñŠs²8Å]Ùšü»&ä3§Ä&I*`â‰FÇga¼4g˨”}˜öÏ=õ@W½:êEh[+åšêæè¨ÕïéWÕ•V‡š(«¢Dù•ñæ¹å©VÙerÛŒÒ"‡Ô’JiRqì5ªM®±ª·åhå'*o{D«þåºZÅœb‹™„ˆ'z”|K}øŒjpšvÏAÊß×H{WÐA¯¡­ëLÑØlõ:O^U“niC€aÑÚ¡¢nå4¹:vfæGÉ–iµY6I5â5Õå’Øªz§èÊv—¨ŠîÙ++¹ETœu¯¸éQñO—•Lºª‚9DU0ñD÷R€/i?¥WS (»·èl£üõÀÚ BÔ¶Û rƒW²~>¿°ÍoJnë -㬖“ôu1¢ä¦D³„Æ «¸…mÌÚRûUõjÇÈúõNauÛg}PwÀ5¸n˜|ï¬þÅù5s U3b7ÑêÃ%z†©Ctÿ}­ÀŽvÚ=7õ[€*J5Pjfqùoù–Ez›ƒ S:B‰›¢Lã6ÆÏˆiO3Úk¹¾Ø6¬­VÒÖ*YÑÚ%]Öºß)¨õŒs`ë7Î-σZ˜Ã²f&^ÞÌl'ú–~“þLÏÁ êÿ!Êß½ÐPvK'í^Ûe·.=Ó‘Ó#Af'—ÚíÃOìÐÛl³=Rµ-Ö4²+YÖ•mÒYd±bkµuÐÖfÛM§Ø_³ÏÞOsÚa‰æk‡%[FÅ~›ßØúmf6þÌz¢‘ñÔÒPýû(›h§U¤¾(¥Õ(«Hí"yŸ5öÍæâöÍåE÷/æ¯ê_¦ÑfÚm¼7A¸|o¦iàžÑÒ=•3ýz›Ìõj,}{û­Þí=eíÓ{ÝÊg÷¨•ÏÎ7–>=ÌâÝf>ÑyzÓ  üÊßLwoÚTöÑþy€ö¯#@ÌàD аrH‚ˆ!O.tȇ<´TgÅ`°nÐàJý€Á8ÿÁT£Åy‚÷ʦú 4˜.8ºeú¼£}¢¹GO쮉¼ŒŠ¼¿yb"¯ƒ“¡¥u°—ò·î ÐÝkh+: d ±´‹†žæ°lX€ a ¼¯uF€vüµ¾Üm ·HÊóÕ~¨³P›¨;_›­7W[<ÅK[gðí&COí#™ö„±ÇÙ¯Œ<†Ÿ»Ÿ~#p;Ån'™p¢c4û}”¿ò×S¾šî® Ð?K;è9¾õ0ÿ’ æ]²ÂÜËÎð¾<^W|1çJ Þ¹Æy^YÍyŒ¤òÜFòy®#Õ:.#øÎ#»øNW?æK¯~Á—^yÊw¼4¦'ùœéK.N¶Ÿæ¯›ò7R~ýA „îž}XCÙáç(ÿà{ðú’ÙßÀéÆTHoX@zS -Ç›žüàCàp+ö·WC|;¶wT°¹ÓÀYßíä,ïâ,îžçÌïüƒ3¿õ‚3ÿñÌnN¶“úßAù ”_z ÓÝ×|Jù”ý>eû|¼ó5àr°¿£ ÛBØ<˜«‡¶°xè óGž˜ùhfüìÑãL{Ó'é0yZLÖA8Ú á³c<» -ãÑÇ0~2ãŸÙ$Êo¢üòœaÊ?Où”@Ù ([ö@z°ù0ÌÃÌçF=7…é 3˜¼°…𥂗î0zå ƒ×‹ ÿz9øcQÐù- ¼7ÅÀoÍ„²1jîØ5ò„Œ‘ßþ«™ò+)?W $ÐÝïK¯ó(Û²(Ûâ0ý`ò -2=˜1 ™ ô™|f މæL<ÉB€ß_ý!3zøýè3:£B³‹äÞ$Õ”¯ üDú(â+Àÿ;`.e»þ °»ª3eÓÑ)߀z„Çøô]" &DD,‰=q!^d1 !ô¢Ìè‘5.BCÏNMÂÄ´[:8¿eçÊ1 OŽM[À1“Ž "8fœÀ1Ã3(%u¤c[ÈÒKö“ŽM9AÎ’ÏÉwä>ù…¼mµo0îÿ’8qLJûå8 ÃÆcæÞ›áGg ¥³Äñ˜0‹¨H YG6’.²“ôÑY“cä ùŒ\#'£ÜÁ+î•€q/…ÿóâ?d×yTSWðûÞK¾„!/@Ô Zª* ,²Ê¾…H€HXHØÔ¸ ˆ¢êТHÝŠŠ8µmµÚ©£ãîà^ENµw~ÌŽžÎŸóNþúÝ÷ýÝûr€ÝÅOqþw¢)Ð^/  “(sÓHl—CbŽÔh&±M+è_€°Ãà<ñÞæŸÄ8ç ñšó–xÅÁÄKð‚ƒÉç¶ÿK„O>ÂŽÐNÈcÊtÈfô' ú“HbG‰ùÐZdACô6ò=½“|Gw“¿Ñ½ä8}˜|KŸ ßÐß‘¯èŸÉgôcr”?F>âcò!S˜ºîMà€§Aí\„=íàiOÈc†áOgØ#ˆÀSbH¸ýØ5—|'.¡ÆÅ5Ô˜hõZ´Žz%ÚB½µSÏE]Ô3QõDt€£‹NSEW¨»âGÔ-ñÆM1füìŠ×ÁOà¸úì u?³AxáÙ„}aM>nðÛ“ÀÞóˆ÷žÄÛO“É_§)©çS ¨'zê±ÇRÆCÉ*Æ}I ãžd+ãŽdó¶äÏÌ[’}Ì›’~æ?$'™7$—XW<°.z¼6º0€sà;pv*f} Ï„ú¾–p¬-ö·†#k -€,æIÐøœÙÄKŸ`btVù`¦ŒºóY.uË»„ñ‹w5ó†×2ÖU¯&Öß½6]òÜn|Ñ³ÓøÏ“óžMF<MÏy~ozÆë¶ÙI¯fCÞ¿›ðƦÇÁ€76ù#ì uç™Am„ƒa¡G‹ÑÓùÓÑý€¹Ä-ÿâÆ¼òò\%ãÇ9ZÖy¿%FçüjMÎú®0=í»Öì”ÏfóaŸv‹o}¾°ø‹O¯å O¿Õ€Ï)«c>׬¿ñµ>èû›Õ~?lùµ¶èóÃæûü°ÙÇð\¨È‚º „Ãa‘h<‚ž„ÓèN¨;º: ý-4„ ‰£Î˘'¨Œ‡™Î×› Ö[ 0Xö4[ößÂ>èßa³ß¿›óµÿ~Û}þ'l÷ú_°ëñ¿o×0ÆÙ'([wb+`Ù5ÿ0ñþð¹Š@hl‰FcYèqŒºe®F9¡ Ñ3ЙèâÛ¨(ÆñÈÖ‘p…Éá°<ó u–}¡•Ö½¡KÙ{BVq¾ -n±ín³ßÔÉí -Úë°+èogÐY^GÐ-º=øw[¶k ÅÀÖ[?‚0^õcºŸø_’t-‰…®Ä›¡‘:•èçýñaŒqñFûbe¦=‹²-º£µV»£Êؑ՜vk¸íá›¶…No ëæo ;(Ø6ì¸1ì'Ç–ðgüõ¿;¬Äö͑ض9 -Û|ì¿/‘=‚ú7SºœŽÐ…tú>ÕLµCÒOÐ!él¢/5˜Ñ“c´;)Õ´31ÓbG‚Úº=®ˆÓ[n×[ÇÝcàm\ÔL¯_Ô&XÝåØÝ'\=è¼*ú¢óÊè 1ïø†Xìì­! ÞBwaî¼²¡ó0óœÎ$Ѱœ‰³Ñ¡ 1êÍð&ºåT§,Ò¨Cšhº-MfÙššÍÞ”¬á´$•Ø7'V94%6Ò«ÖVÆovZ¿C¸,nKCÜQQ}܈¨.þK]¸c]"¦ë’0Ø»Iûàiq'ú AýþR„úàúÕ :J™¨µÔm(sEM¥^¤¡ÄŸÑP¼Ð¸¶(Ƭ² Ų\+g—j²m‹óµÜ‚üRZ“W#P« BUîQVîW…ª×-SuÂ=#÷*x!ÊTc'… ”jLÞ¤Q8×'ú •ë\C÷T"Ô ¶ê ÔRÁFM.ÈP1hÐÏaÔ, 1ª(‹6-+I²ÔédìÂâ,[MQ¾½ºPÇSV ² -–;ej׻ȵŸ‹eš=nRÍq÷4íew©ö™8]‹…²,X\€iÀ›ôPŠÐ%ô28Zû 2¨ƒ À˜×ÔX¡µN¨¡vQSãKé«‚X¥•‘&Å æZ½Ô:¯\ÁQ-QÛg•9(J+øò’F§ô’uÎiºí¢ÝW®Iºcn‰º‹à©(Y÷»SŠ ªÃ¼IwàÊyÎÁôàpB{ëa4ÂÜ· ¡¦„–7X úFU7þ‰(o˜E–42 ëÃóëbÍrkS,³kälEµÊV^UÀM¯*ç¥U.$W4 *Ú\âôÝ¢XýqŒþ0ê«ÿ¾ë4ª©k ð»3€!ˆ„A4¨(ADqž—óp—U«Ö©½Z‡«­ -X+ -8"8Kl‰Uk+“âÕ.DéÕÚ:¬ÖZÀ«uÀ}_»Z¥þ¸?ž•äÏy÷÷í}rÎç?hžôL1+¦'Ǫ¦$Å;LZÞ_3~ùPí¸ecŒ^:A?òÓižÃ–Ìñ²8Ágàâ~ý³üû&îhÚ;±ÀØ+ñœ±Wƒ&½^úõI>äÝ7AþR9™ûÀ°ï{™¿u{À±dÕ*`}œªÂ¬4=f¤15½˜”©x/µ‹j\jÇÑ«;ø|¤vØÊñ †¬œ¢øÙG û­XèÕ{E²wÏ”Lßø”m»¥nÜ5ålã®É÷ýº%½ðé–$ Ý“¤W÷åoçÁ×óy?²ÿ»8ŠlJeÒ¤5œý29ûeïg»aÂ:_Œ_„qëÚ‹QÙqÊáÙ=ÔC³8Φ9NÛwíûn½×ÎÒõX³À£Ûšåž]2ÖxuÊØjˆË8ä›Qæ»úgClús¯¸4Ùˆ<ë+al܃½¬?/ ÈböÊ, q=0›³èDŽGcr5™ÛÃó04/ƒó"ÅÀ¼®Š~¹}T½s‡¨{æŽrì¾e‚¦ë–é.ræºÅå,ÕEç¬vÜœ«ï°ù GDΈÍ÷ôáŸëÃ7H}øzéQÏIÞ¸ï;˜¿q-¾Žç€ÙóriÛ9îâüÇѨŸÕ }¬>èmm‰žV â­±¢›5^t±öWt²SÆYÿ¡Ž¶NqˆÌŸí‘¿X–ŸæbÎߢm—¿_Û6ÿ´¶íž*mè®ç®¡;¥k莿+àÈgÿs˜¿–µ'ç ·³vöÃ÷}8 ®À±þˆ.B”- ‘¶8DØz Ü6P„ÙF ³m¢¢­m–²m‘ªµm¥ÚdÛ¤²}éÐêh‰º•Í®nyä™cà!éxP:Õ·çn+ó³9 -®dí ;¬À¤ýÀH΢ýl@—ã@‡“¡…„éѺÈÁEA0[T‹V%=вdKÆ Eé4++J“DÓÒu¢I©UøŸ>%üKïˆÆÅ¿+ü -¥ÒïÔTÙÁü ÌOÍ–°ï³¿&3{ÇÑþ_3ÿ[æ¡§À«à_î ß îð©ðwE .†R¼.uC£KáÉ—†•Óàñý"èÿ“÷ËÛ¡»rœ®AWY ÝE ]…º ombþjæ/eï?fíSX÷hf÷?Åü â rh^øUž?¨¡»åÝmw¸Ýñ¡fp½íݸػÀÙ>šª±pªšûb¨í™PÙ­PØ‹!ìv êIÀþÖæ/ßÃ3x˜ÊÚG³îþ¥Üÿ³@ûïS`üð¾ -诮·—j'hjÝàXëU5@‰Â¨#ñ¥¤†ãÞø5¼ékxÐk¿ ^øñ]zB/ÞHfþž¹Žñ,d~Љٖ‹@«Ë€ÿO¬ý&Ðàà|P×Ê:¼t^¸’;5âo -¤¶@] ðªñaôŠë\‡ä:$¹d‘’–wÞXÈü鮹€NR]åÕïã%žü±Š§ -RJŽ:çR¼p¾„§ÎU¨u~Œßœ%¹Hüª•x¨•â]²…/d ²…×ÁϦ¯{̽‰Ÿj  ©ç~¸O¥9Ä^è’D.U¼ÔeŠçºâ™.O<ÕíOtûÄc µºBTëÊñPw÷Ük`w%î襸M·þt³ÔÒ䤇lÉÏVìGKž‹f®§#dã¾<3#ÄKï‰âwï™â‰a¾¨5,Õ†â‘!]üjÈ¿rÄ}Ãvñ³a¯¨2vÃ7âŽá¼¸á}[üèS-.û¼R\ò•Š -º@åï!ÌnÓ€\!CuÄ~´a/ZólEâY`<7"Œ¦Š{ÆÙÂn\$n—‰›ÆÏÅ ãq͸AühÌ?÷ˆËÆŠJã ÅÅ€2EyÀuŹf¿)Ï4«S–6—Êb*¢B:UlË|³ ¤Å 2œëçšÚû ÎÜÛšñ0¤#ªZ÷Å­àâ§à âŠi†¨4ÍUT˜å¦$Åw¦Tå9S¦ò¬i“²Ì´MuÚ´WUb*P™ŠÔ…¦JõÉàêcÁÏ޶–­¥ú~‡ Ó2W ©„Œá:b\Qíêq¯}n„EàŠ%‹󖱢Ì2YQjùPYlž¯:e^¬úÆœ¬>iNs8aÎr8Ö.Çñh»Ž¶vûŽ˜O82ŸÓ0ßÕ|aù¯Æj‘š|‹tÚm‘ޝíªGF8BFñ¯"xÞY º³ª;ºÂ£ÇÕ¨&(n‡³1PÓO|3Bq"z¼ò«èÔQÿt8õ/ǃ‘KœöG¦höE¦ÿé2 jë¼Âð‘Ī}Eb±'{¼Åc\jl!ƒB¬!„$K€@$6±Ib·Ø±@Œ… Æ8ÂÄX$vl'®×Y&‰ÓvÆíL“Ž=iÓ&“I:MÓÔmšôö43ûÇ3÷þ{ïyÏ9÷û^ê+b?uU¼L»$¾L¿(¾Î8/~›?b.ü†±”JÐϤ´ÅT‚ºJÄ> ‘FþIÿ‰ l€Ç -|,‚ßKið #Þ–í‚_ÈÒà¦LAº–YB e"®dÔD­±G_’¶Åž—vQƒéÚËéãŒeÉ sI²ÀZ”¬°OKBœ€äÇ/yÈKÿš3›þkFJ0§¥}únö3‡ÑƒL̽ø{T€¹óçƒ -¼Ÿ÷røð†r\Ëý9¬çf’W•” 9e‘AEEôr¶5öŒ¼‰º ï ²z˜þ¬!Öœl’=+;ÅÎ\æù2×øS™·ÞÌ‚ Ù‚ñ¬ï¹cr‚0GåãY)kXÿŸPû!æ¾Jî«îEÀÍBl½kªd¸ J'Ÿ+RRΖDž.0ÄøóÍÔ“yõô™ÜÆ ¥‹5©ìçÏãŽçLóG ‚aÅŸ!Å5¡Gñ®h@ñ™h@ùDЯ$¸ý¹a!Ì0ÿ÷à«\Ìœ¨ÿ/÷ñ¨»Ü(#AḦ́˚Ͱ¢y –4i¤Ó¥rÊ)uQäL‰6ÆWl¢zUúDQs´°=\ØÍ,â+˜ôçÏ {óƒ¢îüxwþ/\ùœÿ: >Âul„æG<Ú>G~‡¹ó=<òï˜ðž„ðýr9‚º8£ß óz1iV—Iñ•çE×–ÆŒ•¨Ã3ÝSZÏP·°ûJ\Üž’¾»x"ÎY|RÔ¡ZŽoS­'¶ªî&¶¨>Ij)þ{|K !hQ<‡šà ì0ß*°èÁ¯õèAÀ-3æ^d­ hŒ…E“ü¦í0cJ!M¥”ñ£ÊÈaCq´G_Ní×UÐ{Ê­Ì.m#Û©mçv”õòÛ4£B‡fFÔ¤YJh,]K´—ÞN²i~»É¦ù[‚­ŒÚ´ß®%¸'Ì7؇Ç€1wÞ­F0w]©Å¼cXªŠ¿™ÓÕ[ÁkN&U&VfG TF÷˜4±nãQZçÑjF»ÁÆn5´r›õ]üFýЮóÅ×ëkË/'Yt·6Õè~ƒü5±Fÿ_¡EO,‚g5Ü0_ã.|\Ž}ÀºßDýWë1÷Úñ®Ïy+¦­ðZ·À¨u/iÐ’Fî«‘EtUçG9ÍêØö*=ÍQYÉh®¬c5T4sm&¿ÎäZŒ“ñÕÆ@b•ñbR¥ñÆ&“ñ#ä«Ä -Ó¢J!@xU&‚æKîöþ>zpu¯6¢͘7ð9‹×o=Fl›ÁcÛ ½61Ù]ŸAé¨SFµÖÇ4[Ë© – -†­ÆÊª­näXª;ùÕæ¸Jóq‘©ÊŸp´ê|’¾êú&]ՇȗIzó"ƒ™ˆCøGÍ/Ì_p…3øúu×8mxßÇ 8Ù„™§‰Çš¡§y¸šRHíéGƒ"²Ñ^m³—QëlFº¥¾†Y]gçTÖµóLµ}q†Úq‘Î:— µ“4Ö×6•ZßG¾HÔXÿ#*³qeµ_k%xa>Å}ügð6Ö¼ú—:Î:1wvŒâw ´Ò »MζmÐÖ–Ljn=Lnh•GÔ; -¢­-¥±ÕÍZe³™ijªg[¹ºÆnAYè°Ô>_b9Qe5©Èþ^R¡ýs|ÿ^¤²‚b;ÁGxaþ€}xçà&Ö¼Žú+˜?º1s Çð[Ü®hhwñÁá~Ý{I6W*¹Ö•I©væFVvǘ:Ë©†Ž -zy{-«¬­™£nsóŠ[‡ã -[gDùŽåøÒ­ŽÄÁ­dZ–$¡EÉ,¸”,…siœJ÷Á±ôg˜ÔÃ|XHæCBr<(dÍò–s r€Å뀯ó‰Àhf'07й!™_ÌüRÀãÐâ¤å:ØW˜ÈÆŠ¶°«ôƒ¡2ˆúB_}Õ(ØVM‚®* 6U« ­Ú MÕ º Me´åâä0?ƒù©›É;Ï‹€ÄÌgns­e@‡@›Ó€s%`°½¤„æ²-T—MP^q…âŠ7äW;AºT÷á†(ŽÃÑĉ¶ff>X×xãkU@íc^_“ø “c?‡ãž\È÷p/0„ÙQÿz:ŸÚU­ÎŽÃe@û+ ¿n:Ô¤n™€ßZpCìÉÍ7¦Ý€›3¶ã!Ûñˆ/þão)¨caëØ‰ºÀïÏ?˜Ç±ŸÊÚã×CK€þìswöÙMö>´ü0U³ïµ€ò&³ïЃ÷)yoïcêÍÀ“ÖÀÓöܤû¯¸Q}ÍE¹‹Ñ.>o9á¼Ý¼ÛG윸öÁ´ÝÀxÖ~ØQ`³»Ÿ:±ÏžWXûÀxƒ}¿Í¼{ô~§çï½ —ôš5i`MÞÚ󞬉ð"¿¦C‹àFQ°‚íl‡`á‹.~ œ¼ Ì<_šÈà ¡²@€ç]„Q }Fãi -ͦ…”IÙ´†6Ò6*¤½TL'éÝÁ;<¡wxËßÞPß-ÝÙôÖl ùÙ¦-„â/ü¦õ§!ô9M¢J£%´œrhåSíaÖÞ· ¯q–%ºÉRÕ³doð”ß6zò'D+7·l9º²¬‡­/„6BÙBÏÿIhêûZ, ¥´’=ÌeB3·2³ÏX‹'(ar9‡íóç!^ññ¸ßH¸÷„»3DG¶ÃÂÙÌz´‚pèÀštÐs1ãE;w<öêˆ;A¸Þ&WÛ ÆE÷‘8ç>Uî“Qá1§=æH?y,’NxdJÇ<²¥2uÒQÏ-R‰ç.©Øóì°×iÙ¯›²½ÞOeEÞB¾‡v½Wøá§jz•­ä/Ã+«/ü xàã„ëí=ñK{+Î~†rK NX†¢Ì2Z*µLŠ-ÉÒ!KŠì€e®l¿%]¶Ï’%ûÁ’#/²äÉ÷X -ä»|ö* -}Ž)vú\Uló­Slñ}§Ì÷ŠM…bc#ß?4esZ à4ÄéþÕtU£¦“çü\qª³/ÊüƒQì]â¥}þŸIEþcd»ý¿”úO‘ï´Î”ï°¦)¶[+¶ZW(7[sUùÖͪÖÝê Ö#êõÖsê5þ÷ÕÿôoPçtêU]„*»‰²™èÊ60ÿ>§´k½€‹t&TŽÊ Ž˜QÔ?wÃ÷ÝÃQØ=F*N”m %ß4N¾)h¢"/èÊõ_«ÖÎWçf¨Wdkr6h³vج Øo³"ð´nYàoºÌ 6ABûm°ÐúcùõÌ¿ÉåîB$ÏQÀñ~<ó„)p0ÔEaøW¯ÎØ‚üðHiC¯xÙÚ°áòܰъœž_(³{NV­ ®^šªÉ -]d“²L—’k»$d‹>=¤H¿(ä˜aAh­a^èS}ZO¡#›¹=…vn˜Ð4Ý›–¹jfWpj/Äs'Ï ?FJØÓ×€‚7äGúbCdr#ûH91²•ýåËûPfö£Ê蛤^Ò'Y›Þg¦ÍÂÞiºù½3lÓz¯2Ì ßh—¾Ë8;¼Ô8«÷û™½ëíRú}J_¡#Ò6{ÆÜŽ~fþÉÁÜçóì·×ÝlËö:lŒvÁÚ˜ȉ醕1aRVtYFt¼bñ€aÊ…ýÿªšß¼&-j¢ö›¨iºÙ‘©¶³"Ó )+ì¦G¬7N‹ØišqØ4%ò¼Cräcû¿G ;Ò“î=›Fÿáø×ªâYžûöcþPæ'p¯§FnœÙñÞXoEF|ˆ”×O¶ v"-6QùÍ ªÙÇhfLÒΈIÖM‹™©Ÿ=ßeœ½Æ41z»ÃWÑ“¢«èCRÌ[cÒ@a [Ò5{Ìç š}>•ÈðÜ÷Ãî³)8÷ú -|—`BV¢– ñÃÂÄ`)-¡·,5!Z>ëÓÁÊ”ÁÃUÓÖL¯MŽŸ¤›7]?1n®Ý—±öbW›þ»Åq\ì>óØØró˜¸{ŽcâìÇÆ ;Ò“m³û|/²ÇÙÿƒ#Y.õÛh=?góo™ÃŒX<¼5æÿ/ÛeÔ䙯ñ®‡Ö ©GAkµÐ¢ˆÊ!¢†#@‰ AåðâÐz@TªBt]O »®°µÖZa»Ý]ÇÚ-3Ýum«µèn­­Õ~û×êL§“ùÍ _¾|ÿ÷ù?ïû~ψµi¯ŠªÓ扫t‹$å©ñ¥))2kŠÁ±89ǹ0¹ÐÕ’´J‘—T«2k7¹gk[=LÚƒžYÚSšLí^í &3iH™$¨²’%QùS™‡èû{Œý Kž·rèÙ»œµ>¯5镨`‰:Ãd¬1ÌUæŠKõ‘kÆR‡âô$iAzºc~šÉ97Í⚣[©X®«Vuî†ÔúÔžé©'4i©ý^ºÔ›š4Ý#ušNpK× J¢æ×â?˜ÿsYÀIjËöç³çËe¿C?Öå¨1ù Ê4«LÓ±Òôº¨Ø¸P\µØ!?k™Ôœ©sÌÎÌr2ò\² V¹A¿Z™¡¯w×émꔌýžÉÇ5Iú‹^Zý ŽÔIzÁ-Y/(‰b˜kÌÃÀÛŒû8µ²ß(b¿Áq£¨1;£Â¬A©y<¬¹/£È,²˜ÃŹ9‹$Ù9 RSvŠ,3Ûà¤_nvI_^,×™*•)¦ nZc³:ѸÏ#ÁøM¼ñ‚&ÎøÇûêx£à–`”D1̧ÌÃÅLžŒ»‹Ú¿·²çbI¾c­¨°HQbQ£¨` ,È-˜-Ê)›,Q’LËR}~’,=?Ã15/Û99¯ÐU›W®HÌ]§ŠËÝæ¾ÄÜîkîòŒÉ=O®yƘT/6 *¢Œ5 Ša®pݧ' ˜‡æ¥ø.–Ÿ›8VrVb±ªk}9%“`* BfI¨H_²Pœf•¤Z—I“W¤É–­09%[\âŠKå±Euʘ¢-nÑE{Ý# ;ÕEç<]UGþàQ(¨" ed æï\ÿg¹ŽSëµÛVÍk€zŽeÔçµìr9²*¼a¨ðGFå4è*CD)•á⤊EâÄŠx‡øòéÒòLÇÅ«r­*q‰*«–/,Û¤œ_¶[^vÌ-¬ì,ùÔ-¬ôž*¬TPÎ[É -m¥ ¦Ÿt3Œûµ[k7Ö²ï!%uÔçÿµNH­õ@rÝXhë¸vâëæˆ–ÖEˆcë–ˆcjµ’èÚ iDmŽlAÍ -§ðšÕÎsk]C«[å¯WQ„TÿYR3 YsW²Zת眧'J#Ôo§nË ¡XÝ‘L¢m!®Q‰%#Û41MÓÝ‚¨¦pQDÓ"Ñ‚¦qx“NÖhrm,”¾ÖX! n¨wšÝ°ÓyfÃa— †^— »Ì¨¿ã¦Â¤c>˜Øá‡ x±c&ü;æb|Gü:1®S±ùÝY‰Q›1²³#:ÿßÎáÛñµÈ÷è‘ÏAìsø9‡š€Ýô|Ûž‰Œ¹ÌÎÒ©w Xг=|™}é„“ÀèÓ2øv«àÓí ïîqðê~ šî xöÌGO4Ô=Ë î5Â½× -·ÞõPõ¶BÙÛEïE(z¡<ó”§(O=§Þ·Ð÷zú]ɸ-ÔÖw "N!§©ß ¼ÈötÔ;€æ<àö¾ ®ýJ¸ôiàÜ7N}“àØ7²þPHû£I2$ý9÷—CÔ¿èçƒûÎp¼Âñ;ò˜¿°“Þ7µqÐóBÆIíÄSÔï¡þÛôÿàÿ0ò"àñ@ù!àtIÑeà²ñâKo4pe" ¡WX²0û„/ã#ÜtÜpLòÀò.ùœÜ#?áo0ï5Ìw1cÏbÜËz©Ïxƒ©;…º~Ôõýà~ 3 é'þM®J€ÿp×T,†=/^`Qî|9•…Y0ÈyÜâËðüÛ\t·ß$o‘>r|O~|FÝ!žÇüÊDϵg¹/P¿d¼c©ëE]Õcªû¹An’ArKĂܑ©øÆlÈX î÷Y,?`‘ø/ƒG|ñàb‡hôE†þE>{F)×[6S•DÏ£¨̘.cþ xò6o“>ÕýŠÜ&ߦw -CÁ}1µœù<%ðăÍ=&‘Y„EºÀy<Œ…„›N é Î>Cp Ám49qÉ42‡,&©$›¬ Ud=ÙLZÈ›ÄN’rœô 乎ŸqOð?Â/ ý -Á“ý­z%qâßx:)$„D-É"d©% d+ÙAöPg?uã1º¨q -ðâ¯x€«´ékÚõøžÜ%w~…àíAãE?4ô‚t¯N"³È|GÒ‰™XŸyñë8ÿ&>y;uvQ§ ?àuŽRã8ŸÛƒoñ>Õ?ƹpnóŽAþò+ró7Fx@ðq§nT¾\ü9æCô41$™±©[À§•1žjêl Îfê4ãh¥Ž:©Ó…/éÅu¼‹Ïñ®Ë<¨É;ãß÷HòæTê}Pem:–X5 $p 1$Œ$"B<Šâx-ÞJ=pQ¼ê¹žÕjkkÕ¶êV«cÕÚÚuWÇíÚÑm;Ý뻫Î:ýã3¿¼ïÌ›çúý~Ïó½€Û´‰oÑ7(S×韮W™°Ï_'Û}Õ”%åCC¾P>"úQmCTŽÆoB -~•ZðïÂC¾ øjü«Ã]¾ßóóñ¿·ùøFÒŠ›’6\—lÇUÉ~\–Ç_¤çqQö¾á¼BÄY¥ˆOU"Îü1J±¿”üà F* ö!ŸzR}ºÀ/Ãñ£&4ø›ºwÔ|£öãkuWÕa\QÏÆ—긤þ#.hVãsÍ|¦ÙŠsš=ø´Ó1|ÒùNwþ'#ãøk"Žuq¤‹Èþb Ù€ç $?¢ø-2z÷Áýîƒp§ëÛ¸Ñ%Wºæàb7;>ïæÅ¹îU8Ó½÷˜Žz4âÞÍ8Ù³'z®Åñ^m8ÖkŽô:ˆC½Oã`ïkØ×çŸÌî¾O˜]‘"³ãÛ_áǘ®Ù§ƒéØ÷1¸-ÇÝÈÎøºO$.EÅù×Çà“þ&œêŸQ…8åÑèEOÁŸ£g`ôf_ôBfOôræ½ï2»lav ØËlp‚éx‘ixŸm‹ù7»1Fd7ë‰w_AB> ¥«'–®ÚÀ ]ûÃ8\ŽQà|L|4hŽ¿9‡'ã€6{µ6¼§õ`§¶œÙ®­f:´S™-Ú™L»v.»Y»„ݤ]ÅnÔnb×kwr­ÚÃܺ!g¹5C¾çVý™_>Tä–-È¡ÿG<&»w銿6ø‚ZÎY=ðñ[|0,‡†Ga_ìpìÒÅ6 [tyجs0›t%ÌzŸmÕÙuº:vMl·*¶‰[ÛÂ/må[b·J–Æî—,Ñ–.ÒÝ’6x,m!Jæ?‡Ÿ÷ -OÈþ?Èþ­$Ò[F²M|@¿Æ380Jƒ]£ûb«^‹Íq£±!ÞˆÖø,fM\³2®]WʶÄUqKõ“¹ÅúiüB}£¤Y¿HÒ¤_%§o“ÍÑï–5êO³õW„†¸‡Â̸§²ñ¢”¼ÊO£¨%Rë¥kýLÍù™À!jy{“Io$(ñ'ClHüÖ&ÀÊ$–%™˜¥IfQ¢mNôpM‰åܦ$Š2BZó -⩦P ÌÀ)Òž‡s=´n#ÚŒ2´¦tŪÔh´¤Çâ´1hNMaæ§f3sR­ìì÷NŠ—Ÿa¬ä§C’°1,­36 -µÉKä5Éë¡äe0ùj’ñ¼*`¼§ -¤üG11EÙ«ü`®SÜgÉöññÀ~+ÅOëfz^—Áb¹)K2ú¡9sæ™G£1sÓ™ÁÌÈÈcøº 7_k*“Ô˜Òiª¬:½A¤/”OL_­¬LoWU¤P—§ŸQ—™î¿*ËL¢¼,Ch•½ä>åürp:Ÿ´>»iìØBëzòeE6él šrú 1çM4ä¾é¹¦>'™š“ÃÖd[¹P¶‹¯ÎòJY•Òª¬É2Ö ¡Üܤð™W(½æ6u‰y¯¦8ë´Æ“u‡øEåÉ„œ^òWÚ(æ“6ÒœN`g!å€XK- ù5?O…Yù=1#?áño¡vüÔä™`¾™ äå³UyvΟçáË-RŸ%(óZÂB±e®Â“»LU”»QíÊÝ­qZ>Ô8,·;9,?«y¢Â™'ÊQxÉ·T÷ódë8ÙÜGÚ³£˜ô±²X@~Í*P`š­¦Ú¢Q3a‚6=¶$¦Òfb+ -rÙ²WZPÄ—X}5 +²Ö .k£Âa]ª²[׫mÖ]šëÉNVëMâ_j«UTXEù „gܤ:œ![GÉæî Ý´-^šõ]4ë:¥¨u¾†³&¹´¨rD…ÓÀøœiŒ×™Í;¬œÛáâ ^‰ÓQ%µÛk›}–Âj_¬ooUçÙwj,öš\ûuâ±Úb•„‚Â3¾²Ð^ ¤Øw–ÑY¨ ÍGãðâr`¦‡ô†›Cµ»3ªÜ}Qáye¼ž1L±ÇȸÝfÖåÎçœnowKlE~©µ¨FÈ/zGn)\¨Ì)\§Ê*Ü®6½¯É,úŠx¤2‰Js¡¨ ä„ðŒKtNRöSìÛ*©4~®$š'õô®ºð—jà+í…’Òx|ÃQ䋃Ë7Žqø2Ø ¥¶ t7¾ÔÍç•–Kr½!Y¶wºÜì] Èð®Q¦{;TiÞ£êTï—ĪÔQ‘V"Ê_ <ã3:ïS öPìí4v¶†¨“¹´N¡±ÜOï½~%ÜþnpUFÁQ9ªF¢ ÊÀX«Ò˜üÊlÖRiår* ù¬JŸ$Ã?IšîËRýóåFÿ*ErÅVe’ÿˆ*ÑI•XñP™T!*’ÊE9!Œ{Á:‡©;)ö¶`õTÒu´i ’/>òÅ”aB0ÁHŒ½¼¹¡1È™ld²B™Lf(Ÿ5…\Z¨„O N”$ë¤IÁ¹2Cp…0¶º]<¤ˆ^PÄUÿ ŸôTˆŸ$ -ñQö’S”ƒýoÙß&Ý7h"i4L#ÝUOsƒÜ: ²ë{Â\?™áa0…G!=œˆÔp:c ç0ãÂ66)ìa õ~nLý>®¾ñ¿\—yT”ׯŸï›°Œ3,² -ʦ0 ‡'Æ-DkÕ$F­5O465"¸eE© 8€¸ÂHÝpjÜ%б©§‰&*.ÚTÓ¨(1Þ> -&©üÎÇ÷÷y—{ßû¾ª¾™EêÔÌ -9³^“œÙ¬Iθ­Ižÿ³&9]hÌéBmž×Á^îÁ]ôu õKY²+Ç¢ùVæÏwÈÛdX¶'†äø!-'¯Yã0ØÚƒ¬ý0Àš†þÖèg#Y¬¤Tëû²Ù:Ga²f)’r -”/çlV&æ¸TÆœä¦Ò˜õXe\*TÆ%¿RÏ=PC_7f1³&¬àyÈeVr¬â알 ƒódô³ia±¢¯=©ö¤ØÍH¶÷‡É>}òßBRþ8ôΟ,í³$ƒ}‘o·É½ìäX{­c?&ÇØ¾“cò~RĬäv°‹ywÒ÷RŽ@ùÔ´ÚƒÆ`5÷GÕQk׋WÖI%0–ê‘PCi4âKˆ+MA/Ç@ôt C¬c4b嘆HGº;ráX/us씇¥°ÒëRغv)´XH¡k…üœ­ô}/²q ©;“z“8¢Žåxøû@ÿM€i3_ôpz"©G¸3Ý*£ViDhe -B*"¸r8‚*Ç¢kÕ$T͆UüªŠá[U}•ºÊVè·<„¾B@_.¤ç”S}·YÔžÃqp -5ÇQod%0¨0o¥þ6 r'ÌùÔ·î%è\:­+’àã2“Aðv€—k<]SááÊ„ÆeƒÊUe]#9Kî@UûêÝâŒ{!ãm¥ßéôyê`<5ßäX:xõkƒ‹þ7AŸzŽª>Ðô†¢ÉRS0‹\$1θÙ„¹yáºYèÝ<ðn&ú ;¸|Æ&íy@Ä/¬a®—Ó÷Ìr`:}ž@í·ê€×ê©OÍø=@÷ý@àA@wð:(ƒ†ø_àíh¦Í´ã´8C;Îð2na±oa‘iá&o¡ƒ-\´å ¹Íß?$Ož±’9_HßgÔéó¨FêÓWó Î „Óô€c€ö$àñ9µÏ’¿“ój6@ÞÀWzàBW6¥À·.³Ao¥­¼Œ®òò»Ê¢w¿Æä^ÛÇæñKr‹üøŒÅN`Ãôî'¬ÔN£¿fêö¢nuýNÞ_Ð÷§š_“oÉÒJV\Wp(y ¸©es̘|ÂA%šÍzpvÜccpŸ…ø>‹Þ&ý7Vlc<Ú.=c6÷Ù$æz ã=ä0Â8÷¤¿¡ôWO]Ï Ô¹Ô©yÜ$ÿ&ß“»äBwp_ÉõF;~â—vò¨óù[„.˜vB¨»ò-¨ÓŽx’J†Ñd"ù€Ì%‹ˆ•¬"«©SB Ô© N 5jñ{ð‡qç¢á¿|»Ë¿¾Cž?ŸòŸN„¯?D_/‚§„b"ƒÈäOd -µfRg>u–  Ëp6êQ§”)ÙÈõ\s;SU[ŒÅwh¦W˜Âp•ÿ}•«´’+\îDøë ôZÆÂBÓ…9 à×îÄH^!¿£æêM¤?Ó¨3›ÑÍÄmæä&–SÇŽXC56síjnŸÝ¸ÈX|£8¯ð%­:ÇU¾àj-äÌ ˆ@oÚáÁX¨ ´^ÞzÚÂXH±Œo2ýLÍ7¨7ŽZ“©3¶ÏåñX€ Ȧ -êä󸮥F5*pš±hf,NÂc<ÌGiéaæãõþ& ¸ÿUÓnOâG;ôŒ‡Ö<#pW€ - .+†â¢âmœW¾ƒsÊ)hQÍÀçªpJµ'T98¦ÊÅQu«KpH½nu5šÔuد9€½§±Çó:½ÚPï#à"uOÑ -Ôv"©ÚA;öý®*< ·µhõŠÄ?½’pÖ{š}†ã¸ÏXÑNÄ!íûpkg¢I;û»,ÂÞ.VüU—‡OuEhЕáuú]¨ÕïÃ.ý)l÷½Ž­þm¨ -p’-/ XbEwõ(àKËeë‹Á*\Ðyã¬>'üâðY@*šº¾Ž½o¢1ð¨zuAS±;hv§cGðbl ^†š;ªCŠQ² ÎÐT„6`sØQl -»„²n?JŽp!•’’!­û "–¥¬'õ,ƒ½YÊù<Å÷ã8æ‡}á‘hˆHBm÷þØÑcj"G£:r<œQ“QõÊ£>Ħ¨ lˆZŠ¿D­ÀúèB©4z½T픊£ë¤51n©(æ¼T{G²÷²ä‘UHOicÙ{ø†WÞé¾À‘T^o}Økd4Æh±»g7ÔÄPßåñiØh‰2ÃX8 PbxņéÒÃiuB¦T˜#å'äI¶„b9/a³¼2a‡¼"aŸ¼ÜxFñqâ-ENâcEv¢³:YÚÉ=–ÓkÔ=ןóÖ@Î|¤áU –×ß¶Þ^pöĦ¤X¬ïcBIŸXc†Õ¦QÈ7ƒÍ4QZeú³”kš!­0}$-3-–­¦år¶©P‘e*S,1mU,65*šN*$ßPf˜)ç›…"ýîšêŸJ£ÿC€Æ¡ìqùs5¿•§¨±>ÅÅ}{`µ%v‹«,iȵŒÄ2ËÉj/e[&IK-ÓäÅ–å…– y%[‘a±)Ó-%Êy§j®Å¥úÑeÕy†ñçÞ™{D ŠK] ¢qÁD–pFf`¹âf–Ù•ÀD -5.ŒKãq-5zŒX›˜TkÔÚÄœ4§mz‚5m<96ij4IÛ“dú1ùãwî=÷<ßû¼ßýîûÔ/¸*×&~,×$~#Õ$…´Õýhªû¹¿€Ÿá…ì¸heý¤3xÙÄ9[/ ˜‰­)ã°I?-†8¬7°Ö`Ás†l¡Ù' ý|Âzßeí—mìA6³G#¼îËäŒÉulI„–…£ð¼1kLsÐlJF£É„U&›à3-ꌡÖX,V—‹UÆZM¥±I[aÜ(•wÈc»Îm<©+1¾Vlú y¨+6…ä"SH"Úâø?ñ7©w‰Úgø‰íÌ:xÝÃu¼H?6š#°Æ<Mæ‰Xm‰Eƒ%õ–4ÔX2„EðZò„ -‹S,7{Ä2ó -Û¼Z[b~^*6o“]æýºBó‰°e–Ka–?’º‚ŒL¤KÚ^þÊZ¯eÝÔ}%Ÿs¾ƒ™“4¨Ž…k¬ah´EƒíIÔÙ¦¡Ú‡*›¶t”Û²m©Pj+Kl¥b‘Í«qÙ´…ÖµRu‹l·îÕåÛ:ÃòlÃsmï…«¶/ÂÔ¬œkëCzÌÜ¿åÈužú]˸9оD¶q-ë¹®Æl-ê³#Q£Œ…W™‚ -eÊ”$¸#J”L¡HY"8»X¨‰Êr]©×æ+~)WÙ$/U~®ËQއ)Ê…pEy'<[ùœ÷!‘‰ô˜Û܃—YóÙöÁżÅ1t7Ùìžãº|üÛŠ¥ƒQ¡ŽB™Ru&ŠÕ¸ÔTª¡@Íjž¯:Å\µL£ª5ÚµIRÔ€¼XÝ­ËR…YÕóäVx¦úO^¿×YÕL¤Çܢߗ¨ušúÇJÙû@¼o¤/ÕìK¹} Jí#PdŸ§c:–9âápè‘ïHGž#KPªãX&.q¸5ÙŽš,G£Öjo‘2ì;uûÙq.,Ýq3l‘ã>¯ßÉéöP/’ùÞÎá^ ÖÉà0GOÆ4l¯ä^\NèG×Vâ G¡3 -ÎñÈw>ƒ<×\¨®$丌Xâʲ]9Âb—C´¹JÄL—Wcq­Ò¦»6H&g›ltÒ¥¹^Õ¥º®“Ou©Îoå4gH꣰Ÿ+y|'‹˜{©ßáåûÀX²…¬[Ôrz²¬TB^i$ÔÒ1ÈqO‚➉ŞdyRaõXéÉ,ž|!ÝS$.òTˆFOƒ&ͽNkpo—RÜr²ûŒœä¹FîÉIîÿII¥¡>’KBÚ^Þ gŽSÿ@-ûÀH þz¸Š¹“¾dW BVåHX+' Ã; ï<˜½)XTe‚©Ê†…UªVU(¼ebŠ·NLò®Ñ,ðnÕÎ÷ÔÆ{OKqÞ«ä)®ò¿Ú¸Ê6®â'.ÒïSÔ8\Ç>¬âûÀ8²4’Ê•Ì] Ì\¥nÕ …±~ÖOFªo ¾è}©HñYäSès >·ï«çùšÅ¹¾ÍšÙ¾ýš™¾SšXß[äïšØúÿhbëBšØÚŸ8Çt²þöÕÀ®f`£ÈÚ5Ü ÄÃû|?`ãï ›4HnŒÄæ‘XÐ< ÍS1ß?ñþ$Ìó›0×oÃ.fù‹0Óïfø…iþ„©þ½â—8ÙYŒñßcš¿cž õ1¹±ŸÓìûQêï£Þöu@ËfÏìC PD–3I&s[Â1«ef¶ŒÆŒ–hLoi­q˜ÚªÇ3­fL (˜(@L Ñ<؈ =x*Ð)Œü†ô`|ËWÂø ¡~Ö÷óKúÝÁšwQ{s+=ØÔoÊ^ì[éÁ6 •Q1žLßLjˆ§Û¢0±m,&´MÂSÁX<ŒÇø`*Æ30–ìè`1Fkð³àZŒØÙ†á;aXð"¢‚FÔŽG¶=„aÛÈÖÐËQêïeÝÛ©ÝBÍFêT'£¡²‡ùï%`Á^ võ÷ãˆÑ>ÃÛ£0¬} ¢Ú£1´}†tÌ'iˆì°á‰;w,Ç ŽF ìØ‚ˆö h?‹ðö[?øøûCˆØ×ÏAzdÝ›X§'Ï$ÆÁb꩹_f³1G©Þ Džqrº¢ ë¹+RW,´] »Lz?¾] „Ÿ ¾Æ|ûÚd8pa™D8vs éæ0ÐÍC¾›‡K77[7E.°˜ ̹Þç3_oI¨¬}ý^ÙÎ3™ÚvÖi=èEýW©ËG£ºA—ù €a…:-‰®Drâ:®Žå0ÈÁü‡Ãë‰À 3áGà_üløÝü]¿BèÅõGä»>Zé{#=¯`ݬ7‹º†óÔ§æ¤_£_†¼ DðQÍÛÔ¾Iþ@Þ!ïj€ÛáÀ{ƒ÷‡r ü‰aáCçÑ =üÞaOîpÃßaƒïÐÈžß“{¼ÔG{]EÏ Y÷bj§²ÖÙ¬5šµŽú{ÞßêÕ#´‘ᎄ|Dî»äc‰ƒ1=¹GO>Üg`ù|C ýxÀóî/yØ=dOÒì‡4öá r·jÚãbíÊE í20ç*õ©;’ºƒoÓû¨ñ!ééÕ"\>Ã'uÈgä_äßäKòH¾üŸì2ŠêÊÂðÿÞkDTÅ} ** ¨‘EZ nèÆnh蚥YDQA@ ¸×B"2¸¡b¹M0š1ŽÆrRV&NÍRV¥œ8ff\*5qÜ¢o~T¨™êúêÝ÷ªúþçž{î=缤O~âÞÈldÚ!³H’™ˆåu„Á'3Àen²üù²ãDö·ãÙöÃ^Ó‰YD¢‰…d’åd©"õd3ÙAv“ý¤ƒt“Sä"ùoq?ÑØ×xWüò’¼ä?äßä" ÙÁƒ£~Æ Ú1‡ 1‘t’KŠÉ²žl¤F3Þ`'5Ú¨qsÆsçügñ#>£Æmjü O9zÂ<&È¿Ÿ?eg7ÈN®&ýÏqd™OTÄ@’¨•EꬤF5j8s#5¶P£…{8g;çìâVõà!}ñ×iÁ_2:¯¹2Ãèÿ‘G;C5²äÄ·QÄL%ïó_AÔŒÂ3Äs»Ó¨“CBj¬¢F%5j©ÑH-Ôø˜óíÅ_¹'ÆQîB/¾¦/îð}Å ºMËoqÖ~¾$7‡ » §/DÆÃÃc¸ øâ9}ñr]*|=¾c\| ur©QDUÔ¨¤F-5>¢ÆVÎßÊ9÷ãwèd4œÄoé‹«¸Ë´°û”»wi0Z†"¡6¯;Ù™0¤eG'¼\©;‰kô¦æBü‰qñ5ŒÔJ¡N&gÍ£—‹p «y]Uâ -ê¨ÓDíÔhÃîÉ9ÆçYþÎÐ’Sœ¡G|ŒÒkwql˜Œ£Cyœe¥ç䑯›ÆãžäŽo$Oü^17Á¸æ Ã‡xô ³âÒ°,\pÌÇ9Çbœu,Ç™áU85|zœ6ã„S Ž9íG÷ˆ#èqGF^ÁáQß Ãù1Ú]Þâ  sÔhž¤!È“¨ÏëíŸÓxå»|¿ã!â+ÆÈ‘ãpÕy~ãâ‡ó.áè­Ç)×DœpMÃ1×lt»-C—[ :ÝÖà·jt¸mD»û6toÃ÷O°Ïã$öxô¡mÌ´Ž}„–qo°c<óÙ6„WL7OfRß›Ú7æ0L.OtÀ%wWôކ“ã|Ñ=^‰Î ‘蘋ö‰˜”}“–`老M.Å®ÉkÑ:¹-Sš°cJ ¶O9ˆ­S¢yêlžv Mï=D£ç+4xÊBý yÆ4óÀ—×ð¦ Ïó¼îO3 ž˜: -]Ó& Ãs6L÷ÃÞ*ìšgš°sf2¶Ï´a«×R4{b“Wš¼*ÑèU†Y[Q?kP7«S¨Ý+¬Ÿ}]¨žs_¨šó\¨ð–…uÿÃS^ë÷¨}‹)æ³Öù¼ZO.º˜†;¼±Žv{{bçûs±í%š}4hò1 Ñ' >VlðÍD­oj|‹Pí[Ž*ßõB¥o“°Î·UX3÷P>·G\=÷ªX6rÞbé|Y,y‡P<È÷\÷¨{-œ©]Í~‹t…±¾S²ï\ `ç*‚a­RƒÕJ=Ê”ñX©LA‰ÒŽ"e®°BY$,W– Ë”µb¾r‹˜«Ü+-]Ô--YtIʾ#e?!²”,‹C¹Gÿ߈.1Õ÷0ÍvƲ×1-ZÖ¸´£.Ø UÁc°6Ä«C}PˆUŠT:ªŒ(P™±L•†y!E˜å_H”¥«Ü÷^ú¿‹šXòµäò<’jŽ‹éú#5Y‚%Ù É`JžcŠ/bS`H …Þ…ÅÖD[ÍÐZÓ…(ëRAc-"¬Ub¸u“¨²î•B­Ç¤Ô+Rpê·R°õ9‘¥”_èã¾÷PëpûÞ|Þ l6’ -Ž iG}’dcÍ> -1éc ÏxÑÞÐÙ@k[„H›Ûb¨íñˆ°[fÏBí+„û:q‘ý#1ÈÞ&.´•í—¥€Ì{R€ý™h“Å2Þq‘ë<–Í3IÍÝ+Øs±¨#kþËv™Ç×x¦aø>ÇY›=–Dö…lvBP&-~˜¶ª´Ú,È"d“’åˆ%‘ÄžåXƒÄVjI ¥5bÔ´Æc´1ÓZÚi«CƒªvZ3ß\‘Ìoü1\rç{ïçyÞ÷{Þçæólb™>Kz »4&ÞVqñ.ß]#‚4"!RÃhXâ0Å&>§¡‰“øŠ'ÎÔÀÄdӀĦ~‰ËM}’ªÌ½“ö˜£“Þ5G%]3G%>4G%æèVâÛhdö ³e®T‘Á>` - 3“³8ßE<ˆgTJÅ&;iHЧbRü48%LSûh@jŒú§ŽT¿´±ê›6Q}Ò¦+:m–¢Ò²‘¶ÄÔ+­ÂÔ3m—),í„)tîUShêShŠñ„°dÃÜÊAr¬Cc#º«±†Ë° Jsùùz®ô2“ƒ÷ãÿûgÚ©O–‹zguStV ¢²Â™Ý_Ù± -ÏŽSÏì -Ë™¢ÐœçÌSPŽE9kåŸS+¿œcò͹,ßìûòÍ2L¾™†É/£·R¹ŸX¿ݲ|Þ‡B¼§…}€©0ß ƒ±ŒÑf…8*¤À]AÞ -, V@a¤ü ÂùZƨ‡e¢|,3ämIU7Ë"uµ¬”—e›<-ò°\”Ga‹<ó™XóÚð‚Ô|Ó"i z˗З–QƒbjP"M‚1+¤Xè aà·ÂFÝJÔµÔC]J}äU"ϲ(y” ’{ÙH¹•—kù«r)O’syŽž)/‘SÙf9–’CÙy9”~#Ç’Çr*az.nc[5(¢XÒÅhda “Vñ>®‘ÆcËF®g øWàA+%×êŽr²:ÉÑê!«„ÈÞ-;kŒl­qêl}Q­3dcMW‡êÅ2UóP¦¶ -#[yðº†LëÛØ@î«Ñ^¾RÊE7­_Wá?7Hq›ØƒÍRÄ)`+ú5èo—kÍêTg/S·Î |!°Žadg,ŒÉ@£©ãPÕ‘XÖâuw\ êöŸÁxº2îèÕìÚs­ìš/£õü)k¹S -Ü>i¸ì“Þ–lâoaHa1Œî~R=Ci}_šCP ¸æÛÀo Ðõ,^ÏÇÏÁ×<÷OXI½-äAÎ ÛèGhŽFoz‘<€^—Ã’s£dwT2GÿÝvNÇIâ8EïaÞg0?Ý:Ç™g -šhrMÙ@-šH¬‰Î| -ðßý·Š/‹:'‘ó´Ç†Ö£¦?¥ó†¿<Å•Ölíd˜løÔlÛëÑÍjÖŸœž%§ñhMFg&³õ…Ò9ÙäG–Eúq\#Ž«ªdÍÍú“êôGÐç(À+tƒŠ|Ç+e¨éÿ`Ø›ØކÔ‡=ßvEÓÍHr‚Þsh½@¯¢ñ:sØét4²ÑÈC£ÕjQµN«Fïi-¤9­cäÖ‹]DN£Èg:“Й¦CŠGaoA:»ŸƒN:KÑ(gÝJm§5¦½Úb>ªÍÎi£Í-mèøPÕ UÙª|ŠÇ´”û´ÚÊU¸Àçˆël';êà®c¿ðWCÇ(ìÅÊï±N‹zÔèMßýÊõ=©~W”íO™þ†2Àÿ|Íu{¹7WØéØ é?w1Ô„ã¹ÍZßÃY«üº«Ô?TÅ}µ40VEq* §ü ‰Ê zM ƒf*7h–§+;8WYÁEÊY¥y!•²GsC+-ô¢)%ì®)¹§ašó_ÂÚ¸ŵ7+x(×û0flØ#Yiók¨ÍŠP- õ”%,@ù=#µ°× åö¡œ^£•þ+e„OÒüðiJWZxŠR#2•Qð¢Ë>ªéëŒãß_ê -h- ZåE%¼Hy!„@$ ’$µBТm«”´µõ… õ•«N»!´ÎZµ­Ò­Ç͵{œ¶ÛéÖÍ3ÏÚnÖu;íÖžvNûZØñÏ!ü~Éý>÷¹Ï½÷û`ÍŠàO= šS‰&ùÂ'¿,V¥Ý iw‰ô¹$þ àuLësŽ×쯸£ä0¯þ^5õù®K>[R£Ñ)G{Z -6¤g¡-=­é…X—aÁÚŒ -¬ÎpŸQ¦ ?|™mX•¹ ™Ï -oæ^áÉ:"ÜY§Dâ’X©ø\Ôfß!’¨U<ຒ¶ƒš§‹X´`ƒd?¯¹|ÖÍÜlΞˆö¬H´) E±k³Ó°:;Í9:ørLhÌ)GCN¼9uðäúàÎ]‡ºÜQ›Û-\ʰ¨QŠjåIY•ê¢Ì¡úTæP/*ITÇ80Œ´TfÚ?Ú¯>²§ØÎ8žb팣E5ªyhV-†O½ jêÕùð¨ Q§¶`ež®<œy ¨É[ƒêüváÈß&*ów »¦_Ø4#2«ö]Y¹ö&ùŽHâ4c¼O79÷‘rZM;ë ‚—ìƱ™¹ ð½_3š9ðjâáÖ&c¥6.­N­Õ:ªtåpèªQ©÷À®÷æ_/¬ú'EYÁQZÐ'³ Ë̆wd%†ä["ÝG”Œñ+æà õ†¨?XÅu ;i¿ºøÿ& }6ó±Ê0î‚(ÔÓ°Õ9†lT4° -a+,…µ°寕(5ú`1„¹h‹(.z^˜ŠÉŠLC2£iTVhú#ù†H÷…Ec¼[LÛËy¿Bí~'°×ÅZ$[«ésùÜÏ8ܦIpšf¡Êƒ -ÓbØLËa5e¡¬XÒâXŠÍ0—ØQRâBqÉ*™[`4…Áüœ(0zËOd:Ëy™¶ôc™Öò/"É´fIèÆ¥ÆÉJžGÔ=L+ÞëžsÓë×Òë3ŒÃY&Pa‰€Õò0ÊJÂRš si:ŠK•0•éPTf‚±ÌŠÂòʽЗ?]y‡ÐXŸùÖ}"Ïz\¨loÉT¶È×Be•Æ(—„š¼me-Ðr¡þA/°{"Áz`-ãòò]c,³M‡Ùb{,ŠìKQhO…Áž‚Š|è+ŒÐU”A[Y…üJò*WC]Ù¥£[ä:öŠlÇ1¡pœYU×D–ã+~–„¢òg9þíæ Û€}M¬Úð.¶G}\ÆáfN*KqõC0TGB_]Í"hj–!¿&y5*¨P9ÍP:+ëZ‰lW3® Ètu‰ W¯H«=*äµgDjíUòO!wIBî§F¯³Žsþ/QwÏZîÉÇ€'Ɇ5ìyøÌÕ0½(ôÈ_µ{”î8云í‘CáÉA–G‹L õV¤×;‘V߈ÔúVxŸÂ2oR¼/#Ù{Z$y¯ Éû¥Hª—D’GÉ÷qKâUÎó(爺=®C÷$ ðs[‚*Æe^ èO¶o*2}QH÷ÍGZÓ"È›RÚ”M*,o6`Y³ÉÍUHjö"Ñ¿Kü[ð¨'ûà?……þ÷Ém$4ßCB“„Ÿ$‘!Îsúû×;Ú¹›¸'ÉZ¶gîÇ[#cR3¦ôÖ Hi‰@RË$¶Äbië,i]G[XܪÁ¢€ ;âuˆ ¬Fl  Û1¿­1m¯anÛ%Ì Ü¼Öÿ"¦EB̺1Žqž}Ôéíà~ØÌux‚ë@šˆs `á3]9 Ë;Ùv<„¸ŽYˆíœ‹G:ã± 3ó;åˆéÌż -˜¬Áì ÑÁÇ !2x³‚ؼˆ™ŸcfÇ0kÝó}6Jøõ÷sül»·Oó\ê<ÄNŒDIäd ‰%sº§"*‰ÈÐ\™¡$’ŽˆŠ1#dÇôSC-˜ÚŠÉ¡=˜Ô}ºÏcâÓŸ¯1©KÂämcôqž½[™ƒï‡g¶í<ØŽUï`ì4»€ ’Hâv³íf…'`Zï LîÂÄp &„ Â4Baö—½¼ü{yù„¹©Ã,¢0æ`=ìïzÞàæ¿ -ìbc¸ë'.ýÀ¾.þ¡v7u7RÇj÷ð#%L-[Jଓ¯¦ßpm¾c£r‡ù¸«î±6îñ0”¸%&^ââJïËìö¢I$‰"³É|’Hÿ£ºLÀš¾Ï8þxË¥^È©œI @’„@ ‚\*""^ ¦bÅ2T@©W=j­ó>gm©­t´³]í3ë6»v[Ÿéö´«N»ÖvݦsýïËñTûø|ž?Dò~¿ïû;þïK ÄNŠH©#«H3i#Ï‘m¤—ì%‡È1|ÏIð1çÌG,ÜCšý¾å¤ÉyŠ|G»ä3ò—Áy~Äg_2•„)Ñ,’OÊÈ"ƯgüFü—óåCldüÆïbüçñOì£ÚajœÄWœL¿Äîá#–è>îð[_0Âçäoä6ù¹9äaOÆ’ #5 &±$…dP/—:jTàι_¡ñ›¹•ñ72~ãw1þN.Ï~æv·p–Ëö:þˆkø˜ߤËß0Ú>$÷†®É%œ¦‹×q®Î±Rg¹r§©rŠœaðgaô°ö¿·ôÈ¿oj²^Ô“2Ÿ4j™˜‹…ŒZŠ×èã}\¤ ôqŽ>ÎÐÇ)tâvpgîÇÏp/ó/^â·_ÄŸYop€jûGØ7‚0Žú¼êîò8ÝâÕû=]ÇæèOÍ™xÑÔ“3’ç‘I<î87+]J…j®übfÜ@Fê´Rcãnã)Ù͇¹CÎòÔ\ÁvîV¸›{³kð4‰žðWÊßy”? äõC~9èç•wyÔDîì)ŒBÍ8ê%3¢Ž§/ DìåÆÞQ¥ØíU^µèõjÀöÑMè½]c:°uÌtŽ=ˆŽ±'±yÜe´ÿ›&ÞÃÆIÑæ-`ÃS< îm^i¿æõ:À«¾?_˜Æ× ¯Ýã|px\LˆÄÞ‰R윤B¯·=ÞVtù8°ÕǃNŸ2tøTã9Ÿ%h÷]g}›±Ñw6øvcß>´úG‹šý¯cíäûhš" ‘¬xÂ^Q¼Žy­ö‘³1À±H௸}^ØåçÿYØ:9?"Çæ56±qj66LubÝTZ§ÎGKàB<¸kW£)hVubå´]X1í¦¿‚eÓßGýŒ;¨›ùKf ?â6õ¯Iø:’çÔçóE1g ~ÞÊsš7Ú§¡mFÖÍ£eV -ž™•Ž5Áf4ç`Up>VÌ.ÆòÙX6»õ!ËQÒŒ%!íXº5¡‡°0ì<„_Euøg¨Šx„ÊáG|L½w’ø -æ«öd*g>÷° éIàÌÇšl–°)X>«#¢±"B††ÈTÔGP™…ÚÈ\,žSˆEsæaáœjTÏ­CÕÜFTÎmCyTæG@YôÌ‹ ·QûoÇ -Oˆá}Åö«ŸÚ؂ձÕ!½|Õu² iKdŸç…•Q~X=u1‘¨cQl2ÆjPkDeœ qNÌó ,®¥ñ‹Q¿Å⟠H¼nÉ (”œDô -ò¥ŸÂ•ðà’>áµ.SÿLç ¶>{H·h§%°’kS'öF$ $¡¨”Æ \*C™4¥ z”$d¢(ÁOB܉¥(L\€|Ù2¸dÍpÊ:àïF®ü8ìŠ7`Oú9IßaÅ0ï²Íx•ú'29g°ÝI¶°ýiãgkè­ž>È' B>eò`”Èç¢H![‘ŒB…ùIF¸’lp&9áH*F^r%ìÉK‘“²Ù)í°)w"KyUŸÈ¢ú(Sõ€ƒ`¥€¾Únæ:ظv`é`볎>VÑG-}”+Ç¢$Ååt(#àRÆÁ¡”#O• -»J•Ùª\ØRݰ¦–#+­–´F˜ÕϤî…QsD”¡yUdÐÞ _AdÐüú©s޹¿Ì6x¯ƒëàä:ðÙ’Ã>Ÿµá‡í(¨}áT"O -»:ÙêX5)°h´Ä„Lm6ÌÚ˜ÒKaL¯A†nôº6ètÛ‘®? ­þ’Hc¸NþA‘F?ŒV'ˆ.³§©÷µw°ý/äÌÉçZþ¾”õ¨d=Üô‘§÷FŽ.V}0,ú9ÈÔ‹aÒ+`Ô§!ÃÁ -}†ºŒ¤Bk\µq=ÒLÝH5‚Ê|Q¤4@îA¤4 "Õ F¯±'˜÷Aj?ÏxK1Ï#ŸMQóó2úËÏâÌgž€LÓd˜L3aŠ€Á Y†t³ -Z³šL Ô™yH³!ÕRUÖ2¤dµ"9k’¬¡°^€Üú>dÖ{|~y–0„‚¼BcÌ{?µwp$é(ã^ «J¸ô5ÏÅ™ë”i½ÕéÖ h­!PÛ¢f“"Õ– U¶ÊlR²íHÊqC‘SyÎRÈìÍH´oÔ¾’Üsç¾GîBlÿ$v’œaÎ3×#Ì{/u»+9ïTq/𹼜ýþ<ö»ü¿\z1:EPçyC•€”¼YH΋D’# -‡rGdŽ $:m:ó!q•AìªE¼k-b]ˆÉÑùg••|A#Ú% Ú9ÌiÆ?ÄüwQ{Û"î…î>ë9*VÐK!G½èè%Ù=²B$º§Aê…Ä ±;ñžÄyÒë± Æã@TQ æÕ`NQ#"‹7#¼xŠO!´ømò9B‹!¬H@˜‡+9Κ ~/µ;9­_ -4’ÿs]æQQXWþe‘ueß‘Eã=Ä-—º¢ˆfQ¶(̸ â`T 0q×8Q£U bÕ¦­AÁlVMM“`L›6{bÝCRc4ÖhÜú±äÄöoö¹¿wï»ï¾{ xmÌcîb-£×fJ½M”`ôP¼ÑW±Æ`Ř¢ÕÕ” hSE™(Ò4\ãž‘¦°Œl…dZœ¹R™[¹_þ™Çå—yEþ`¢ƒoÁجloCk£i…Y²Î§6BŽ…}à3¶UÃXOÿ|)‘uFç¸(<ÇK¡9]’ªàœå$*0·Ÿr‡È?w´ürSÕ%/C¾y…òÉ{IÞy›dÈ«•WÞ{òÌ»$Cî2ä0MÌicvíEÒF±•ÅìC û™Œ©©¼µð]oÖǺÂÀÏì*_³üåc—·%NËÓ$OËó,ËL¹[òåf)•«e­\,{äd©—“ù39›oÉ¥ˆ.¾°×Ñ߸Hª\Ây`[¸ŒÀt˜CËÈHà»HokG¹[ÝåjõV'k \¬‘r¶v““µ:Z«ƒu´¬$š•M´â„µ’Dß °¥\„ßÀC “-i–×`·|91†Í]%™^&*¤‘üõYH„hð¨r“ÍU6/ÉÖ™ 4>6— ¢V•ÜvЫæÁ -œdά<Ȇ3D¾Ü$­ºÍ­lÀ×J´—aÛb“²WS×2®—m”zAWÚ„þfô_•œ0'»Ðxn¡é³ÓÚií]¡'Ð Ø)ôvba'©ílî–uPC!d Ý|îRš[Y¿å¯pÐÍÝÀ 1þ5Îâ6©Ïv)–ñ4˜0úì”ÜwIw£ÿ¨všòjÖQÍ:jXG àÞ.@ Z.ÿZŠ}-»–äÞK€÷b¬¦Žßÿnaç14«íü,À·tt“цN_tb«ÑßËþï“\÷K„’æžKþø3Ž4@ o²/uìËaöåñh j !j 6pøˆENÖc¨þ$\bqä'-%¾ó¶r?£= Ýçk¥gøY,zAèÐëô&Z‡¡ÞÂÉÈ@ÖÎq†µ.ÒI†…¥XÇÇ4iÃ=9]lúi‚Úx„ç3ðo¸'3áÉÚ#MF{ºýñ-Í ~FK.çwÚuNéćv>QË@$M1p2 uÎ1(}ÁÞ\®°Ž«4§×¸ô¾¤7qÈ›|Ám‘¦¿3Ø}¥lÒdêêÚIèÆ¿…>_{—[4ÿÒ®÷)üÎÁyøWëP(]†«ð%| ×á[òõ6ÃÓ]?é>ùñ€FùÍÑCÎê#ác6þ1¢qðÇF¦./ðlÇ~ ½`0Œ)`‚(„b°ÂJ°ÁzØ¢Ÿ˜ò~dÂ|ÄÔùiôsæú‚Yö;}Ï/îÀà\óp¶eÞd¾¥Z ¸‚„AwH‚0¦£‘Å´˜}³îk1ö˰_Ž}¶×ë¶ìºÅÄù-Óî ÷:‰ó5ÖÄã5þuµ]û|g ±u Nà‚Žíñ„|êƒOCЃ©x“®›Ì¹7T€} –c¿ û娶a{ƒ.0éž×.ýSûññ0“îŸI— èÝÑévÍáC8 ÇZ× ÿá‘ÜðÍŸ"ð§ZIúJÃña/`߈ýÙøñ"ihÁ~1ö­Ø/Ƕ Ûõ¶¡\M¿¡?éŽÐ§<~£·ˆb*-ÔÃa8ôk¸7刦©ÈNÆ Õ­AèŒBc")š†}ögc¿ûfìcߪ£¬£^¯¨N›±½ƒÝØÇ*êÈŽSDå2ï½Pó-ÚT+ünKëK­éï†_¾è…¯´ú±³Ï¡1šL›„Õ4Ê“I¿g¿#?ªEhX±¿Š¬Ñrc;YÅvþ½O^ÃÖÌ}µ5ƒá1%÷;JÝUÊîç<Ÿf §È‹cäÅQù£?=ÑIBg~ŒE#ëihÑÈæ4ä£S„N1ÊehTh¹±oÖ±²5­ÑùHUD¸’ÓR‰nÅ|O¹¿FIùåí”7åc]OÉ;Ä99(o4ƒÑ‹Õ¯Ù“í€ÎpÖ>–h§°óiœ#:Ùèä£cFg1v—Õœ\;¯ªµ keìàÒ§.«´Ã]•:6k ”8µqÝW:DyåÚ{›+çeöŸÕPvw9º“]Ñ ×Æ§ºim‡¾ZÝqªG¨ÂiœV9¥h¥sšV8õ’s¶Ê\ -´Ôe¾J]¬*éT¡âN›´Ðu·¸þA·2»_T‘Ç=z6kÞ\F·‘«ÿÝXôá·”×Ýaôw”Ú͆§´ÎÍSUnþZå¥=´Ì£Ÿ¬žƒUê9R%žã´Ø3E‹¼¦i—IÙ …*4,Ö\ÃJ½hX¯|ïÊó~C9>Ç5Çç¢fûÞSvçæVfµsÍ÷¸¾¸öpåîN”¶ÆÓ_FÑŠp®ôuQ™·–ø„¨Ø7N :÷’¥s’Šº<§y]Fin—ñ*ðKUžßtåúeiŽ_¾fûÏ×,ÿeÊ -X­Œ€m2îWzà»J:§Áw4#„Š÷Ÿ ÿ^o®c®–Z ×Ÿ¡­à½­»´‚R¿$´ƒxª(0@sƒ¢”Ü]¹Á}5'd f‡ Ó¬ÐÑÊ MVFèTCÓ•6[3à -5=|‰¦…Wê…»¦FÔjJäQ¥F~¦ÉQ·59º¹¨6>Ä÷£h@ h;ì™3¸bʹvK‰É|ö§ Â]9¾ÊŽ UVd¬2¢ž–1ª¿fF ÖŒèš=ViÑ)š=MSºf)µkRbŠ5)¦\c7+9®ZããŽh|ü‹¿©qÝšyþ…÷ñ»Ý}´;†ƒ¡Ä`síØ¢¾ô—¬#;ÎE¦Xo¥Çjz\”Òâºkj|M‰V“ã‡*%~”&u› änS5¡›Qãr5.aÆt_®ÑÝ7èW=öhTbF&6jdÏѳ¹•‘‰mû/×åu™Æñ¯—T@T@Så¢Èý2à 0 È]`H°ä²¨1¨ `yä ^ÖK$ëZf*h¹YÞ)=æZֶɶ—vÝ:¶µk%Ö´Ü“›®Š¥³Ÿ€ÝãÙ?>ç÷ƒ™yžç}žç}ßçËšá£ÆŽLô¬ã}%q4Çbb\9\ö0Í ó‘-Ü_EáÓU¥‚ˆåG$+7ªœˆ\eGÚ”Y®ÙQ5ÊŒª—5ºEéÑíJ‹éTjl·,±ïÉ÷5ÜãÝù?N“ÿÃøÜ›Er˜±a #ÇŠ æ+âø q”‡-z´ -£Ç)?z²r£ƒ”¦¬˜XÍŽ1*#&UÖØ,¥Ç*-ήԸjYâ—Ê¿R)†ÍJNØ¥¤„£2%öȘx~àÝ)SÂ'ÉýFàNÆÏmŒ›É[ó©q8ˆ­’8æGÁE9ñ^Ê2LT¦aª¬†J3D)Õ KBŠÌ ™JI,Prâ\%ͪ”iVŒÆ'4˸Q‰¦2˜Ëô®â“.Á÷¼;ùßÇñ³?—^À÷Öb¤€9Ÿ1ô1âA¶éaòa£VÙ¦Ê0ŽQšÑG£ŸÌÆ%#”dŠ—É”$cRºf%å*1¹D ÉÈR«ø”fÅ™¦óE[*Êò| wø›)É<@75؇¿øÞÂè¹~²ˆg×2jSHd¦“Åì®dó8™Ì“d4*Ñ£Àì‹åyS~ùNùC@žSûKèüoÅ÷ÆÞ–t/ÏÚ*zXlȤ,bI&–XÛPÍ,­é…cR4QÁE -*š¡À¢(M+NÔÔâTçÈÏV¢)¶ -M¶-Õƒ%«4±¤CJöɧ䔼K>áyC¾6§&35‹äÿ¹JzY¸îQÎ…Zz-¦ˆ§€XÒ‰oyaë+Ø>J¥ò+¯)¥“5Ù¨Iö0=hÓD{²&Ø3åSV$ï²ù_V«qåOjly»¼Ê÷ʳü¤<ÊÏ˳ì:8åe  ûÏ,â\¨ã~Zʹ€4\öS©š÷¹H£lâ2óy1†’Ÿ€ª!šPé&ïJO¯ôÕ¸J?­ HyU%ʳ*UcªòäQe—{Õ"®n’[õF¹Ví–KÕ«UõøF.•wåZá”ìÄþVümh È ÆFÎ…Ç©ï…ËéÅzr@LÄ´ýI¬ãê†ËÃ1Zí˜(7ÇT¹:BåâˆÕ(G²FÖeéº ¯«ÒÐ: ;л¥ñXÛ_ÁméQg?Ûñߎ¿µÍÔáI©~%g#Ìã=Ržb«Óù|J“ä cš†Ê¥ÉUÛйMÞÀÒÄÐ\€\kð1¯ƒ>$mIÈÒÈg¥ià»üSÎQ;ð½vÿâØM»ÉG'ùè$]ÄÑÊ0ÂP´‡‹`ÏC@ÓuQàN wäûôÄî+ -}àÔ|7²¶Å[Ù‹øËÃO -áFwJ]øß#y‘ûðùPR¸ƒˆƒƒÔåúaô#> eÔ¥›8º º¹tºÙÔÝ4v7‹íÆà1êq”zýîjÅÏiW|?ü-/Òƒø -ÜÿWðOØÁ×1xNÀÉA^‡S€I½ANÞ$'g¨ÍÛãR‰ã,áY†ƒú³§h¼žmÀþèyÏþ×µ„u/`Í…¬35Æb¥ÿº%÷ãÒ°“ƒ>ÞZ™ÀïÁ Ò…Á\âèOpn„ô!µùp8>cûœá"5éec÷Ò½,¶ý†KªÜK²f+¡Å³Î`ÖéËÚÜ߆¼…ÍßÀoá÷ÀOô!|…¿Áøz-§Kp…ú\uCÀÑ«×é É7Rn±WosøÜ¦©ûHv‰¼ý;”–;¸ âãÀÂÁ™PeP h€fhµ(ÈMº‹²ûEyEÖ‡¾»…¶º‰bü7:󺮢gïéßþ‘Kp>€÷û5ïHCaxÂ$˜q`\,<¤ïÑu}èÜ[ªÃörl¯ÐwÄq8¾%Ž¢þ®¢Ì¾F^F)^Bg~I¿ AùÕgXú>?ÂYøu º!ƒyñaMÓðÉZŒøÊÄÇì—êŠ`{!¶—`{9¶›)E ¶×êïhÌQ»çÙÀh¥;Ê:ÏÐ.çi›o‰fÀï»p^‡ÿ'Ѻ7/}£ÉúJ3ðG©ÍøÈÆ~1í`×9UÐ~ YKmÒ@k6Ó¢?ªÚµzGOѺÛðÑ©ÓläSäâ$ß8Aä¯Q©n<ä¼r_ wà_pYÃX›;þ|ôrñ¾"ð“ˆ4ìçê-ÙØ"el• -Ö±ûì×c¿ û«Xù:V;•ØŽý½tÆý’ˆ^Àâ*×…·ÎûØ5·ëhíú·š 9óÂß$Ö‚Ÿ— V,æa߆};öplÕõZ|Ô㣠»-Ø]¯çéÑç¨É³|k¹øÓÕÓæyF„;lÌݘ؀±m°°¹Ù@°an_€L áb ’…Ê-÷¤Mš&kš¶k«^¢5´SWU•v‘¶6Ò¤jšTišViÓ¤®Ý´n«¶UßNCÔíÇÑû}ï>ç}žç}.Wy’Ëôä%zù"Y/ç¯_{•ÂO™b> -Û»þï3>wi‹·J>EOOUE79¼äð“£›}¸Ž!\£Ž+Ôq‰~¹€FÆ6¶øeƒÿõ­³Êè;K ¯0š—Éûÿø"ŽWœ©íÒ½÷€×úMêy…÷åÞ‘ÛÈ`¤ç‘ËHžRžÅÅsxx/YÚ±É8]§Ž§¨c•~9ËøXÆze TtšŠç™ÔOÒ¢³ø„QüB|„éÇøŒ¥æc–š29w¯²xzž£¶°8ž-™çRðLò˜ÉSγ¸hqy¼äi'Ooi=ÄlØfÂf1¾ŒÉðsß÷ Æö½„cï`4ò!F¢?Çјop4VÄðcü:‹öW»y,=Äó|¾¡`oÅ”>1 ëѬD¤`1*§£u˜‹±`6ÆŽ™ØjLÅzŠóa"Îñ¸nÀhüFâ§0œpC L¸ŽÃ’{ì¢_ò‡ÿŒCI"éÿð±šå†%ÿM–—{ÄM–»«l‰¶˜bŸdK° ‹ÄII¦%é˜LTc,QcI%‘:0,­APÚ€!i ŽH;0 ëE¿l‡dcdsèI^EwòUtÊïâ ümä¿DGÊçð§Šð§‰{+ñ3òïšè¶wˆëLíçYfV €3Ô7C;Éã1"—#˜’#) ¤¡/Õ†C©•ÒêГքî´6t¦wâ`z?:ÒGàϘÁ÷2–Ñ–y­YwàËz ^Åω?¡9ûBü’ï¾ x±œ6`¹¿ÌuƒïKlÉfi“qú'˜þL)„¬ ô(ÔèRâ Â‚@v9üÙN´g»Ñ¦ô¢UéG‹²Þœ!4çLâÀþE4î?‡Õ-xÔoÀ­þ êsÿ@ü‡¿Ãyö×Éý<ËüŽ‹m–“6¨à¼Ç6(dᜡgmÏG—J‚€*í*%ÚÔ´ªð©­ðæVà@n šrјۊ†¼.¸ó£> uù§Q«ÙDµö\Ú×à,øø NÝ¿‘Ï{x—mð«ä½Íÿt°M¬°œc[:ÎoƒÔÑCíšx´hdðj2q@£F£VÖ ·¶ õZ'ê -ܨ-ð¢Z€K×gá1TΡR¿‡avÃ+(7~@üŽøšÁN<à™_&ÿ-7p…%~ƒXâó îRÇudŒø Ñh,L‚GŸ†z}jõTëp¬p¨2Ô¢ÒØ„ -c;E즣(3@©y6óuX-/£Äò>Š‹Kü%Å"ßY™‰û´ÿ=rÞ`Ûw©™ó±p€s†¦:üÔÑT:“Õ&9œ&*M¹p˜ a7›Qn.C™Å…RKlÅm°÷ ¤$ˆâ’iX¬ga¶]ƒÉöŒ¥ïŸGQ©ˆ"›È}op¹Kþcmß*qš-ù$µ RG7u´2V<ŒU§5« åÖ ”Y÷ÃfÕÂj-B‰Í†b[,¥n˜K}0•u¢¨lÆò åËÐÛ¯@g¿‡Ç uü†ëWÐ9Dî‰($^#Ï/c¡­¾Ÿ£H0Ïuœï´G'}ãcŒÔ1fìöØìI(±§¢Ø®„Ùž“Ý€"G Œ µÐW4£°2]å -ª& ©ZB¾óòœw‘ë|j×'Äßø,r¨ñò‹PÖŠü&âEÚý&ù.wëã‘mç ×¾ l…Ûi“&úÇIßX9¾éëã¡­—BSŸ†üúä¹5Èu¡vÛ òT!ÇãÒÓ†ì†^(F‘Ùp -ÛHo¼´Æ"¥é!R¿ä³ˆô†=Ü%Çù.p$[;ÌÜx„ñÈu¸Ÿw’£I¿¹©Åј©E낪99Ír(›³Ý¬†Â«C–ׂL¯éÞ:¤ù|Hõu!Å7 yËI$·lBÖr Ò–ûHlýI-_ðY„Ì·‡Û´ýÓä?7¸7žÎs,› †‚Œêi¦–jÚ¥”Z ôQn Yþ8¤ù¥Hõ§!ů„ÜŸd¿2¿pAÚÑ„¤Ž;! Ì !°†øÀ3ˆ ¼Ž˜ÀO¹þqÿF¼_|„gÉ…|›£ÀAfÇYˆã¼“#¼üVA}ÚFK=Jþ}ª io{eD$‚ - Bâ â„ -Ä -D íˆú!L`Ÿ°‚0ó•À!Jà Ôû{âëe7ß#b‡ɽ6ɼ0ÅX˜a,LÓ|öq¯†zJù] PSSm$;†„á8D'!b8û‚ -„¿-,F‚I>È d0éÌ ÿ1Èy7ȹfˆÃÜÐ{¼ôœ5¿"ØIq[ä\9 œš§N‡¹úç€î98™ˆÐ|¸§á_%~ ‡/ .Ë) ._ì»ï‘\Zá!„,8 ]àu'0ÅòÃB(5w•N““ æ§FO3˜žæœœ¡>ÏÐdÎPØgôÙÝ<ÏòY Å]ÕÏqpÀÇ8­ð…Ãç{þÃNž‡Ç!dù†çÔ¹‘^Z±Ø>\Ò¤‹Ää}jäCtü‰Áôrºt…œ\åŒ\¥È¯èk¿Æ®µ©¿KàÎÝÇ8DÄ??8{=ä<éä<çäé°Þwâçrx\Ÿ€OÁ êõ&5òy¹Í v‡áìîh©} øÚqºàµÿŠ-Ç t=€ð!À’À P Fƒj0ÌìØ”þÍfó/¶ìTÿd»»ÏVù [Ù=¶ÏvÐ]Ð׿­¿éK¾u‹_}®ƒKà7àíŽ×¥cïí„+ð XA¦¾S¥Ø‹íZÝQvgê+tÜBÇèøœ-ìïl€7Ø?c¿kÓn¸Žèc’ögþ®òËËXû¼ëä>NthÐàŽo¾ð…ò+|Éø‹ýáú+{n›* ÷Â^§k踂Ž?¢ãt\BÇ{l†Ùu¯mø¹29®wttÞÔ"w†7Á1pü†ûày¹©ÞðâG\VxÒõå¡¿Ûa» -Û°]‡íx:Û ðk %´ß^ÖÏi opÂtDoq¤>!;ßp¼(cv;Áöïiø¸ ®w”™|¾ú-±8¯8,ØáÈÆ~>^À‡rlWa»†©Ø¯Çþ°¿@­Zª=ZAÛXÇf8Zµ•_m&:-D´¶`ƒkÁwp~ >s–÷…ŽãÖŸzãK?¸"à‰'f)XËÁ~v¡cñØN^¶©F[4E›ÐÑ‚Ž&tlÐ2­£™¯!'«ùæ*,4±2ù"Ñ^ï÷qÎ6çQ{œr¶›×¨‹½ê_\1øa…#b¹€P Gcᨆc2¬õpÌÁîÓœ”çõŸ.EÝÔÿ˜(>£©œ;ÀÒÿáFø¹vÎÑfó|ö¤únüÚ Ÿüá3a#<àH‡#Ž8Šñ¸ ž --FÇ"tðÃ)¶Ír±k†K†¦wÉÕ´®ªëZ¬)®ešìZ©‰®TÛmºjºÍÕøîKUÕ}•*{lVEσÓó¬F»]W¹û=àx„óð‹æú2²÷‚u¼ndy>˜ÑƒøÌóêªÙnžšÞÓOuný5¥W´&õ²h‚{’jÜ3Tíž«qCUå1Bc=ÊUá9^£=§¨Üó •y.R©×J•xµh¤×÷~KÅÞm*ò¾*òqt¥éS¡â>TÔg††õY¨Bß*ðݨ|¿V ñ{Syþ+¯ï·Àñ¿0w^óÛ¿ÖsÍ6ò|–ÿš;ÇÃ:rTè¢ -?O•ûùj”_°Jü#5Òߤbÿ*ꛪa}sTØwˆ†+? \Cª•×ošõ›§ÜÀç50h²ƒv++ø¤2ƒ¯*3äž²Bð#èÞêëu £èŠ j€ýv®}˜Läê­$7£‚ÝTè­áA* 2¨ (FùÁåÛ588CƒB)7¤P9!¥Êî_©¬Ð)Ê £tÃ2¥1Œ¥†íRJØq%‡Ú•áàéP -x¿wÁÛœC¸^_‹²•§¡£åÔIqD7¼”gðÓ C°"”§ì°De†¥(#<[éáùJ‹¡Ôˆ -¥DNRRälÙ£–ȵZ¢wÈ}LÖ˜Kàp(щCpí€Ã`é¥<é9°ñk6£àd>«²FÃB£‹r£<”é£Ì¨~J2(-*F)QñJ޶+):Cö˜Á²Å ×€Ør%ÆÖÊjœ©ã"ÅÇ­’Ù´]&ÓÏgzWqæÛ2™üïà}‡àó6ø×1¯däZžÊ§yo":*Ð1CБç¦T£·’þ²Cd3F*Ñ'k\¢¬¦T%˜Êb.T¼¹Læøj™âëe´<­XK£b¶*:ᨢ¬À—ж::“àP+±ßç«Ã½‹¨Ö£yé´Ô £EÔÊ r–fí.›ÅS‰_%Xe±„Él‰•)!Aq ÉŠ³fËh-Plb‰¢Ç)jÀ㊰@¶…Û¶È`?¢PûïÀ-^;Âmí&ö-ð¿Â¸·¼„õ¤”zäù8ZÆ›2>†ÎÔkJJ%ؾý¾ÿóÿïó¾ïÿQ·ÑC”|¼`jK0­ó~Ïa||ø}ÚXÉz¨fžäŒXŽd/“,`0èÉo—JfJ€jü_5µ¨¦«¦ «¦é¨î ØT«¨E‹© -ÁWQÌ%+@3‚ÃT.¤VÞ„ð;@_KüÚ•|VKš«ÙH× Ò˜ƒF©7×D`aÀÐLr¸µ_hvÈ£<È£<iÄ™“F¼F6·w‚¯§Àõ›h Ðes-ô*Í*Ÿêë¨1Ë×2eXÑI˜ä3b3IÀüsøm2þƒÀQð"@òz­cn^‘N°VN “èó$‡À)ÀI&ñOœâþ/\¿T Üyp‡×²Ÿ½° ~ †é„éàhÿ -x ð©Nwà 8 ^oÜ7hð:9Ïš¹ˆQ¸L3xm\á¼Ê|pZ¼÷+À×>VÁ4÷XÆ9Înpšá 9ö癎·Ë -ãûÀ%p¼˜b]7ÀÇàïäñ9õ¸Eƒ|›Fäkõ.›ß=tq‚ߣp_¼‰Ë ~ @èz! 8Àd -ñ¨%8¥b&qOêœÖ×,”¯´ ï¸7·G÷˜¼;¸Õ[çs}„»úF7Û}ø#8Þ¿>ÒýoD€XÐô×xÌo•Çb绀ØEÄ.!î}FŸ’Ç'äñW\àG¸Â8Ä?#Öëˆç} -y 7{•//ñx§ƒû8xéÿäðƒBõ/üÝ?”¨»xÝÛxÌÏd!¾]Ê¥?ÉÃ8 -ôò¸J—q‘q`ïâ2Ï“Ç9­cÊšô¦vé·ˆùuXÎðßi²<©ï˜^Ÿ^GÁó õ¡~_[ÌÍ'22–8}@-®á1/iñ­äï š›zˆ]@ì"bÏ#v9±1®¥:¦:fà)dÜ̲ù%•8 ×)=‡`ÚÙAØ€}`7x¦#vL*%¸Èêºx[f¸áéE„Aä>š¥‘Nìlb»‰=•èÓ‰ïeæç2žùįÔ~òxV«aßÈv²];aÜA-¶±…Š63Ú§am›ÁFÀn‰fÄü±”:–ÖiôyŒZ¥G”ÌúÂ1”øc´W6—ÍÜÄÏ!~>ñg¡Äbâ—»E,C ÚÀÆú™¬%ÛF¢Ö3“«QV¼µXnÁù8×±wl=‡¨Å>tñ ÖÈ3ê¡mÌI³†Á‘Bþ6òwh=úX§)päÁ1Žb8æ{±Vò·œ –òe5µ¨¢Š‹YЕ(¬Þ‡q#ˆ%ÆØYÊ/²Õ>ø`ëÝ.¶²F¶(†ñ$ÁÕ žp< -G - 68¬§jÈ£ZÓ´„<‘Gëå ž”“UJ©\ #›‹J‹õ7Ía¥Í{6h¿^b{?Ëvö[É!®{8v¶‘϶ºõ -!J$õŠ£ºÉDí Ï@Æ2Ž-DåÊ‚Ç ÏxòP¨žâW²“¬`–Ök•,@9ùÌGó‘§Y?À[ñÔŸöã0GÌ^ŽÝméí8rÖК¬¢.5~aŒÍ_\Ýô¸ú0žAð ‡ÇO:¬YTÀ W*NüÙÄ.gõÔðd ÙmÕ$¿VMô{Mnÿ+rÞ“+È÷ÎÂ}´GXo|/ØÂýž­ìDëA]ýU¡9fyý;kfÀÏTØWùƒ5-h„rƒ,šlSN°C“ƒÝšâ‘;d–\¡%šºXã «•mh–#ì€ìaÇ•~I™wõ˜Ñw™à[ùá~É8êÁŽÛ:ZÚ²…´Eó¨‰7*HùáFyÂb”ž¤Éá=41¢Ÿ\ƒåŒ© ÆT3f(Û8NYÆÉ²GNWfd±2"+”Y«´¨&YMû•j:.‹é²,æ/€ï>RÁ+ð·Ñ†îâhkk‡â÷ÒnÑ‚”Ò–Q“|æk²9TÎ(“Æ›â•mêª,S/ÙMýõ˜y¨2Ì£d3[•n¶ËíÒØè\¥F)%fÆÄ¬Ð¨ØÍ»W#â^Öð¸wõhü ÷ý„8Þ÷Ó‚î%mâh«§õYÊÿ y>—šÌ NSiœ•kTFì¿Ù.¨¨Ï+Š_£‚Šl#Û 0 3Àΰƒl -(0.£D4DÃwq©;hc¬¨9©&Ù¬i’&VLlkšXS£i›&=iÏñ´Zcܲµ‰UÓtúœÛÓÎ=ÌÿÏðÞ}ï»ß÷½kPqˆQE! µ«0Ô©‚Ð,å…å+7l´F„SNx¥²Ãë”1WÆUJ3nUª©K)¦ƒrFž7äŒr+%ò.^¦önrïÆÓn-dÀòìóŒÿäß‚Y?=N‘¸Ð#£_<&0Š–Ò§z’ÎÚØsú+&ÃOQAŠÌ•1#RV…g&),3]¡Yù -É*Õ°¬I2dÏPpö<å´(0§C9ûå?â(8/ÿœ›<39‚§ÈÓAþMØÓÖª»ÖlîTi&ÏSy?žžŒæ;¹pM¡gqô$*ÂrškPHn„†åÅÈ— à¼åP`~‰òÇË¿ Ì–¦qHA»>+ßÂ#àC *üBƒ ܼgŽ¡ÞíŒþ«9›¦£G0»ÏÅs%\*èË(¾“E¯’Y³XÖ'btŠýT¤€¢P%ÿb›†—_q–†Òà’qò-©’OI£”,Wÿ’­êWÒ¸1 %¿åógàŸº¯Ø­Nò·Õ Ç:iV¨4‡xž Ÿû±iù|‡åU<}‰¢/!㤀òAò+ Р2ƒ|ËŒò)³h`y¢ú—§ë¾r„\áršYþX -0“嘵rŒCÆ ìSÀWæÖ.jÞBÎõõÜ豉óÔ4°¼/Æ–dÃ%žø„Ó› BûMñÕÀÉþêç -æã0wEß=Ì\°.ìBD.„å¢×"°c'1!MdbšÈô6ÜÚ1‹;‚œ-s8›æKópG`O+y‹ɇK*üâøžéaÉ@È¡3$Ÿxºé µ\òµ µ\ú5\t5\@5™×дЬ!`u+Ø-MÃ_M;…à¯"88Lu«m6{’¼«›ÑÂÎ&P»˜ð\´ýÈß U á»þðòjbÈibÀhÄ[6Ò‹†PÀÒÀÓ@/ØDõ¬G=«§ÈúÕl¶ˆ¡›y‚Cø2¸ƒðÜÚD›Ö-ã\XÁ:¬äÏ` -Ÿï¹XÃáÀÂßÃXÒà G}‡Å ‚‹3Œ5ã9pÁ5Û½XÄᲈõXÄ¢.$ÈÂml|Œå|Ìê<ÌàÜ[\ -nm ×Ú5ÒÒÊ¡]ÓÖ±@!H]/YùÎû@à &ïÁóÜ -Vx´ÒVx´Â£ŸÛÊ%ÓÂå×RÓZºöq.|æj&çULÒ+q4+ÜZO¬XÐ9HfúF$‚|‹6K[ЈÀw+ù(ƒ¡ƒÝ‹¶ïÀº´ â<ÚáÑí dÛ¹x·£‹vzÑŽ°Ú(¶­“8èrë96ÃçÀ­5ä]@Î:r¸ˆ;zgA‡”°K2>Aÿ‘‘ÿ†‰àb{û†îÿå©‹žt¡Ó} ÆÝðèA=ð=¶=ô¢‘u“hß à$ß½ÂÿÝV3uÌ"o%9KÉ“³‡ó˜˜FbvI9Vú†ýçÿŠá#ò=ü uÌôŠÇœôc@E«?e¿b(íE½¬Éa.¢^zÑË¢CœU‡>_¨Z§>ÉQAÞ\ò%!ÓóÔ¿_pÀßû5Ðë5AG¼øøyŸ1“~ ÞÇÁ[à”/Æ gÚßåâ?‹>Ïq¼Ç&8Ç¢ž%øYzqö¢ªÉ]ÑÃ9@êŠ"g Ë5à 7çÞ<Ǽ±=fð/΀wÁ9¯Q|Pšþè5X›Ëðø„½r…Ëÿ*ú¼Æ¹Ž.®Sø Ý8ÙçyïÁøƒP  Œãq«êÜÊüÃ-œÆM\Ç׸ž¯p@ÃE~‰û_õ).ô: x•~Bƒ>ÖG¸«¿ë"QþâñUàmp¼þ›ƒƒA0ˆÒ·ø™Ûx̯5’øc‰=‰ØUº‚›¹ Kðø+<.à$ÿŒë;?á1?R‡>@¨ïk?íéÅeþšŸ«´Î­Óàx¼ -^ú?ni 5è3|Õ5¼îezqA#ˆ_Lì -ý^.ê¨Ò{ð8 3øºwð—¿‚ÇÛð8‰#=ŽC=†·:ªn$ó2µƒÉyÒ?t,¯àY°ï?8ÜìVrŠšüôÈEžr¤?ŸØ£éì8ýGàñ:ërX³ˆÝDìùÄ^‚³]MÖÇØ2mú›ù~žƒAÂ馲.²= ö€Ý ÃËá&¸æ•‘GZ§ÑÅq’+œžYÈ‘LüLV·€­8†ØDžDì©ÔRKü‡©§‘øóX%Ä_ƒ"6’c§vñv'¬¶³iÚPÄ6Ô´…¬ül_‘óøÀ+uÏö:¬~ÔãG-rE¢.9ÄÏ¢²BŽ«1į þDâW¿†c³Žø Äž‡2— …ø›Y™NŽõçy: ³ß°Z_j%yWxñ=ð19çÝvž-ÿª÷ê¡{ÑE'{¤Cfò$ÃIÔløêqÖå1•¡Â ä˜BŽjrÔ‘ãßd—kLÛçÆî7ÛØÆ6Æ`0`n&&`CbH'@B(ÈHB¸Œ„[Fi.K›¤¹@š%Yše (m×fiÔ6AZ·N]5mÓº}˜¦mŸ6MÓ¦MÛª}ؤjÒÚ¥Õ4ïG.S¥ =zí?¿ÏyÏ9ï9σ『ðw fùå ¾8À©¦‰è$7e‚l‡{àW”ú('ߡܿ$xýqéýêÖ§™ó8áÉ× îÉ1Uaû#ÜÊf<ߦg¸·3Øq;¦ˆËù±Ÿ§cX5Ê#DfˆÝ=úÙü ˆ‘MðSÚÌw)ï÷h5¯±Þàûì¡…pÎ$v1ÂiåL.x -8K)gYÃBðDài†§žmðô`ÁÓð ³÷$Ÿ«Ÿ]vàÉ>N×K<¶n²¿î'ø2LYÿ%u )tY¶€='±e¿Ì*•³™à³í\v/ä÷q~Jüþ¶¢íŒ êM5¨+É¢mIÙÚšìQ{r‘ڒ˵9%¨M)aESÕœUSj‡6¤õª1m"éãZŸ~Dõ ªËXR(ãžj ?Rá/ª1~ -bª÷áü:mí:rã"8…œ+GS ‘¿ˆðIñÚ–™¬VƒQQƒUͧ6¼Ú`(Vƒ±BãZ­7Ö«Þ¸Qu¦6…Mݪ5íRiŸª3)˜yNUæ› -˜ßV¥ùUX>T¥å3S¼ÿËÕÒKH…²‹õß'‘bCØ·©ÜE¼ÚÈ“&Kš"æL­3ÛTgÎUØ\ Z³_5–€ÖZjUm‰(h‰ªÊÚ©€µ_•ÖQ•gͪ,ëùm×Ub{SÅöªÈþg;>S‰#¦{LoÀ·&‘çÂÇYgù¾Ÿç{ø ¯(ãC$;I!›Qk³,ª¶ekÍ­*[‘*m媰W«Ü^¯2G“J[åÏîSIöŠœ3ò9O©Q–ŸsWÞœïËãú£¼®ÊwÅâ6ç¾ ÷%æÈ3š¬3ŒIcuèKZ~vlÁF9Õ{âÌIW¥3SåN›Êœ.ùù*Éñ«8' "WH>׿¶© ·Gùyƒòº§åv?¯<÷U¹™åʬª‘©ªQÆ5í`‡ ÁQ¥)-¸¨Ôê[J®þ&øø›R‚ÿVj0¦œÿ²ûÌvÆŸ^îDw¢‡;ÁXÒƒ][ðK> ‘'ø¤Ÿ¸êâd­MSfI¦š,kr@¾ µ¥Ê¨­Vz(¢ÔP«RB=J -)1<«øðÙGƒC˜ÎF%„™-Ÿ*.„–ÿE8O#»çwr'EÆXw3ªnçy~iÀ–jlõãñq0:™R”±Þ¨´õ¥F²•ñ()R¢ÄH•â#õŠkˆòÃÕ‚/Ó-ÝÀ ù ø+`ÞŒ ¥àY„óä€tdw‚ŸŒ²îâûSN’¬Ýû0›çuØRÉ;ØãäHfb•ŽM ýˆŸ~fº/Ðäwà‹ø¢_ôዾÕKÁíãÂõaxõÎÚ†¸í eÝØÐÓ"Ü'qÕ!›a$ý|Þ:xÄžâqb€M¶ÊÀG‰#œæÊaƾÂ{i°{™s÷ÒpñÅ Åmx â´A±çY€2`¨Û͸ëÁÑb: ß þ}ø0q8Âk¬]‡ÙAî(ånìÊbL4ðn"ÐLÇ„àvLaÇvL"„&i0“øb’K4A<&è‡?Ã¥C)î{—Ãþ†Æü1ˆéœÇŽJÏ®Ðw‚Ú ÖJàå™í9>£«Àóøc;æ±cžÜ˜§áÏÓüçÉ‹9.Ò‰?7FÒ³ááëBîçÁ_HÏþƒˆéùçx| -ó¸:;_ ˜ÚÀU®:G;0‚€ ¦ ê‘Ø\|üùHäÇv,bÇb É,RØ(2 $Ôy{žœ8G^žå~žù;¢õ?š‡o’½سóEiã—©—¹‡W¨ÀH*Ç_…ã%p p ŒÿÇ' 67ÉÓ%ìXæž,“e -Ê2¾X&±–8äÒ«¼ó>ï’—_{ ðîÅ´n¸šá¨a¿"örÜ„Ÿ²·*ô_üŒañ î|n]ÅàîãÁäMð>¹gV°c´B~®PøWðÅ -I·ÑÊŠtÿç¼÷‘†9c/gÛgè–TW6\ÆÛ÷_Ý›²Â¸†ï€oƒo=^I/ÂGéÎ!†R6òä¿\—{tÓåÆŸÒ6½%inmzK›¦÷¦4I¡IKK EZ.¥ÊE ‡¥\„1AA‚È Û‡ŒÊ ásˆ²)Ê`âe2Ïq2&‡s“ÉÓéYöiOÎæöÇs~é/éû<ï÷ò¾ßçÔÈYzö,µÑG¯ž£G΋>Û‡“é;®h -{mfŸ5¼*c?™ìÅô,küòôs¼5‚§Á™¨1#¥ê‹šÄ àbÔ°þ¼>«œ_×8?ÿ@}^§GnÐx7Øüu6výÌ€çý/b@2°(CAw6·Ú¦Ïq-gÒ¿ƒ³¹£º…óùÇu7ôg¼ÝÇ8Î?R<7´{—{B¿GÜeýEï³R?.€ŸƒÁÁÿj臤êKüÝ?ðwðv·TÃú¬=:‘uïч踂ŽË8®÷9,ÞCÇ%t¼ƒ¯z gz‘â½@ý{–཮kz _ó«¿å>üŸ†/À߈ÅMá³ó_¹p•ê·ªÒ»øÜ·t—ÞÀÉœGGñ8ƒŽ×qy§qa¯â6†ŽWÐqR›Øý6ÒØKùü'HíÛz–Èé0Øvƒ]_Ñp|ÜŸ>ðüÝ›øªóÄâ,Þî´¼:…Ÿ:©áú)îìtGÇs¸®c8¾£8À#øÜÃè8ˆŽZ‹»Ü¬}äd/ µ‡_õR@»ôm!B¾‰à#ê_Ây \—¢åõªbá3Áe‡'Ž"Ö¯¤M‚z†ÚØG<ö’—ïS»q™½šÁú¬?_;µ„õWh‡Ö‰²²‹ -9È1ö’%c›ôÙŠP9œ`-¸ çïÀÑ’ïo±c-™Ä^,p9õ=å±R ëûô˜BÚN<¾£Q¬?–õ[92ÛÙõtÖïdýn=L<ÖéAÖßÈ‘¾]«Xe»¹¿»\Ÿh¼÷E±ô󟧢­x8zõ‹'¨‹môH2ÙG>eè÷k=:BÇêt5ý²J´R“ÈÂ=pt±€n¹ˆ¬Ñ½(\ÈæSݰté*ôO²ù.r”œŠ—ž?àä˜b¿Ì˜×C"œföã€'›½ÀQÆê~ôÙm½«m†§žIšG<º4›µ¡æªv³ƒ·û©âŸPAo“½Û ¢É ÿyš#äy®™ƒ©Ònžs´mE×»o -œö” ž8ÊáðÃÔ,ÕÁÓOUÑB$Úá™ÉúóP´”Ê]£»‰ä*§…,§£ÆéOdñ_ 2€“\ùGð³{?vòìáˆßÀX´-Ë•s^tSŸsd…+ž\x -á)‡Çcž:ª³Êh†«ž©¬ÝÉ_‹P¶’ -~”êÙÅ/k$?‚f§OþàyFŽŒ>OqÅm›¸r¿ÁѺ¼!.s¹Sc‰£>;\™°ä±§"öã¥üp᪇«®±pMdíptóv¹†‘Zª«f ³^Ðéw@dGÝ\‰øÈÅä¬çó -Þ-a4œÇuÓAL&cÔjHÖ¸A©ð9àÊ‚ÍÍžŠàò*P}LHuƒÂª4Z5±Œªê¸9Âàˆ_'üùâŸÑ`ÃKª0\REÂ-U$Fpˆ+mw¹´£‚äª_ÃÕ²¼„±‹q¬ÓÅlIŽî&&cÌqjHNQ8Á¢:Cšj²U“¯`b±ª+4$±JUIµ -$5È—4V•ÉíªHž%oÊ•¥¬U©q»ŠO«ÈxB…ÆwU`º¥BSDEàüOùèÆ®GÀê³m%ã#P1iï×É×(ê¤ÞbPµÉ¨!F«ÆtùŒ9ª4y4ØT*¯©Rå¦j•™†«Äܤbs› -Í*H½WžÔ5r[¶)Ïò´\–ʱ\’ËzD” öÃÿ$#Fžöá#ϯñwï§£¯8!7#kÈßš¨ -‹Yå›J-*±äªÈR¨BK¹ -¬UòX‡)ß:Jn[«rm3ä²/T¶}•²ìßR†cŸœŽ•îxGéi•3-»ˆöÂ÷Ü[×Õ1n‚ÅŒ€sð¶ÓÐÑJnFS'džR¯ƒñ*¶U`·ÈcO“Ûž¥<{¾r%r9|ÊI );­AYéã•™>MNg·Ò+åÈØ*{Æ^Ù2^5óMð©l™ÙÁöý¼›ñ³kûî‹°HŒ=S°l-~ò@­ £Fª¨‘òÜy2“”ë4+ÇiS–Ó©ÌŒ\P¨ŒÌ -93‡*=+¬4L¡={ŠlÙ]²æ< KÎ7•êÚ#³ë¸L®_OøÌ”zÙÿvx75RxÚeXÕ…|ž…¦v¾‡Æꤖz P#¥äÆí6(Ëe”3Ç¢´œ49\ÙÀ#»«L¶Ü*YóêeÉk’ÅÝ.³{ŽLùËddHOñô*ÙsLIžsà#>¡”|îo¸¾ ï#øØÕcð`Ÿ;Ð3 mc‰ÑHrSCn|Ĥ„˜ä–Ä*½ Y¶|³¬ùvY<™À­TO‰Ì>™ -ke,¼K)EmJ.š¥¤â¯+¡x£ ÅßU\ñQÅŸáy|Î;îröÜÃÈ» °ª…žÝã±chiCG3#àt†Ð2˜˜Ò7ÙÄÄ^ž ÔR“L%V™JÓe,u)… %— VRYH å2”·*ÎÛ¡XïÅx×K^n"/·¢—ÚË´àýŒä2K°ç-p®gä]9‘ž]mØÁ œ èjF_-CF2óÖ“bâ$&Ö@¼Œ>£’*-J¨L“Á—­x_b}^ òSH~‚ègCéÓÀc˜c ê~†cÿU€×óE´þÍp>4™óq*=æNÁsµÓ¼–aXFßh©ˆ˜å11ׯ+)”¢¸`ªb‚A‘ M¤h‚PˆÂñ¡i`À†0MA Cá?ÈÄV†jæ)x6Á»–‘{ùLzÌÓßÂû‘Ø£êVB†V7ùq7 õ“Ò§¸‘É$ŠK%l\xaÖ0¦©Ãt˜‡Ù\xÀÛ ÇÄ Ç¸ÔcBê¯Huh¨cžCæÆ걓zœCO€N0yy@Ë0¾÷—"´d£×F\Œè1ŒãR‹¿ƒ¯kæRivHMdM4p…ÛġӄØ&’Û4›€â5GoÅLíLk£.KhhŒh öký\éÁni)![€ š9ŸÚ<ÚÒºh#;’?é;: “û6ÎçÖëá¶æ·+X¯¹ÌyŒ#ƒtóöp÷Ò ¨30hs¼1 ¾×;áÿü 9éâ¼tG7Úè¦&Ý4¶nrÑ…¨ºèU]Gyî,¿A—ûÿ¡U»‘œEpܳŸÖÅ:^Jæ<ÿaÖ<2h6¶’ƺ £õÿð’gG£‡I'¨M/uéE½è³FÛG.úYë¥'ßṿª†½•À; ¾1p¥a0œpÖö¯{ðLß {%€W@æCa„!WŒ®Ò›ÄÑOý c *ôÍr1@áØÔö_RûœÉÛqpfÂËL´tL¬ôR€ãl`íóþuÁ€ü¦tÈ^o$6hVßÓqü ø-ø„8.sf¯pé†>¯rF®qØ®Rà+Ü_W.0Yë€ Ät0R·™Î¿gj¿‰sø;Îå®éÄ×ø˜¿pH¿ÂÙ}ÁA¹Ž#ûGøGÄs"^¡@Ÿà ¯·õ‘¾ÄaùÍGÈ>|­Túp¾ˆ!X·ð4ÿT |ÉpåàŠÆ²~kÏÒg8‹Ë8K8ßÇG¸®‰ã<ÕûÄñNô]<ÍEüÝD4ËÏ(Ìy}¬×YÕÏýZ€ûYðã;bà6×ßÎÔ®OñÀÛ}¨<ý -Ÿûnâ-ÜÌÏqSýäãMâ8ûz{ކõSâx8Nk{ÜM)»(é3Hé4’z¹~=ÈëÇ1pÄp;ÀÍTÁþ¤_+Œ}XáŠ' Ž,ý?u§ô -Îæq¼@>zñS'ñuÏã/ŸÃ >£zŽÍ:=E;¦§N*Ò£nž<¨7ÈÌUí#Ëû`Ý :À.ðœ_ORòËìÜ ÔpÙùµ/±ç°öh=‰kë!G¨Ë!Ü^7îï ÊY¿ŠöñJ¨gýõz\›áhçßÚ×ÓDt†*½bn¨ÞÐ 6ëù^ Èþ489x,ÃØ‹®h2šG»ÊÕâØM>:p“í¸¾6üåNèv4²­¶Ç#Ô¥Y ¬¿E‰fƒŽUŸÖRÁÕ0®‚weõÀÏÿ ÀmªíàX åu’‹Ç8#» ‡ŽtâÏe‡£ÉöD®ŠítØîƒ£Ž2­!ŽUÔeÿ/ç‰Z"\B.jÈäƒ(f§¤mVÃ]ÀÛþ£O{í jþÖÈ­7­\´ÈÌ~ìð¸©tûÈ þ\âχc“9Óà)„§ž2xªY»Å®C-­Z@öæ³³R\ Ú,F}óàö£œçŠ9Å(ô48ĵ×É5¼‹xèêƒWã:EÀiU5YJϨá¼.&Žjâ¨$Ž -‡çn1 öBxæÁSÎÿ¢Ú(fYjç›C(¨—êõÓi>'{·ogžçÊïáºÝG;kç}+ñl$n9¸Cà5Á¥…r²§xx’ÙK<¹ðŒ¢sG“ášÎ‰¹®x*‰jê]r¶ñí~"}ŽnwUL%oß NqÅOà*c}œëe;WÝ&âYC›¯%/‹×P8¼fòfc_1ì)žö” k.\ùp‡«®™pÍg•ªáÓÕt¸ÔÉSOñY:ïຎo'ÿž`ôúQ:9-xÛâ©w3Ó—…þQ‘zݯ>;\±p%°'/\™p ‡k4\5†hò‰r¹È#{#PN. -ÆiÊædgqeê—dñÏʺèYŒ‡³¥½Ã1@3¯åz©MaL$?óÉÉr2ìC¤&…X5>È¡1A.å%jTPªFgiDð «a!w+'t†²C‹”V¡Œ°:¥…7)5¼C)áO(Ùð²’ å1|!OÄwòDútÎ.®÷ÝX‚m )‘1—‘›PANŠ©O!õºÇç´†*ßdT^„U¹‡r .eGx”‘¦Œˆ¥GŽRZäyS•bœ­dS¹<¦¥J07*Þü¨Üæ¹Ì/)Ö|Q1–ëà{ÅZ|zþýxÈÆÏ- ‘k~ïkËæ’§™Ôf2µKmFØÂ•i1)Ý¥Ts´¼æ8%›“•dÉÇ2\ –±Š·(ÎZ(—µL±Ö%rF5(ÚÖ.‡í¨ì¶—e³]_‚[¼÷©¾}ØÃvÆ­–IŒœ¼Öãm3–ßlò4Lb ÉÇ ‹ RšÝ Í¢„(›â¢bä¶%Èeó*Ö–£[¾œö»mŸ%‡£TvGlŽõ²F·Ã²D¿(Sô™œ’Ùù/ÞûtþN¸Ûð³›±ˆëA¶ ›4Ÿ8î§6SÑÉ42’ÚdS›wˆâœFÅ8¬r:ŠŽvË,»3äÉæœ¨¨˜é²ÆË»Hf×™\ÛéîR„»Wáî~Ü×ÀM>ãþdß{àÝÁ¸×< =‚ZìQ1•ð]!µ™2ŠYäQ›,j“LN\ñr¸,²¹ìŠrÅÊêNi²Ä —9~œLñSeL˜«È„*W*,q‹B=ì9¡ Ï -N¼¾UH¢O]pí†wûLúÂ,úXÆß•|VLl÷’£)Ôf9É#'äÄCNœÞPEyL²xl2{œ2yâeLJUDÒ0’Ç(-U3å¤Ui0Ÿôv½|:É®Lô1v™ -Í¥B+×tʵr‚)—ØD]᥊yÐÙ -ݸü~0)\ü*°á¢Ö¸É½¯Ã»{‰œXfm…Õ°Èÿ³Ù2É÷V¤á|ÒßµÈ=růËÄ”èT£±MÐTÆÛ©ª±jE¤Z­WíÁj©^v›q–¹q˜*ËBÕ˄ΆKZã[µo¹5Öc[­¥_ƒU°`¥8,õŠïGñÑ ¶ôbK‚^2Ù“:£±hhÓ5“i|1•/jöZ­VXA>üÒך–Ï_Ùìé,Nš7Ѧ _\Êõš[=_Ôi¼uš`xÔÑD@Öyyö°_Í2%Í´¤ÍxÁ>r‡ÖØ{Ô¹r“œ`jãf>Øè+¶T±e$9 `cöäðU:› I ~Š%‰‡‹Ù±ˆ/è¢/“ñh¼… _è°òs)q¾©qž…lî‡îÑ×â¾Úêµý´K·3[èj¶’ ›ù¾M¹ìL_ß>Ôüì7\;Ö²c­˜4²£Q“kTT×((kˆx¤[í«w -ôሕ¦æ¦Èå¸Kk4‘ëU»ø@ú®ò{.ÔJ£1Pâ•¢4à™ öÅ—qEÛÚ`ÜÆŽ­ü±U®nÕ\¶ÒÅɼE±ÙÌi›]lÓÁˆ'$à“Ó{.þyìÙˆǵ°ð1€Ja}ü€ ˆ ð¹!»ã÷…Ïš¿:mfG3m4‹I3]4óE35q^“÷'€Ç$ã;‚ði츎[Éuñ är@ -1³T÷ƒ<ȸѹm¸ µüa‘éÀ-ÿí߉MKvÇ€z«˜´(j-|ÑBL‡9÷ðÏ~×àô¢3ÿp.ÇQ3 xÔ­ê€GònÃß¶dÜÇàN¸;¹|Ü›ü}áï6Ü÷'Ѷ$œ3'ÙqRý<%&§4ãS|qJ~œDzRÍ>ù”çÞ‹•x/Ã9×ÅÒf ž|®Ê¸'y®Òj= kÍâٱ ]øý0¸’Õ!,£‹Ú£p~ †œ¥Ó³´qÎ0rNÝ<ÇçˆïüQððÙ—cž;ÖÞ¥¸Ë`|øº<˜\ø¾“äø^ò\e>´›ö¥Pë³ÆF<?WŠÃO£cq|”€x™¯ª£¯iøoÐç›räM‰ö_¼a«{ý “u| ©= Ø&RÿŠKâïæå¿š—Û6¤¿˜ÛßW$þdvÿƒ}ê÷öºßÙ*ß1?ÿšXß¶¥½i¿{…#_â¬ú\ü–YŸÅ3Nmc}¾ §àË6ü>±Ó|d†ÿÀüþ¾)ü]“ú;¦ø_šì_³Ãü‚/±ã…ê9ûÔ³ -Å3¶±§%êOl»OIœ' ø•3d,‹ÓmB¹ñ²=âE;ÕÏbW8{¼;Ô -Ã,g_æì…Î^êìUÎþ*IltþB¸›t®‡l®î ¾xÀÛ÷Åý×Çá(´À¡¤ Ÿ$¹/¶‡³“gáÊÃÓÇ ç—:{¬³'Äivœ—6½l`÷ÛÄîµégÇÝvÌ;Åå˜Ýêö8hûCºžŒ›ãûÒûQú§Oõ)ØÍ s‰'éÀÓI©=Ò.ù \¹xºãèëü¡ÎÅþ±Îïì)îñç×9žó)#+œ¿ÖùyáJ×ø¹9š¼½Û‰»xs'5íÀ󠒡 Tªx>)í3ÉtSI½•‚/ WBé)t~ÿø¦-ívì·Ñí£kè´É~¹ÇÆ»›FvÑÈNZÝÁÛí™ÛâªØâM¬ÞÀsëEp]ü†z>óDk;áùdе¥ßédI8š,yùb¿iŽ|ENàü­b”¨WÄVûåf{Šc–ÍrŽVÖ€co¬“5Û(¥É‡¨÷žX‚e1/âýÜ sÐSG j+ƒG’¥Yùv¿œ™øºáꎣ7Žb%8ÊpTÈŠ*J¨Á3ÏL¸JYß–ËÙ²œ_h9õæ -ukŠ<©¡q쨔+cåìv”Ç0•´ ×jk*®zç/jWÎPY2„ÂÑÄ@º8~ïRø¿¡5î3~Þ¦µnÌ0^iÛè³5Æ%Fõ9F€|2™fÆ¥eàÍÆ™ÀW€µ®~î5ˆÃñÆUƒùbµSåÚì'ƒúRYo9Z¤öRm U…ÂNÿˆÂuo‹Ýí€Öº¶û{={V 1ŽòI=ŸL3¦N ›±9)1ªkב–%©‰Ò© ¥ÅÀ”þQœ2$ú§²(µ"út½Ó¦GQÚÜ(L_=Ó7E÷Œæ(È8ù§#/ã|$2ÞŽD—#ÑUÏÀy‹Õl¿uàjãÎ6¿×EWØmçóÉlqšb}'6åt2"·s ÎîºæD¿ŒDôÉèE]zG¯.ÅQØuXôì::zdVEAfmäg]yYË#‘µ1r³›àHädŸŽìœó‘•ó|äïÖ8†ÿÐZ¨”•#èåþ_æóyì›)N“è¤RlF‹Í0±)N¤EQnfôÈÎîÙyQÝ3ò³ûF^ÎàHä”F·œ±‘›;9rrë"»ÛÒÈêöõÈLì‰.‰Ã‘‘8é‰Ç#-ñ*|èïÖ¸ßVÃkZ;'}ý^kŸ\Ê–9[CŒ¤Åëkä(±)›þ=;EaAzä%²påFn·‚ÈIAqü—ý2®ñÚâø¹Id"‘¸‘AB’(‰7Q\³ „! bŠ ©)D1‰éQ5 mQã«šÕØ¾š§˜ªUZ´EÍJI'÷Ïûn¼÷Ö[Ëëzß^k¯{“½ï9{ÿÏŸ³w)‡Ê¢¤c aë¨ÖeZ«2ÑÂRÛ[Xh‡ sí¡q’ý¬ö$z}&fûDöUŸY‹™¶šÀˆÈÔ[SÎFÏÙè8›0ñ…¯*á\ÎL8”µ¥´öÂV[FØ8¹k'OaUÖOX– Îu„¹s3¡qahuaq¥—u¥1v¥Ùteæu92_¸<ÓÉ;“}G2 ¦ÝKB»ñ½#ÿ‹ ¶&`T—q©&˜€I%ÆYwøªõ4vî6ÂÆÍNXº9 - 7gaæV^hÊa,¹ÝÓ½)C"ƒ¡Gú`C³îÁ‹äÁÌëA—àN îyb*ùcß4Zï̲IhW¾GóóV ‰LêgMb©g½XÞ…8|ÍDIQÂËNh<)OŠÇòz”DòDïF(½´73–Ï@tßi–½iN½i6½ˆÁ3OLfÿ1ì;¼ïCuIËÙ…ïQáÌ|ÄÒ{b _âð€?Zøj`&¬Þ±.v?._ŠÆ ×û¤Žþ€éÏ‚þñ(3–ÿ8”—ÑWÒ—Û>Tz.²Ø?ƒ½‡µ¥.­7ãi<Ÿ¿%ÿoD,!`R•8¼—\à‰)ÚÖÐó`.Ð .¯jÄÈ¥HB–@H‘1°%ÚíŽá@TèªXb¨’/&f:û¦.c¨K4®#1ðwsb©‹=˜8|ÁÄL´¤fÇÒ–œ§µxxC¹ÐCxìtœ‡ŽóÐÁ $2Nºæ(‹ê˜mt †5&j0T?ŒCpmG2Žaôè×EˆhË9K(ñU/0q&{–´ž@âAkõÁ¢XÔ =—ˆžóÐà =‡§ÇYÏ&zfͺ ·uÞêЭÕ>Hð¡VË^iPfPwÞ‰žÄЋº¤”ÂùŸž¸‚ˆ¯˜¸±Œ¸ØlxH9,4œÙ²…5 Á‹f`Ñ ,ÂxÃ808Æ!†qÐMÙ )]I†ÉÆ l„Þ"‡1Ó0BTIýà##P$CcbÑa«Ü’—2ÄS’c5#&šƒF¡Ä&ÚG;Τ-gÒ–‰‹Hê#’óˆ„D‘¼ ‹´¡>[3ØFÐ5¶b( ¿Aüb${¦¼išz¡Ñз9×&žl^}„p"F;b2!µ{asÃE‚‚­ -D&Žxâˆ#Ž8j$,byüb!p,dêLðY¼õÃ]Ù‘!1š‰¢C¾H£l‡p…õÊÏøŒÂñ¢Á„\q .»÷…°àçâU%NšLH„ö-Rø‘dch€’Ê -Ñ^ô¡û@äDˆ•À½3Hvägxíq™|òDêp–„*Ýжhc´æHêuå»}û§6¸ñŠ7jê«ÊŒ› -?R‰#•3Iõ3<†Ã ôP€ŠSÊ,§>Ÿ†ˆÅ Øž„Eé†q…„އ™\·¨=ß-øR'6û†¦;Ûø9IA³Š”zɆÙœI6—|XdAî,@œÄW°æÖ¾+Y?†µ[ð»Ú“©C ÷iB”F-¦›ý™(¡ÓŠ †-A[(Äã÷¹¯üožq@jÆ †âÈ¡^s8“j$,A¨E$½hú~?‰XhÚŠ½ôìÈ>ØÇõ-æוaÏ0|É!äStÅ+ŸR)5ÚDÁ@*zm«ah’CãFø±nl¢V7q¹m‹Íp›nÂyÓQÑš+£{±ŸOš#Ok ¹ßRã>«ŒëÊ5yî]…€Nb‹ñs›0 ©;P¨^84~…îEš±JˆcÜÇi¡¶Âlª)ûÁŒ%dátÍU¶Ÿ[ÛPYúÕÊ‹#Œ¥Wq†¢ý€rF‡tÅGz]‹¢âî(9dj_8ôºª`¿Õî…]Äü¨à°­ìK‡J7HŠÓK‡wsÿÙþ䓨—1TTÖ..Œ(r°Û®”ÅÝõéѵ}­4fŽ>¦z±‹{–ÏŸ·à£µ»LÝAª¨òïÊÏ»sÆŒzfÊzkfL°½(¿Ö„ýÔäCõ ~ h?›j¤}«CJö§3ëÕEÐ%‡/c_TV¹ÙJÓ*½p°ÎT°_ëú²xEêì×y\Íùð_’^HÙÚ¨;WiˆÙÉÅ Y¡A›"ÝÒ­[öˆl£!¤I²vå–^ÙŠRª9­$ÊV¦d2öåþnqÕy>ÏÏ=/÷™—ë<öy÷;ßsÎóýžç+N˜Èw $— ÓŸ°ümWy0¦’ù\°*c »†<0gלˆuNä@§Bö„‡É!“ˆÉ ‰·ñ¯+‘‹G[Yš›uï?Rê|÷^î—Èrr¯ÝýH®¬/¯呞íëªüŸÝí,´~$¾åÒKý]×Kn±`ÅÀÿôlëï¥ò‡Á=ê»Zò5ö[7ì‹q|g‰Unr§¯Ä"~²F§8ð—ß›-cYþ¸ÙÞ Ä‘\à³–-K1¿EsÁÁ÷Ø)–`[ƒ©‘ŠË>©ºzy·H–•™‘™]PúF(볫Êܤ;ËdqÁóF™µV –Lk-²ÒzßPß”JäùK{×wÜWÇy~Ç×P®'C8Ød,ß´.ü†“®õ Á:ÚöÝJô£@7 òdØzqFÙ@ˆˆ` €Và"°¤ 6X˜ €#‚Ÿð¥#Î ¤Ž Z¼ÈÃÍÅÙe¾WÅ”õùÕíÔƒáþN~ÒaÉ~¿qfuÝÐ#E".Ýlßйy.ó’?XÏŠÕ µä;®ïmÛ HK¶=‡`(tu4äG!g‡ÔnÞð×_˜à:‚±^!ˆïB@——zhCÌLZ8ÄlÐ$@®ò -®²+è @ ‚eа!¦Oܶ €Ax âCŒ8ÎÊÖ~â·ŽNs]=¼/‘˜I•õûÖ£ü3Ñ¡¶M*{q6lî°jï¾K~!}9Y¯áËÖÅm!ÞóÐ!í€Û_œ7òHlêÞ°ˆ(­®Qpp†‚j¢`Ö0G°ÀXs,Dà`+‚¡ØÔÀ/xŠGÐF°@ó§ÒšSШ -sÐ=ét¢®##URuMÝTÄü„¤ó—³ò‹Ëî?|òšÇÊúôzq+[:¨º³ÖsâÀê‚åMžnþÖ´áÛÚËòú›ä‚¹3´‹å3šoƒ\P…¡7.‚B ¬<¤L 0 -wzB¨ýFÁ1¶Ê2 -òȆ×èˆ ÀDG­r€ <ƒ6§Xà`3 -üˆž83ð|6 Ô~!Æ]{=i¶»ï’5á»$€²>¡*²OE®t³OˆªŽ¯œ1À°Ñûoâï,Nõ4•ÿªðGO\Ùž~ÙêpcÝŒ)ì àGàRÂÀ0X»èÏQAÁl²(fà4ˆ¦ ƒM<ÒAoc0n¦`—¹¿á"ú§0E°€>‚§“)hŒ@<×¹L"]ÉNÒÚJY¨·ùs·Á®«r ,¨'ùé’%‹ßìçhݹv -ÍÓÔ ݆/¢UÄ)ã öÐ|›íáJËæn¡×P‡¡W›‰ -Øx ì£À—•°k’àFÁEà‚Q®…`0|”=t€[“ 1x}Ôf«pÓÁc3ܰA&€ŠÑ਎pJñù  ~ ˆò7%µ1ˆû¦¸ú,Û°ëPRN¹Dª,^ïHýU0u€©±Žf>ЇèÏ7웻ñ]zQàÏZV «¸ÊÁqúˆ6 S𠎬d`Ç0  XA#ø º°G¼V´Jè[ò(ØÉÀ9 -RÔ¡àWvHýoÃ^0¢x¸3À bx -p`+gQ€£(^±£‚1£ö¦¤¡×±ÇûÙ>¡±—9¨½Ú–ã騬ÿ¡*ó®^¾œxú§ø#qb ,Y2aHwcýVMëOüb’/f=-ÄÈ/} Öô‚-ö ´£`=·œç€¾}’ék஄)˜ÏÀ -Âð§à4ÎÜì€`"þ¶ Cø!À)çp3:ÊmðÄuŠ@ÁXoQÄv£ ¸£þÀn¢gn3»”ñÀŸåw:sõö+*ë÷ªòŒÓq‘ÛÂBV- ü›—‡ë¼M$Nre®O{®}v“ ¨R`ÍÅ Â8 3J°yM˜OÁ4ì)XÍŒZGøyºšQðj{âDM {ÙÏ_Ï€ÑøøY ‚ÂöžÌ¸ÇÓºª|)ýweýŸUefÂᨈðÐÕËë.Aó]]éãdci¬§I~\w°„í<Á—€‹8ðº/–t•î èP°¿gÛ+÷ùe >*7E`S# Á Œñ3\ƒG„ëHpχHâŽn aKS[çÕK 5Úuîk;Åmɶc™Õ×VùÞƒ iEÞHDÊúòêíÓ_+ïß¾U\”'û9GVXJ¬Ýë¹9MŸ<~̨áƒô±´èÖƒ™I+Õÿ¾qøÖ¤ij‹L9%ý¸Añ©„0pŒ‚cF"((…`¢77QØÈÅ#¦'q»Üù h[:à-H,õn#*Z_YXOõ -=V…yí!´pÁ’°Ýñ‹ñLYÊú´zY*K;—x2þð˜}{"wîü7ûeÔrÇñ„"Iwž:G”Sy¼\šV[´<¬6'RÜÜÕ¹n%Ü:溬'5Ém¥¤ÖDtbè¸Z·¦òºL\·&$ -³–ܾŸï~vç¿×Ÿ{¿¶}ß»Ïç÷ùœ<%E…„&Ýg™§‡›ËÔIN£†¡DHÁJÓê"ÔNÆ„dDè]ƒ Qè!öcØ@ -°d *¨°>7 º8P°W% \ò² h¢`3‚^l:®9º3â Ðc<…ÍxWo67µü!ÌõúÊʺî¡Ìó·[ñЈZÙÒI‘ü4Ÿz{z?cŸvTäg‹„‚C |Þ®ø¸˜Xî.þqDh\â1ÓyâXǶC>¨M|;…“egA¸ ¨ð:Ü‚ 8XJæ[7XôIÐ|0|ÿ—ú›¤N‹7€¿ áõw[{çEkvËàŠcàJȬÅþìÈýi%J-žöQ–#¿u¯Ý|FBòËéѵµ<~ÐÔ V©Tõwž|“„ä_¥JvV’—{J,ÎÍ“ɯ)›ál/`¯ ¤ûùR|Vx¯ð¡®fmß „œ96 þpL1c½ÐÆ3Y0†Ïßœ,‡§/ÿ–™N‹ÎPà[@õ¶Q†t´+eK¢TÙeæ9Û-pó -âÈ®|BpWãù©×$ˆ ¨ï¿%IHþOÞ¿y­Óé:ß~0?_(g¦‰D¢ô,ñ™¢rE}K¾cШ+)ÊJ_*=ˆÍI,€?!ñ4Ðl=Ã…—[ P½Õ®¿=¸Äž¼ [˜Í÷þö ©}gö˜… OZO$oê!xLõá=é¥5ØdbBCö) ÉO¡Õ´¶>ñRÛIðrþ\šš’r"#;¯@V©2[ƒÙk©þ«‚˜›cç›Ìlz½Œi5a#*¹XÝmþ?”;¼6ñ2.Þ#œ:ĉÖ™Li­Ã’¿«ßÌðü_8L+ endstream endobj 21 0 obj <> endobj 26 0 obj <> endobj 27 0 obj [1.0 1.0 1.0 1.0] endobj 28 0 obj <>/ProcSet[/PDF/ImageB]/XObject<>>>/Subtype/Form>>stream -q -/GS0 gs -129.1199951 0 0 133.1999969 134.8740234 237.2716827 cm -/Im0 Do -Q - endstream endobj 29 0 obj <> endobj 31 0 obj <>/Filter/FlateDecode/Height 555/Intent/RelativeColorimetric/Length 42467/Name/X/Subtype/Image/Type/XObject/Width 538>>stream -H‰ì×ç[”Wðl²»F“MŒ›Ò;HoƒôÞAš4 ÒAE°P¥¨D, ±ÄU׿mïçœó–)w÷‚1rî̼ïøÁyçwÝÏs¾øBFFFFFFFFFFFæ‹¿¬kÿçd6>k‚F6eþ[È&ÈZ¿ù—úH›(Ìa•HŸs>BÄWú|¬k/™ÿ'k¡øê#ò‡>¬ýeþ—¬¦BûáÿºVV"uüÉcI…ˆ¿­ FV×aí/+ó±±ÀBoBýùÿ¾z̘ú:þlYK…1ˆ-kÅÔˆ‘Ë:¬ýÍeÖŠ© cF ¾Ùjå¾±½ÕuXûëˬ ,ŒT¨ m"ßè£Üܦ)ÑÑxèuHŸxŒ](,„ -BÁ|Ëó}Ľo5&Š#ké°ös1Îj,¸ -ŽBP4|Çó½>âÞw*!DøPxXÒ!m|Š1u¡g¡¨PM Û)?˜‡Ýß®0Ñ€p#ÒÆ'½ Q ¡B˜à"ðûïØ±ãŸ”MÃîâÓB Â|˜ðÐé0Çaíg"cæ‚×…ÊBQA( æá'd'ò³ièæN|(”0!ÂëÆCÑ!&‹À!m|J1wÁꈅPA&ˆó°k×®ÝÈ/Jö¨ïèön|Ì”0":j{juèŠCÚøbÁ…RŒ+ ®‚¡ äaÏž={÷î݇ì7ÝÚ‡ðB@à!† סáЇ´aíXt!ꂳ@Y`€ -BÁLˆýûml8p9dº…OlllH #ò‹êƒñøA)jqèmXû mάîuAC„XPYp„&@‚8ØÚÚÙÙÙÛÛ6 îÙÛÙÙÚÚ’"!Šð@{°ò`ÝêÐp(Å!mX;FçTSlˆ(, Ã(ÐDœœ}膓“>tp€"ÂP¼<¨;PQ̆¤a½˜†p±U¸CD°`*€‚™€hpuussww÷ðð8¢®=pÛÍÍÕ•˜€ˆB>8¬\‡R‡(6SŒkÃÚjsÅ á.¾.4ª - € €‡#žž^^ÞÞ>>>¾ÆÁooo//OO0A‰p -E,¼9T–jÃÚkŨ0”ABsDçBc¡¨p!DÂüüýƒ‚‚‚µà*7üýýH‰7 ñ  -ð°g<„ƒšC³a±6¬ý¼6M, u¿ T4D ¡â3A$ƒ‚CBBCÃÂÂÃ"jp&LˆUùPx0F8ŒlðÚà4¤ŒŒyaÐ Q]ÐA]`·8pÐÖeáì‚®€ - `&BÃÂ!2**::&&666N ®bcb¢££¢"##€$,@Ð!ÜñPt°ê8ŒmðÚP&Ф±a±X´`¨.0F¨.¢¶ G<½}üü ™ˆŒŠŽ‰‹OHLLJ2 É,Çø ® II‰‰ ññ`E@¸΃•tPuˆæ âÐllcÛ†˜( - k?´ÍÓÂP uŽp¨ ûÃ"`²ð -  -f"!1É|,%%5--==###S ®ÒÓÓÓÒRSR@Å!qšâ!t¸¹¢:XsØìGq(6mº‰"ilTT_ŠÂP‰²_ÀÆÈ![{'gWÎ"0]A*â⹉4xÈÌÊÎÉÉÍËËËÏÏ/Pƒ‹¼¼ÜÜœœì¬¬ÌŒô´ÔT1ÄíA:¼½8‡Ãv¶Td3E« NCΓ ‹Ù$Ñ -çÕ¶ Ô…‡§`q42ŠT’QŒDn^~AaQqqIiiiYYY¹\”––”äçåB’BýhÒàïëC8\œ©8`ƒfÊΟPz²46*–&‰nÁÀy„æ\8²ºðñ `,¢cã’ "=3 &ˆDIiYyÅñÊʪêêêššZ-555ÕÕUU••Ç+*ÊËJK „Æ#1tD„‡…úûb¬¸»rXFYm(eë×[Ôy"Kc½c C™$ƒ/8è]ø‡†G€E¢áº"+'/¿°¨¤”HTU×Ôž¨«ohhllj:ÉrŠ¿455664Ô××Õ¨­©®‚ŒGz鈋‰Š8 -~(fÃî« Ú6LiÈÒX÷˜¯ìHB…Á Ω6Ø;™ #¨ ‘°HÏÌÎÍ/,.)«8^UÍH€Ã©Ógš›ÏžmiiEÚ(ô¦µ¥åìÙææ3gNŸ:ÙÔØPÏÀñÈÏ#©Ç’“Gxhp ·áäÀjÛ† Yë“C›$T4H˜ 'î"(„Õ†cQ•U5'êA¢ùlKk[{{GgWWwwOOÏ9%xßÓÝÝÕÕÙÙÑÞÖ -#|€GyéÈÍÎÊ@u$ÆÇFGå6°oˆÚà4h×ÐV YëÓCL­0h`ïÄ‹ÐðÈhª‹´ŒìÜbh -BÑÚÝ=çÎ_èí½Ø××߉rùòeöÚßß×wñboï…ó`ÒÝÕÙA@šá<˜ŽÒâ¢TšÅÁløy{²ÚPhÐj¡4¬ý?Ó˜­ìLÂŽ$ja`Ápóðb}—ˆºÈÊÉ/,);Pq¦¹¥­½³«ç܈€…+W‡††‡‡Gxðnxhhpp`àêHéï»HBà‚úà:*+PhŽŒ´c²äÚpuÖÑØþ=ÍYsÊ$Q ÃÞÁÙÕÃ{gõEbr*ÕEqiE%c]Ý@Ñ׃CÃ#££×ÆÆÇ'&&''¯«™œœ˜»vmtdxh„\‚HWG;tœ:Éq”äeg¦¡7b£"‚|½Ž¸ »Æáu»2O¨4¤Œõ‹…Ý“`°I ' o?ìÂEÕEum=cÑ h -B12:6>1yýÆÍ©©éé™™ÙÙ[”ÛìïìììÌÌôôÔÍ›7®>.õ_¼@:ÚZšO7GUEYqa^vFjrR| j#ÐÏ[ÐØ·‡hèæ '’ƺÄäP"vO1It…áå¬wQ^YS×pò cÑ CÃ@SÓ3³·nß¹{÷ÞÜÜÜüü¯”ûìïüüüÜܽ{wïÞ¹ %3ÓSdìÚÈðàÀ”Ç…s„ãÌ©ÆúÚªãeŹYé)†„X†N(|ž¨¥!ÇɺÅÆ“„Á`†‹»' -#ôhTl‚A¸¨buÑÚÁX\„ЉI € ˆ˜ÿõþƒ‡—–iYZZZ\\XxøàÁ}(™»w@f¦n^Ÿ¼zÕæhEqÀFEiQ>Õ† ]Ãé°íA>OXiÈt]czZe»§X1Ø$¡#‰(ŒÈ˜ø$슋ÓgÛ:{.\$ׯ¡bf(`âáÂâңǟ<]^^YYyöìÙs¼{¶²²¼üôé“Ç--B€™iÒqm8úzÏu“ƺšÊòₜLA#À×ÓÝÅ‘æ‰RfãÄÚò3‹ eÅø‘`ì?`kïè↠#…—x,-3·€¹Àiï:×Ûe`x,¦¡bnþþƒ…ÅG Ï{ñâå«W¯_¿þò†ýÅÕ«W/_¾xñ˜¬,C|<¸??w÷έ™©“ã£ÃWú{Ï““ µUå%¹D#&2,Ø_Ì¥4ä8YßX>” ¶b`’h…Aƒ$'¿¸¬’»è>ºã,HÅÒã'Ë+ÏžCÄëßß¼ù×Û·ïÞ½ÿoʇØëû÷ïß½{ûöë3*ª3øÙs²{v“¸nбFÍšÄXbÔ £&k4Ѩ«±kĈ¢Ø¢ €t:ô^…¡ 0ÌÐgh‚€‚(1É®Û7ž³Ïÿ}ïÌÜ49ÇáÛ}¿0—Ï¿ó<Ï?; PÒÓR xÈ¢##ÂBƒÃÇËÝí²3lØÛ?bme¹g£±|É‹¹¬Ot¡!ÕÉ@¿~aè¶'Ý$hv’°À@‘lÞ¶óë}µ%Nh/_b%ƒŠ¤äÔ´ Èɽ‘—Ÿ/—Ñ+.f - -äòü|8ÉÍ! éà‘˜#‹ŽkW}½=Ü.;9ž=õ퉣‡8u«W,[üÉGš›Mã¡1r¸®N ãw’ŒxO‚AÛ“&n4ɬÙó,.¡ÀظuÇK«CG(/áÂÃÛ/ („X @’SÓ3€&ä… P¢P”–––•••ë~ã? -E œÀˆ@Àƒt$%ÄÇFG†‡Æ—»‹ó…s§¿µ%»·oÙ°vú„‡-1º:‘†Æ€½þ`еʷ'›S¦™™øÑ'‹°0(’ýÖ6Çíì™ OŠ‹ðHYlBR -²"I åJeE…J¥V«+ _*UE…R %¥Š’’¢Â¦#3=-%)>V×ý¼=\/]p#Öû÷îÚ¶ }ÂBc–ÙT:O¨NŒ††$ÃÔ¯ÂQ‚íÉ'šdN(’c'¿;sþ"w|=":&>,²rHEQqI)LT¨À¡ªºº¦¦¦¶¶¶Îðð…ÿUWW‰ -@Àé!Ï'ÌFtDXH ¿‡£qÂæà¾=;_ðИAu#…¡!ɘ÷4Âöd7‰ÅÂ¥ŸéErÚáÂ%7¸ cq‘šž™s#_^H*EUUu 8Ô×k4Zm^£ðè·V«Õhêëa¤>Ô*ey™Ñ!ÏËÍÎHKNŒ“E……RãœýÉc‡¬(4Ö­þüÓEóçÍž9}Ê$64øÝ*'󞃎’7ib˜ÍÂM‚&YµvÃ!000œ]=¼¹‹¸Ää´ŒìÜ<°P”•W¨*ŠzÑØØÔÔÜÜÒÒrSôðÙÜÜÜÔ$ZòÈŽr$läd¥#6b¢(5¼Ý]œÎŸá¡±}ózV'$CÃ0A…ãä9i‚šôýF$CãåW ƃOŒÅÔ$›p’P` Hœ\®xù_'),.8 u%TE‘ˆÖÖ¶¶¶[·nµ‹>ÛÚZ[‰HsBDC8*ÕŽ¢‚üܬŒTЈ ðóBŸœµ·=j½ÿë[7ü‡+ ÷%þž×ê(:J°=g˜ÓÄX¾r šd·¥ÃþŠÄÓ÷êµëÌEVNž¼¨¤”±@VŠf˜€ˆööŽŽŽÎÎÛxwt>:;;;:@„|­¦®–ÙPƒFvFjb\tDhÐU„ÆE‡SvT';·nÄÐÐËxëÑ£$÷ŒºÄ]«8J°=…‰Ášä  ÃÝÛ?(4<:6¹((R”)UH‹:–T0 Aºººº»»ïŠ>»»ºˆñ€Ž›-ÌrC ‘•žœÀBÃ×ÃÕéüi»ã‡ih0 ž’Ò5ÙûEïMŸÉ``blÜJMrÔ§*F@pXdL|rZ&wQ¡®B‰hšš‘PA(ºDOOÏ=¼û¢Gß==$„xtÙhÐÖ×V«+¨Pò(4b£ÃC}=Ýœ14 ÃRÑO›H·‰É߯1›mOš»öZ:fwÊá" IRjõsQ[§ih$¤AA(ˆÞÞïé=Ð?|ôöö’ð@x¶VŠúš*µ²´¤0?'35)N~z<3ô;£ŸÛD’a²÷K0& 0°=¿Ü„‰qàðq4 N -Œ(*’ì:õh”È-Æ¢›¡ €ðÞô~Âc?ð<î!:ºîÜæ44uè„F~nfZR¼,‚2ÃÕYh“´@—-æWëÄñãÆ¾¯¼4x$ÃÔ¯/‰`¼? 0p”`{nß½ï nj/?i™¹ù…%eJîqq«X ? ‚P0àðWzã~r"àdƒÓ¸Ùܨ­«©T• 2âÐ&Ø.N§N;„ÛdËú5+>]d1÷ƒÓÞð6dŒ6„d¼ ’! P<ÝY"‚ñ"`¼2Dƒ]«8J°=¿±>b‹›ä²»7N’Ș„”Œì¼‚âR¥ªª†¹ ¸@Z@X@™@<¤÷wzìÂpÀ§Ñ‰­ÑÂê2X›$ÆE…]ÃmrùÂ9{Û#÷íþjÓºUŸ/]ðñs³©“ßyëÏcF :eC’aªg2ŒÁ€1ÔÆò•t”Ðö[ºÐbŽ9‹ ¬ !2¤ËĤ¯Œ×†Œñ“¦Æ<ŒOŒ‹.¾!²„ºIJ••B` Hú¸`,Ä&ŒmPh0<2heP—4jêªÕìbÍHŒ6,w}µ‰uÉüy³gNo’xeô¹L$ÏðÄgɯ‚qŽÁ¥í™™+/.­¨¤“„ƽûß?л0°à~6<Á†(¶2Ð%£­…îµRQ”Ÿ“™šy=ÈßëÊ%dz߰9¸o7Ý%+—ñ‹uêä ÂaÂW†´?M÷?KžÇ½J0†=vÜø‰ Æ|~®2ö€á雘–uCUU«ili¥…ÁÃØ…Ž…ŽÄ#‘ A 6?12F“F‚_Ÿë×ð‘an6•.VÌÏá€aXÒþ4Åë³>_`÷êkÃF¼>vÜÛß6ãÀXF0v -0\ãzT,ŽlÏrUuM" - #tÏX‹ ‚ñÝ%]·u0x•ð{ÕÆ–õ´>|<ÇÜlÚv—ó“"ƒÃxNŠŒg~}×'?K8Œ cîüEÆÞÇ`dç±íIãVãž.0úºxdôºÈFƒÁ6`¨ƒÏ˜¶1.9žªDë>2Fèç§neH2žù=ÆÐ¸Wcª™ù\‹EËV¬!VlcpqIéC©ÆöÄÄ`M"îTƒ‹Ÿûºà4xdÁè¹+ŒOv•çjD®7çól|ÒÆ0À˜Îa#ãÿì—‡S–WGgggw“ÝXvâÆ»fÕh@°5Q׈HoŠ` EŠØP¤# ˆ" Pz‘&¨4!"X°¢fÝdg'ìsï}û÷~ˆð1™ì¼ÏŸpçÌýÃÝ ŒAŸx¯þ zUi¤i3Œ…ËV®Ù ½Õˆ×ƒŠ¢ÝOŸ¿èyÅ|.(~ÁÇþô–pä“ÊÕêÊÒ¢kWŒÄ¸èȰ ?/œ«{­·›Ç@SBA+îΖHd îäfɘqÊ“¦Îœ5WmáR†¹Õ;œ«ŒÌì<EIKÄ*(Yª2\üÂûeˆ€Ñ `4ßi¨«®(-Ì¿š•‘’{."4È×Ëã «£Ýëm&¤Jsh0>c$CÚE¼,Á½:u€±`éŠÕëµt Íwì±sB`É‚„, ;$b\2`P¹úèAgG{+£ª¢¤ j59!ælÄ©À“'Žº»8ìÛmea¢¿EkÎU$Ÿâ`-‘ÈÜ ·„ÎèÕ‰Sf@¯.X²â»ušº†f–»íœÜŽxòÁ€ZÅQB)Y’>¹à~,=/Ÿ“(i‰%Îö¶»v˜ëéh®_³jù’jóæ|9]Ò– þúÌ’é†ÆâoŒ-f–»ö0ˆcä”VMT”<#ŠÁ1 q.¸_Æ4]P«-MØ=‹÷ öCîéô=U«›Ö­^¹l±†ÚÜÙ_B®‚|‚cÈ€!‘1Ø‘ ’%c•¨^]¼üÛlÒ10ݾËÖÑí°çÉ °3ç!WÑQYsó6 jIú₃­%ŠŽ{"ň; Šq‚R pO]ìžË©«Î5sÚ”‰ÊT•H`(ø’AÛ'›%ê‹–¯Z»q³¾Éö¶®‡Ÿ =s>>™TI5j•¸§`ID¹‚ÁÔ*‰âž%…¬bœôDŠa‹À(®Uc²Šò¸10þÈC"c 'Ø®}B–à^E`èo³¶qp9tÜ'04‘Å£[F\ðÀàE åžõµUå%Œb„#Å8|`¿½Í.KŽbPî©2~ܘÏF#%0|¢ö9 -Ù§2Ø'Û«ÆV{íó8}!)=+÷:–O Ö=كš—G† ÏŸ¢(¹×†Ü³Üózn£^ǹ9ÙíݹÝÌP Œ{ÄS"mÉ ®/ûœ2d êU#s«=ß;»{xûŸŠÀ`\ƒ£¼£··WîÁ‰’6¤5•eE×À=‰b"ÅpqÜ·Çj›©QŒ¥ _ÑŠñŧ(JF—ÀPìÉ·Oœ%s¨,^ݱÛn¿ûQ/¿Óçâ.¦_¾z½¸¼ºŽ ‰†Ì’ôö²d0`j•vOPŒÒBäžÉ ±D1ޏ;;@¬Zëo%ùöX’¯Ñ’0Š¢dp!†DÆ@ý2þ@$ƒkŸL– ^ݽÏéÀ‘~Á§ÏÆ]L»|õZqyUÝ­F08ŠÁãBÄ=Ÿ²îYUQR€Ü“R o¤$Võ˜%™K2–dZŽ{ -«DcÀ'"|û\„ìSGŸôêOß ð¨ØÄÔKW® <0¸îÉù0z{¹dÀ`£„uÏb재b¸:qc-ÉÜÙhI&(É(rO Eœ¸dís³žÉ¶6¸WƒÂ¢bŒü¢²µ -í|0dCŒÿ0~¢Á ¢ä^;qÏ2pÏìÌT¬AX1öqbuÉÂùüX•U \’ìS Û'Êë½ö¤WÏœOHÉÌÉ/D`Üii»×ùð±(.ø`ü‡€AEɳn%-M”{‚b¤`ÅÄŠaŠÁÄêâj$VÉ’|‚—)Æ’b(òäK†Êd U%`Ÿt– ^ <Ÿœ‘WXZY[Ïã5åýC¦VÙ(©®,-DŠ‘u:$À+ŠU#*Vi¨âXYI1xò$ƒØçlÖ>q–xx‘^ÍÈÎ-(©¬©¿ÓÜvïþƒÇOäƒÑû0èZ…(0 JJiØ=ƒý½rCŠc–dÅÒ…êò—DR ÅL°~4œ‘ }’,A½š”ž•{½¤¢úæíæ»? 0ž½ìl””— ÷LÅîäçåqÐÕ+ŽÕUË!VÉ’¨È]’ßKK¢ˆãl G2>ÉÚ§­£ëaOßàpҫ׋MŒGOž½@`¼/ž3µZ_{£œãž¾'Žº»8PŠAÇjK"}Š;Á–ÉûD’ö©ŠíS‹±O”%l¯Þjjm¿ßEÀx3`0èZ…(©½QV`PîîyÀÙÞf§%R :Vñ’LTV]©Iu}KÆ °OM]CÊ>}è,^­»ÕØÚÞÑõ¨›ã_ÆÏïÔ* &J’{úƒ{º{Zo75ÄŠc•^’1ŸKM2”'#Ã’ìSÇÀÌ’¶ÏÈèøäÌÈèÕÆ–¶ŽÎ‡ÝO_ô¼£·Ÿ`@­¶·6¢Z%Qº§+¸§Õ6ƒ-ZL¬Â’@¬ -—DRO?XE$Ùçf}dŸnÄ>Q–䔢,a{õÍo‰}ö ŒŸ0^t­B” 0.¥%]8O¹§ ¸ç c}M¢T¬*+.‰P=%0w¢’1Š/´õŒ·YÛ8 û BöI²¤¦²ä^烃ñOF7 %iã¢#Cƒ{:ƒ{Zšém÷dƒŽUfIþÄY‰ Åœ`K„’¡$CK×È -Û§O`XTLb*/K?0˜^}_0 VŒÚZP­b0p”D„Òî¹ÝÌp«6vOŽbHK2Ô' VQÉØb`¶Ù§‡w@h$Ø'7K˜^•C®|Ò`¼&`tÝGµÚP[Ej5%G‰Ïñ#ˆ{èja÷dƒŠÕ>–ä×~ÖßþÉnɈ‘C°Ž¯Â• S$Žzù‡DD_HÎÈÉ/,Ãö‰²DЫj‘…k•ŠÚ=5±{j¨ÑŠAŪH“H†¢N4XG£-áKÆN[G°OßàÓçâ’Ò³}Ö6 ûìb²d `<ìê0nl­†‡à(÷´bÜs‘†@1FŽþ­Ò’(úè/ƒ Öá#þ: -m ‘ u¾dœ -ŠMLÉ(AYÒŒ²¤›¶ON¯öŒ·0Úï6Ý®¯Á`\NOƵÊD‰¹1rÏï{~ýUŠ!-‰bON°~1$cÆ,dó =“ -’Q\ÎØ'“%?ɵÏwÑJ‘år:§VQ”˜mÝÌsÏ ã9Šñç?”–dHîwb[BIl‰ÚÂ¥+×p%ãTdt|rfN~QÇ>_ŠÛç{q³¦²´0ÿÊ¥´¤ Ñ‘a¤VI”èjÓî‰ÜS&V¥%QüÉn ¬“aKT5@2Öko¥%Ã$ãBRFv^a)ß> Æc!ã¢#¡Vi0p”0î9mòDÅ–dHŽ·%°[B+– M]#sF2ÎÆ^LËÊ-À’Áاh–¼Œ7¯{0Œ[7«Œ¼œK©ãÎEž¤V9Q¢:—vOb0K"¡À“ VvK Xç£`ݤ#" Íwûì3KúFEiA^Nfj"€q*€®U :JÔ!J8îI)†«Cv‚-ù gKp°‚dlجg•Œ$åU´}>yg–ôŒF, c=MëVƒ{ªsÜóC®büÚúÿrœ-!’¶«ò¤©¬°%ëµ¶[XÛ8¸öôe$£ìFÝ­ÆÖöŽýŒw‚ÑH‘™’{ötˆ¿÷±C®L­’(™7çï|÷”cHOvKFŒüoÉÄ)t°jn1d%# -$ã2– °Ï6°ÏnE€Q' \«(Jæ“(QV¢ÝSRŒ!>Ñ- [¢‚¶D ¶díF}SËÝvûÝ=¼¹’Q§¹HF¨d¼7¹Ù) 1 /ƒ.¶»,ÍŒ¶jo$µÊD‰<÷”C±'wK&Lš†¶«¬þ!ÑñÉH2Êªêø’ño""öù~`D…û1`êjoXKjG‰À=%ź٬Ÿ¥‚¶«l %qIéYy¥7j‘dÜÇ’ñêõÿØ/¯÷=¯;° Ç=çreÈ‘T¢R†Ò¬yžPI*)4h y¡h@%‘HÉP)ÍÒ`*Ê"qŽ¡£sïZÖýþÞaïýîýîÞu×ûý~ëY¿çóÀúü}hˆ~} -ãÙ“G8•F! FxHÀ¶-î.ëVÛZ⵪Œƒ!…ƒAnOfbˆïè]‚«œ¢Ê¬Vì`M>z<7ÿâå²J÷Û=ed£ÑL#!†Ãi•­… »VQ”ÀÄø‘½=™‰!¶£s ¬È%jàK;Î`Í9{Œ†–{8GÆ ß‘A Æ'N0Zq0®æ)‰`ØX#0T•±Zå‰fbˆïè]ÁŠ»‚ÕÜ\‚kRZæ‰3…E¥Õ02ÚŒaÑP‹ƒ‘“uìïëµÉyí*sã•ÚËT‰Zå% ÿóàù…*KÁ%fÖ«— `Í>]péÊõªúÆ;­02ž T0þÀ0>㓌h C_[céb%²VšŒƒ\Œa¶§8¿Kdæ/PVƒ`5±´_ë.!ƒ5ÿBIYeí-42ºéGÍú{Ø`X›a`,ÂÀÀk•%Ìö× t‰*r‰…í2X“XÁÚÐ #£Kä‘Á ”«¼`øùxºmpt°63Ò×R_`@”p€Á%ÌöÏ u‰!¸dÝFÖÐÈø©Ù¬¥åÕ7›`d<yd Œ  {CÀPY„Õê ®Ze&†8O—XK6oõ—P‚½¢ !`Ü↕™!R$Ìö”Ä Ã%d°ãÁÚÁÚ .<28×'Œ×/{q0ZÈ\åÆB ˆ I—¬Ðe»$jObjƉ3çŠK°`…‘Ñÿ¹„ÿÈ ã˧"‚aj¨§¹!;Wƒ³V™(ç p‰œ"Å%!q˜K -.²ƒ•=2ùŒ j¯ò‚ÑI†ÇÆõºšËÕ”10fJM#Áû%b<¡.1&\Fº¤\¬_ø¹D8÷ø€±ÆŽ†<7¬Ze¢Dl'‚Kl0—î"\RXTZQs“3XŒ `¼bƒQu½” KS0¦ã`LÀÁ`×*Ã…xNd—øc.I']Ò„\ -ÖÏ_øŒ :0>üðþ×·¢ƒ¡H€Q‚À`¶§dnX.9t$+çì…’k•¸K8ƒ•{d|£cè+ã7Œž§]Dc Æ?0$vÃrIRZæIpÉÕŠšä’nÌ%ƒ• -Æl0X`4ÖU•gOç ÆÏ³0$~l—ŒMuÉ|^—¤¤gç\Â]ÒÖ‰¹Dp°’_æ}ÆÃ÷n7Ö#0Î cÆ÷ ;ê—Aë;N—œ—”UÖ.é— -VN—`|âã>€Q]^ZŒÀHOILàc™* ÆTŒq :®ù)ŠKŠ‘KZ0—ô¾¬¼` "0Þ½èïëyÖõ°ã*F*†§(`üC¼G™Ÿ¸K~$\²€Ó%~„KN#—T×7Ýmëìzúâ%Å%Áø èUãųnFS}uÅÕâ ù¹Á†Ûa`üCÜÇ=?ÇO˜.™&ůK—4Þ¾ßñèIÅ%¼#ƒ®W10Þô¿0µ·ÞiºYSqµÀÈÎHMJˆ!Àp´·"ÁãÆ_I0Fú ÿ?Ë%cq—À—»DC‡tI¸$\’GqÉ+¶Kh‚•Œl0:p0®á`¤%í öçOŒ:.—`ós -ÌO鹂\r“Ã%ïãçn0†X`¼~ùâù“Ç­w›jn\+¹Xpš†/†!g•ЃÁ˜DŒGã’&£ù‰\²\bD¸d'¸ä ¸$ŸpI+æ’þld|¡VJ¯þ_Æ ôêÈ’×/{mFí²ËƉ̴ƒ{cÃCü}½0þ Gë4?çÈÈ)ª,ÑÐ10³É%CB\BöêGÔ«¯_!0:Ûî¶4ÔV–]¾tÀ8|p_lDH€¯×&A`ŒaÀÐád.Cu‰’šºÖJKÜ%Áà’d—¼Õ%$Ÿ¯z{žvu>¸×r«®²ìÊ¥sgNf>´/."4`›×&çµöVfô` ƒC"Çí’qÂ\rVt—ð‚zõý;£ÀxˆQuãØ‘Cûã"C·m0¬Í õ0FöØ.5¹¾ z—„‚KR‡åš,ùLôj_Ï3ãþíÆºªòҢ¼œcG’÷ÇGî -ܾÅãOqÔ/wÉdpÉläÕåZú"¸ä½K„õj;€Q_]^Z|>/'ëHòø¨];·{»»8FzZÆByYŒ:®ù‰¹dæ’yò /]¡kdN¸d7¸äð±Ò%÷»ä Æ7v¯~åèUF릛ÕW‹ÏŸ=•u4åÀž¨Ý;wxovqZec`¨/Q0æJ3`ŒÌ±À@dp¸D\¢Œ\bjíàäŠ\ .É8q†Þ%_…¸ëÕßñ^}ƒõê£FMŵ’ gOOOIܽ;hÇÖÍ®ú,0f2`ŒÈqÏÏñ&.‘UÀ\bliçèì. —=ž‹¹¤¾éN[çcA]òM@¯½ÚÜPsÀÈÏÍNOMLˆ òÛêáºnµ­…±¾6CcÆHÕ%cq—L>k¸D\bhn³z½›¸$šê’öGOž‹àž,ùÏã^sCmeÙå‹§³3R“b‚ý|<6`hð‚1–C‚‡,—`ós -|Ò0?•ÕÔµWšZK<|À%ñ¸KŠDêò¸³äý»_úY½Z[ ½ZpúDFÚÁ½±áÁþ>žׯ±µ0Y©­±tñ"n0¾cÀÜq»}“±ù)«°Hu™¦ž±r‰÷ŽàðØ}˜K.^¹^%‚KxÀbgIß èUÈ’[uUƹ3'3Üâïëé¶~¥ÉJ Œù2Ò3¥8À€/ƒCRÇvɨјKˆù9GF¹DÇÐ sɶÀÝÑ „K®Ý¨»%Ô%l2p0°‘AÉÔ«Uå¥E…y'>´/."4À×Ëmƒ£¥©Î -cÁ|™9³¤¦ý“c$Žúeà.ÁççüJÈ%&Vök]<|üC"㤀KΕ‚Kš‡çÖú$³¤Ï¬Wór²Ž$ï ÜæµÉÙÑÞÊÔ@wÅ2U%Œ) #pa,—LÂ\2O~Ñbp‰‘…íš ›¶ì -‹ÝwèHÖ©|Â%­Ÿöˆê®õ‰²¤£í.Þ«ç¡W&ˆÚ¸}‹»óZk3C]M†Ƥ&2`HüX`àósÜxÂ%Ò2rŠÊK4t ̬W­Ûèé¸+*!)-ódÞypI­È.áYŸì,^­Å{õxzJ➨Ý;·{»»8!0ô4—«*)ÊÍ0þ…ƒ1žCÂÇ=?ÇO˜H¸DVAIu¹–¾‰%¸dóV¿ˆ¸)éÙ§Á%å57›ï>xØõ æ§—PÀÀG%Kؽ´Ã{³«Ó*s#=­åjÊÆÏ³g0`ŒÔQ]2wÉT©YsæÉ£ù©kd޹dûΰ˜½¸K.³]Ò×ÿæ—wB]B¬OV– õ‰²¤‘È’™iI{c‚ý¶z¸®[ `èk©òS0Fæèæçø2¤eæ+*Ãü40µvprõô Ú“ˆ\RXŒ\Ò.é~Þûjà-æ’AÁ.áZŸX–´ÞáÈèÕð`ëWÛZëk«/QY(/Ëã{ ‰ÝüœŒÍOY…EªË4õ-íݽý‚Ããö'ÍÎ-¸TZ^ .ië¤¸ä« —ð®O®,9´?."$À×Ómý;K“•Ú†‚ìÜÙ3¦Oý cÜٯﯨî4Žã'çd³º®“5bÙ˜è -ŠÁc/Ø£ ¢ ,`A¥IïEšHSšQ ÅXh‚Dz³‹`]kŒâ±þ¶ÏóýÞ;sïÌýe&'Þï_0gÎsîçõCÕËÏ^~ŽÔÔ&[²hÙÊu›,l½üƒ#b÷J?žWPR^Uß [rnÉ+%[¢@Ÿ÷¨>+QŸL–Ð^Ýl -‡¡?ÖôÉãá0F ‡Ã½*†êŸÐ–P~ŽÑ™[¢g` [bãèî³/95+ç\qYemÓ•·%[òFÙ–0‡ñ‘¯ÏVÐ'd è“ÍìUKs3ã¥úógϘ<^g ÆÞaüM< Õ=A~â–h?yÆœ‹Ÿö®Þ¡‘qIG2Nž.„-iÀ-¹[ò¬G[ò‰ ƒêó «ÏºªrÐ'›%þ^®Ð«fØ«?Ïž1e‚ÎMèUzýÄÃPý£Ÿ º%ïÍð¶d„¦ö¸‰ÓfÍ×_¶bíÆmÛ<ý‚÷ìMLIÏÎÍ'[üìñ–°È`ôù겄è“dI,d‰·›Û«stá0´ÉahÐÃèÃÆ—âa¨êñ·„å'l ðsÂݹ –›˜m±vpÛ±3<:áàÑÌSg‹`KqK:pK~W¶%‚Èàé“dI›%L¯êB¯jkÁa ÕøŽÆ?ØÃ\†ºÿ¸¿ü“n åg?ÊÏFj¥ü4Ä-±sñ -‰ˆ;pøØ‰Ó¥Õ -ÀÏm‰ô0Þ¾yýêåsDGŸ¹Ç!Kâ¥Y"íU-ìU ìU<ŒÞ½¾úJ< U>Eüüœˆü\Jøéäá»kwLbJZvî¹ó¿\ªkÂ-é|øø)Ù’·Ýl "ƒÑç3¢Ï۠ϺKå%y Ï¤È?I–Ð^;c09Œ¾}ä³DÝÿÛ_ÿñùÙGÊÏÑ?~.1ZmjneïæŸt4óä™"àg#ò³ãn ùdÀa}2¸Èx‹Èàè³¾ª¢´ð4èóà>È/;+&K¸½ -‡Ñ_< u<~þ³/ÝÂÏIÀÏEËV®Ûdaëìé»ÿPúñ¼ä'n å'†B~ - ã‰ÕY}B–x»Ú[K²„íÕÁ¼^C•O–Ÿ}û}M¶äÇQ£u(?Ÿ6Žî;‚£÷%§fåœ+Æ-¹róöÝNà'gK?ãAŸ¾N¶›×¯¦Y2qd §W{Ó^C•O€Ÿyü\ü´vpóÙ•pðh橳Ee•µWnܾÛ[òœlÉ;á-Q€Œ»m7¯µÔWƒ>ϰúôót±³475ág‰¤Wéa|)† -ï“ÁáçH>?]¼üC"b>v"¯ ´¢º¡åZk;nÉ3fK>(Øyd€>ÛQŸ5}† >­@Ÿ˜%³™,ù~˜P¯~!^†ŠžÌ'CÊÏ1„ŸzÆk6l~ú펡ü<ü¼z³í^çÃÇ\~*Üyd€>kQŸ¹Ùé ÏÈPVŸ˜%TŸŠzU< U=n± -òÓùiïêü<ƒülD~v<ø/ËÏ‚üä"ãWšêPŸyÇ>%Ñ'ÉÝ©t´Å,Q÷SÂÏq\~zúïÙ»ÿPúñÜüäçµ[픟pŠø©W›ë«ÊK NŸÌ8z0!:áÌêsîLÌ-I–ˆ‡¡¦Çßæ“Áðs -ÃÏ-ÀÏ;â)?‹ŸMÈÏN ?ß+ã'n AÆ+.2ª 22S“÷íÝìïåbgÅÕ§Õ''KÄÃPñ“á'ýd?GðøiGø‡ü ô)œ%êþË>“Çãgo²%)?u8üt–ð3/¿¤¢º¾øy?Ì–|P²%²ÁÚÞz‹ŒŠ Ëͦ«3§)ѧثª{Êø9Y–ŸÉ©YÈÏKÀÏ›m„Ÿ/¤üT°%¼`}Ìkc-"#W‚ W{«-f&Æ€ŒYÓeô)f‰zž`±*ågaéÅš†ËÀÏàçsúÉø €Ÿ2Á -Èø­ƒUÖ®X¶èçÙ3dõ)†zž?á“ñ#|2Ÿ eù™ ü¬~Þbùùº~²[ ÖúªŠÒB@ÆQ 2¶2 /˜£+§O1KÔòdŠ•åçpäç$ä§!ðÓ’ð3ø™‰ü¬¬m¼Âò“l‰0?+ ãrC AF c/‹Œõ«—ƒ>gNdh"2 ü–§Oñ0Tûd>}ûý-[¬ÈO=ÊO'O¿] ? ¤ü|ô¤~ò·„+é²È˜7kA†}ªûû\·XyüÔKùi´ÚÔøéƒü<Èð³®éjÏù [ VDFs]UyIAÞ‰c‡“âe1‘¡@Ÿâa¨ò ñSc(áçDà§>á§-ág¬2~ö|KÕ€Œ3§2¥È°`1C‚ QŸê}ÅÊççr³-6Žî¾AáR~Ö?Û)?á0X~*Ý’.É–´Þ¸ÜXs±¬ølNVZJ¢¦\d 6„ÕgoQŸjyÅJù©©Mø¹ˆðÓÎÕ;€ð3ƒÃÏN ?9["ÿÉøôéƒì–°ÁšÁèãæ`M¡Ç ƒÑçQŸj|òÅ*áçÂO(Và§ò3ù™_RÎðó”ŸøÉÙ’wÒ-aƒµ¤‚õˆ…!êS}O¾X¿ÁOÆX¬ÈÏÅÈO+àg`XTÂÁ£™ ?¯pùùN?y[òòųÇÜ¿Ãë¬1»wùy:ÛÊ ã{!dˆ‡¡Ò'W¬È'cĨ1 ?¡X-l½üƒ~”VðøÙÕ ?ù[Òq‚µ©¶òi°d˜2 8ÈÌ"CÔ§šžÂbýÓxäçä§µ£ûŽ ðè}É©Ù9ÀÏKuÍP¬<~ödKž“-i»u­¹^¬d¬1^ª?_2ÄÃPý*Và'ë¤i³€ŸP¬–v.Þ!‘q„Ÿ….Ö~vñ³g[R}ñ?X·m\»R)2ÄÃPõ“|2„‹UÏø¹ÕÆÉÃw×)iÇsŸU”Ÿ¿öˆŸÂ[RV|.'›¬ëW-Y0G—"c¸ˆ µ?…Å -üÄb]lÅjeïêOøYTVYËç§ð'CpK:ïA°6×Á–@°#Áàíjoenf²Ü@ožCä‘!†jŸ’bHŠø¹m»³§_ðžØýX¬ÀÏꆖñShKÚ[¯·4TW”ž9I·„ëV@ÆtDƸȀÑ¡Ž'T¬ƒh±ŽÇb]‚Åjíà¶cgXtBrjVιb!~~ü¿·älNn‰L°"2F‰Èø3¼n‹øiaëâåGе°øyùúí;ÝðSnK^p·ä<³%!tK X—,œ«;¡)"ãÏðº+V,VGwß ð˜})iÙ¹ùçŸÍ„ŸðÉPÊOÞ–üñâÙ“G¿r·$¶$*ŒÙ6XÇi3Á*"CÍO¨X5†Òb~.2~ZÚ¹z„FÆ“b-º€ü„bí??‘O†à–¤¦$Æà–`°®[i( ÖÑ`‘Ñ‹E†x*~Ê‹u-ÖíN~X¬ÈÏüäçµÖ;÷)?_+à'gKÞ oIúᱸ%$XWá–Ð`Edük@ÿ~"2Ôù”ë,Vs+{7ŸÀ°¨R¬Åe—ꚟÃ'ãu—B~r¶ä Ý^—dIŠÇ-qs°ÞbfbŒÁ: ÖÁƒ>wd|Á>õþ^±~Ãë(V}R¬¶ÎžþX¬ÈO(Vä'S¬dKÞI·Dî“ÁÙ’§²[’ü?òË;¨ª; Óìn¢ØH¥7á"piÒ¥_ŠŠÝØbé ½ -‚ÒPAŒQ1Y *ˆš¨±D,kŒf7Ö5×ìh6³“™ý¾ßéç^Š -:sýþ¹÷ž¿Ã3Ïû¾å˜%‰˜%0X1Kxƒõ,ïu{oþU‹•(«5o±&¤eç—“ÅÚBëÕ›ìb…úù›|ýüƒ§ Q–|ûÍWG0Kª*0K’I–L ÆÁê p°¾`tÏÄ[ÀC¼X‡ª*^¬+q±bý<|+S?{^¬¿ÿYB”A²äÖµ+çOŸÀ,ÙµdI*/KÆb–(¬Ê^2zMÅÄC¬ ñb dkAÉ&v±vÅzïác¶~v¡ R?ŒgOEYrhoíŽJÌ’t6K¼ÜH–¼kƒUÑÿý}¹{ópˆ•Á_¬Žn°XAÜb…úÙtŒ,ÖÜb}Þ…2xõ³äñƒŸ~¼}ãêe’%ûëvR»Dœ%Ƙ%Ì`ýPÙkÏHtH?¿‚d *«µXCq±R‹µ‘,V¦~þü˳î«‚,i;sâhÓ=»¶máeÉ$*K,Íé, Vå£k(þ$¾nðèÇ—.V5\¬  \¬Þ§Í^Ç[¬¤~Âbeê纩Ÿt– 20Kî~ý»‹íçNo>´²d—ÈøYò. VÅTyø3þõŠéo6Xe,!ÊPe0‹Õ,ְؤ n±žÀÅz”qÿa·õ“Ÿ%ÿþ…Ê’›W/w,©Ç,Ù Ê{Q–(3 -¨àhèò„|ô+òÊÀŪÇ,Ö\¬!Qñ¢ÅJê'(ƒªŸ/º®Ÿ¿Sõ²äŸ%w„YRVœ—Æß%6–æ&†º£´”~°ÊcÁgâ/]Ÿ98úáEʀŊʰeÈPKÃc“3q±V¿Ôbe•A²äÉ£%7¾»„YÒBeIiaNF’`—HFAÉÐT8Xûö›'Æ‚£‚úÿÀ݇p¼Ÿ<>8:ú‡ ¾2²ÊÐeHQd±.NH[•_RÞÍbíeýÄ,9úëV:KÖ®^™'άjªJœ%".T°4à àóŒåƒ¥CGß¾%·XAj¨ Cs+[G7/ÙX¬K#VЋ”ÑËŪ°~Þùþúß.¶=y¬é •%ùÙi Ñ\–ØRY2‚Íe¬Š± ©à€Èœ -ûm ‹Ÿ޾fC¨ŒÁC†±‹U -‹Õ7•˜žSPº©kƒp±>eëÿä”!¨Ÿÿ¢ê'dɬŸ ûjkª Kr3’bÃ!K¦ÒYb¡8K” <,(*h$TðÉ>¥áè áès6h0eàb%Ê0•XÙ9ºyûãb]—’µfÝFj±;y®½×‹êç ®~þ€õó<ÔÏÃõýâóÏ6®]“•²pÎôà@È^– UÎ,sÁÇ‚… 7D|øp0Áƒ¢ƒ‚ƒGß±ñžHP?eØ€2ü‚&Ïœ·8,&1#§°´¢ª¦nc+(£“(£»Åú‡¸~b–ÜÅ,i‡,i>´÷Ëí[ËKòW¥'D‡.ž7sòøw"Kø\кఠT(‚¡pèSUU¥¿ ç„‚í'޾bC24Fhë2¬íœÜ½P!‘ñ©ÙyÅe[p±6ãb½tõæí^.VÌR?!K°~’,iÜ¿»¦ªb}QnfrløÒù³¦Nô÷v§²D_y³DÄ¥  ¤‚†‚ÐðÜÇ‚ƒ%ˆÒ¡BÑp°âè+6øÊÀ)Õ‹Õ뒰ؤÌÜ¢õ›«wÖÕ“ÅÚÙãbÔÏ_Ÿ=…,¹ÿ÷;·®_élƒ,i:@²d]^Vj<É’I¾.T–€2fIýwÞÞÉsAt!À‚‚‚¡¦6NwðSMM„GÀÁ‰£ÏØ(C…R†–¶,VT†O@ðô9 C¢âÓ²ó‹Ë·¢2p±^ ëƒn+¿~>%õó.ÖÏös§Ž·Ú[»£²¼¤ '=1& ²dÊx™§«£­•ÄÔê'/K”¨d(à‚Ò¸‚PA1¡®®¡¡¡©©9‚wð"!ÀÇG4Nrl¼Öë²YB”¡®9R×ÀÄÜ•!#ÊMÎ\]´as5,VPÆÙ¶‹´2z\¬\ý|ŒõóæµËçOÝÚX_·³zó†¢Õ+“WD,[0{ÚÄÈ{’%P?ùY¢,`{'Í­  ¤ @$FŒÐÒ9rä(rÚÚÔ'<ÐÒBBŠV}Æ¿~RÊÎ(ÃÞyœO (cÑò¨„´U%å•;j÷5et\¾vë‡nk×õ³ êgÓÁ=»¶m)+ÎËN‹Z¾hîŒIA˜%6–f&T–€2Ø,y_)²D¼GX.@" H24èèèêêêñ~êè%ÀàöÎÁA‰ƒÇoþò ‹”‹•aëèæ%›0uÖü¥+RV®Y»ñ³ÏAMÇNžm¿‹õî?<êy±þ—®Ÿ˜%?Þ¾yõ©Ÿ ûjk*7•æf$Å„-ùô“)d^n\– W¾]"$<.°[0Xà m@BO_ßÀÀÐÐЈwðÓÀ@>F¡=498ˆ8xl¼®6+C”!eøNš1wQhtbzNAé¦ÊšÝûŽÀbí ‹õ½XŸ÷®~ÞƒúyªŸ‡ë±~nX»&+%.³$8Àgœ³½Ôb´±8K”c—(ðp2X zÈ„‘±±‰‰©éh83røÍÔÔÄÄ1Ð×<dc q¤¼Ò+ ”¡†Ê041·Beø£2–EÄ¥d­Y·q˶]{6;y®ý(kwÊfÉ“G¼úÙ|hï—Û·–—ä¯JOˆ…ú99ÈÏÓu¬¥¹‚,Q†]" >„ -€‘037—H,,ÆÀYÂá§……DbŒ˜š#<8 Ã)¯†_)ehjéèÃb•:¸xø2‡Æ$fä–VT2[AW®ƒ2î?ìq±*®ŸG÷ïÞYU±¾(7396|éüYS'øCý´³–˜ÑY2³Diê§Ba¸à° T ÀÁÊÚZ*µ³…ÃO©ÔÚÚÊ1G<=]'dvŠ‚Hyéwf•1ˆU†©ÄÊÎÑÍÛâ´Ù –EÆ¥få­+Û² kóñSç.\ÂÅú³X_È/Öê笟×åe¥ÆG†,œ3=8Ðwœ³ƒtŒÉueË!”0p0\ . Dh,Š1À„p°³·w çH¾88ØÛÛÙ" €Ðî08t´Q4T}]m•1xȰ‡kŽd•á4yæ¼Åa1I¹…ë+ªwÖÕn=q¦­ëX¬½P†¢úÙõsÔÏ‚œŒÄ˜0È’)ãe%¶%†%T– Tš,‘ƒƒÏÑ…¾‘1ÈBbTcœœ]\]ÝàÜáàÃÕÕÅÅÙ q°<¬­ÆX€:LŒ ˆ8 T .Rmp#åå^šSd *C(ÃÉÝ;•ŸšW\¶u;(£•qY ŒßºR†¢úyþ̉ÖÃõu_ToÞ°võÊ”X?',±m¤¯­EÕO¥Ë^Å Á`¹ÐÖ] - © PáˆL¸¹óðôôòòöööƒ//OOqîn®ÀE‡•¥šÃ+Ã?Rĉò2/ÍS,VF6.ž~ãAKÂb“2s‹Öo&Ê8 -ʸʸ Êx‚ÊxN)£Ëúù\¾~ÄúYVœŸ–µ|ÑÜ“‚|=\lƘÑõSuè¥ËŒ0H¿ \›š™#¶vcœ]Ý€ /o_?™Ìß? \@€¿¿Læçëãíåéx v˜Å¡‚l@¦º1HE…Ò†0Q^æ¥ÊР”aM”<}Ψø´ìü’ò­Ûk÷5´|õÍ·¨ X¬~úìWþb+£ëúYõ³073)6|ɧŸL™ órs´µ’˜býÄ,¬ŒYBÀø€„ñòË4¨é;óûbu;³Ý}¡­®ÕZÀEh-š—XpÒ¹$q‘¨° _˜›“ÅçqÓ 8èÑ‘á¡ä `ÃÃÕÙÑ¡ @ãµèFÙ"ë+ÔეÁeˆJ*êZ»F' eü”ëºÊø·VšüüòóÓ•üZ3?ëæç6#ÌObKйŠâsã©™¥µ­ƒ“ëIo?R0s‘ÈNN,²ròò Db‰´DV*/+S(ÊñS(ÊäòRY‰TR$*æåda8؉ñŒ-N¶Ö–æG 6ˆEA±±54ÖPÆ®=+ÊðeÄce”U7uô97³p ”qo•2þµ¶2~лX!?ï¢üœAùÙ¼V~î7ÆüÔU±%¿A÷4ãˆ9Zg7OŸ€ ªš NOXˆÄÒb™¼¬¼¢²ªº¦¦¶¶¿ÚÚššêªÊ -EY©¬X* 82ÒR’˜j6ý}<ÝA6  t¤ì݃nM‡ˆÆjeìeì'”ááH¡1˜)™Hõ­ÊÁÑÉ ¯¾s)ãKPÆ3¤Œ„26ÏOt±n˜ŸG›ìû?ÈO´%¿ƒÈ@ñ©–ÄË—‹¸H]äæŠ¥%rEEeuM]}CcSsKKkkk[|´´4756Ô×ÕTW–+ä²b±(?/›Ÿ™žzš•€Ù&ùy«µGŠÉ~"6P‡ŒÆ/ÖUÆ1¬ rxL|Rº )£S_Ÿ~ûü¯[Oúùù ççg£ü\ž›†üìh¬.#ò36B'?ߨm|ù¹JhKP}ÂUÁððö $Si1ŒV2‡ËÏÎ+(’””**ªkëšš[ÛÚ;:»º»•ÊxJewwWgG{[ksSC}-Ð!—IÅ…Âf¼A £€6\ 6Ž¡EÁ±± - o× ”aï Êd0S3sE22–@®(ãû¿#eü¸Ž2~\QÆ·Oãü|_›Ÿuëçç«Æ•Ÿê/ù—ºÊ€-Ù ÿ€†%†PHXdl3)5ƒ‡¸ÊÊ*ªjë›[Û;»”=½}ýƒƒCCCÃÃð188Ðß×Û£ìêlokij8ò‰(?7‹l0ãéÑTr Ö†=^øDÑAcCi¦Œ¬|iY RÆ8(ãú{wï=xøè«ÇO¿{ŽòóÅV.VÈÏ¥ Sª¡žö†ªR±Ð€ü40´ÿ~e@dì?窵­#ƒEg%§e -r‹REeu]cs[G—²·o`pøÌÈÙÑQ•j ?•jtôìÈ™á¡þ^ewg{KS}mUE™LZT€ÙHJŒ‹ ÇÚps&bƒèP-ï‰Ê(’UÖ·)‡TSsXö'¬Œ¿l¦ õÅJäççŸàü\ÐägqA6×°üüùÿ˜ÿÕ§?&je -A‘aeãàìî…Á`$°SÒùÙB‘T¦¨¬©ojm,€Š‘QÕØ¹‰‰É©ééé™™øœžšœ˜SŽœèëéîlki¬«©Tld¦¥°H$?/¼( “-¡±ò3«aV+ÄP†VÆi¤ EMsgÿÈøù…Ë×ß{_O†]¬D~~pë+g'G•­õ•²"ÈO¶6?ÍŒ4?_“eìe È°wróô Ä`À’€0Ä%òŠjࢣ»·èÌYÕ¹ñÉ©™ó³æææç𛟟›»0{~fzrâÐ1<Ø×ÓÕÑÚTl”J‹òs\Îi&hjÃå„=Ž “7÷ŽÆ:Ê8xÄBG<=eÜT+ã‰F?¬«Œ•‹õÏO¾þ]¬wÞ½¶<ó³ ågÖËù¹W›Ÿš40ô¿å_k*+ÃÂÚî„ëI_RHXÀà -ò -%2EU]#âb`xDunbjföÂüÂâÅ¥ååK—.ïҥ奋‹ ós³ç§'ÇÇŽ^eg[sò†D$Ìæ¥Ú ð9‰bc#4Ö c=e6;f«£Œ‚e,"eÜðð‹Í•ñÒÅúHs±žG+ÊOQNf*ÊÏ #ÏÏUcB&Pè.-ñð S#cãÙ iiyM}s{pqvl°˜[X\Z¾|åêÛ×®]¿~¿ÿ_æAMÞiW´³³»Ýqvv§ÝκU×­Öªõl­ZuWë}BU¬7r -Ѐܠrßr( \Ê¡È  wHB€I ܧ܈H«¨ûÏ>Ïï}ß„£±Ç?éoFíŸw>ù<ŸoiiIIqQaA>;/—••™žúøQBˆƒ°áéæähkIiCû[¸(;¶mÙøåÚU -ÊÐAe¸x„Des+ˆ2º)eü€Êx;IS-VÈOX¬O²I~z»:Z›]Ô=£­©îù9å1ùë$ó‰2à–ì=ô­öi3+û뮞¾ ŒˆèûñÀEF+_X\RÊáry|~yy¹@ñù<.·ŒSZRT˜ÿ$/';3-%éaüýh`#ÈßÇÃå¥ 3ß=r`ÄÆ&@cƒŽ×÷ÿH‹yîT÷dFel¡•a22ž©ªŒ^åÅzËÏý†Å%ÌÏ=L~.RÏü”'Cö™I.^B)cûîšÇO×7¾bi`ø‡EÆÄ%"9y€PÁã **+«ª„ÂjxB¡°ª²²B >8¥Å…l0ˆÙˆ¸xÓÛݵab¨{öä1̓ë4`¼þy¢1ív=¥2þÁ(c­ +EeÔPÊ ”1þãÊ`«,VÌÏ/«+F`±NäçõÌÏ©2ƒ>&Tee@d]¶°½æ‚Æ‹Š”œN¸(ápùå•UÕ5µµ"‘X,–H$b±H$ª­D>¯ àÈ’“lÄÞ ¿sNŠëu{kóËFzçNׂ46ÐhÌ—¡¡|OfËýO«Œ¯(eœEeÜp÷»F+£\Ue(/V Y¬,²Xƒ|ݮۚ›èc~îÆü\½|)Y¬ê˜Ÿ“ÈÀÌ ŽÉ²•k¾Ø´*ã(n‰µƒ“»O‚‘ð(%#;—\ÂêZ‘XRW_/•6PO*­¯¯“ˆªJ„£¤¨€b#1.&*,$è¦7Ô†…éEýó§´µáDÙ¬„^í)ј­‚2NÉ)ãQZ6›VF‡‚2þ§ Œ+krbLx°¿§³½¥©¡,V’ŸkÕ8?gMWs ð¸vÃæïØsPëÄ]#Ó«pK¼ýoߌOJÍ`å—rùU€PÑÐØØÔÜܯþ4777566Hëë€GY)a#=%)áAthÃÇÃùšíÕ+Æú:С‡öÑh ÿžíyrª!‡ÆlšŒ9**#!93·€Ê‚2zTS†âbå³³Éb ôq½fcf¬ùy`ÉOX¬êšŸ“Ž6(ÉŒ…‹—._µŽôçá£'Ïé£2œ=ü‚B#bâÀ¬'Åe·´ØÈJK†“èÅb:ô…Æ&²PþµècüÖÌ@QL ÙSÜR“•a„Êð| SF#Qư*Ê{ñ|˜Q,Ö‚ÜŒÇ Ña¸Xí®^6ÀźgäçuÎÏÉd%›uùêõéc¢c•Ê»÷ 19QƯÖˆ€‹¦æÖ¶öŽÎήîîî§øàß®®ÎÎŽvBG˜ÄQlåç±2SA÷ÂCƒnz¹Ý°·2»dxFÊçp¸ÑÏ÷eM§Œ†ÆôÊØµ_ó(ÃBN…œr¡HÚÜÖÕÓ/¯ŒéòóåÄbm_¬Þ.ŽÖW.êB~ÂbýzÃ:’Ÿ€±Zæ§Ü4™KVšŒEôO—‰öÝ‹¦–öN¾  ¸%iY¹ùŒZ1á°èê~ÚÓÓÛÛG¿ÞÞž¤ è8¥õ²7…›•žü0.&òÎmow'ksd¼~þ6UûŠª¡š2ö>F”ᨨŒ:U”1Ób½‹‹ÕÖâ’>.ÖÝÌb•ÏO5SÆ´d|òéŠ5HfÆY=c3kGÏ›·ïF=x˜œÁbqx†¤¾|ÑÑ Xôöõ÷ Òo`` ø<޶V²!à•ÁIae¤&ÁE¹àãî<.”_¬YùÙ’Åð½ÿ¦T¡òh̤ŒZ?SÓ-V/g+S£ Ÿôb]öÉ?I~¢2Ô.?g ¦É— °Y!3Ìm®¹z‡GÇ%¥få”pU5` rÑ×P ?Ãÿ !ý@˜£½­Ù¨­®,çm¤=N¼bÃ×ÃÙqoþ³ÑX¶„(t…þN–20ÞA¬wPÆ›7¯_1ÊèFe(-Vs=\¬»p±b~.TÛüœ¥ -GOž7¸™áæ“ð8õ¤ˆÃ¯¨Õ!]Ý=½ÀÅ012òœy###àpÀ]¡Ø¨×àI).Ȉ‹ò :<„ aC¡¡upï7Û¶|µ~õŠed L‘20~†2^ª¢Œ¾ÈOé¤ÅzÙPçäQj±®a«zæç¬‰U ßÀÐÈØÄ䌨Œòª‰´©ŒÂ@.€ŠÑÑðÆÆðïÑQÄì14â l@oÀIm”ÂEIyäÇ qZ[ëàžÛ¾†-ˆÉO*tRj0ï”ÁCe´N¥Œi+*ƒ,V^ ›%¿Xua±î§+\=5ÎO|So(ÐÉd…FÞO„Ÿa>!*£­ƒc±ûžycÒæ Øhmn¨× -øwœàpÐl@‹vw¶µ4ÖCˆBläe¥Qhøz89X]1Ö;÷ÝÑÃûvÂ@ -ý”äÝJ÷„~sßûe•¡˜ŸCôb­&‹”àåÿ‡r‹u%³XQjêyKäÆÉð;\Àüwî?rüôøÜΞþÁá1ñÓ¡@¹¸&ƒÃÏžR`,ÞPÂàs6†‡ð¤t¶ÃE‘ÔV äÐôuw²·4½¨{–lW”5•pO([SÓ•Aã×PÆ[9eP‹µ¶’_šŸCë-_÷ë¶æ&ú3/V5»%rã~ˆ@ó¹qœ¬…ý†ü /_µsò€!x/.)-›]TŠAÈyN”AÀ@(Þâ£ècƒhk£µ©AÊ-¥Ñ€ñêãvÃх30P 56S©÷äC¹{ 1—(ã÷¿–2Am  !(#/+%16"ØßËÙÁÒÔP”!¿X?TßÅJ½©g+ó½'BôNÔý‡)Yy…~Um]C é pÆ ü5¾¢À@,àKÓt8´6z D!6p¢ ©IqÑáÁÞn×m-.êœ>®)—ÿý#jºþAƒ¼÷(»ýÒÊx5¡Œ–ƺšJ^I> ”u'ÈÇíš¹±Þoi±þŸü2ÿÊq]ãøò Û>ö ÉPR*BûìmXh¥D†²ÍÇ%Ñ@¦"Q*%¥D”PR!sdjS[%jG™ÚÙ¦°ŒçlYÇé\×ußÏó>Ïû¾9ÕÞgµzï¿à^Ïó½?ßú ʆ~p§i³Ý<á)†n‹M€/žsŠMPøäà&°3 M – ~(<‡›%×®^ʃš±?y7¼Ê@øøî`…Shj ©ajlÈÕõ/Òh°`4 O n¬%`¬gÐXãwD„ø®XæJÆjÓ°±¶¸`(v_ÓÐ0ìfÖÓÒjààŠ:··gÕI!Ö búi-,PzŽ,‰á‘f;嬗/Ö°C)¸5@^îÛîê…+T˜–æ&âüÇ—‰Ñ ÓZ@†v#QÖ(dÔÕ Æ -Èxp÷vy)ëÉ#û’vF…®_ååæŒÈ°ùycýÿË?÷H‡†ú:avÞJv’ß<ïòÕ¢ÒòÛw°N`h¼~;ãw|?ò\HÂÁ²AØ€FÁ±;´’EãÜécÙS“vEo ð]é ß:M(ÔûÛ¼MëÖb0¾l‡Èèðyd\‚K6ÝW/d惱f¥%X»b© «=\L3Œ•éÐ`u¼•ã¶c >¾ø¥´Š ñ‡Õ ¾Hž üäÒl 6dÑ ­qåâ¹S9Y驉((°B—¹.˜5ÕÑ@Ö'DmˆÖ%ÿd<}\ƒÈ¸ŽÈ8Æš¸3rKà:ˆ,Þ‹«¥&+;²¡¡À4ÚI„†}ã !$ƒå‚‡|l@4h†þÌ>š™–’ËW(LÉEuû„M ŒF›6_ðùùÇ‘ñIŠŒ·÷ª*n\ddHÞ½uó†5ËÝÍE–Q`5ÂXÙQ®“¯è£áÅçh;V*®liäJ—†¼M>‰G’ßÙØPD£¢‘!÷$¸+­ÐM45æL'lVîŠF -›zdln2>0d¼|ÆúàÎí²ë`¬¹ÇØÐM~+=“±ŽcµbÆÚ±¥+;jêDÑã4VžDÆÆ§¤eæœ:§Ð|’rh|’¥h€¾b4h†–—äŸÏ=q8VhôVpÅ fMs’ôI—μO0t ÊcÈÆ~‚1i2ÐXŸüV}¯ò—E—Ïc…K…oö_ãíq4ÎΜ AÆÓÚ¢• ¡NøeаünàÒ“™ó\–â‹ ß¾+)õ`öñ3çó Šoürûîx“µ/qåÐøÄߥ<H )Oƒ¼VÝ*+-º -ÜÆš¸k;Ÿø>Dz>± ßÀú£A‚¡Œ ['ÑM@†ÜX2n••2²Ó÷&Än Ýèçãá:+Îf8rLCŒ•V B_ä÷ìíÆ9M›íìæ¹ÒocHDÌîd¡ùýõ·ÇϰO”¡!¾Ky4hé½Dy­†F/»~íê%\¡i{qj®_í >CÚ'€n=!/1ªÈ˜¬ŠŒkBÆG 2 ®€ Hë%† kÿjï% ÑXíFÄŒÑâ•ùeÐ@=Á'i5pðpøò?Θ»ÈÝ[¡iY’>©a}BÏR …ŠÙ¨ åQMõÝÊ -pWZ¡85b"B6ú‘ºJûĈ÷ F²Á‚ú$ c !cšÉ2n6 x¥ûˆŒÂüógŽe§§ÄïØ²Ñ(«x‚½f+;*Ð =¡7Ù«ß_i„:òª¦Oª•úD),’hÐ  xTCîJ+§FRŠ€¼OàRŸèjQ4  dš2fÎwm62þIÈÀñS ÆZ -;{òÜgWTXÐúU^,©¶#Ùu˜±"2Zúü¤£ è€ùáoý™ -tÂ>h@60„4ý®JÈðþƒÈc½_#dd¥¥ÄÇD¬]±ÔE¼ Cîž–o¬üÈ¡ÑV±ýMÌñãn3FÖ'‚Ÿ\QøÉsô¡O”“¡ˆFkuÜ{ÏèWÜ‚©q{=3 ñÊúdÖTVìèÔ',¯1ÑäÈðk2êï822®\$d@N·®[åé¶`ÖTG"˜•%›=ºÚš`¬ü( Ñh+¡øõ}‚~âµ315=ûØé¼ËE¥åä'Ojqj4Ô'2E$z¿á‚r¯Š¦ôÉ‘ ´DÖ'.óPF  ƒ6Ø€¶Vûo¿Áó-v _AZBÆì? Åùy§s²pöl Þ°vùRØhZ=`¬F¢±j2T !¡¼O¬GƒŸÌZ°ØÃÇ7 xktÜž}‡¡Oò ‹oVTŠ}òVèÕdH¢!Y¡Ðì•05 -àÇQ]yŸ,Y8gú¤ñ£9Á©OX4à´×ÒÖÑB‹M§@Fh3Á/1ÅÙS~‘qâ0^%r˦u+=/ ÑÆjÅš­£†«pDh(F¨­<ÑOÆÿ8}ÎÂ%^«Öm -ݶ#>ù@æQøW~†>©º/ªkƒ}¢6¸Bijº²>A?ñXL·EQd}¢¡Õ¾½£³AW2–x¯ño2$wù"ã"ø!…Ñ““ È@|­Yî¾2:D‰ŠÍD“Œ•ŸVJ#Tèhsó^}¿4dĨ1'ϜǦFªk:WWè“;êûäßê¢!®Pìvaj ºž8rHè“åK]˜)²§ÚݧF]m8::zÐ%†Æf=z÷1ÏÕÑ›˜*CÆãF"šeô: #!62tF=i Þ£Ÿ¦Æ`¬† u}‚~‚Ô6éñý0k;Ç© ¨kåÝꇠ®ÿ¥O”£!LJRW¡O"·‚*.AUÄ· -õ}‚S£c=]]]†>߯ƒ†J‘Þ©¯W#ïß¾~…Sƒê]è“)»c"B|}–¹Î§~% >164èÜ©ƒœ!âþ±QAÆ ¨¸Æ"ムÄ® ¹Ç³Ó÷&Ä¢%ùx¸Î'tÁÞéOÆÚŒ•!C#æ';’>‘ø W×¾¤®ö¨®Š©êzôÔ¹‹uÅ>y§Ú'õõB6T¦†¢Opù,&íÚ„¯•9ŒPà¸ô F£# †1ë8Ž gŽŒ¸äÍ@Þ‚£‹òyK /KÈå@s‡ ‘¡!Æ*– IŸ`4ôäê:qÊÌyL]Ã@]÷*©ëÓÚ¯^«ë1òŠ}òLÚ'Çøs…†÷tÃJú¤§FC¿3 ÌOâGÆ2¿„ŒƒŸGÆ'dðR{TsQÈ8CÈÀ!Œà¢µÃ‰#㛯Úiб -G}ŸèuÒ7d覩á4U¢®I ®ÇQ]K¤êúî½JŸÔ+Eã£Øð/kùö+½ÆÞ+YAð†µ+ØÅñ‡}SÃȰ‹¾¾ÃLÈ*GÆjŽŒÌœÓy -dÔ6 Ti'À­° õ«½Üœgc8¡Ñ¬h¬ÚÐ%š† •h0u¥©Aê:`0Lñ“¦K¦FòC2u}òìyÃ}"‹YÁ{¦®Ø'7K-HÜÅFèBÆÑÖàä--Ìͺw5ìÒÅÐ‚Ñ ï3œ!Ã…± ‘q˜!£'ñg!r‹Q+ ƒ°••–¿·f“¬`0V#˜Ÿ:4?ÛjбŠG蹺²©Ñ §¨ë„É3æ2uÝ"¨kÞ¥«E\]ŸruUî“ú¦Æ›×¬Oî2/È;à °ÜƒPÖ'} &ÆFx æȰ¡#í2¼Vû‰È¸\X,"ãU£‘@j=IS'* ¢ééæ }F#تOO3cD†¦«pZ5fj8Nùû|œªêÚä>Qä89š‰£c4‰± vA*HQ\`iÒ‹€Kg¥÷¥.i‚( ‚ŠPŠå¨±DˆJPQƒ‰å$ˆQktrN&羟·ì»laõK>,Ï?ð¾3Ïõü~×¥˜Lêó³°èPÁln@›¥%Pa K!cúG‹ŒIjT P»³»¯Ìt=rôäÙÞK´OFUùäO>»¹Ü{JèARBó˜Ê·±43Ò×Áh,˜7oî¼y ´´/ÓÑ3µñÉ0‰MÉ’È cˆFÆõAõGDfMÛª°‹"~TÍÑ_¹„FÆ)24¨~Rçoʫƶjà}H§+ø¤óøésÄ'·ß|Ÿp+àÀÅ gäK(úÄP¢±h¡Ö‚ ´.Z²lå*¬Ã¶Nn>› 2Š*ê)dô2^j!ã9"ã§‘á{ 2¨o—f§ÅG…@˱±45Ð]†È˜)E†F-VöŒW5ôq):¸zûÃSMÎÄéÚØ²¿£ûúäšJŸü©bŸ<½³ñjßyº„Vá: %v£µ¹‰DcéâEÚÚÚ‹c0VCåáCåAdˆ%eÕÛw!2¾¸piàÁN£~Ò^%‰Â¸Œ¬ÍôV,Ñš«ñȘ¤FÕ ß*5]‰Oö<Üsò,ÞÉ7ãîE>ùõñCRB‡®“ÚMæAESB]ìm¬H4V,[ºÎÒå £5 5'¢5‚ŒZ(<‡²È¸?Ê ãõ¸Èø‘_¦pEua# D†öü9ˆŒ©ŒIJ«Æô™TÕX…UÃÞ™®è“}m„ãoç“gOa_.µ°„V—Q%4ÐÇ |bifl §»rÅr8+t ¦k­yP…%¥€Œ}m]H.DÆ=@ÆCõ‘í÷*‹ŒºÊâ<Œäf_7G[k3@Æb­¹ŸÎš1BÆ{‹ ÙªñλlÕ˜ÆT 2]iŽ'ãt­khÞßÎúäÎûä9w \î={²çð–F¼!ìAèÛu ýUº:ptõ æV ž€Œ2Nb:9Èx¡ Òï"¬¿ü‚ c{MYAV*jÌÓÅ~½…ÉjDÆl)2þ¡±ÈPY5>Ÿ¯½LGŸ™‹Ä'’ÒªmM¬OßÂ'2%ôk¼¢.rE帡„‚Oø<ˆ†‰áj}=8ú ;'7ï€a “Òêzõñ‡ô³/eq¹¬"È܌ʰÍ>nŽ<+Ϙö¡,241*«gºzPŽÏ),¯”">¹|õÆbŸ(…Æk¦„Ž0%ôÜ©£¤ ÂxÄàãîl¿¢ajlh```hd¼ÆÜr½­ÃFÏMh´ )2μ 2˜~3HÑ…«)“d¥ÄEoBd¬d,e‘1ùý1Èø«ïé/8 -«™®ŸI§ëF2]‰Ojå}BAC¥O–ÐÁz=6ÃrÅJ|âæѰ4_cjbllbj¶Öj=ïâî*Œj•QÈ8ŽÈdü 2ž2˜M„;JVQœ›žH!ÃpÕràMGÆ$n4(ŸÐUƒ®àž£+ùXŸ°ûä×§jø„)¡O¤%´J(µ\«áý¦Æ£O<]!VæfpÖZXC0œÝ¼ýƒ#DI¹Å,2zwޔŽwQ½ÄAƼÏfÃb*]¬Œ EU㟓?NW]œ®ôfLõ ³O<ä¬FõJèO#ÃX¯ 4z:ö·4ÖV` †úzlt´ç­·¶²´°°´ZgcËwrõò„ECÑ)(£† "ãkÒs@fê  6:,‹ŒÚŠ"D "W‡ Vk >ÿ‘A/VMG†²ªA|Uƒò‰½ ñI’¼Oîr}¢F }I•P¹åŠ%4=1&"8ÀÇc£ßvƒÍúuëÖÛðì ž¾!Bz!2z2nÜ"Èx¤”À(dÖûÕ—§ 2ê«K%b‚ g»ukõW.]8Œ1G6œéJû¦+ÏÁ•”OèRŸ¢¿”–бÑàBã»\oݸ®Îö¶<ÏÖÎÞÁ‚“˜–]P†Àjë<~šAÆ42~S‰ Rz 2È÷z:´4Ôn)ÊIOdx2,‹&!wÆV vºŸèp}’Fû¤­óØéó\Ÿ<Ï'ª–k[ën(¡è“¸¨0¿§›‹“ŸÏwptrqƒ`…GǧˆóK*e‘ñYdüW!2d?GEŠMi¾896"È‘aÈXȘȘ"EÆß5òUƒö T ôÉòU†¦RŸdÐ>éñÉϘ§«Ä'Œ-¡ô#¾z™^®XBÁ'I±ÂP¿¯—»«‹³³³ËFWw/_A¸$#—ÉäñÓç¿b‘ñøÉ³çê#ãRïYì5Í€ŒÂœ´„¨Ð -º2¦Ä ƒã N†Âé:…L×90]Wê)÷É•ëCô ±o÷w•%”yÅÌVÀåz¢»}sc]e‰$+-Q*ðóöôpwss÷ðôö „ -Eàø®WÃÐp¸ÈøŸ - uMŠŒªÒüÌdQ„ÀÏÃiªÇ'ï¼ËõÉ'¬Olå|‚o·ŸP}ò y¼êø„~Æ£÷áß¼ÚßwþÔ±ÎCûvŸˆSD‘aAþ~>Þ^^Þ>¾›ƒB#E ©b`ÕÖ»q˜Ðȸ+ƒŒßÇCÆm‚Œ32¶"2â£B¼7òm,M t—!2fN Cþ(ô ™®Ä'¦²û¤}òï3. ~ó-Þí“WŠ|"¿Èrh°Ëõð=Mè“ÜÌ”‘0,Xè¿ Ž  8L(JHÉ—T×7í‘AÆŒ"µ1úã÷ 2Ž!2¶U• 2Â~îN¶ÖæFz+–hÍ•"ã½ dH"ŸüK¹Oªëwî=xä(¹¤ëC·ï p}2>4à¾1˵ßÕÞÚÜPWYZ“шŠ lÞ,‡†c02r -J+ëš[Û)d\£ñ3…ŒWª‘A !:ƒÝ€Œ[Ë ³ 2¼ˆ íùsS)dL,VÎQ2]Ñ'óµe}’žS¸ÀÞJ½ÿÚÍ[w „> Kè«qJ(Y®4F† 4Èr=rhï®í[+J$ÄØhaDX(œ°ðÈèXF¶¤¤bëö]lYdМâöÏ?ä‘ÁFðÔ1øNÓ¶Ê’¼Ì$@†/ŸÎš1m 2&\BŽÜtUê“äÌxÁC·I }ü䩪ÊBã5 º’åz¬³­µ¹q[u9DCœ–œ+‚—œš™_T^U×õ³“|Zïí·@~åÈÁ=42bÂ7d˜2SÈøp ->±#>‰ILÏ)">iï‚' Ö¿ùí]ð‰%T®â˜¸xá údßî†:ŒFnVFZJRbBBbRrjº8GRTVU»\r¸‡.½ˆŒQ¯•"ƒ+­@Æ ‚ŒšòDF°¿—‹½…Éj)2&¿? GÞ'“?àøÄp¥ }›œ™Où„,…Kׇ¾»G6¤:%TÊy²&ï4  Oîݵ£®zKIA^Ž8#=-55--Cœ+).¯ªÝ¾sÏ~R?ûúd~!áŒìübȰS\ªRWF/*£(#%.*ÄÏÃÙ•q@¡ŒÙ3gLý@P†¶SÏ“qãIžÌ€<™y²óäé©s6vN¾Á‘â¤ôœÂW)¡O%ôN ì¸ØÈ° ?_ooo_ÿÀàЈhQBrZV^QYU]ƒ¬Urbž£³2 -@±áDXeü“RÆ4A:œæ<™õ•'›¶þøËþ#Ç-/\qp½åŸ’™WŒ%´U¥„Ž(ç´4¨Ú&k¬«*/Î4’âEÑa!ÁAÁ¡áQ1bKFm}3Y¬ênz©¤ êÇïQÛ‡(#C¡Œ“¨Œm›×¯Z¶x2þ((ƒïxóä=È“é'†K–¯Þ°eûÎ=Žž<{ÑÖñ¦wPxl"ŒIN …—™)¡º,Wèˆ'­ÍX5ÔäqltdxX(\XxDTŒ(.1Á€’!mjÁÐbúÌ3v©*ã)£ ˆª&iU)­ /7TÆa£]ß»‘(ãKeep²D CåxódæÉÌÙsæ/ZºbÍ7ß~ÿó¾CfV—¯;“šÌ”P¹R }Á[B¹Ò Ëõ^?æÉí¦úÚJD#3-%)^+ŠKH–¤#åÕ°Kð¸FPÆseeÈÊðõp²»D”±cÛæuŒ2>eüYP†öSÍ“±ïŒƒ:yÊ4Ì“…PB×m†<161·´¾êàæ“ ¡Jh‹¶Ê/Á>Ì“–¦ú@£ 7+=5%)!N,‰Dâ¸øÄ$IZFv£¡Ë DÖðCmÊx¢Q§A?ƒ2¾f•1y2Þôßðÿx*y2†ä Hc:”P,¡°„~úü{¥Ú&×8(y¥Á”Pø Û[eˆFyIQ~nvfFZjªD"IMK.ò -Š P2ätýT¬•‘ -Êðru¸‚ÊØG)c‰¡  =O9O¨:Jè4(¡ó OV¯ß²mÇîGOœ½x Jh ¦ªa¹² ‚Y®˜'P5ÚªŠÒâÂüÜœì¬L¼¬lࢨ¤¬’³„j2LÉÕI¹¨Œ`_w'ÛKg5*CX¬:7OFQy2É“ù'k6’jjaeÃ-¡5L Õ¼\•¤Á”Ðû'=ˆFS}]ue9°QŸ—‹——\TTÕÐ`À.¡ë§Ve ò+ÃŒVÆò%†óPS&Oš@)ã-AZ/O&bž` ]°xÙÊu› „îW+¡(Ž.Ôý£Ç#,×—Ê%÷ ‹Fƒ´Ù().*Ä+*.)+¯¬®•660¨]BzŒ®Ê(/ÊÍHæ(Ãx÷Ž­›e¼âiÊ“é'†K–¯V-¡b(¡°\¥0ø–«š4˜úŒÊ–æÆúºšêÊŠò²R¸²²ò -䢡Iv›cà§~jV–—nTF(# ”F”qž(ã»m ”1ëcAúž²4Fy‡Î,¡ó-]Á-¡®¤„J2Ér•‘åªO Å}rˆB£½í6h£¾®¶¦ºª¯ªº¦VZÂh½#gÀ YBM–Œ—|Ê«+ã(ãÇ­›Ö®\¶hþœÏ?þÑ”¡×)KƒÍ“§Až` ]¥TB}‚"D‰i°\«Èríæ,תËUEÔI£ÑuWŽÚhj¬—Jëjñê¤õ (  õ,y©U 1a·\íQ‡öýÊXÊøbÖÇSeè}ÜúÖÛTžð”P3 «Ët MÉP,×~¥Aíh¡4¨ÀÑØ€×ØØÔ,»Ýz§½£SUaôP†8RUKieüUP†¾÷µ<7~‚Z 561?c}õ†Y®©Z—«š4è•<3–¯„î?rüô…+®*˵–+Ÿ4~U!ƒ© ƒý}½Ý]w;äòvêä„‹¾50ôV†¯2Þ+(C¿Sɦ„Ná”Ðí;÷8zòì%[GwŸ H,ײjFÃ?Ñ* .0^AÀFÂA_ggW7pÂâ‚¡Z?uS† 2 KeèyÊy¢©„6;}þ²½ w¹¶å:„yBip^t÷‡ï "s=½ÀÅrñ@z(c¯š20Keè{#•Ð…‹—­Z·–«ñÑgp¹zÒ˵š,×f¹**Ÿ4¨ªA¡ñ„ÖÆ ÂÑxÀõõõ÷ Ò\”ñŽ·„RËuY®PB÷<†ËÕ–k³\ePB{ûµIC ¢ `Ä7Œ‡<|ôè1Ú•áËÅþ² Œ×{šJèŒO?'Ëu .W£#Ç-/\¥—kjVA)Y®(!"g¬4^²dð¢Ú Þ8@ä¢.?.P<`ü*(ã܈%”]®Çp¹:¹ûGŠ“ÓAìrÕA ðÊmPpô,‹Á”ñ{¯4°„Âr5`–«.×+ö®°\câ%Y¸\AÝ}( \®ÏŸk–† † {ˆÍ‚2ÞÔqK(‘Æ»´4fΞ»€³\Ï^´ut÷ Š%¥ãre¤1ŒÒxÊ' ^40R¤Cqðýႚ«|`¼‚2Þ”ñO½„’åŠ%ÔÀ]®fç/Û»xú‡FÇK2óJ*@d¹òJƒ – „é`¿2\¾¸`訌3‚2^ÿ)åÉÛ$O&Ê]®Æ&æg¬¯9ÞôŒ%¦¡4¤¸\{úAO4HC ¢ Š €ƒÂãÁ‚p¡ íÊ0U(c® Œ×t<%tâ¤Éœå -ÒØ{Ðô”•ÍugO¿¨¸”̼âŠÚ%û”4þÃ# U4(64ˆÅ…*#*#«Œÿ±_çQ5õ{Ç×½îZ×õ,ËZæéQæL•© e(SfÑBÉËU^®“Õ5´æ4,í]½üC#ãSÒ³só !×%Aãg»à68ëàFü%g|Ãhï0}1²ˆöÊAC ¡ -ÐÐY.W3'wŸ ð˜¤´Ìœ¼“Å¥54Þð¡!zhœuP"þ¾ iÈè‰ÉEt4ÈËuØÈ1Ê“Ô54ç-Z¶zƒå&WOÿȸ”ôGò Ï^¬º Ðx@E£a”iÛ`ì‡À00¬GGƒ¼\Áåª -.Wù SGwï °˜D7›žxÐx‹Ðø* Ÿíôm©½¾ Ñ“!U‚—kot¹Ž§ÐОoh´ÖÔÚÁÍ;0,:q÷¾ƒy'Μ¯¨¹"!|Ûºú*dGÆ¿1ÒÄp¹öé7¡1Qmæ\½…KW[Ø9oñÛ±3yïþÃÇOC4®h¼‚h´ð¡ñƒ¾ŒŸ ÿë"6ÉèD ¢Ñ ¡1j¬Êäi³´õ&­Û£GåÕ\4Þ‰CCpÂâþTŘ ¹ÅEL£+ïr -.Wˆ†.Dàá»mÇÎ]’²Kµ§’ !Ù6ø‰Éè$Ñ/WˆFÿA -ÃFUhhé,_cbå°ykÀö¨„TˆFD£¾¡ñ¡„hˆõg$m˜ ¶£_®$Š©3æè.X²r½¹­³‡oðŽØ]{4*I4^pÐø& ëü&£ÓÄ€¼\ê³´æ46ZÙoö -ŠOÍÈ>ZPt®¼ºŽ‚ƱhH&£sEG£{.ª ˆ†™­“‡Op8@#ëбS›wî#4Þ“hü- I†ÉèLQ/×?À{B 1rŒò$u M„†¥½«—h$D# q±ª®þö½‡ 4>Ë LF§Kðrí .WÅá£Ç©N™ÐX¼b™“»OPxlB£ø· ÉètQÑ@¡¡#ÇL@h,Z¶zƒå&WOˆF -×ï>È Þc"Ž oL†¼bFC@c¶Î|€†©#D#&) ¡QZY{UÆhPÉøÚÚòé£02\0rŠ ~ 4Ô44õ ȸ”t€F!Dã:DãÙK¡!ŒŒGLdXc2äårEhô"ÑPhhsÐð -‹ILËÌÉ;Y\ZQÑh"ÐøÑø.Ld¼i~N!#“!ïø/WB4&ªÍä áâé‡Ð8p$¿ð,¿¤FƒBÆwLF§I†FëL­ݘÑxóþã§–VéÐŒŒ8L†¼c@£7BC ¢1WoáÒU,ì qɪ.Ë LF§úÊEcب±*“§ÍÒÖ74Z‹Ð ‹NÜ Ñ8Ѹ!+4ˆaü`"£žŸŒ -1rˆÿrå¡¡8BidÜ‚d” 2’ îŽÖ¦˜ vD£'BcäåI4,!¡Qñ©4ê -CCì4D‘q¥º‘‘A’±Ébƒp2þƒÉø‘ht¡¡1|ô8 ->¬ÃÇO•”UJ…†¤d„d¬#ÉPd(ÐÈè‚Éøý‹ï=áCc(†&‰†+‰FöÑ‚¢såÕuR !‚Œ€Œóñ2V/]¨‡É`%hL™ÐX¼b½™ D#<6iOÖ¡c7!/^½}ÿËh0ññ’qùRYÉéㇲö@2|+ÏמÉ`%¡hL@h,Z¶¡áŸ’‘ и(42¾ 2^½à‘qâèÁŒÔøÈPOWHƲE˜ ¶D£@C@c¶ÎüÅ+Ö4Ü}‚8hhÜçCã›Ähˆ&ã ã0 #‘aƒÉ`74ú 䢡ÐØ`¹ÉÕ“ªºúÛ÷>æ ñEb4øÉø›GÆSg2¢Bý½0¬' €†6DÃÔÆ¢“”ÆCãNGÐ ãIƽ[×I2öïÙìãîÄ!cúLK‰@Cm&†D#DŸ%Fƒw²¶µ!2>ÿEqÿÎ+5û € KD†& c‚Òˆ¡˜ ".W±hxSиÚ4„‘ñ ã,IÆŽ`'3.ãFdô…düÉgŒhü јÈCâ—’Ð(ì Ld¼%ɸZSA’‘€È°·ÜÈ%cÌHDFÏÝÁc‚Éc4þÇŒ†5D#,&1-¢QÚ!4¨d|mmd4C2ndäÞ¿’á ÉXÈÐá1L†|ãC㿽yhÌ•bÈ(=s2/gßí^›k–-š§©¡>‰KúþÄdÈ5qhQÑ8Y\ZQóËh’ñ‘GFÕ…³…ùG;¶fëW.YÀ#c&ƒ„ 1B‰@cáÒU ¿ˆ¸äô³ë¯ß}DCßyd<ºç&ÑÛ¶"2–`2Ø þƒ†«2yÚlm}C£µ 7©Ð †ñƒBÆ›æçˆŒ:‚Œ{“wîØæëáL’1gúUL»1£¡8BidÜ‚d”2² A"ÈèŠÉ`+f4†È Qd\©.Gdd¤ÄE†øyºØY 2æÎø?ûuùTÕÚÆqüåsŒqFQÄ•c€‰ `¢twwwww€¢ R -Hƒ -ØŠRõÏÌsÝ÷Z{í½aƒ¼y†Å<ë;³ÿ=¿ù\÷_‹ÈXÀAƒFACœ×Ë^hüËÆ˜ÈˆEdؘêi 2v1dЩÑÑæB#93;Шn ÐøÀB㟡h !ã­@F9AFT°›½…‘¶*&cëF† 5ÃиÈFã 7¿¸ÑàAF?ñ‘ÑXdäçd¥Ä†d(1dЯ±£hääßæFã+/48ÉøÉ"£§“"ãæÕKçÓ£‚}Ýì- E¹=sg3dТ±¡a?g#¢ÁŠ‹ŒO÷k*JŠòsϤĆx:Û˜êk(?¼׎ÍëWB@CÓ物µ“''÷Úž¼xÕÑE¢ÁýáJ “ñ7&ãóGDFGûóÇ­Íõ!¾n–F:ªŠG0dа1|žh“h$¤»Xp£ñèwhð"ãåSLF)&#.<ÀËÙÆŒEÆ– CÈÀË`ÈÇþ7h %£$ãnýLF"Ã"C -ÈåE³Œql46r¡L¡Q‡ÐhGh Œ€›ŒÁ¯ðþìïíFd<¤ÈH ôr¶5ÓפÈXÉA³Æ€†‰Flr Q‚Ðxh¼íê ŠŒl2^dÜ"ȈÆdë¨Qd,_º‘1ƒ"ƒYÆx÷{4TntŒFU]óèh %ã}÷;‚ŒÚŠÒëW.^.ˆ e C“!",4_€Ÿoú´©S&q¾?Çûïù?nd4V“h(6Nžc@ãßQÈhCd_»œ‘âçî`dœ:zp¯Ô¶Më(2ð1aÈ G££±GNAQUÛ£E Qh´r ñ "Þd4ÕV"2ΦÆGz»Øšh*Ÿ—•‘d‘1È€cÂA—x£!ÄBcÿáã g„FJVN> QÓˆÐx hôEƒµ ‚Œ¯Ÿ?~JFfRt¨Ÿ»£•±.CÍû Ž 4,íÝ|ƒ£ÓÏ_ºz“B£³ÐøŒÐøÉ9ad¼2Z€Œ2DFZ|D·‹¹‹Œ5#’Á,c\úðØ”3¹ùE%5÷)4> Cƒ$ã;IFAFC5IFL¨Ÿ‡£•‰®:"Cš$CpîìY tk hèY:¸ù†PhÔ7·>~ÞÞÑÙÓ7ð‘_hœd `2ž="È(@dDy»"2T0â ´m46la¡afãì‡Ñ(Eh<|úòõÛîÞ~ö‡+ZÆ/bl2zŒOÚî5Tß..ÌËÉLŽ õ÷p´2N“dˆ1dжÑ4¤(4Ü}C¢30wêï¯x ÁƒŒvDF]eÙ‚‹çÒ"ƒ|\í, µTN*ÈîFd¬^ÈAϸјÂFc%…†¦¾™­³W`x\*F-|¸²Ñ ¦ñoŒˆŒ #+9&ÌßÃÉÚDOýô±Cû¤·o^/¶bÙbÁy ´l3‹–.§ÐPÓ1¶rp÷ ‰NÊ8ùÚÍ[€F‰Æ |Op'£¹® -“‘žìãjoa¨ dÈíÞ‰Èøs "c&"c2C͆Æt>~ùBÂ" @CYÓÀÌÖÅ+0">õì…+×K+k›Z†¢Õ †ñc“Ñd¼æ"#6ÌßÓÉÚTOƒ70dÐ,N4ð”BcݦmR{=¥¦klåèî”™}ùZñ­; <Ð úÉ&£§‘Ñ -d”dDû¸!2TI2Ö"20dж!hÀ” IYùÊZæ¶.ÞAñi2ŒÆ³—o8Ð §‡ÁEÆýÆšŠ’¢üœ¬”ذO' C ÈØÅ1ú=ê ¿Ð˜dŒÆíê†{mO^¼êèâ@ -ü~pÑÑþȨ¯*¿yõÒùôĨ`_7{K#mUE¹=;·n$ɘÍAÛFFc ‰† - açê‰Ð(4êšZ=kGh 4¾}ÇÓ †Á&ãåÓ‡ˆŒÒ¢üÜ3)qážÎ6¦úJÇïGd¬&ÉàcÈ mÃј5{® ‰†4 qZ]×ÄÚÑÃ? ÐÈÉ+,Ah<4ÞvõRhÀAßwøbe“ñ¸õnýLFFbTˆ¯›ƒ¥‘Žªâ‘l2æ2dйQЗÜ-+REËÐð ŠLH?w±àFYU]s+Ï_‰i a ~û‹ŒÔ¸ð@/g3}MLÆŽ-B 4oD4ÄÖmÚ.½ïбÓêz&ÖNžþa±ÉYšF„Æk_¿ã¹Èh2na2’¢C|Ý,uÔR@†(Cƈ7 …EVa4äNªhZØ»úGh”³Ðè¤Ð@ÓDÃà$£©¶¢ôú• @FD —‹-¡ dÈ 2Vr‘—ÁA¿x !0Opñ²bë7hhè™Ú8y„Ǧdåä•TÔ4Þg£¡° b$¯H2Н]ÎÎLŠõsw°2N9°WjÛÆu¢Ë—.BdÌà$ƒYíâBc2Bc&BcÉŸ«ÖŠKìDh¨jYØ»ùG%¦Ÿ¿tõfùú»­ŸSh i@_á–`2Þ!2Zšj+gSã#½]lÍ 4•OÈËdˆ Íàç›>mê† ÇÆÃÐØµïÐq% }Sg¯€ð¸”3¹ùE¥‡OI4ð4ppK8Èh¨&Ɉ õóp´2ÖU;uô ±‰M>&ÿaÞŸ´' 0·îÜsàˆ¢ªŽ‘¥ƒ›oHtbBã ÑF¢Ñ÷äóØÆ|Kú8È(Cd¤ÅGy»Ø™ha2$·lXƒÉ˜dÀ1™Äùþï?‚ih<Ñ4V¯ß¼c×þÃÇ•4õÍl½#âRÏä^)*­¬m¢Ðø O n ‹Œ{ Õ·‹ ó²3“cBý=­MtÕÒ$‚ "hÌž‹Ð4¤j:Æ–î~¡ÑIÙ—¯s¡§Á0o 2ê*Ën\<—–äãjgn¨¥dì–”gȘH Cƒ@cåê [vÈÊšf¶.Þñig/\¹^h´hôÀ=i|úô}ï»O0%…y9YÉ1aþNÖ&zê§܇È#ȘÅ1†Æ „Æ¢¥ËE×mÚ&µ÷àÑSjºÆVŽ~¡1É™ÛÕ ïº{û`Á.> À-Ád›šÚö0€s^è9z<£¨(JPPš R¤÷*Hï½Z¨ $@HL(^¥JAšt±+z®÷Sܵw -‰à½/ïÙÌ~¾Ášyæ÷_ONF - Ãav€Œ(ÿøýA󆫸„@CUCÛÀÔÚÁÕ;0".9#§¨ì§A¯ ?þ ãËÇw|2ú»Zêª*(ùY¸DL¨¿§“¥±ž–:D†$J’"ü !#§  £açäáŠÁâ²ò)UuÍ]ýÓ<4>~ÞƒªñãÇ¿þÅøôa"c&£‘â‘ )q‘A>nŽ6j(ˆË!h\¼,%{ý¦êm.A‘q)„œ"‹ÓØÞ󄇯îûO_ö¾}ÿäûÞ×Ï„ɨ¯b”ægá1aþžÎöûdHˆ£d )Ð8¡!¯¨¬®¥gliçäé†IÄgå—2ªê[ºúG¸hì¼ýUãÛ÷ïß@1>¾ß}%øe4>dÑŠÉ„ÔøÈ`˜ C]Mˆ i@†J‚òßÐ04³qtó ŽŒO%‹é¬‡0ÓsKk›¯Þ¼Õøº··÷õ (ÆÛí>Ռ҂l|bLXD† CEQþ*JÒrL?xhHòÐ0±´wö ‹IÂgPÕ46¶Á=ùøéó¸/7×öÉ “‰©ñQÁ¾îŽ6æJ -rBdÀÍ@ÉøçG4¤jšº†æ ß਄Tbn1ý°±ƒûÓXƒîÉû?€b¼·deA@µ ;=)&<ÀËåž•‰¾¶†Êk0gQ2•CЗ¼*CECKßÄêž‹W@xlR:©€Ê¬~ÔÚ ¡±°¼¾õjg÷Ýû  ¯·_¬>‡ÉèhzȦçS¢B|ÝïÛšñȸr邨™Ó§N¢d ("hœhˆ¿xEZNA  adn{ßÝ7$:!-3·¤œ]ÛÄEãùêT·ï@vA16×–çg&­õÕLj)=)6<ðð19Žþ?Q4Nýù×Yk m}S€†w`Dlr©°ŒY¡1<13îÉö«7»»»ov^mo®¯,ÎNñÉ(É%¦%D‡øÁdܽsK@8&¿ “ÿ÷ÃÑü¯ðÑ W10\!4nݹkdaëäáMËÌ£”Wrјš]\^±ýòõë×P1À-™žàþ2˜ÔBRz2 Ãa -ÈPdHIˆŸGÉ@^xÍøm¸4¤ªÚ¦Ö®ÞAqÉ9…exhLÏ=_Y±µýdkscu |?G¡a‘‘ “áádg‘¡¬ 'ƒ’ÐІŒœ‚2@ÃØÂÎÉÃ?ƒÅeñÑœ™ÕØØÜÚÚÚ|±Š1ût|¨OFFrlD ·«ƒµ©ÎmÕ›×e¥ÀdEÉ@b„þ ÇÃU - WÕÛ:fÖn>A‘q)„œ"®þ¡ñ§Ï–VÖÖ766Ö×V—Á-x,  -“a ‘¡(‘qîh3á?(ŒÆ¹ —$eä•ÕµôŒ-í=ýÃ0‰¸¬|J@£½gpdbzváùòÊêÚêê -(ÆÌäè“ÞÎf>qAf(ˆÏ/ÑPÓÔ14³qtó ŽŒç£ÑÒÕ÷dtTcqiyyyéùÂÜÌÔØPw[G@†¿§“¥±ž–:D†$Œ?P2Ñá -£†+@C aÐð -‹IÄÃhª—/¢dˆ qòÔix¸JË)pѰqpõ -Ã$¤ddç•Ò¬Êê‡SS]Éb€bäeg¤b1aÞ®¢dœƒÈ@'+²#ŠôhHAhhhé™4Ü}ƒ#bÓ¤¼¢RZ“Å®¬¬d³˜4P !-16"Ø×ÃÉÞ’G†ì>'P2>‚á*‚†µƒ‹—hT\ŽªA¡ÒË &ƒQN§R@1ˆ¸¤¸¨P/sCÝ;·”åQ2ŽL®ûh€ŸÆ}7ŸÀ°èød<‘”[PL¡–Ñh´2*¥¸ —DÄ''`Â}ÜîÛqɸy%ãEøœÐ qÏÙÜ“˜„<1›œ_X\B¡PJŠ óÉÙD| -6&"ØÏÃùžµ™¡®&L†$JÆ‘É!h\¼,@ÃÂÖÑÕË?$2›‚#d‘ró -@òórIY\ -662ÄßËÕÑÖÂXO ýe¹ü<\Ïœ ¡c`jeïäî‹MNK'f‘rÈdr)‹˜ž–Œ - ðvs²·25ÐÑ„‡‰ä%q”Œ#‘á*@C ¡®u×ÈÜÆÁÙ|5¢b’Rqbff&‘ŽKMJˆ‰ - ô·Z¬Zê*<2ĸdÀÍ@É@tŽDCü’$ŒÆm}K[GOß °È˜øÄäTŸŽÇ§¥&cã1ÿa¿LŸÒÈ¢8Zm&5 ƸàduCÖfQ1•(¥¢QGÆh*ñÿŸ{ß릛Ś–{¾(|²ÊSçþÞÝõÅÙInI&ú&ƒÌxÓôDè.ÐJ"•-ìVjõÆåõíýÃã¯ßžŸŸÿüþõøp‹bÔ*»…,¼X1sj2†)b ˜Ñ ¾4"«ë›¹bùð[½qquóOëáçãÓÓÓãχÖÝÍ£\ÜÚ\Ç+$â>L>þMûSÔhð‡« —/'ÓÛ;{ ÆÙyóúæî¾Õú·Õº¿»¹¾×Ò¼ÝLÉ©'S3³ðruzü¡hœ©QÞ¯TkGÇ'õzý伨ìï™Ñ Üx±ÎÎP2Ä£_4Ø=YöÂÔ@5¶‹%pã°Z­ÕjÕêae¿\*ä2(FÞ%l~N)ÂÑ7ìžàÔ5R™\ÜØÛ?8¨ìï•KÅüVz#bxñ–XL3“˜ %C,z£#”Ý·ÕHl¤³¹|a§´ ”JÅB>—ÝÔ‰·„'ã%C(úDF(Ü®Ftu-™Jg·¶óùB¡ßÎmeÓ©äš"Œ ~K(Ò/pO`j€r(‹'’©Ít&‹dÒ›©d"‹ °>mø.[BÉÎh>}æ÷ÄbG5¼r07Ö“)d#¹^DÃA?Š1gƒ‘Án %CDôÑøÑ`÷¦ªáòú¡H4¶_K kñUôBöy¸³_@ íÅJÉŠîhŒŒ25Ì Æ‚ÓåñÉP8]‰!+ÑH8ð{ÝËK\ õ–à‹•’!Ñ€ªL ¦ÆÒ²Ûãó‚¡P ²ƒ±8ïPÅÐn %C0´hð -ï¦Æ¬Åæ˜_t‚^Ÿ_–å€,ûý>¯Ç…Á°+bàÈhßJ†`tFƒM Tf¨ ²n¸Üñ¸Ý.çÃn5+bÀÈÐæ'%C,ÚÑhO ¦ÌP³Õæ7–œÎeh±0Á0›fºÄPo ™!J4ø=aSÕ˜œÆ‹bµ;ææ9 …ÃnÃ`ÌL11F1(B"ußE ¸(&³Åj³;s‡yaú2­ˆñIƒ’!$]ÑPÔ˜€‹nÌ‚VÇjµ pIŒ\ ¶>•ùIÉ]445FÇ& èÈ¡Z€Œ1&†A{—P2D¤N5Æ™ `2™ðh1Õ†á£ndP2DDêUCscr -ìP@-Œº`h#ƒ’!&Ü åéŠj°lp7&Œ`LjZp/ø%QÄ [",’^ ¬†š pclìàŒƒàÃÀǧ"™!*Ýj(ÙÀn|;8£ óB™$†øhfp5x6°L•‘¼#Ì c@èUÝPäÐÆ;‚^àÄPÅ 3D¦K¶ ê1Ìœ0 Ü  cT5ØÖ`Ù`n Ìþ¾P¼`Á 1½j6À ”C|¡z¡ƒ‹AfŒ¦†š UðEÛ -$†ØH]ÙhË¡ç}‡$Æ@ IÙÐäèà¯^/H áép£-ã}û·wÝ^€Ôë†^þq¨Ó c ºÜPåh3D^ *=nhz´?K$Æ@"õ“c¨ŸäÅ ¡ÿçK/jA^ "ÒÿóÚ"ñJÄKÄËÄËAAAAAAA¼>ÿ 0x•¦ª endstream endobj 30 0 obj <> endobj 5 0 obj <> endobj 32 0 obj [/View/Design] endobj 33 0 obj <>>> endobj 12 0 obj <> endobj 13 0 obj <> endobj 10 0 obj <> endobj 34 0 obj <> endobj 35 0 obj <>stream -%!PS-Adobe-3.0 %%Creator: Adobe Illustrator(R) 15.0 %%AI8_CreatorVersion: 15.0.2 %%For: (Matt Cherrill) () %%Title: (deltacheck.ai) %%CreationDate: 11/07/2012 12:42 %%Canvassize: 16383 %%BoundingBox: -129 -203 462 -68 %%HiResBoundingBox: -128.7456 -202.0967 461.8418 -68.8965 %%DocumentProcessColors: Cyan Magenta Yellow Black %AI5_FileFormat 11.0 %AI12_BuildNumber: 399 %AI3_ColorUsage: Color %AI7_ImageSettings: 0 %%CMYKProcessColor: 1 1 1 1 ([Registration]) %AI3_Cropmarks: -263.6196 -439.3682 578.2705 155.9077 %AI3_TemplateBox: 157.5 -142.5 157.5 -142.5 %AI3_TileBox: -245.6748 -421.2305 537.3252 137.7695 %AI3_DocumentPreview: None %AI5_ArtSize: 14400 14400 %AI5_RulerUnits: 1 %AI9_ColorModel: 2 %AI5_ArtFlags: 0 0 0 1 0 0 1 0 0 %AI5_TargetResolution: 800 %AI5_NumLayers: 1 %AI9_OpenToView: -432 193 1 1081 688 18 0 0 54 208 0 0 0 1 1 0 1 1 0 1 %AI5_OpenViewLayers: 7 %%PageOrigin:-149 -538 %AI7_GridSettings: 72 8 72 8 1 0 0.8 0.8 0.8 0.9 0.9 0.9 %AI9_Flatten: 1 %AI12_CMSettings: 00.MS %%EndComments endstream endobj 36 0 obj <>stream -%%BoundingBox: -129 -203 462 -68 %%HiResBoundingBox: -128.7456 -202.0967 461.8418 -68.8965 %AI7_Thumbnail: 128 32 8 %%BeginData: 5280 Hex Bytes %0000330000660000990000CC0033000033330033660033990033CC0033FF %0066000066330066660066990066CC0066FF009900009933009966009999 %0099CC0099FF00CC0000CC3300CC6600CC9900CCCC00CCFF00FF3300FF66 %00FF9900FFCC3300003300333300663300993300CC3300FF333300333333 %3333663333993333CC3333FF3366003366333366663366993366CC3366FF %3399003399333399663399993399CC3399FF33CC0033CC3333CC6633CC99 %33CCCC33CCFF33FF0033FF3333FF6633FF9933FFCC33FFFF660000660033 %6600666600996600CC6600FF6633006633336633666633996633CC6633FF %6666006666336666666666996666CC6666FF669900669933669966669999 %6699CC6699FF66CC0066CC3366CC6666CC9966CCCC66CCFF66FF0066FF33 %66FF6666FF9966FFCC66FFFF9900009900339900669900999900CC9900FF %9933009933339933669933999933CC9933FF996600996633996666996699 %9966CC9966FF9999009999339999669999999999CC9999FF99CC0099CC33 %99CC6699CC9999CCCC99CCFF99FF0099FF3399FF6699FF9999FFCC99FFFF %CC0000CC0033CC0066CC0099CC00CCCC00FFCC3300CC3333CC3366CC3399 %CC33CCCC33FFCC6600CC6633CC6666CC6699CC66CCCC66FFCC9900CC9933 %CC9966CC9999CC99CCCC99FFCCCC00CCCC33CCCC66CCCC99CCCCCCCCCCFF %CCFF00CCFF33CCFF66CCFF99CCFFCCCCFFFFFF0033FF0066FF0099FF00CC %FF3300FF3333FF3366FF3399FF33CCFF33FFFF6600FF6633FF6666FF6699 %FF66CCFF66FFFF9900FF9933FF9966FF9999FF99CCFF99FFFFCC00FFCC33 %FFCC66FFCC99FFCCCCFFCCFFFFFF33FFFF66FFFF99FFFFCC110000001100 %000011111111220000002200000022222222440000004400000044444444 %550000005500000055555555770000007700000077777777880000008800 %000088888888AA000000AA000000AAAAAAAABB000000BB000000BBBBBBBB %DD000000DD000000DDDDDDDDEE000000EE000000EEEEEEEE0000000000FF %00FF0000FFFFFF0000FF00FFFFFF00FFFFFF %524C45FD92FFA8A884FD04A8FD74FF537EFFFFA8A8A8FFA8FFA87DA8FD72 %FF7E2806A9A87DA8FFFFFFA87D52A8A8FD6EFF7D585253062953A8FD04FF %A87D76A8A8FD6DFF7D270504525329062FA8FFFFFFA85252A8FD6DFFA851 %0427042D53582929067EFFFFA8777DA8FD20FFA87D52FD19FFA8FD07FF7D %7DFD19FFA9527DFD0BFFA82D040504052E530453062828FFA82752A8FD08 %FFA87D527DFD06527DFD0DFFA852F827FD15FFA8FD0452287DA8FFFFFF52 %2727FD18FFA8272752FD0BFF520427042D277E2705522F06297E52F852FD %0AFFA8522752A8FFA8A8522727A8FD0CFFA85227FD14FF7D277DA8FFA8A8 %522752A8FFFF2752FD19FFA8207DFD0AFF580427040504525305047D5328 %06530404047DFD0AFF7D277DFD05FFA827F8A8FD0CFF5227FD13FF5227FD %07FF7D2752FFFF5227FD1AFF2752FD0AFF0527042D042D5952042DA8A906 %292852040527FD0AFFA8F8A8FD06FFA82727FD0CFF5227FD12FF7DF8FD09 %FF5252FFFF5228FD1AFF277DFD09FF5205040504052E5304057DFFFF5306 %29292D040558FD09FF7D277DFD07FF7D2752FD0BFF2E27A8FFFFFFA8FD0D %FFF87DFD0AFF28FFFF5227FD19FFA82752FD09FF522D520427057E270527 %FFFFFF7E29065352270458FD09FFA820A8FD08FF2727A8FD0AFF5227FFFF %FF7D7DFD0CFF7D27A8FD0DFF5252FD1AFF277DFD08FFA87DFFFFA8262E53 %27047DFFFFA87D28290653040527FD09FF7D277DFD08FF53F8A8FFFFFFA8 %7DA8FD04FF5227FFFFA82752FFA8A8FFFFA8847DFD04FF52F8FD0EFF5227 %FFA8A8A8FD07FFA87DA8FD06FFA87DA8FFFFA82752FFFFFFA8FFA8FFFF7D %7DFFFFFFA82E2E042DA8FFFFA8F853062929520427A8FD08FFA827A8FD08 %FF7D277DFFFF7D7D7D277DFFFFFF5227FFFF52FD042752FF7D53522727AF %FFFF2727FD0EFF5252A852272752FD05FF527D7D2753FFFFFFA87D5252F8 %52FFFFF87DFFFF7D52287DA8FFA704A8FFFFFF7DF80552FFFFFF2704272F %06292E0504FD09FF7D277DFD08FFA8F87DFF7D52FFFFA8F87DFFFF2727FF %FF7D27277D7DFF7D27A8FFA82727FFFF27F8FD0EFF52F852A8FF7D2752FF %FFFF5253FFFF7D277DFFFF5259FFFF7EF8A8FF2752FFFFA8F87DFFFFFFA8 %052DFFFFFFA82727FFFFFF7D04042D532906532D27AEFD08FFA827A8FD08 %FFA8277DFF20A8FFFFFF5327FFFF5227FFFFFF277DFFFFFF7D20FFFFFF52 %27FFFF2727A8FD0DFF5227A8FFFFFF5227A8FFA827A8FFFFFF2827FF7D27 %FD04FFA8FFFF207DFFA827A8FD05FF04047DFFFFFF5252FFFFFF51F80504 %522829065204FD09FF7D277DFD08FFA8F8A87D27527D52522727A8FF2827 %FFFFA82752FFFFFFA87DA8FFFF7DF8FFFF52F8A8FD0DFF5227FD04FF84F8 %A8FF7DF852527D525227FF277DFD06FFA82752A927A8FD06FF52042DA8FF %FFA8A8FFFFA8F827042727530629287DFD09FFA8F8A8FD08FF7D27A97D27 %A8A8FFA8FFA8FFFF5227FFFFFFF87DFD06FFA8A85227FFFF52277DFD0DFF %5252FD04FF7D27A8FF5227A8FFA8FFA8FFA8277DFD07FF2752F852FD07FF %5205047EFD06FF2704042704052E2906297EFD09FF7D277DFD08FF5227FF %5227A8FD07FF2E27A8FFA82752FD04FFA8527D7D52F8FFFF84F852FD0AFF %A8A8FF5227FD04FF7DF8A8FF52F8FD06FFA8F87DFD06FFA8272752F87EFD %06FFCF055352FD05FFA827042D042704522F29067EFD09FFA820A8FD08FF %27A8FF7D27A8FD07FF5227FFFFFF207DFD04FF277DFFFF5927FFFFFF5220 %A8FD09FF59A8FF5252FD04FF7D27A8FF5227A8FD05FFA82752FD07FF277D %FF2727FD07FF7D532D7DFD04FF520427042D27272659062906AFFD08FF7D %277DFD07FF277DFFFFA82727FD05FFA8FF5227FFFFA82752FFFFFF5227FF %FFFF7DF8FFFFFF7E27F8A8FD07FF7DF8FFFF5227FD04FF7DF8A8FFA8F852 %FD04FFA8FF27277DFD05FFA82752FF7D2727FD06FF7E292853A8FFFFA827 %28292F2953292F2829282953FD08FF7D2752A8FD04FFA8277DFD04FF5227 %52FFFFA852A8FF5227FFFFFF2752FFFFA87DF8AFFFFF2727A8FFFFFF7D27 %F8A8FD04FFA852F8A8FFFF2727FD04FF7D277DFFFF52F87DA8FF7D52A9A8 %F827A8FFFF7D7DFFF852FFFF7D2752FD05FF5A282F28A8FFFF5228282F28 %2F282F282F29532953A8FD06FF7DF827F827527D525252A8FD06FF5227F8 %2727A8FF7DF82727A8FF7DF852527DA827F87D7D7D27277DFFFFFFA852F8 %52527D522852A8FFFF7D27F87DA8FFA852F8287DFFA852F82727527DFFFF %7DF827275252FF5227277DA8FF5227287DFD05FFA82D27FFA827042D272D %272D0452A8FD0BFFA8A8A8FFA8A87DA8A8FD09FFA87D7DFFFFFFA8FFFFFF %A8FFFFA87DA8FFFFA8847DFFFFFFA8FD07FF7D7D7DA8A8FD04FFA8A8FFA8 %A8FFFFA8FFA8A8FFFFFFA87DA8FD06FF7D7DA8FFFFA8FFFFA8FFFFFFA8A8 %A8FD06FFA852A87DF804040504050458AEFD75FF7D522D2D5127527CCFFD %77FF7D7DA8FD7EFFA8FDFCFFFDF9FFFF %%EndData endstream endobj 37 0 obj <>stream -/¼ÒÍØ­Š—ókÌr[g‹b¿ß*¸+j\Èf+ Á úé'ºÔ´ï¹éùòɾó’­YT6³Ìð*Q&ªõñÔŒ‚­ÆšÜöïO»´Hbo¢-¢WVD¯´ ÒŽ0‚ø/æ[38hZ¿¸¡AËêë½Ñ\À/³qó%mÜîl·Ù‡ó¬—ÖxŒ“ó§Å¸@Oò²{rˆ– zé<Í‘U†ÿàÕ¡ð#éÈ9ÄC4[vŠŸl™Àåö¡„]ˆÇŠdå»tÕ³¿ÏZ½£eúA OOi 3­n ³)æ/H•’_L QÓu+ƒUÿ±7 €­ùÚ½®½E¶">E§O°´j7@5Qk¥L“†ZT§I¤ÈÞûÄÁ¸ÉøÉÚ©8 m´ S»›9êïwZ¦Ü-R¥Ý#\/¹ß$è-„šÕ ¸Õ fMV¾«V·€K5ë½ì7aÛ,&¿P¡ãÜ0ØÖÄÖuúƒ5_Tå(î•1m‰ QÉM‘RžZÍ'‰o³,A}>e\æ/eìºö8ÔßÙuäyÖaSÍq¥¥ ¨ÉRßèc€gþŸ¬íâþÍÁVIø»Ð–ÑÖ{€`ùMÙÞÉ7ÇÌ“¿ˆ¡z!Š!“cȰׇbˆ¨ÃQ:­Qš QrYóQb¤ƒ(Û å’úÖ‰’é`^k$¹(áÙ2`PfÑÜÁgÊÅ)@³£„ÍÝz%m¼¹Í³€vå_|òÿzá1½8†‘4Ð@ ¦†FiÜ'£Ôu˜(¤í(­ðJ”º%×Ï!Jn­`zKÈ(Ùçó ü£ûÿ=Êþ.FÉ!>suÀa%n|ƽ"Qòà‹€e=‚¡öÙšÜÿê…꿈¡k;ŠáÊ„ÙÖ¿¬¡ovô›o†®O:†ÆxS©-Dib£ô˜¬¢tV;F©¼x}óà ä•Z0-Î §º€ò ƒOŒ¾9f5·Ïh]Ê‘§Ró—°O0Õà4ÈÕ9‹pAîÔþð¾*×ê/@Œ#¨mƒÇ$†Çü1±®Û¥îè| £Ç~ y¤C“Ö&†z›Û7ŸÜ„>1Tn€½ò¢xÕbˆ¥Û€oŽ™5Õ¨cXz„rWëãÄøáC´¸{˜Öøû–qò/ Äû*/Oç,qÏêZ®=ÂN§ú—ìï>jÅy4_>iŒL„ïJf»#M Ì Ú(†7ŠÃÊyÃq႘FݾB~$EÉHlΆƒÞŠΧU'Pä3èÅ‚<ªïñš½KLuýšÎäós¾ðÓGCFs÷Ö˜®ø›G¦îcì«æIƆý’›—ÃÉ6Œ‘ñû#Å(ÆÃ1ZÛç¾ÙQެÅhAbÄ_cD»/#¼{?„ÇçæŒÆ‹÷{2œ§¯‡³$^bŸ­mùñÙî¹ÇRxtîÉáÓ¿·³ðø_XÓ -ÊÞÛkñç–`ò2‘09ëã7{z?¶¥WŽ2Ì—N›þEŒHôƒ}@x%èÆ@c & ‰‹Ñ÷NŠvÜmÊ^Wkf²|ï‹ó°úK0㻯»Ë§ïô–‰¯×¸G-fÙÛ01K×keV½øu­yaV¦tžÁÎèÁâĹÜÉMGFâ"Ö3wÀ7~qßÏ;ù=噹Ýi¢fv2#RùÁþl@9 Æ. b<’ÿåDW­?éåFÔƒÏb0þâ3Œ{½Æ>qó›’³6—{«éžg};™/â}Œktz„s×Ïí¢tžùÁ☭ï–°ÏTÆ£Ý)mçšô_Û;²A7¯â+¿©®Üì:l­¨õ"» ¾à¿ˆÑ|„y–báб˜Íü·’¯ß8p1ò»úH ªê÷x³qË'uñl”wòѦZS·»È.’ŽíöƒÑç¼SúÞݹm¼SÂd;åŸäæ5{‚@´º—œ°®§ºf¯ºäqµé)Ñ -M? Ä,©ã‘I_ð/ØÒ È/btÞ1–¿?Äè¿á¥lŒ;»R¨rdþÙ„Ÿ„wêÄ¥òçë× Ïç@/¶Õ;©¹1ÁvýÑŸG;QOžÝrOïÕ>¥+L¸–d½ -Ë#ŒrˬPï-.øuazYáaŽß î†0*…z‡¿@¿øÖ—×ĨMCP„ß«w·ðÓºï?Ž1üøýYœC -hg‹íÏH¯àøoŠÛ'§³¶òÐn_Ë“•àfL«7ÙË£÷:,.'÷²(з—9‘îßßäzØo¨ XqnHæ¼–lÔÖz¦‘nn{"¬Û “_Äè4@Qn>ŸSÿ–ÞwXpñèP>_^—ðx©F÷;¢Oö¶W†ÙÄa3>¹Ž×ý’Ý9œuˆv¥<j¯56µk³l̓!¼ŸsWò6ãÉK_ÃXª#\Žš -V›ÕÐVÒzÛ±£Qjù=9‰;0™œÔS:9™~ò%úEŒõ§Ÿ/¼’gLLîÞy6;]j£sj»Î±»'·{å¾t·ïþèµ¶©*¼ÚÅaÖÊVìbÜmðfù ?Ø<·ýYÜ2Æ3¨ ÌôN_°§b—Ûk‡%wÕ¨´Nr¡ˆŒÕ†™W}ýÙTµÎÒPËùñ]-s™T- åXÕ.â>¿ˆ1k~ÿ7Á,!÷]>½^§ÇÌáÔ†Zk—L¼ÕþböŽ>b¶ë£ÀŸ?^^¾+z]2oÔô}e–C^ßl™ît7Iûš+?ÇZfçÚ>¬ÇEsíªÚÝs±ÑìâDJX)¥>­Öä˜õUy…x9–ç‘ÏÜH©§áçKð‹?×<ð—µÐðþõ¼äÜ»÷H{?ºNΓÅç›eúýßj×­“•›ªoó™!!ƒk\23[–tl‹Wµ#sà'ÚÆ·D¨÷ >½îEsTË–·J§ÏòªY}7Ý.:D÷f°ë×)Ë;{1Rúü`Ãîx~yÿ"Æñ[üçšßÝŸež·S[Ûì÷êð½Þ.XÁZ£éan)ÓX5盚2oÍò†¾K؃vŽo|÷–µ†ÖežH¹s˜ÓC¢Û*ÿ`ƒ _ª÷é\Òî‰Ç@*ÃM¬DGK¨Cc×âàW'µÚD§kd»ùþ,ÓáÖIOÌ»Ûd„WךŒ¿x…ˆ÷ºyÃíˆÏ•è÷Ëæq½i ku¬®ÅctVçvÃëé{ñ2Õ.†·”ÎöE­Ú/e• ‰ŒVØÀ=KÙ¾ÒË—zãʧ*M‹—¦8Ïn{Â_Ž»+ÈXtÖŸå¾í¤{0KjÕi>¶Õ·zË%Äs´£ïöŽf^_î¿x6ïÆób ˧ƒÜcmè.×kL£,«@öç&לŽô}0ìj£-??˽%¨/ܤí*ö-çË8|ý †Í)ÒÏ{,)=†I^|‹^Eø´¶|w„ÎæÕSÚ;O˜ƒÞ´ç´²¨á7é ñ·Ïðc¥4áoúØT×WóìV–DÀ¹üÙüå¾Û¶';º»ûâŒt66¬oVÇ3²ZèAq6ojЦ²x{\¥žƒ‘µˆfJ§’n‡‡éõ2Èù³WOËשr"q‘3¹nÊ.ÁÌ¢‹F\Ï6…ñÃæe]˜6 ¨°áKÉðÚÐÛû¤þÞ -õ÷>;ª¿—ñ¡¡sëWãaœž_ü_xJùîa>?8s3Ù­n´c˜Ââd–öÒõ¾žÈj -BÙ4ú¢L8¢:8T«?.hGé¥Ô}qAe>BÓyÃÝÎêHunRhgʽj‹Îó­f‘ªôù2ÊLU’·ëuD8s–¸~0®IV -µôké:ÜsMhö⬲ùà"͹ÿâbØËþÆw›dzÙ­†/f³¨VÔål—_LµblG0­¶—‚Ûè—6ç^= 3P+? -±l2>w»×׫C6q;+èh+ß暥¸ª,Ï5µ*#Ô%FåZ~Yë{Çênº«R…£«Dˆõ«Ä-ÞU%L{fê®fÞÿðƒýÝ?:ÚëäÌwØ~ ö~©ƒµM3æô|jiêGhd2é´%n‰•…5 -7dÜUº¸>_v(sµo]Ùñµésã7ÿ,Ki#¨ d=b*4(±´Z©rH©Ð®á•Š\í©‚QNõ{y¿vdsl¡ÁJl)îÙ|:{°ù§vgGŒõ›½Ÿ7ö*q­ë¹½3ëWm5#ó7ŒaØû{¬³´ß׊PKè ßl›6Ú•VÑ‚ž+ãù׈\5B-ïÖãyÞ«7½RÈuž¸&†9ªê6˜Rå\)Ô+´Ùî±ÅÕH/kïýŽyÕÉ3‹—`œÌTŠÈTg"öÒ‡G)DG)­û/œÚ=ÚýÂÞ]ÌÉh;ÇØ²¥1hw6j†£:Ô$q•_4ÛgwÉ6>5ÍmÛ*Ë ÈµóƒÕöã¸Ö+¬êQ_ªÙàèUFõGÈz«.?¶Q¦\!¨2c øNésÑÕ_ý«=­ùÅ6ëEÙtŠðÍܶà^è>N~¡{tþü`6k§Mž—SŒÛpQ[OóéÆTù÷{:¤¥H–YTè nÙoôs ‡·ë9Ö߸¹r•ß•™‚ZŒy¦®8f)æ]ÉŠ¸[ jöÂâZ£?XUžù‚èS\Þ- ý|ÿõXÒg®v¡üд»kÒ´î¬s7$ºåTóéçTõêýÂR_×Hr¾£g#ÈR#»4žküXZ¸ß5½ãlÜá ¿Wåð£V`ïK•âuƒ*­G†qï"óuº@¬9Ï õÜD¯oÖ%Ç´Ó0û¤;xV¿9L&àñNÆHu=ÃóGÊÊ/bŠ÷»•ŒLý%™²Ï ™æð'ioŸ÷_˜ ;ÝéG£l͆ô_j^úÔM¹s|îDÞä¶Úáá0lù|Ë–`:‹ç‡ìÍ>Ëž ÿ¬ƒÍ,`¶IÅ#©G5ý±JAÖÙ$»ÐkC¢—ò…س ¢3†»n™ÁÓƒˆôÞ» ‰‡]è …)ÞBÄò}måËÑ ËCÁû?Øl?Õ¶“J¸Xþ—œ¯]}µsbÂ>on“v­Ÿ<+åù&δFÑÐôdÌj’RÇ“¤ ”ÄþPjãÇW^ćó†Œ]1IÃFûùõ™ÝeÖáåCfXæ ›L …¹ôV†¢³ØƒšØu5ñ‰ñq…‚ø„íC|Zv‡púüeòœÝVŠ„'FϨ"“.u(Êü) µ,…sÌÇÈ€þ¥Ð[4²9ÆŸ`TSòRBBôž„v]ï+˜Úñ¨&Ù<2Žš°© -Èšr"Ôd{2`?ƒšrP®€8šÕqjÖŠÂÿIÚ?+T… Ôlm¨ÉÓÛ_ŒP§´è³ÏÊ´Û/tF|hûÕ Sá™å¥Sɟƃ|¶Ú× ²“¢¤Ö|E ¶€£xÿ¢£Øª×£¸oö£¸éQÜjoÛ3 yFq›C`gïRWÉ Õldp”¸V\×(ær€ -^ã,êNöÿ“ÂýG qÁýàÏg”ö-æË ¢d~G£„û~÷-Jp™bÿ4ˆâ;3è[ÀôQzF`KÞ¹(>‘  Áæ]@8Šâs„yN½(¾ô’h5r¨O·,g>ÔÿìÏ&ÜmšÔ/bHˆ@xFêÿ!ðAÉú ZO¢´U%£V™(¹\ÛQbRr”ˆÂpt¢DÊÞ8¡ÆúžKãAF Oæ 0åg1Jšô -£Ö&½}0-ÇZ ²Kšz_x”|+IH¾óÇ×?‰¡+uÁ¤Ìÿ“O¦¼ -(­Ã¼ÿäÁc¨~ £4NkQj3B”'ãèÿ¢ë?×TÕ¶†a÷ê P1çDɈHTsBôü7ÖZó­µçõ|n5 ÍNZotñ38,’O£¼Î²OÕ“äÓ³îÓDë 9EÛI¾+ _«»†½ŠÃ9ý\?OãgÌ0®SÔ6º”¥è~xù[Œ8囕jå*åk"°¥«]C‹W;×ÿHsõÖ%͉ƒsšÓ§l­›¡g('ÆÅ47ìvRà)b)0w¿¿ °o=º}7)4úá«ÔÎÙX}Ä»ãè7Ú"ø8^õê£Ý·ZÑå±D=ø€eéÍ܇DG¹ÍVÚòŠ«ÑíR€žŸ3cƒ¥¿;€Ç}ÃÊ¥“œËÆ• …§ôù# -Îb¼‘§4_Ïi¾cÝÒ<—û¤y «¤ùŠÕOs»‘Œžá¹Í[“xìL­GæÜ¨ß£×w¢·ù”¾]Ó»˜Ä¯Ž1Ë]§]¾ŒÐeãRx_úg–L°S´øS­<”Ùá°{ä¶ñöպ綨ê~B'Ó0É$ÌAƒç¿Hso. ŽÕ3ëüû«ï8-4Éï¯.jiþ|î½*Áy\Úyüƒ>sè‰tͽõÆÌâ\NÿT{ ÛÃÙn] íu¨ì‹~n}ÐÊvA±í-VpÐÐeŸÂ¸Y“ÕšCís Š—Ô?—/¿Õ–âÕùJEÿYŒ‹kš?­/i¡‘5IÁD¿¥[+Ÿ¼Cõ9. j‘q™Ôn@9m]J¥0;WžE…Øß×ef7#_ã­Ü´pIœa>ìmèä¶]oz·óºb>Á.‚ºÝ-û€ì¬.# _©ÅŽî öJ¸„_ûȵ¹}ìâ/r>;óæ,¥q¶¿ü.þHóÁ: ï{ì‚^S°ª|‹·‡Â«† €¢»Éµp£^§&ðÎ^Ýu¸³ „"¼æ_*>ÑŸ<7ÊêÚÙX«žègã}Ï8î6ËgisX"ÂñîÚ‡øíâ“vÅY^ù¾SèïX›™‰ËNó}ÿÍ¥Çù:oXëó—Ó¼±:œÿH …CÖ”Æù‚=èö”ËÛì³Ü_ûûþŽ»ãT€ðpÕ¶Û÷ìv߬é,u9ŒøŠßYöÚÞ£@ËW-"ÝwqϹ9À“Ñ(Ðí`Øvéø a¸?Ì÷Êé>oÜÒÏLvj³öÓA@KÃpÓŒv—Є úÕx•ó—ã—ƒ1>û?ÒuË¢ÀóSîlÏ×Õ½±<×µñüصs³ƒ¾Üζä v×»V}ïOŸÉóŽNv´t?5¶ì¬6PÛæ†ïÁBìܰùA¼1³3³gg5µôÜvn>àÃÊ„ìGöÎÑ;Ò¿¿Üª;É¢«çˆ¾¨‘§3æ¤þ䤮f¯®'Òî—ðo=-,ÇlK÷Û§ig©:ûøsÐw@S·ùÃ}²– ÕXÝŸ’¿´}ä俥§Íwoùù‘–g’kYwšq­‚ ˜Œ ŒÏgçJÝõòšž{åçø)*7¬§Û9™LEtSSˡȣòBiAÁ^i¢Ã—Ý—PiÓíÿiʃr»\éîŒKs¶õPNÍöJ¡¸¯&"¬¢A$/þÓvX ÒG¹Ì®þË2VŸœ‰à»ŠC˶NU¤¾ÆpTÝ 8=Ý‘ˆ4­ÏM‘¯”3¹ô'á¤+ÛwY¿< ã¸Ïdyòx–'õñ¬®Ç³!±Ï(6ü²ù²þã·_F–“ßž¥Iêíê`nÂcQ É"¼í饣 ‚Ãò#c1ž¢«™¦€{óy8ß³ –äD/,¼V’ªUµÊ¤­iƒ8 ”6ì¡Uõhù¾Æãø˜c¤rZIö¸p?gü-Rݰ-棥"rœ'RÂ-)íµù|ñù)ȳÑí3Þ…Ç+,·#Å5×Mû8Y=¦ ~ùž¤³n•Ù…\x)3½þpÌEÍÝÙ0ù¤±Ó|¤Öê—÷´Y -@¥ýPª“ÞeÔ–‡ûþ`<ûø(9•N–‹RÐT, ÐB`Üñ–óaÌóZçö•ªÈWÕ¼ËóÉtà EkýÅÿ²ú#áªìãêOÍþ¹Aí°¬‹†Û/'Þb dçJ—&:Äâ°t©™®Ë¢ùJõ™‘K0OãšÅÝô ’NâIß~dˆÁ1J=ªnÛ¢çža!˜mGBézóÕÃÙâÆmpÍž4âÎ*ýs™¹Îöd;…Ù‚UÆ­5«˜ƒ€=E¤ÿeõG|ޮΗÍ]Úìœ}y±>ÌúÊ:Kò2? 9{çˆyWÁ03A*¬AºšÆí>ÎôXÞm”.¾³ íûxÁ=_’Krâè&„õ ÛÛ6×çk‡sMšæXe£ëÌ X¯èø8Óæ†+Б4¤·-Ú|úÆ+¦¿È{L]ù~ÀÇ€ÁçÆ¼lG§Û<˜†ݳ1~âÓ€_L¸a͈2¨ê†Ò6‹ËdzœÛ3¥K+yV«nÇØ::Kùá<‹q*TêXž¯—ºU®U«wØ)ÝB~@FÇë ý0ÓÑkA»#ôn¨·Y(wôB)—Ž ê}4üV_®F qç}YþqO´Öáxuž~X¾¹3ߨ~ôårÇ(ö¾fò³h¸Èú˜±¼H_SØôÔ`¥§Sº<»¶Ý1^×¢¿tßÝx©Þq“R%Ǫ@Rbô×±AÇX ’á¢>û˜"ú‚\ÈYì¬Sbýчdwô¤Ü'ýÒdEå™™÷eùÇmi&ûýü2^®ùél…@ÝežCe¡ÐŠ`ÍÐÂq£5UOhmFžOªêØ?"󙯄êœÝþ€|CG/ìµe'Qõñ¡Ÿà®8ʆ2µ>\ö)ªc$3ðEb3¹[DåÖ -qi ¿ðúîâõ1ÅwVÕ#*á/.Á÷©ÿáZâüìȿ˙M'PÛôÌ8_wjåª2ï«#Á̉41=—¯Í‰y5ûã7mÒªêÅ2§ü¡1^rŠÛß°}ø}` ÞùF£ÍÕkDTfyjT*$[–:DHªØˆï•ÇNÄÒÀÚÖÝG¯HÖ!Õw¥…ªŸæ½b×%Ö¶ Kl’4Üq¥mÈ)oǃ™,mcq& Sk1îz9KE'ÖDkdÇd®µÄõ¾ ââøÖRÙÛ­¹`,ù½¢üm7"›« EWô˜ä -܇آ|? ¨vnã0zë3 Ú— 1/w~õáž?’ŠÇsàâÌž†9Àש™G_¼:b|œ·j5a'YîâN»\¯þ1ñõ%Ó¶|6FK~ëPLÓÝd}Œ¯L„T oø¥SìBâ zG†u$î÷pã#SÕ)äFGÊÃÝëp•Ü«ÃÕõ%Aù²ig.D²§ 4tþØåaõ{Mæ\‚X2_œß’ea‘©è•Øå”!7ä’8>ÅŠ"–Ú5ô äåÖøíWö0Yªß :WL†kº]lñFkPó¼ß\ROù罎Gžº7¹RîÞ˜¦ÐÕ®Ú¼«…Žûv5Ç´»×ÔsþzþÙ[‚l¹èÞf +:Ú¸‘“'óMžÅÜa­ù«Cmª÷2f ~Š$‡°Œà­´ ®8 S}U‚|ki@Åä{UtÈã—õ`çÅÇA£˜ûíjè©£F­{_3P'ÞÍøÒHfíä,îÛ¶ÚÛ¶ÐçÚØÃ™·±Cè¶±•çü Ï­-VöqT\Zi»7Ëò±pªhŠùEi=7FÜw:»¢ÍͲðê{X+Dé ÙKýÓíõ;CïÝ -O±§­Ûz÷QA쮹›l:¯†}î ÊîÕvni±öº­Ñ\ý€ÍàkMv)†Íòxk–i‚i„—pÖðÀn=ç߸Üė¢íYÓ NÇ«¨"ð*R"éå§ '«ÕBÞåjq(½÷wNo}´ÉUîÕò[ÝB‹¾L[­¢z@š¢Æ6ùÜHiVÞ¢±“Ÿë†Ôî]õtö©Oˆ¨Qoç)¼¦2;¥Ö+“Aõ~/¿«÷=DW (°ªƒuàT 'Û_¾ çEMþ™+ÝøöZA­ó¬Ÿ–Ó[róçr.9ÞŠ|¿ø.òUwµ™q@iÖ¢ÁÂÉ…Si;/L¶µ°ÐÖÍ8‚•|O²ó~.ê–¦9ã¾ÙOˆ/³dÊHØt2F€8¨ÿ‡.‚éb’ˆàæ¤3ï~#e¥O 8‡ ¤Ýè"÷Ø hZ»Q‹G#ë·°TT—gPñ§Â¹?):»ž×[³VΪJ]à5Va=ÏÈŒDÐ ie6z=3Þu½4€Þ'6€X`¹nÀê###ÝX£qú¿(³2dJU“›²¨Ãä±0¡8.#ƒCµ;ÎŽ(7k‡i®7I9ï×£bµê_ “—tÉ=¦~œƒ“a>{£jÀö½jÕÌP{ßw‰î8€=Z\ÆTð|ÎðºvðFÌ8ÁßÛÃfÿs{GêßÛ œ!G= À±ª÷‡öûb–cªnY¨Å98íþØ!+N£!ÖÅJœß¥ÒŠÁÆþå¢;r°Âðà½ðÿGûÿ•nÕVƲ—qGþ©Úþζ}ï‘ œ|Ø,üζýldÿcá÷žµÉk̈Ûý#ùômòÁ‘0ùTëämD~ò.„A’²N˜$·ð’$â3MjTË8ÁI2Êv¾d4›þ€Ù"^$ Ý 2äcÆêžñH’„©ç3ÈJ†ÕÎx I¢\Æ3{Çì2Žq’ð% Áv—÷ÿ'Ù»ü®li—Ádªø&ù…,Pêä'éi&é¢%)Ææ“´4“d—fzx¨·œ&É*og`Y˜–¼ÏX]3Ï$™Õ ºüý,={âä|k°'o0“ôRê$©MaIªúB’j#ƒ[f¬wïk’ŠÍgÆè“a3õ$•À^ÆOð“Æ¿¼ÎÉx2ÏÒ&®ºÕË£á·_?`tš4^‘‚T_Q;þORž…)@ëlMò³h{Ë䳑²àÕå5ë ÙÖ|ªåä-ãÝä ÛDòn%bòn“f†ë}?Ké±Ká}IrõZüMjé lòOVƒËÏrŸkÆ‚%÷â¼%cGz´¼™Ñhw}AYÒ†®ÇË›E·Ýÿ±kÞþøÖ`Wþ&Ë [3V) K^ -X—C -LZq -”ð\ò ÕZòvƒä3ÈRõ|)/>×ûdú¬ó³¸^Í/ò¸²‰Îçú!šNáÛývÇž÷Á{ßÏï·Ÿn0jÔ¯¯÷exŨ:sÉwôùi“_mŽb¸¾ZwÞO“îqßµ+‡ÝûìÿEš«]6i® 26«4GÄÙ?ÉÖ)Í ²ý'—ß|ïEõƒæ«$ÙΣèr/wc^àn3¯I\5.Ÿ´´¸PDcu)ä홡ñÓi³¡î§j•OŽÒX)&S³¾¿|"xÔd;»Ï–!>·w¯lìÀMA¤¶ë > ×̱±Y—&…õiNH²Èœ$ 4ŒWi¾Œí3äËï,á„Î/xœòQŸÕŠ7”AëoUëË•7rÜgò ˆkîÜk“µ¼‹®[mõÏ‹íܺyaš<6¡ã€§0—k>6#ÖÁFo¬Y!;¿×`ÅXsµêŒÐÓÎBàõ§„¿|tû«‘ænõð[O.ç³Ù–Ÿæýð[Mè·=¦gõ|[8û󥌢ãaY~ïý ¸}]ìZèšFw³ZȼԺüT¹@D 9¨½7¦/“;{užíüU'½dc1ïæV"oPgòKk²éº¯7v\±pò¥m`¯Bwek¢ûËb} ?Ò|§µNóJÝKóOnýâ³t÷¡î©åC5¥õõB‹“TøÎºÙÅv„Ÿs|ÞŸ›g°sƒ¼?A´òJ%´¶gÀÂ`Û -¾„£1íÚ"ºØËÓœe×›9yc¿²ég´·‹‹ÞsÁ}´ò|7êfÇ©­ÏZŸÏʺ,ïËÐR©cõ -ËÅ¿Hós8øñ9Î}ü³Ú9ÜoåSãKÇS>¶)ȶÒ[[a½Òàôì £ëÓM§ÞÇŒUÙ)(zý´™­Þ[ðGžïK29—Â…0;åÜé¬Mg]AÝÜ|3ª•ϦqŒg;×7'œèYƒ.tüØpt`R˜ëÀ¬úoÒü‰ Ò‚´Ízòí]¾ÎÎb T÷?á¶ï5ÚæºWb-¸íw4vêAÖq‰s?t@©–õÃÒë1?änŸÙ)Ù€Ö5²«VZvLsjCÆóî“ÚØðº£\TØÆÙvÑFÊF+œä‡Êv’ê4\n¹©0YÓ¤,¾Ì”ý‹5ÿEZhèáãºYzWðè̉oɃÊl*R€‡ms¸Òù¶Ä°µð:´²6ÛØõæ­¾Z· |2ûnl¼ÖÅ·þvŸˆ£Š¶jGmuÝ? Õ²|%¦Â%”C­ *MyèNέÙqÒÑ -9Y{\Y¯»šÜ?ž-¹?¾ß|ÌÆý߯¼¿ Ü=× ÔÜá墴©V#ÿrc‘Õ YuÝר¶5ZÔÝɬûÈ[f<=Ãnì¶:¹5Îíó‘Ê9HòN·(¯.µ²ÒêuÚ“é¸É·31’A[Ò|&½NQ(a]ä%¾£6$ºÞ},ºÒü¢‹îƒÓ¾¨$lc{¸‘•’s¸OÏÚ2–/J!yÏI ^ܲ7þ¦3?>–¨U—¼‰LmM_žø…ƈÕZ-•vÓð<+­×>štïN"E~³‹êÞ®º滨¸,81O¼Ô0ëGý•Pd‚;ÏmÇž»SÏ}î:ÏWßðEõËô¸Óºø×âàéìÒ‚<ÝT7Æ·žPKÇ~ÃÛ­´æ'‚jXf¯=0ÒE/‹é£B[VC"5¦Rrr”¶9÷'½éx'%â6%Näw9Råd»neGØâ’¹½òúq»g, ×/÷Ö¾¼)WxVÈÕè‘`Ø ®Æ+øVÞy¸ØŸ¸Ø™BøvŠJDù¬Mˆòk.\Óÿqá%o;B§ºófò’ŒŸ‚-ÉýÎŒSvº¦T¼¥\l-‘g\>ÏVEô]¸/ð Lð\‹ãÚÅ–Âö€¢Å ž±C÷?Âb?;ÿ¶—WÒ¯Í^dQŠD…Û¸èEv,öd¬Åßo£_jÝ»¬%•Å hŒÉ»Ñ/Ò§czZ†e‘þÏ…—a"qÑÞt ycèoQY~BÐK¢Žë—ö—~O'N©&h¶Wj`Â0f%ÓH°QG¸;›Q^ÞöH?lˆÍƒ9ÕóÀ=!‡MÆfU§ŠÜwO1ÆÌŽÏñޝù2lŸyÄÈN_$ä~‚þÃø»8 /Ðb-#uÍÃ+ÌÄ©:'iÞï5YËë¨ÚdÓ–4"ϱPënœb>¬¶—jŒy¶‡ôüîS#çé‹”w›)dpœ˜Dxå¢VcÖx³ˆd[ëT‡wTáÄäØü\Îa-D<äpÃDTîÙ€xw!"çrCædÈé¿D(ÕÁÿa—kÜfþ½—hË…+‹Ó#[èéÆèåË·Å”AÞkŠÜ°üfuîx¦_Ïãkô~?ÁQ¾ÑîP´Âà$§3,!Nð1~¸2:v> çèíÒöÐAµ½E`°zÜð ¹T†r6Òå;XS sÀ5ŽÇpP^ïZ6Ñ™Œóx°nݤ/â›­®[^òÚhŽÀö¦óþÅ«íŠQ[ã%"Ûm§-ÔOƒ¯Â¢º¥ÓVŸƒ•›ÖïKƒ+Ó;Î{Fo‚^½ÖçÞìµö2Ýkyk©'GÛ_Ä?~À@-aÖrTñÅYok6F%mÇp´2ìJˆÄXD‹ë¹•°›ïTÃjNƒ -áå2÷ˆü&8‡†¼] B`c¯Yå:„Û«7¨5€,ë7;…¨§ˆ,;ŽÛÝ~ûDuL½§v ÁqÛ†I½ý\ËTû¹ØJíçt%¶ŸÜBøc…¬ê3G,ØÖ,nEªÁtËÒôZëÒ20oÀü)¿wœ~Ã1HÖ¶— -7ƒD+ýËÓ¡ç`Ëà@œm[ƒ:[Fúò¶O÷[¢ ÷¦GEïÞÛs§;ЂuÇzD—v‚4>mLÕ[î#ÀZäl¨´òÈeÝ\½ãêØ\í¬Q“¦¶R“®2 ñÿÔç»"nšË,uÔ¤ø&MÐîŽkV±pyÕ¤8Ï/bFÛN ßPÎý‰emºC“;uæ wuœøÚ)ßj¶ñ=6l¦Eµ<è,µ¨Ô[u›, ìá(ŒB±S®ïy®kÕq½ñ8µÓìR®P™¬Mnk©6ÙºR­MÊÂö±p±³œÿ?Ó?¢˜+’—¸Crs4¯pWwLöZÃ× ñF/ùž ›ëÔ𱝷rþ­v^}>µ³OUkSÆèÖºí;^½Ýê|U×9­: Ùx¿båSfu œTÊÝòâðe̤ܒ›´Þ¥O©‹—ÞW}\z‡²Tr LücÇÌÜ`AGŸj׃<¦© ËÝÕ=6òãm¿..u$WÛç¿÷žqgÁoOÍõ=Ö.µª ÝhU¢‚pS :×c1è¶ã"sòÅÒ2l€á¨eH,(€;Z¤Ÿíþ‡{a¯Ñ•¸ÿÆ x®çOsþ’?-ìl¯ÌŸÔThØää_˜ïjªÊ©6•óÕºÀ«Lƒ¢©„9ËfsÛ^eЂŸ–ïÍÊ}p¨õcSvºO¯¦ Ö\3ÈŸýy¶¿ä»¥ù9w»F¯œ®× -¹)4ËÏRXXž«¯[ÛŒm -À›i€¯Ä€oÕ5ßK €°ÀÑ"{\tXü¡5 V—Ý 7Îò±oá›ö¦ -NLû+É£F³³…Pb_ é.^ª¦°Û—V@u nç§ ÐM?7Hû€ôÑ0C9F"–>¢«â^ûrgh­|ô@{Ö@ûÍ'€¯6€Š{@%u &¨…Á:ë›ê{Á“Åã0ŽïÀ£ëˆ8‡/¡ -I¯¯zN¹M¥Ú»~º»QeAæ/ßzrO:€õuu—»Í5€8Dö:¥ùö¿ Ï €²½,†¹–Ð`_Ð×°` M5+fÝ<óµÀNP À^…€ƒwÀ‹nàm®þ­ŽvÊ€£ÏÅ¢´.ŽçQeÉÚ!åž„ûZh´AäV¨w«¹´¼Ì×ïE¾·¼Ú\ï ×qÖ"mo—}ðô`C&ú/”ôøÖ“%5°å3ÿŸR3LÚÞ‹¿·J€¹Œ•àƒþúŸ›9üN ²çÝßiÁŽt¿3ƒ?’÷ò´LÞ†g'ïáxö{B­$I_F’çY’Ä6c' …–3‚~’Œ£Œ‰œqÌLW¼ 2{­Ÿ3¶Œ÷;I˜.˜!Ö3ÖÙÓØ2™1ÿ­ôr½ ±+ê%#þOeöõ‘6ékÙ°“ÿ‹äS¼äý¹:YÈÎK³lôzE„D½8yzufrx•È͹ÖçÂÛœ­»þ#ŠEF) ô|Õ“Ï4Ð’ -ØÉn’wÄœ“÷X}&o(*%¹Êµù ‚°ŸmAôÐÇxÐÑ) „¨SÛ+wM>·èr]Ü ^ä]ŸBxEáÒñâØû% Óóh¤TNÁ¦€`[ΞƒÏwzaèllÁÞZ¥,®Ñ"|iûù¾„`öK -ȸÑËÖ TKº­Á¸›?Ùdíí_^>(=âí³‘FçÃ9Tåëk=m^Þ»iÿ’;MÑ3ݱF§µî -ÇíÌ’gªþ€‡VN·öSÙuw·Ëz½à¯óÖZ5“-L»Í‡h2k¿5Ò‚Mµ‚Š×4ýÝ4}©ð2|ñyÓÿHÇÄÉm-ÍÕT;Íålï·6¾\?ù3°ŽTÝÜîm¡ÊÖõÄ·éçaB÷€ìÀS-î†Íj};[ {Y7Âѧ(j³ nS(ó㵿éëÒ`cüìø»wñâK[#¿šœêÈèõ\Z\šyÝpŸ ©¹HXŸþâ¤B^ù#ÍÆ"ƒÒÓœcXi.ô¯ò¦7 -{`q7Õµz¡â£ryk­‡«-:6ž[½ý€ëÀ¯&AÖòþþP.ûÍV­¹RÔNÕ騧™#j½ai.Õ}~¹‹Žónò‡P M1Ô‚]ûÓù¶¢NæÂ}2™× Fž×$|üËø»ø'<{öªÐ”uKè5wñẩ*~fý!¶ï/"tIBZ¯‘å6åjjöwž>mž—–ÑŒÝżövÞïèƒzÅÉÅXǦ2Ë.kG$•Ü”ŸoW[u^/¾œ™,ô÷³VíX]D€MÝ9‰F\;Ɇu$–çâá4—îiù¸wdБÏacj:Dv³µÒû· ùnmèƒÕ÷c™òîKY^"Ùr>±èÙ¾o >-*—Ám.õ°dv4©ü¬uÆË–ÚeZæÝæà4ÁXÙdoz‹uݶ</lÚ’›v@-å•Ê¢ZAá ÿ/~c|¨ÒÓ¸6/ùÐgljK,BhÃ2æº*wË«kaW_ÎóÈñúÆ.m6“ù~ì[3¥ò‰[ZA -̈ì'É\ ÄçcÝN™·¨Q+­©`o¨2ö‰š†9`<­ÒÙ²/nJC›Õ&“¸‚OÚ¾Èáä+C2òuÂýß« ìßçeG?@]ÚºrXobàOa±î=³¼›Æ½’<ªíù¡ZÅfʹ͛aA1^í4Ûh‡½­çZÙ¬z[•çi¸öÓZ%ü(cj[ü'§àÑšt* "kT“G«ŽM6 -¤çÝËKs“$tta%t3_è/£?â㆚ßrù±þ[ö‹|ð/$Ø÷fÀ îº.žûmðróæ}\±tÖ¯£FꮦÝ)*×#¬©ØìØŠ\½ÉY*o'<Éýmî1¶ò¹·ôbZ uÑ͉¸H¬²°*5atbÔ¿whç×Å|}!ÿÄ/k_\ÊZMÝašÏ®ëO[ á[ß]ΓšÍ—`ž;3n¿ æƒ-ͯå`•»äèi]: J‹Ýfg±IW\™ò€ÑcË™®¤ÅJ ÅwÂD—cae9|m<‡%·õº<'ŽÜ9›í•göö[ìáú¤ØÃ»4úB~!þˆL¨šùOï­†>-1þµ¬aË”_÷æv¯ÏOÀ °q韕òÖ˜žUîÖ‹¾2¡t|Ò}Óô8öaQJœº"aŠ!’IÞünâ E3¿ã¹sþÊíº­÷ÈÕ%¤ÂʧIŸ¹t64Ó5*:}®÷´në´Qd1ú.Ôâ þÇeÓùA߉ò†ç‡ô*ÌõL«o×ò›ú¬w6>.@§l¯,õÊÏ©´®ƒ“k£Ò”[#Ö‡p5fEj¼†³ž— ƒÛªÍ5ÊFÀNÈÙ‘¹ø~ÌtÓS6ÈRgû81BJK…ÊÎNÊ–à2•D5ôÌ(>Bð1ñåw ûåFl¯ó=||ç{Wio>C1g êƒù¹=«›¯ŠhÌ+Ëü{Ÿh÷#ÓMUJW›žèñKT`A‰äùÅf-¶â{B{S¶­·-F‡µ‡[z¨1÷Ñìaæ(û2¯S¸ äòAIÊJ|²€$ Ig˜¤Ö Nz`û‚þñ{afû¹}çÀí@/´ƒÙb…ìÏtBªîpL÷;w?éçñ<ì>%BF@ì! ¡ áno ìéPl§S˜~-’iS~ê£çæiа¦ºt¦€Í0ÛúdaÏV 4‡DiSðPõ=\èûy<Ô§ƒŒF”nô òÇYtX+dV;Ñï£í®„&¾ l`Y¾¡Ó -š›ÜXz7^´’Ã(® ÍM`?Ç/6*'—Ó;­§0£—®mÕNÜèE‡õæ=•"Wž´æ·;äÈÉsæÉN] dtÞ¤ãR0%¹9F}ŠZ–[R$®äÊÓb½L¢üQ–¸H9{ì0èÙ}€hçYë"·¹F#ƒJu† ª­qÑ ù#7Gþö3c¡þ^ïXº³9-%ðMÌŒ9ÿ•§ë“òö1ä¥rê²jûºeŒp§cÍ’­QŸÛ¥¨Ö!™êˆ$6c–'*둌×w„M -„ƒ^X!Dîäø† üuþ„gå\JH”„ìúÓ„°ÓýÙà® ÙÅe£ƒ ¸ ý±õ:äÔ×›”èúm‡]´±i!·ò[TVz—G^«÷=«C—ž=´õ÷žÃóDyÕSB¬Z™,ÙÍ6!}ßC};e›k5UŸ 1nåüô™gׯïáòqh ©”øÒ@øïÍmàßgÍå‘a¡Z„Àïúã÷«^Ri‹Nµ6fç½ûkdx«ü[T–çÑ¡Àrõ =={5_å-QãØ>ÞY9ì<<×±Žrî¢ZÖã‘ÇàM}çôN^œìȉs”k—WѬÅ&ýðâVû‚VC{û‡©ôyûÜÛŸ¤zoÜá/Po??ÿÎün²¤%iq’Ǽ5?m¿WßÊöVtrß’V «ÀMåî}”Ï.+Ö ½ç-0‡w:)ëd^ÄrÆfîrÓ5剛oò`<Œþx¹ý:]Ûõd›xv/€ûýþ~—‘ν8Ÿtôßwt—©vtýwtqutÒþ±²^êÄ ÛéøwÒ·±Òõ‘*{>"Û%³%ì$df|ýAV‹Aˆî†~wùB¸»Eë>,ñÛÏ K˜+þ0èô÷- ê7Tšìz–Ðk›+µ«¾Îv'ÂÁ°34¡¨={-J­ÏA-0Ç-|Ål[øŒ©¶€’5?©=l~®F–Áþ.~Y.ïÒtq®E ƒš‚¶kÔbp“ÿ)*F…$Æþt€ãÊëÕSž¶×½¡›mW¯T¢ÎC|:VC*w Ó²Ý~)÷¬µX‹n½—ظåRºÕʇ 9úä®5Å\áѯo9V¨‹ÕvPsÃJ]8,¡zV3`iø‡#Ì)mf©¦lÝ3?½<Š” (%(ËaN»w“v’c8ˆ›' -CûóÓ,¿{³ÖÇzšM¦zršÅCaÓØXÔµÁó´QINÅúÎ)µê@õFÍfjÇC¬d¹æÀ®^®ú¾ªêåOåOÛ?`Å˜×ØÊ¼Ê°W(W†À*Ç&—ã1ý±èÚºI„¬5˜?A+>)Zæ€YˆrÜ“kü*F§žº!ƒ6¾å ¡çé?`õzïèH©-ʯê—ç7íXFõðQzs¹’[ ê%"˜K96•rSI+®ºw¿8ЏG´òup »#MYdof >C î8 -»!òËø»°ðj êU)7V ¸ÅJe·±Ö¹Ü£ø~±†§R -p î]¯c›Pb¿QsJ‹ªÎ FéÓ>(Eú±Ò ;Û]¤ÞÚ+Ô£×.´:÷ü>?`¾]<Ôr—°6̘s¹^íi÷#†’Ãβ +´ {ÂÖØzÆÉHñ?ŒÒ”Nû„þß¹Ôƒ½JˆÞÁ¢¥ší•PðŸÞMÝÛ¹¶«KÒªOƒE‰BËæ¶Zþ¤Š*`N[&ua;Cö;ˆï?Hu‹äÔº:#º õŒK@÷é€"lðД3¢-=KÍŒ @¯º’Ñšþ¢v˜×d¼Z&w·¯øˆ‰o¼?ý´ µ¼{úÖ{µ|hq®·KËM±¯°£XÔæ¹nÙ»À#Ùà±»`óðª¿àÃø - ¹è u¨ dØgd›²×wrè|$¡Œ« - ÏYö·—TDf$­Œæâ—P&ùÈ«“Þˆ.ÜQ×[X.’ÃZÏÀ@ Å˜¯¨¦µ“C™àÛëby¬òç˜Xð~›½D\ˆÐYý§t‹„Â&{Ùý@Ë¥@Qê JXùÖ“×½>€¾ü€Á´`L.°Ið°ù¸`kj`›Ö6ãX¨÷lË*¦ñ‹0Ñgí-L¿­ô!Ì”ívÏ8³ÅVé%µ¾_ÀÒÊV øjüSOÖ ÿ¿u[$Üè$:hPÈÂ{¶¯ÖÀï6”b“®yóÚߪ­Ñð~žÈÇ/û¿¿C6ì3žè·ž mŒîPÉýsG„Uï—äݙɻÐ×’töš$I( IBpRÆÚH²¿Î¢$¡:`ƼóÕA¿ÕÑÿïJ(\ÊÐ[ÿL´%ø$á -nbWøc‚íüç¿+´äI}ý‹ä- Æ·¢8ìO“ôø'iÛÊ^‡è2 5‹±ìe˜—,Fð[ 72®¿3‚³‡ŒÔÉ?åå*¡¿“‚í¢uK°Íêùúð‡ÏkYý”~ÀW^®4Ÿ~‹Cžôå=‰7}Â+–¶û-É>v°wyˆ‰y~Ôíñé_$ï¢'oU’wý-fºâ’äʱY˜â8ãega²ûo‹‘××?up¯—…W ~›õõáðÉ‹ÜQúÓo0ó'}š,ãMgÄÜm–Å ûç‡ïú,ùDò»Q¹_È tïúŸñíÎÒ‹Û`; ®±Èù?àuVÇVÿɧ¢©ÉûNÈYÛæø$Wt’úç,d6—mÕ[y‘$"ºù§^Ë'vm¼È-Ñ‹7íôØõËø£nÕ² 6’“¦p¿àMù®v»ÐÙm°FkÌ×Wx§.v#xœ?Óx&ï&rò‡‘p*.JÚ‘{?µÃŽ ¦©îýà Tùo…Ëß­¿Ù¬ms\r'‹»AIzRÜ$Uöû×O÷§š¯˜‡9 šŒêà½WÉW¯±l\‘vÒ¿ØÚ9âL!yæ\( Æ'FL¼Ü5’Úu~ÀýĬwWкïz*ÜØšÀšç0(oÒõc¼q¦·ñ†èn¥‘|Îû,Úµ"fÑÖFɇ²¿u•¬ÜM# ØsžeøíF§õ4Û.÷~ƒÛ_çâàz!Ú­ç¹ð(~ŽáâY8Œ©}y -®Í}§õvšø„·Ñ³@n!´Ä†Ï÷PQ’26ÎqæmrÈç²¹³JPžÄˆ_sëÜJ®}Xï,ï˜Л¶–ô¿H誤¨pÿ¥é}ƒ”„ܧÙ&¥›Eªâ…x4”SÙ8™û³h:»~:¶pN9„XK»mÈ,‰Z¯<7÷®‹àª¬ÏfPé¾z¾hæ¿>‚蕼IÞ¥¢^׈‚å=K¨³XÓŽcCCÒþ¬{#›ªäÈo#â0¯ãï¥ÄgE׆ÝÒ¹!h¯ÅûÞqW~õvFÍÂB×ùMþµÐžg¾¤è«ÕDŸ†Þ5ïž½³Š–F¸MÝgíXpg‡¤ê¤í|ÛÁ.ì—²²Æ?à"(žg æ*ç| Vfã ['Å-å{/¨ö¡ƒšW¹Œü‘æriš«õÔ¨ÿpéËjnçZ´ö·Îº¸WãA>tÀMi] -f¿™·ñl»hšÍ/!z9uQ~i9ÄxéÚÞÙ_Û`wwX°Æñ:Ÿ—x.,`¶ÿT˳&ÕnZçYvF›Úö81û½2ÌWí£/*zW{K+Ds¬O=Ë`5â$C¤97ç9$^ɲ$a°µÅ[5ÌkõÜzÍaOÿо§^ÿ–ÝÅ8ê;¹OD,ÖAÄÏ·Ûh2;¯YNÚ~ÚV·]™º…? 9@ -'ÃÒ -‘þzTSƒûEíŒ[‰;ˆºò®¬JËü|º¾Ö£i…Ø5ÑÏCJއ_“cñØÿ#¸qŸÕsâi_Ž©mºÐzY ¹âŸº‹Ïª›ú·%lN~Z•¬ÍŽzs`©LŸÌN½}Þ˜ÑMY·ù²¦}öµ™F5›KÕçjZÜuS¾‘%G•Óø­HEyrÔÝIë Yvq™YÅÚÛñ}”ÆÃ&Õ—â×èý‹X^†Óëòr÷¦†`6¨u|%T¦ ñäõV6âsšÓ¡æuuR †Y‚v8iZÕEU‘bÙšœÚœ=ikr «ý0ŽZ‹hœ;ó\$Ö÷‡6ëOì—‰Å"— éü/NA+ù/Öµi¤ÞÅ4ê)Îö´'¶T`mµÁaý0‹å:býqo”¯±«Ox¿¯bÔ©62g«²«Á^¥|‘÷r—éÜ¥óxü–ú9/ÞœcYäÀ&Z$òÉê£ñ§Ï>çiìˆË®:K¶V8ܘµV€˜ÂiÌ3…ssHïšê•)¸9$’g -€ÿò—.^æRUf]j%'oJ°÷†b2­Ãà vÄòÒ€á+û’Ý.t­'Ñ‹ÉTAÇ•ÈËgaJ³± ÞÍ,?çEÌlœS?Çå¼³ÂÖâ[ƒi¼“½WÙ.­3ÙEìZ yi“]ò¢ÿîøI^„&/xˆPD'N¡_à/üqþUŸ§ 6¥íUu¢¬0hÏÞyÒ¨•ïúQíÁq¥¾ÛŽer´i–kóÕ]Ñâ|Tt9-vö4=§#™]Éð›Í{(ÄlÕJ™)-k4ÝÉw%êÔrêy<öI‹,¸Ä£¥‰qù ðgO ðù6ñžðX@øô¾Dðé~ÿÏ=nêªK©Ï?žNgŠ•¬ùÞÕ½m…?À œéDÚ‡$v•N¸‰‚ô8¸µ4˜ò|Ô¥»™ÅˆÆ¹€:wÅ#5 ºò6´ß¤Íì’Ë"¢@ˆHCǪÝùÕ`ýãójÂËnÜ7Z+— >žš(¼|…(<A(lŒ¿{ðóõfјjÃöCtFŽÐIj ¢UØYp-:˱²Žƒt'¯_=¹í é°¶h¦Ng¾‰ÏÛP¯bü§^€-ú˦QñƒÐå¢Y@ëÚ‚BóP,—› ´Q.íq©ÝŽ6% o½Š½A„IfÖ(’åͱHÄZî*\zR¸Ô¥ÿYxX˜c9]Y†;„Rz¶ÜÌZ‡ÇþŠÊX6®ôœW(¸ÙSÀdgDŽ™yóíS­tˆôF NñôšŠýÍÓÿŠT ÛneíV°Ž£wí^ ùˆÄè¼à6$Q.=ÄÉ©>"/£44eØ+²5Xñ¥ì¡,ìLv'ØÿÀd\iš6Üòë¢TPSÔ×è™WT¯®w¹j‡ bq¶š‡ì®Ô(vÙcA£Þ)£ä¶Qà‰‚€¾WƒÕÏ΄5[ÐR£æ)ú¨m/PÆ>²ns›ÆË5KL:Ãd÷é8Û&ˆC&.³™^:Gdz(kgzX5΄ڣ˜EGïÒeg˜9DEËè¼¼A¥y€{LM.ë%¥ÜW³’XRc®H–gxn‚Œ R¸bzù'b4`»éAíêÙNobf¨r¥ ¬®:¬è8)’5`%åšb kxŠm° -ýH3—F:ÕàXÅd«Ê}ÀòÍ`…|éJ›:ó¬¢ò4»ñ飷¶JNª ½7¥ìFçÂ7’k¢óÅW:[l•5ÝR›Žò³Éµë¾bævlº¼öº5ÀµÌV -¯Ÿâ¢Æ=*•¡IÆßœ^7l ,üô»ÁcÄâÛbµ´bSúM¾‡@¼¼@Êso I‹ú ¤Ó¹dØr€Œg Ï<É¥dy2rE¿¹Y.¥ØVó`[éùEƒFrŠVG3¼ühd Þ8‹Ô¤µ£{ʺ⃽…S—:Èu¬¡ÿWOþšôÕ“ÿ˶ýJ·¿cåÿH·ßf d5üÕm¹2ïæ_wø¯¢í·þî’ùWAÙþÛ}¬ƒ¦8p_$¯àÙI^5¿™$ò’$Õ‚šBnü*ŠUw˜$ -¤˜_“DÍgR ñw1I4®þÿ¯„þ_åŒÒ¿D[LKœüfœÈ;zû›;jÚÿ!Ôf{øÿ[²ÍA©¡Á¶‘$/¶š$•›øÏÚ~jCõ=OÍl§gVþ”p(¦Ø°©qˆòͶ­µR8ƒ·ijYÙO˜Ý$òZ9þÉ_õŠVü̶ǟ8ÀŽùîÈȸËJT=G ­¿’ìýXÑŽÿýAú~ o“¼¹L+yE«Zòê¶Ò­xtŠDNmTšÿ—¥MŠÍãŸ>"y¹àŸ^~¯<³­°ëÇ‹­‰{?2®`¬¢óhEÅßZjå ½¾w^Üáv®ª·åwßWÞ—®ìÆÔ.“ÒqpNºÙyŠ‚wvð{–Ï·ù§Ï`ç|ñG¦}—WZÚ©†œ¼˜O’A$¤âú÷îÿ§ƒOÏl³úˆÖ¸ôyìhy”GzÇg:uë þzS ùjç,ýÕ­Æe’w;ç¤åÏòq59yÄÁ æ´ áIþvØUuäPöO¯:Šäë{|Cš»AñÝÜQûkã!yïòFòn­ªi§ -lò:Ròšt”ÔäV-Iš-óùÑ­QdœpçŽOÎþõV=î.‘~¾œ“Æ1úNŸöî}RNa.Ôo›üqÍmʇ´ öGeDïñ`*ìȼº½57Æ–=\º› L×£îÖrT„VžX#WÙyWXø¬U»ÿ¿µ )¬–¼·®ZÛúÝ.ÏÓöÕÔäUý™ëPÆ%Ó‘-¦Æé³kvàWvéì»Í¼¿ŒKÛ­í@ÇM òר•âµ³*½×•¾¯¼Ûo>ÿ*ǵÈemj ÁúíkA±õîúfÜ\øè©ò^Ÿ%î í˜wïñ’uGÓ)㎖ Öåë#æ‹äÓOCûù¬|ñÔ» -mÛçù5w6çÄtžsO2 ˆ nwq -•-ÁÆúuËv×™\a´‚ò°³¬· -~°9–7AIdC¿=n‹SÉx-z;ç]q¯ä1ƒåŽÁGvy_iÍg°?u\¦þ˜ùß=¸gÐ+CNÊŸ€ý/$Ÿg³ž|V’þ øˆ½¼ˆ¸|Î&ùl¸ -ùgXn»+V~n¤À+½ ˆ`›Îë~§R§º78öŒb=ÔÍCíYG;ûB-k•á²óáAýbê™è·¡JŸ/.Þ`lì¦í;¿ìdf¨÷ =ÈY­½çÏo~µÚ–7\t‰^¿ñ\ »=t8ëÐïùºÍ—Ë)—g«Êœ@s1~äKç”Ζ ä¾ÅÍèÁÅçS®·+™Z-\`ÝZO«OõËjºÖ™Âý®Ý÷¤ñØ’SãÄ©ñk|Uãw¨Óùãý‹Wú2óþâŒhJ}›‘ç\@á4aÜ÷ší*Ák|c§'ëýÔýmñÞí’ªÔK{*cµÞä¶2UfׄtþÒ¨¯µ§ÑÜ+™úÓ‘:Ö×KµÞÍ õ4ÒbõahUµé7µÔ5ªOŽL#>Uš6OÊü½Ju§ÑJ¦v•L•ÌxõùÅ;}é¾~‘|¶ Y}½R›¬Ÿ>è¥ci†JÇrÓ:ac#1üõ‹äÏÿ°{VÕê²s%Y¯¢±Øl/ªÈ4É·µh¶¯}tZÌ$”:u%[­¥£Q”Ï=Ý&¢‰6a>©30)ìTù…Ÿ¯ÊÆJÖ>Õ@oÀUxm–+‮öëŠlbQS:ð‘„ß[éœ,žGH¤¹ R¯’.ÜÜâR¤Þ·gŠè‹ø?l]­Ôã͸¹~›†›J~4ÝÕ>Ãå£yë‘dyÕR]¨m è.`"S×&óZºŠ©ŽßµT€®¢ΛjpÂ*Âî•uR~WŠ"'› º(£Z’º«®(^ -^]¤Õó@°We_àŒñ•i<ÏOîW…Ç÷€ŸD^ÄOŽ^ü‹tããò.þbSwÙçô';G®lreÄ‚ý¶Û™Á¶Ó~t™-¾ilÆ×vmHzUM¼в@]É9D¯Š,¸IeÓ¶—•âÉÙËm*¸Èèíü”z\œÎ0ñD -âPFqáîq¼Àç:?­{}îåÞ\nn„Ç»è÷Ó7˜{ì¢)Þ8°éF1o<¾XíÔì½Ö)™Ybüð¹¢E{¹t}é8ÕG;ɶơõuÞòTEkoÄÊæ6U+%noÊíÔ‘NÔ•H»²¯\÷$2Sû!<¤à#ðîægÙ ÊKK„åÜ<§r¥–.HAà0«<|ü˜úv’aò¶"0ùQ{Îä;Å “×éÇÔÇÜý‹%!dw±pÙ}ÈQ"ˆåáºÞ„zÔLOÌîêg³ÔOv]«´ä&+õ%R¯Ûº‘Ž1¶<ì ãÉf*â+àŸoâÀË­ÔàOÙL8¥;ÌqYbYbõæ†bV‡Xf -Ñ¢[Ãî”:<Î;ª3Ñ?.Á,…Ë̌©ˉÂiäAáEpÿâú6þˆ­ËsrùÉMüÐö'©áÓÕàNŒ‘q«6Ïj·Ku¢“[SÆo#Q‘—“«ÂyÍ’É©%ÍfƒñÐekÂrÍÂÉ=dÖsäÁ3èo­Î¯åiîT·1ÉóþnƒNeLÜÎÁ–à˜r‚G‰GÑ{ŒGá<Ä£exÇ£éæö¾Iô³cÁJ{eþa­ÚÆ{— ·ç}2I×—ý":êRIsªÅKÔGܢʩ‰Ë2MÈè}\£ÛÔ©K£whL…‹ªOõ´ÞŽLž 94öO257GŒÚX™à±&Oû‹öºç,l>l0ðLž?ºp&ªU}ÕØúÕ²ÃÛ,ØÑ¸p&_™ì™<ªf>ü`ÊáÝы̷Vì;©_û»Põõº'“¨Ç¿——Ó¢}ž´T˜'ÆõlêÃBž5ðYKïá2ëN±wtò1w -í1EâoX6õ­P{( ºYbÊ«PSËF/”¶7nYjÙ»¸xxV°â!鋇 y(†õû_4«·/~ §œd”q¼W­úzÇ÷ÉkL´ÝÅ;ßèlV‰îäaÕÌBœˆY‹][aƒh[¯ˆ; -EðPn ìj¹Hï¥ÝýÓ/™¾0+¡z+(†°,v×ÇT$šT¶08hî¤üàÛyn¾ñXQïÈ$ŠKˆ8ÚˆhlwÈÄ!ézSJßh@0VJµoyêÁaï ÝIöNµÔ*_‹ý©" -¦3ÌÝÈø¼VüŒé^mo⯯ yæÈ{û"™ç·yš ‚ÌP¢ŽHa­ ¿{“1ìJQ»²ÂÕõ„3Þ |Mf!Ú´®Îr«Êå”32íÔOÎåQÍå§Ê2W?eâ\}=sËgzô‹o-‘áöU®ön}S0ƒƒ†ô‡´\ü‰+d#…jt]2* 'f¶‰=K+öÏkIú9VË!›že½YJÙ?¬dM}YÏ¢pÜÍ7Ä$Óm6p>8!t’ЄZLI€&Ç}@SÒÐ…×ÐÅ=èLâ:;¾*:½þƒ §QNê[™¬ò?DåLÃËVN y2G]ñ‰dKІV2‹ƒ@Ö‘9fËÐÒDø\‰Øsà -7EÀ4ê•Ó`¯.`†Ü8Å$Œ›=ý@iÓù¤ø`€ñ,%M¤•">f<ÍfR“35Àô«IŠC1Åýbˆzçe›r³½®Óúû(« -ÄŠP|°hó¹'^½ÀMŸÊß4å"£œÚHÕÏÖ £0Q³8‰È€ÏÀú—*`ƒ¢Ø£Úl4±RœÀå¨5àÐÙp•\{E®#ÕRÄÀuàzfp®þ*½ h¸&“~²¹g×*V¿èÆ«Hk5¡6»n ¥ütñÕô>l»Þ¾ožZ“” c³4€ý‡nævrÒÈÐdMœ%¦¸v À½íÆoNo~Ý<•µ/ 3À÷-ð«çðI=‚˜)a’^•°Û)nNn„s‡ÂE2pE–@ØÞvç -îF÷ 3¿¥+õ ĤcLÙgʨ$‘Ä4ïûìø'IYøŒ¦p­yæPGï¥Ö8Í?z²€)ÚÖ£Ùä¯NúÕoK؈ÕÞˆÃðÄ-ñâ{ñ[ñ@’E HN¶¤µ¿ÒF{éJR@ú»)¢-€œ‘ g±Þ{‰jãÒ!¥'g—¸r±ž!å v/É.?/\ÿ~Ä¿ç’D+ªÿtÐo¢í·žíïYþHè‡)V÷Ò5õo¢í[Nœ|»ûü˜`ùôÐÚõ?­öOµØ/`«xÿ“Óú›Îš¼ºVjæþ53`~ÏRY³ÿ¬ýf_k#h»aœÚÈ#3‚ÕXú—hû-9°¶:Ïy•,ž^!Y=•=¼ >Åú‰MÏÜo€È¸MŠ¿ìÿh=ǽûQ~z_IöŽ/°ýí¬A»?žGÿçeò -,5y‰lzÒŠM¤˜¤6TŽ|j!û›hë[ÿJ8|Upèw1yÉŸÊ–!â ,q1ÜÕ*‘qÑ´¨0ì5­G¯s? -½Á½“LÇ·se>¿ À~y½i—ý•]ƒÛeRä3礽gÏò™kž·É¾›û»³¡\wÔîSÞÚ©7°‰ú-ý&”¢m„«þ‡ä]îKi§f‰äòIrö°ïÝàßnõ]-ûlN¿õ_HçjgˆñEX£ÞéÓέNÙ>Ø×ìåth½æ—}73zìÎú(ÙQëYfkgð&j»¥pò‰µCøÕÇ~(+å™ÿ­ ÈõI°VãsЀÚPPX…¤oæ„_Þ ñÿ…?2í+*¤†ú –¤Ñ;šš|&Óá—®Ö§Ÿ;'õzÊvöh<ØÊ¾óùè?ÐŽZF­MÔÚu×I7®åËz²ò˜»ÊŽwÁROŽÛ`]9‡Aaq»ùfzí‹c#—]à{´è ° -íÞúŽæ²wj2öïË\”ŒóÂûˆã$Ä©8zºî›/’·;æ“7+Óq}a–nW„~_â“v½LÜ÷å”=çC9>>¶·ãä³™a~å­{IJÖnrAcÖ®ø¦×Ô!:l-ˆÞ¨ç ®SÛ½+‹ÙärÁr1»¯Ý\¼æÎΜEcZn¦!Ct–»æ*ÓºâX“M¨Ÿ&%霷]/3ÆZNft*ÀH>ù«„º±Ç¸x -äÍûÐñ+„쮪éïè7â¯_Î|µ\ÎFg¶Ÿ‹žÕ‡<¦Ö/»üpLÏ¥±%8ód\u2 ¿>Óö+sºÂ¶ýi¾M¶ÔÛ”ØfÜøÓ[ “QŸ: öuTJgÛÒö¦5 ¶Ã$ú†r;z >Ø1¸§àùžúÑïƒY@Ÿ–z»°»3Ï ˜ž+Þ»Á6~üvÔylÆðç³Aëèd!ã1]ÝÌôÙŸìrƒü¤\Ÿ`cÜr˜Q?r„5Ù)¶Ü +jFmkR.XÃd"̇òËX>sÿ2PKïO?è -å~M|¨½õv¿è¹Ê½kNÅg•ˆø‹Îñ¤1ò·‰v¯¾|™/kôþÚ;Ák4ŽƒQÛ/†§¶çN¡7—ŸAoVCíñd·õ–cBtŽ#º¹‹¬>–è\áÜËC Åä@=çøþ’F*}dTLGroËf¯8« »íÂdÞEw·]§‡îâiÃíÔäÛL)31GaØŠ“Å£5uF÷ÿ…ûå WNù(Gm>Ý´,ß䨋ÆÄaîÆ˜÷ÍêϨ7ÙOÑÖè2¾ôl¾x™ç³Ër°Øƒ°¿|£Þ惼{%ƒºí%VèœHï–Ä´¯lSj3“žfŽ^SÓäç»5Ë"‹æ»«›.±~7«W‘Hc1_Å{ =wÝ6 s7VýÒíWà2—/®3–÷·v_­åÜâ.~"4rÇYcø^ŒÏjзÇzOºÙ@6èm{ŒÓí(üªÓïj©Ïß¾^Ú6Ëô^æxle[ÏdœoÉ•q¹é.\º©h±á¯Z¦±Ž±º·gF_nêÛ¤ø¨—£BíXRkµN$5|â_¾ÐÏâøüD?õ`Ï~¸“¶Î.aÿœõ¤ê-vòhxœô„íÂŽI{0T¤õ¶‘¥uñf«Ù¾ –eF ßm%íÕº%?§¦ò9Ýz}6jP6c¬ 1 -»Zß™Bú¼ÔË¡"Õºdߨá×ÓPpù…NçFÍ®¿sGmd5ºs5zÈgu’°ÿ §Âv"mr“SÙîv97ÓYÇÓC}Ž×·@¤~ ~±þV;ƒjA6'ÆYkU¸¨ÝT xÔv”g¬cac%%¬›®~«…Y=®uƒÐ/ˆètÓAµ{éÌj\7«ª1¡tTq8ž)/%P*Aû£€:öë[‚¾:«.¨Û1Eþâø…óáp~¡üj³ñË >´³_QyÒ·¦¡=ƒÁJT­n¯uÕLqåñÍEά4j©wl'Ý^½ÓZØ<¦žR8œ7ú¿µû ºjÜ=‰Õx\̨bBä•×Ú¤”jÑ‘°OZU gÇÕÜÙÝVêÕü³’ÏlÉJ>{·åͲ»Ka*ùœwüÅ÷è°‹µez_–íC§äÉÒ 7ÛíçØÎŽN–YË~k(ÚíGÍך O¥&sçêíZN­‘ ÒÒÓ t¨zªñ•š¯Nò†Ì£R5ºW%C¯êâv‡ªÐ¨²|Š|%/wkòæsÊ%]JlûÚç&aÃv_Â,b“¢²ÿƒöµqøb;L”’«l»Ðô8$Eå~ÕÃ}Ô6gCKKŸÊü´HÖNø‚Õ‡÷‘¤¡e]6åm^l¥ÆNÕ'_~²Á¦²âˆSÅÈÕâJ~9ÊÈ­ü¢ —¶/Fê „*a¡ÝûÒÇÉOç&\å’`éd÷J›ûJ°jÈN¸Äþ?l4[£}¶Â–æµr š\ îË~‰Óë`S°6]Z Æ­lG×êæ…ÐÙbRgF‰U\›W”L\oVu©×¯¬ÜiêU -Ù¹+o—µÜê£t¸"w©cqoñi°ØŸÌIá†ÄÁÚV:|Tö\~âg^èó\"?Û\BO^Ä»¿h\¶_¬óSŒZ{ê·Þxc¢ÂzÕ}§=2ºõ÷vÛáÎô´±vX­Fù{R•§O´d·T®=¤J¡ùÔåV„uäò¤jKWÝ‘ðù0ÏÍÍ^”â«pë‚D° ⣡–úc¼ð˜Š\2-N~©Sö³‰BÖkÙ0«¢³&«bõëi‹íÿ?Ý,Û åU°Byf¾8xÌSæÇ‚ªÓGï„Zû¶T!f†yÇu-†kªTkVøÝ„—ÑLŸ“ºº«ˆçóµ%†Ð@¸=JÁö5_àôÞ–áÅ™›˜{ßY®Ò)•Xï¬ñ¬:ØLð(Ž™ZºÌ1H­’¥7y½N¯?¥4O›ú†A¨æÿ@Ð\)7'%åéoèHÚõ2C£´ˆ»×iÿ`fFE§Þ †ú:¿øJ3;.Hĸ\i§@ -£¢"óq»Uã§üªË‹ÏßzãÜ\~Ϲ*(-9°×Žì¢=¼³¹ÓüÃÔ©SAîK7ùvÚÏB‹jWÔ-…¥>a ƒ“C¡1³þ‹[}ásøv6Ö X®ú0Š:Ì ÊúôÙ_[ÇŒ¡n…”–âÓ/Qƺn9³Bý)S¯>“ïC;ºy+é’­%T;îÃ6½R?Ùû*q5§CÂÂÐ5ÁœÖ/ü1XWðGs<ÅÕÇŠ`ÜLúÆð¹ú›gô·ŽöXй’¥Ç#ø[¯¸])ëac×Gºpêw«Æ¡XoÈ!]‘9EÀŒ¡ˆEz{lt«ëIt™8ëTÇͶ)\¡,òœÕ²œ­Hª{ -‰™ Öb³xÄŽq\˜<ªØì-õ0Ù ô£6bÔÃê&ÂuÏîòú6èöW_¸Ög¦ýnDŽ*°Š v×:ܵïí—™ëêgƒ°s¾äñ@aTÄæƒa‘\'Ïë!n÷¸LØc”!8A­àQ2¨ã“õ±÷ábã3Æ^%ÜÃ*aƒa|E½$h®Z.•—™±TFôw»´Az^©±E¥¢ èRQËÚ¥"ßZ•Š˜¾,54㜖ÜáÆÑüF[Ë"÷»Q_=Ÿz}ßþ4›CáRãk÷@És}K|¤Æ-ŽÆÈ]™à+wˆ4u‰°ò2œ3åú4®–órÑ(m>•^ -gZ*™Ç xÀàc±}ÒŸE¬¿Îzw8]+ ähÜÌÈÉ3Îü‚<ª=y°“ažQËË<ÃÁÁ m|9=›-a4/%ܰ„¡D÷!JÓ—u`Ppöò§´N¶F‚ÜæZLËyË$ÛŠÈËò¤ÔZ’¹Bÿñ̨IËßDžÍÛÈPɳ›e‰šq™”‰"Í5œLÎ7Ø‘è,,¿= -úxTò¸ÕRžÕ”uJ%(;äz2?û†7?ä^>ë/&\hU¬eräû}*Gvä*Yl–{™îpÞ¹ºs†+q2aÇ"ÓòŽª˜÷ÚÑ¥=u/èm)¿Ö+ZŒÜ"¤ñÊ=ß<”K¤tË-ž™«W6v.ŸÉ-²_=fK°“dö›b9ƒeLôô¤ˆWgˆ7ÿÄ­®â^œbßX0ÿ`”QäÊQ9Rý•ëœn½~ ±jgé¾bVüù‘žÉ@ÈÓ} ÉæW·¤Ö Ðc LÝËÀU¨g=ŧñ­û\“-¯Wä»GÓs¥ ‘égW¥Óâ2ý½€0kfÎá¤þWO~Û À×Ïí_=ùOp¹ÿWÁýsø*¸‡LÄLuDf~¢… ^78(SR¥<RãzÒÐG€dÒ¿Ù9FÔ#E„i¼2R¼ÓûRV%F1ýlYœÏã‹…—'¡´­ciP∷äÇPqìÁ=ŒÚ€Ÿ~º­ùOµýfÛ~¥ÛM}õ?ón¿Òí4wþÕ“ÃFdô¹­’@~À:¨0§É¿MÈ~k#ð¹jŠÛô_1[9ÿoÃ4è7WuÇýS?¿øUmÿHvßñ¿çßßï7)îÉ¿ñÏ©/ù_¢í?%ôo¡X2/ÖˆWÏÿ!Éàþèrùó -_‡Ã£uÜŽ¥â|ýÆ™Áp³^°'ÝÅq{´óXƒCgNÁÙ‚ƒßÓe½M‘-Zœ4ËhWŽ÷Ž£/N…óš2"Kƒ†=ìLº6ƒŸ'Öhðñ‡ÏŒ§þì5x#06¨Ÿ¾_-ûPwqè­Äù¡W ÷ÿ!.ÏäUD6…C˜gž?Ц²<î—Á|îwÞãžw 4÷~"êÎ<¸›3ˆ‰G“-ãö#·õ’ÜÙ¾6ˆØz´‹o‹?•²Ã…å‡Ò§Š \U£™eSìûõ¹Þ‡®—NÏàrã¨WÈ5VÝV=¸vË”ˆt:‹r­}Æó6å@[ó&?7`}Në/îç•Gs5¿yÓãDz41÷ Ææóלê9Ù¢N&)Ž«·2¢øŒ™Þ—ñ ²‡NŽô^ž\÷–9öá í/)îîš蚥>Ü9v¬R?ÏÈöÙyHm:ÕÌÛ¡Þ79|>oMœ×¾%VÍwóUùfeZ5¼wkÕP·Æêj¨®²ü×)× öVÞÍ­Œ¾üXÜñ0wü;ËŸ’þ8|í5ûN¹Ì0Ùâü@­ájaX³WÔu»‹:n§{·VíÁ³³3ïÆølŽ -«t}iÅæêÕÃ]¶ùšeKÍʧL5§Ë u›Æ²wµŒ:Åò õÍ¢‡×K¸Ù¯í_ðªÖv’å_ nÁgÍ-’[¹«dƒ.Wx¸I‚¤ÏËl½Í,&øî6´Çã©>ÌÔº·L¨.úlðíË®«™÷Øi·žùµÝ’ZG§9?Þ‚fFüìÚü6 ¥|7êžÔ·—­7/ÝRíÀxL­ýø¨ú©&§ŸÞG|W'¯ÌY»Z‚Ææ.¿»B™^Šÿ?13aÞëSÎ~Eå¹úy§èáá¸éÚªEMïm£Bx¬Š¨É¢%]~ë)53þ«ÖÐ ¢k¬†œmä§ÞÒAí°6·5¼hõÓ~rÓ)ÌK´ëókl¥ˆ©hHêx±k* ŽO”Ùy¶Sä¹’­~­VõPÄIA/ª^õ~ oóÅÞz2ååºe¼ió9yîޝð)°^ïõ¨¿ÁøZç¼N˜VÂ. ÍlICU¢1õhVjGlhÔðþ´£÷?[»©û™fçb_ê¯Ô·T'…b¨$6÷P®ñQäxYªzRÌVÕ‚Z«¦7¬Ô0|]Ÿ§·¼vª¼Þ,'òz~qåFu3ÿsô‹m²}—‚^꫺Õqý•Ç´ü@CŸõƽvŸ7ÚÜbÇ4³/ ªïŒk¾Ö宸>ž¼vÛ•T5 -K U$É®’\«¶Raµ™²C¿ªÖ¦ÛjÙŸ+ÁæWjV‘×Ñ”“X‘öùJz÷%s»ó%Ôî<ÅïËb(TG)Fó¿À{Î/:•|Fdæ:ìÅߪöl¿Z V3éö R³õz¬Ñx~Éõ!ÍÞEurl¥1²â¼,QUW«ª¾ß¨æôk¯R;?í -2Ì;ò†á—r3Ó;J{}z—Úð5#¡{ÌP‰t„ˤë Ãzù.0œ˜bf˜¢:ÿ…#0€Ÿý@ßf]GüÒ‚_v3ŽÑ°ãñMYœ­O{µîïˆÉ´ÃªL«éSWÇѸ7›QæBUM­æ[©ãrEÞ ].Þ–Ôæ®ÔÖ¶ ƒéÃ3¶Iš? —ûð- Ïÿ€ž­-žrsîÉœ¯Ü4:3Ü4^õ¹é3ïübÆMO™é«R©SôdÒÌÎZ÷ÁsÌe+ûô7½ãÆq~ ö„Ó: ôyýÚ€e~¸¾ªd+y>(È%yMHmïÌK˜–UÄÞ©lˆd_èWªe ÛvŒwZñ -yþ$=¹g–ç¤Ûƒaß#­ÎºZœz}¬Nlæ2 ØÌÖè¤8ÍØŒ»°Õ÷uúEpÂ…‚«XjvÚÕºÉHìίÃÚé´ïÃÀ5eµk”§ ^{ï|:Ïb¹´Þ¶ãrbŸ3Ñô¾\§6#XÒZæîQçÇfÖäpÏP™r³iõËÒáÀºîûÁ*ª 1~èRŒÞ/é LMmÚXÚðÑm”I“ñä/ºý1½:Î&?uØâ¼–­äþ¨ßsqy4åá˔Ò³´~í|UÕicåJaà'bßË&‚¼‘D‘,‘äf‘Êsòd °ŸÚºÁzHÔc•maÌf-eñ1zÔÚ2ðÄ¿Òk) èÂÖ ¨]ëªRåP’ÇÞeCvå Lvécƒ< yl*ãÿ°ï E§Ñ¡ñ}6øX‹Ë&ê¨ç©Ã¿Y·¤A:'ß-SVË(}øðQ°‹¸JÉ~±^gžcU!F™`žc˜Z•–$£êôú0iÓÎ΢‹xâP»»¦Ðªq&ÃÌêM5%.H¿BÐV¦ßÙQºVâv\ÏâösPÇïxyŒßaxô·çÇö*P¶4C74ÁÀ…#>jÈ1ÎÓÇ"6S"& «úzI:w‘7:÷8ÛÇ:7×6úÚGèËÛÙóà}.ý@S²Êåí÷nšì2á«ËNWg3ÛðVFçØÿÉ.}ªŒ0²«]Ž1…™ñ${÷„^ßY"]KøC­Sø87q~¹Ñ°g?ׯfm¡o[ÿj°œ³E(æ|C…ËzáÌ•¡½Þ*ØÕ-åÏÓ¸”¿„Õ’‘X¥ú»›âÚ°J+r`;MåŒþM©6—H¿+lÞQ:Ýšë½·ù›Ü=¦q¥4à4žû à"½c¢7aÅ÷&OšÔ}µ_¨2w!4›¾*åV >›¿hj~ˆ“Mä>¨Þ\îpÄž*Åõ_QY“±¬z´3@J+×ø>ƶ¤Éš˜íÔ0×úÍLjËTÓ–ï—‡"` øGEþú€-·IŽCåøG,ý£ßš •W# Mè5S35òêÊ}ÓÖ^ ÈWiä[*…ÔƒìÚ|ºÂ…AAâåÛºþáÛD\¹ˆ4ÖG·‹g^g¹ö| Óâ°‘ÆÙ¬ „UµòÏüU­Ý6¯‡ðøÿäïþÇwû:‚¬ú -þÊ¥î @ÙJ6P!t -Tìú*>ã*Æ×¾*5÷I÷±¯ÛV^Åïhýïªý"èW[T£ÂóYÔ­¿ÖÕ¤ûX²ˆ?iëÕ þvËJŒ¶I¢mÐüÅ“WŸ'n[kû7 áñz† ~|j -ñж}å¹åÎ}J]׿2mbª]ðà’˜j9ô$í˜q²œÿ¯a5ÑA$ËŸ¬ßOÁÿ"¾a¶Ú뱘ý•—ÃôFz|*'í1B_•dû>ÅsÍ»¾g;w¤¡†‘y.M¢lÛ]ݬÈ?\wòô~%/äâA¶ô_˜é}´9ùM‰Mò`¿Q±‰P›¨£¿Jè™V÷¬ -™ÿ*¡E¨˜XWÿ†Ù–×Q¦|¾ë[ñ™G)¾[&‘×(å¯d_¡.GÅä/ÌÈ”Ï~Ú,žùYÓ8hÛ:>­nã¨ì†$ràµÞë=ˆ]vpfãÐÆoÖÀ±†Z¿ø¦¬øCÛøOÿ€ñ+}&"hêWh~hs™¹#¶ªDÙ†]½amß»’AØ?_Šþä öò檻È(P³tÛ#¾üÜ™÷ØbCÚ¸šù×G#­.5˜ZñÛ¼° HQ[<½šµP®³î\{²ûÙTÝgfFJàgȄ⧠=ÃM«ð‹ý‡çÓ£ ÿ5Ì4ˆÌ]îy%;³¸º8·?]äø¬|8aMa¯ŸšÚv-i• 9Ƭեòq—‘µ–Ò.ê.B* -ç#n7šCÝÝl¦?ë>Ój -ºMÖzö=ÁBfLndbäž<¼4ŸõxÈ_çëAÔå^ñLQ‚ÂgH¦’­/ŸÏ… -'û#Væzu®ÜöÌÙa”GVÛµP]­=?,£Z:Z<šX4;›Š(95ß6ž¿ØÐY:&—·òÈËßÌáŹÙCþxk öá‡Ïn* •':íTnÛ‡ÆåkÏ€ûP°0ßtÝÌœîÚQ§]›»ØÑ?$èì<{ŸàÛ[ñÐ{¦2çk½~>Œ)m±µµS{ÍLRÎ"ÄÛÍÙ”—üivÀ Ç;\Œ¼¶úv ö -C!“êz¤¯½sùÞTË=dšcƒ…ÁJAv)à®W+Su:äÑïø»·/ç¶Í?¥g+PtKN ï9E^h #/>ÿƒGújd/\+óÚÏíauë±ÃEJy7f™ÓGŸ¬=6ÞËèX™‘Û¨„!×pûÚ³Þé!ãÊ ¨Ây×fZÛÎnÔ;u¨tïæ{Ù»}9Ï¡¶ÀÏ3­`zÀ[ræ½ðÌ=À—«ÍÒtÐþîaÔȴ壻,¸Rrk"Ts6s§ïÔ›í¡C0Öà~Ee¨¹y¯¢õg“8k'äªÝÞ®FiPh—Ùx7¾—1-¡[O7ËêܶÛ×i»Õ©yØ*p“Éì &Û•—Bûf¹z87f›CÔ¨Ôßñ üÌ"n^epg ,Á!ÊýR½q¸9ö™%zvŒ·67)ÃV—’Úc° jýâ´[Sò£ÿàP³x.¶9ið}>†› MÔ‡ƒ×Rjö§Jlð`;ë‚ú\ÍÃZ…°Äx©zUiÂ}ßl ëÐu-|ê;ÛÞ1þÃßÇI½©öyzÙÚqí}±ngìaI<›úkOÈÃjбªÃ3¤Wµ¶é™Sè05õYýa"Í–R©‚œWÉn(ÿÿà˜vj¿¢rbö}qÔŸ;d+ Œ—áw˜ ×R9‰k~ݧÝÕ»“sœÝ€­7_KÅöó§Š9÷º%½Ò­ZXÌÕÏ$7¬–HjV…ŽêÆ4ØÒÑÌ€ðY©–7p%‡dHÃö ÅÀÙµ­»&Ô)çv)Ÿ¨[ü|){ï[f‡³ÖÿÁŽnð|>×nc?%lÃi;jûQþd¶Š÷®ÐXÆ×ŠCiÔÇöÃGÚ’ëo¬¦†(W-óŠ9#DÃÌ4¿‰ •ê¥Ü46BÙ7êéÎ@ßW:½.×åSûz*·üUfïõl©SÙð%1ËV´ûeÑÕ -ee§t“*¾)Ú*Neï¿Xšë®æ@3lDÞâûØWVîW‡ð¤ËÚŸN[•öµ¦¹²D§)fPë\ŸÕ±Yx™A+µŠ‰›U“Ó÷QWÑÁ\/Ÿ^«Z™«¥–ê”D7Õ×=Õz¯ú®ø.ö⽇ùwºXÜ•iu"/KjùÍzj\ü,•¹}Ì)¦8©(¦7“x7þaù¡øÔ万nƒ1 v=üöžú÷YÔm¥½¥åîvšdè=[tZQ|Äj»sdƒÃG§'—L™;>‰R—CxíÑÅ­¯kFñ½ÐjE-§»ÅԦ髓Îj¨êâc¡ÌDül­ÖßJ6wÆ -«[Y)`ÒÒ‘wc*ïh‘‹ÏÅâ«Æ¬'n‚Å´˜M;™gVJ§]÷LÞfíwáÙk.¯ËºÓ¢´‚¥Z÷¼™í×î:ÃYçÒí©Ýµ'0RÅl E­é‘êäðªî/â:YY¤z|>|,%kbÍÂ:«ëÒžÈ;a¾•]½ËäÜA¥£—’$æZ±ÄËŒb¾ów¹ç{ÑN­¢~c[ž9õÎRWkéé 3vŸÚ³䡸±ãÉ×|*2i¥šÎä -ë -Nlº$Ê»¶¡Ê.ï2lé8Ú’WÁK[+ñr^ÅÕ¸(”0X¸§»œˆš 7Ç!ÿRýÿ*NUþ¥ñ.ÿâIçPŸáýÝõÛ)lI=/Z ÝÃcÙ°ûES3—§¦·„òK‹gnÛXÍ þQÉaîµ`»áGÞfˆLqLò&WF¼"wQ¼f(MôOFUZAS¸=¡asAÞ5Ïü€Ú¥xõ,Ñ< n\ªô¸r|IseÒc|âjœ+#QýcéYŸœF”ŸAÞ›£æ+¯ -=‡.3ß{Öðf•+ÛÚ’(‹Â[ÙëM¡©{Ù}Q'©•âÕ)¦ÄÙÈ -w¯E=vÏ,„¸Çf’1ø£:|‘ô:EÔnÿk¸r_æ`Xì<'·ÙÊÆ²h'Ú2µ'óø™|8ÅèmM*ÒNþТ‰]gGc†&v&æÕ?p`Ï=vª¿fãªFà÷—vºüÞ6öògRïeïZFj›º/ÍhM[YéÂvÿ9‰]v¿üù!S[ó)¿qä&ÂøÎÁ³CŠ›©,kf ’E/‘Yù­"c Ë*½Ý›´cR}šÌºKêpyFT³cçÈóã(“í…Ó ù·&y«F‘|)S!Û#¡ö“mŸª&HÚ3&)Õyº¹¶2ÏÔ> —°þµ[Í»µrðÜsEÃjg -äCŠ„œ–"ˆ+6[ wŒ5/ ¦Õ^ô› ´³;`4ÙHÅUu¤éÕ|× ò¢µê¤º$¿§fDÔ.„t~Áøsä‹xXÆê¸ -Ÿx|òã1öʯ*RW3Á@è7N¤(ï#¯v$NI´Ä7 ¿÷L›ÔK¯WZRkØ0']éÁ—ê×ï‚ó÷œ:NV+ÊsÞGòr‚#Òoñ€8%î©>I“­@ÈFJÃ_®†«­Z ÜfŒ"ꀕ¦ËôœŸ¡—7V‹Z>c[“|¦>Éå3zYË“™7ºJ~z›aŽ¢ÒÝN³ÚåÓéÑtªô%‰–¨ÍéA…a·Rjɪ -Ìq±w1?Ф÷Ôið˜ýÀDOuøkìlð"Ü>áà°}bãæÂÊ,–ÃÒW…ÎÏ:M9_)o<Š ÜrQ syv¹ÉnnÂ;KH3 -Ý¿Ô -ÚBC”ŽïXhëè~ÃY"W¬üÀYˆFoߢNáÒZæ0ýi0ãüÕþL™í¯8êl½_p{9!·!Æ©âg²«Á L‡™i]C‹œ…C›\~Ïœ³Û†þÌ:´ŸÎïö£‡ġ͒TD™tÛÊœ÷n†gÄr»,ïH·SÄI¼—áÇ3ìÃýÁB$z® ú0náý‹€›à¨¯–»½s‡ó‘5Ë0ûÄüMþëÃIŸær¨S—á#7¿É­ûäe^Á$#¤ÜyŒÅ‰&Ñ \òßVK™# -[; Û¼ «|·þDïizÔ«_Ó%ÎýÀÐtÔR¡ œiAÈãs‚ c¡©;¯¤¡éêr:f´á=Ï©z|RÇм%}ö™_.Å#zº(‹é ¦S8…Ø‹t¹Z]§á\p‚f›õªÔ_„’T>UóJ\*φ¨w.u@HR?Æy¹ ‚æä/Ù]ü¸Ð€H™åÝ*ÀoMóN: -zø¹—sÅê¶a}ý†Kèdy¤0â‰ZåTaNW$ûK5™¾²ÍIáŠCÄÜ=•E÷ú冯‡¶‰2ÈQ¸»~|8w¤Õ™Eæízþ×ÔëNœµIë~*–ºIWª¿^ÕDÿLäP#ÿwf›D8¤†ýÀI2 ?Ãt{|*5æ¡­êâ}Š5Ô»¾ûö{½#§™s[s3ïfÝNÁu']‡W2|Í/Ç"{¸0ûuö‘~ŠjAí$í þ¿xØD«M\«Iàj"‡þ•i3©D¼KܶÔÿ,âOtp­öT&]ÿ1B¶ã»¾Y­¢ø6¸aþä"ÖÐ…îгí°Sdìâz¹ãŽa~'>αxÐŽc?eŸv8ðo×*nvee³>šÇ’W>1¬$ƒúøf ̵W 'êèÿAñ¡Qóà ¤Ëóë_EÔ_ÿêHÉVûЈt{Fš÷ƒEEÙU{õUG±Õ¥ãRÈ?¶Û¢ï µNûÒÛ,Ú~a¶ˆ·öËx|ìõû†#›Ëñ…µ—[Ûºû+îWÊ*ÏnX™`cµ6ò²lsÏ‚ðÙÎ~àP‰Øe_{±ûž‘ÂÏÁBÇ£ WÎ];G¤:»:t\BÌûÇfò™K‘oûBMmE½ŽÙ’Þ˦j¯°ù™É›f ]|S;P5chÝrà;?pò#ATCg©SÁ,œ7´_-Š!Úœ.W1v/'~x±`*|zs¶¯=ÚR°(™Zw]qjÓ]ÿè:ñ!h™ý6³Æ­@²æÞ3´Ö^¨¶ñìõ3n_š%¸ûhLÍ4õu«xD;ëf¶à`­Zw…¾_'ôÚö<"o³ê¾d³-Õµ®ëXõ‹ 7ÀßÛ]ª¹_Œøôh²+LšÃ(ç};Jù2×›4Èñßž½›{:Þí5ß -ìë©…W¡9ZãFcz§ìF¦"{nu%wœ _ -;²'õ½l/êÔÒßÙ­|x±Ùíåcuä\¶v¨l­}W -¿vY¯Z”»ój -í£æøR)™åÑØ6áRÇú‡£6ß¿W²ýÙNë 7Þ]Ë Óf©ØÊw-BÛbgýð¶újÌÚ)àÖ 8ëÔ[ý×]B´ÏHA³¹jÙ´:›²m‰õz³Ö{tºÕ·Ò «ÅõxZMÙ×­Y>€KeÎPŸJåÒÂŒUé*\²õmó2ÒöédjX(¶Ê/šÎ!~"'?¯r"*¹~mN8­Ô÷¸é_ç±¥¶FÙ&âWïÎÖ‰Øô½X·&úª=F|¦úÞ+DU£uΜ ‚‰@ÍRe>훕,5w «5÷~`ãn=Ýé¾Ç:iˆ›rsY¿•™Ö )µ#š)ñÓ™¡(ÕÕ†¿Ÿc„/>7m£Ö-ýV÷ãä6ݺî6‰Öí­Æïœß¹Zë½~|tQÉÏ; T~ZÑè½­~x}kN3ú¥²àœ·±Ö}ÄÀa|¶²kV'¯©ì•?Zé2ÿ%¿ŠÙZ´!›ZÐ1{š,gÅp~;Õjᣎ®B-u‰â¬¦õeK™eüƒbœCF1>Œþ¬œ`YÜ4o¦¦nÐŒ›n•éú2–±=ý~S]2ïKœæ^Õqeµ©äØh¥ï‹mù”=^ˬý|ÿÀ%ÿg´»Baš”ìàPý¦jù0H£»/‰—Ò¤Xœ¦jy²_)•ãõXX±¯øèÜ ou1#;H“Õ --5s-Q¢¯›’xî¼jb[—Û"c¡ë=‚Ä€¦ž ßßuãY¯4 -c^éw²¼ÊE^ñ„ÿ`J¥‘[’©ì#tîúHßCk áÒÖÛ°M5såGª|4ÅÀŽSjŒ -–Z›ÉN¹¶‘óF\õIÍæì!ž/+ ¶ý[F¸Ý˜Ð¬ ™Åÿdz¾95xĕܧl¸‰½9í9¥8h ìÔ‚‡¬pŒ“ÌNë™";-5ÁøJEñ\,œ^ûîÙ€í¡‰ šVo9\ô¨ÔÆ—«àwºtçÚ'uÚÀÇòŽåB©yHÄ‹B,E~Äí… -^øg¥ðàCÔJñÊy˜å>íÅJ¨Ä•Òb|Žqоç°ÓÆ5` ¥´b‘ÏîÁ,6U‚ÉGÉIó>½~RôúÝ™\a¯29z©$Ý[»[Ò¨¯Ëgàeb˜Qc—gýzw–ªÖàK(´—B~`mÐm_”êŸJ,þ„hµðÏÝd«ÔjÅ}N#WâÞ7. Ñv:ÕÖpÛ8›¡æ³x§ŠLu(Ö˜œ5èÐ<=§í»Q{ä¨F6P)j£ÄÏJŠÚ6Þµ+ðTë(Tj ß©uÔÛ6ö‡ŽÔJ­ZȈ7țܵŸ=®šëuAç>ïLqBvo…z[XˆÂþðê´ÙçJKsÈÎòΜ5¶ÃÝÌ,ÉÉ™©>oO&7@ zSäst=ï04¾¨}2©†\l‘§åxB¶,üLrü%®wµ@tzŸ!V‰Ñ¤ØœL\Oâ/†)}ô«}ÿÀ~XD7Ib±ËêB`švL»´SËÒÍdþm{#ôù>;ð]¦¦#}z³ÈŽézUZÑë©ýux§ hBÉÖ#I®ª‰ÄÆOd¢s}Ä5ʤ2Äúõˆ–Å4ön½Dlȇ ¬xŸ_±¡8¤°wý-%À [Tçap˜ÕéÜmámsSÒNG2B«ô÷~ÔJá¸@¨U -Šk~™iü°Ï ™ü6Û¡šÙE—<ÛëÙÆŸs’;d·Ä­¯\ˆ®â¼ðÇÈOã}{‡á -qØû ±aXÇ4îç'ãÊ&¯;p*=.7? vΚñU™›_4"7_w…<’ˆy]™‹¿¹'‚v½¶ÓúÐØßÍU½w¬°qÚtm\®ÐŠ}ÂÒâZœ9ƒ)OirBvˆîÝëàýs)À•¡?Æ>Útùc#èrÀ´tà† òSÚÍæõó”Î-´¨«¦ùj.;‹o@6/³Ø¥÷BwŽFÉÇÒÌÃõ%_ÇêÒî* -ŸÅf¦„iû¯é5j¡záü»ç†µ±‡U{\µøŠ¯ßšY³8‡œ|ºA°P¤¦W67ª-È«Ä8 «~ÞpÓ~nqJ÷rÕ;ÉåŠÕuv“ -NY{²~dq¤Ñ}†ÅPªe ™·ˆ+¥ŒaÍ ;ÌkE¼#ôL ºU{„1G„IE„)Ä#~„ü"`þÇOUÀÓÃÊ͉PggMãI„éD Ü¿dkªe6I‚?¬ï?0ÝL·6D!ÔùJ[öÐýuÖÊœ?ÓêÏ §œ¦È ¤71Ô ÒÙD¬ïQøAæ¸w´5¸àÝô»¨ÓÃÔãš.N.*ä5hü¾ö¡ øÎ+Ó)vʦ‹ÕŸ,"¡óPP¡iÐ8V+÷zh9‡Ä\a©Z/I.x¥š$ŒT(Çb“õ‹ ÐÖ›k’ÝAÕN†¯Ï¾aì•6¬Ø^úƒÆñ^†ûý*­5Á1 1ìš\ê0¤öTj§ )s^µÀ:»î¬Lnc\ÒÓû2Àäš°Bð˜Âc .?\ÏÏæÑ”ëo*O{×ññ³?ø«òkB÷ÔMmaI½B¹À-ËN]«£>ÆZ§œ}0癫ì‡ðKÝtÚôRÕs¡ð cg1z;€»û+Àt*Fx3]ˆáÙ?0À½ì Æü𚙸Eh×îñW”ãËwÀ¥lü Û™ô2Û¤¦Äo´„…ÊíëŸs—CPà¶%ÀZ`èó…$ŠV*ŸTâ¿åÖÖ)¼.³´Ž°íÍ€8Æ16s@\À6†p‰á½cœP@\e.Fü($nŒˆíd -ˆ]ùˆ}žÄlc¢ïÇ_î²€hµ¿é¦Dí\OàÕéYÞíœò™ßp‰¦ŽËP~3Wšg«+jÜbÝÜC%•—Oæ×v%úR1BT|xLgRŸ5Ø]Õ) ÊòPbu#8ÆX\¿¬½cÈHŒ€Œñ”e˜µ·P•züq1ö X¿(N_ƒ¸JÌ*]©ò¾ï'VàÆÙZI´DuGÝÞºr÷ênS˜ÆÕ…:€:>ÝXäkä(XîÈ’hÇÊÃH -_Ðò<¼Ú{î½Ìÿ‚¹Ä0Ÿ1|èÓ‰ñÁýÖø“`²t#Æx˜œ|þ*½¹; -˜T :Ò»€¾sg@âLKñËÅqrp¥`FÕÖ¶r5®þ7Fz'øÕ˜ÛfÝ6Õ'Å*fåŸ2Ú­, x²xd k®¥3?þú€qüÅв€-uIÀÚ[°Tì¸föúò{s€{Œ)Ø»eöQöI½{J‹€]1Ã[’Þ ¥ÿˆÊÛcJ[®Â«ühVÖ¼å0CêY¼{˜õnêhP6Dx:íP%ùŸ 8‘n„)p ”ú«äV„¯§—/Ý+€weð»ãik€@¬ u”º-d€À`e °èy8£EU®+)Sª°W¡;~„ì`µ å‚ÝÃ6íÄ׋|=jp¥”“R´#Ò¨ÈNGü£Û&–Ûÿ"÷kwý£ßFM 6õHðc$§÷Ò´€iŸÖ¿>Øû®dxr2Ògœi5Œ[ gÈ9¤T†7>¤´_î]Xì‹{úÞÉNñ|næ£ýLÓ‚MfTíIOüãNTÛozÃݶ‰ñW|ÄñoÊm¢àþñö7ï¶±:s…^/ -ø À (ÒycöJc ¥@ PX(­¡üÖï'H´Åÿ³ˆ? ³MÚz»e%FÛ$r ]ü_ñÓö3„w½§2FOYüÀm“Ý> ºr½ëësGš;,Z°f)ÊvóáÍz¶öI -B‚ĺšhµ7S¨¯.õ•IFÿªŸ9è¯0šÈË5‰²MܶI[¯$ap~†Ðõýenðª=rw}û&£•â¢l+%ÝÖ\¾xúyýº“¸Ú• E÷rTk 3n Î>žÛúÿÄ&ÎÚDM–ö'­©þ_%4o“c¼ŸrT~@U¨~GêY?Ê6™Ñu'dç—c!»=_4èxŠôg|·w}ÕHݬ‘¾4·ì™Ýè“4{ËGe ëÈ}[;ó¶r7;Õi®eÓ_3s³·ò³æpÙæt)íÍå"¤ÍíüÓ¶Ns-jÞ¿y°rfÈ`‘f'kjbgu¼«91¹+Fu€ã|n’Iðk]U>Ïôdmæ>þÊ¡“ÕáSc6;ó2ÚnÈa)®`WC},£ZZ„d!;×®41Ó;](´4Y—Pe‚ÍÐÒxW!*crEX##ÜáÅ¥ÛCþ$öWœ„Ïn}*ÏÞ¥?RÏ©¸@⨲Ø[A5ïÍà »½œ»kg}ú¾6Ö_=ù…piÞo«£²8¶k~Ü\ñóƒ»PöNs¦GÌ׺JÆ;›ŽUl;¼Ô±ó jfïáÓ‡Þ¡rOC?pTHgúÚÇzS-Gö)ÎÕ .u×5Vëb[Ùì¸dµá½ ç3×ݼíKØ¥…a¶%§šµÿZW‘÷°ùYœÉÖ}ŠÊÑùÒ8¬~à½ATã11çw­LRN»éeaèc5|¶D£?’h»7U¹V°(ÓAwmÃ.¶¦gçÖþ±Áí}æLŸÚ—v!jóQáÕ -d òžõÔ”ó]ý4ÒûbZlË #ŸrÜ…Sè»ÙãlçØ\p°H0ê»Àuÿ®îóíge¹|ä[þ E±Ê˜º¼Ä¡_eCåJfzúû˜ë®ÿ;äÖf}æhËmþj—ZÒîz¡â:ÍÏÈõš¥´ÛiL+n¿¬Ü‘»°‚¹›Ý…ǦFÇú®µ¾×ÉÛ²=9‡Y—AI´„TX©Ô©É™í©ZÝøo©†v^û…ЧРŽoF<¯nûÒxfvR­Q{±¨„C«!ô*c3×g^/ß«·V €ÈS^×wš`wQ]åÝìæ@;ØaÅ×]f[¨“—[\Ùžp«X—ÞͲ|%Õ¨E#دÉ8VÆ83?ïÆÁ,i³geº‡³£QŒÅûZ3ªëÑÔÈ×}ãh’¾é:Š^—ôB‚_QŠÒã±·Zµàƒj°^¥3Âò­ÇøòôRØißÈ8ÄÎÁ…öÆ>Ñý‹uÕæïÚÝ]Ã5ùtÌWÅ']“'oŽ]R5Ó'²\™µ¤jMé®QÓ-ßÈ#á^÷Îñ9VÞßò×r£Û„Kg䯖ZËšYâ,j¬uîxZ릢&îwÿÁ*º"Ûi]އ£ë÷–—j­Ór¸BëUð&ò™¾]ª¾³ÛisZëMŽqÕW-V§K³ÜÙ+•þ.2VÙ0òLF¯?r˜NôYº|PJb™•”R·õ·m­K/Zšx†Å~IÛ•ô꥛ ®jÌ\SS×Z L&þGÑ]–ý=¹d;Áb\ÙmÇ­GkêœÕ È´a· ±èÁD¹ø}l¡*î«£|4¨˜D¦gä¿mºÉ4X”ìJí6~ÕnõÔºAÒ$£•û‹ýe‹(*µ1§o›‚ªu_º2yelE_V»…E~4+˜y-d§‹¬l£˜$cç[KÆ®¹‡´o¨'ææ¨n‡OmÒÏ:z·ã£rýniBkXÝSçΨV,ÓÇÊ’ŒÆ:yÞwKü ÐÕ¤mqX ¥ÆRý„½Z½‹ -mö/Å )9¼ÑBUE‰Bpœ¼W”øíT¤ÝáÔ”ÜPg|â „ÑÛ²i‘íîá*;®ÐAW¡“­1Zô?LÙÞFxaò}²~°2žQu—|~¾QØœ7‰³~ëIIØuÚE@<Ú_Õš9JF8 -ÕyjYÈ™Ä^¶×ôEÆýÊKrïfJ¢zmT<&„ØBߢpµQM赺 Ä“ -þ>W·|Y¸×U¥¹Á±¸ÁT YÙMYø­˜y90fY¹3YØMÑ«Ã:O[Äјª—¨˜»”³ÇÙ=]É£\Ë‘Ì*­‘Ìšœ‘Ì& ‘^®Møv—#j—HRª{„&.ý¾¨Ê½ÕÙ9È i6ä¶â²È2éµ,»—Úõ¨E¾Ù<KSÒ³ü ÉbÓq‰žOÂïQ0!T\¿£k.¼…½„nÀôþÆÔù*jEÊ9£—óÎÖ3©kÃÔá'¢¥×¡Ûv¹u2µI;ýz´î5kSÇ1*ä0ÇiÔ‚”U‡ãÃn›©ÁU‹¢Üe5þ¿x§e¸rè@ GS|Œß y‰÷2ö—OÓ;6àö)¬á¦U!?v•|™V:¹Y{µÎUJ&È¡0Ìg—ó{;»\"÷šåryšËÍ‚]>®Çòûû±Ó}Ñ›–¡8óU uy´lW—HÓ4X4íÝE¬£G/ã+n¢MºtýP­VÇÄ R±Š½ž5„=+V÷,•{Íòã-¾Í—»FüßÏÍžî;W —h­åØl\”³yçè¡[ÙZ õ7üB‰á„É4ñF#s(O¯(11²hýVýE¬¼Î~Øâv^õÒ\º­âzl…M§š{-½Ã— -ß4à÷6Wpã¿Fxð kîV}ŠQÜ:®ä[•¼žË›¹yµâ»(×Ì¡‡r7»êwFY+®Ä²ùÏý€:åÌ%‰ -œ9Æd¦éeÔ £ ä’"gH{²züÀoÄw‚î™sà(k_¾Je‘vÈþ¢{^/íq^<47ëáÆéÜvS«¼ß&®œòƒ÷5u¹zÒµï¼ùÒ6·cpù0$¢KØÄ m§’µÓ7#žï“«‰™9ZÛzÆÃ­ ³G{È¥!NŸ®­cl®ÿɧàhTÃá ´/À2QrÒÏÃeœ=ç}nº« -ô¹gvißÃé° ² :÷ˆâgeÒ¯ï`¯öõ~7ZÔÌÆ=¬4÷W~Yq°7%Fú‡]>3ªÕ:O¾ž“v®Fj™Så #ÂÔ1à^ƪƘ4`ùôÒ¯6:JøÂ*­F­óœÁþ•ÞpJ+BªÍ*©ÌLîºÞƒÜÚ€AnÃAnRžÜ4ó5¨‘K¾•ñûÌÓ[Mzw·½_íù\%.`ƒ¿ÞZÅE±T–Û¯Cüã'÷*Ä*u!úÝóc€m†À&: °)QXݬ‰çbboݦ!÷ÊvÞñ³R}?¢Z>íõÞ 7*Ö⇷,–Nee–æ 6ƒ—­S”#ˆù&#Æ…\Ôñ„ -õ ´”žèÆ(„1jãƒeŒûûˆÑË|5XüÅ‚°Ë€ ¡vŒ~ü¡LáþÞ7~Y¾¹°Ÿhz Ýø•{ãaÊšc·G¥ ¶%ø( mçØt«Uf“ç­~÷’s½{¦»îamðYBYOr¶ìÿAï¿ìç–1âcBöK‡AÈù äBÅc,d@.y ƒyüÃrü‘š¤úõús¤þL·oÀì³H )˜»C6}+¿þdVÜw+ë¿9;°…þ·³[}X¶œ,gX€ÅÌmŒÃ£¶ö›ˆ!‡ú³¸ ¨ƒôæÏSÎÉrrµ¼Ó -e8&Ó õ^ ùæ]°B6X_‰·.MôW¼ýUp³ÃÜn¢åšgp!ÁnQW÷Ü€§Øàõðý øf6c[¼× ßò#À½oï›àë­ø =žŸ -·ÒjÃÑM~Ì[[Óù¥¦î­ünÕ­dÔ¦ ¦—rù3†d÷_ù?é ‰å6ÁÿJoø*¸‰púOÁ}›@T”ˆóÛHȉ*s@*àu ™Ð2Æ’÷6€ÔÚ Ô=äb íg_…î6}æ×­í’–{£A\ÃàÚÍ ÆQ9]Ûgä?éÄÉ þ¥7üîù+Ööü'¸á‚w¿y·¿¹ ÅüȃÖÐÜýë¶mlr Ð·UP8j]PxÑ'  y(ÙL=Æs”Ü4”¼Âþu­&úg¢‰&NÖo<Âï"þ$Ìö_2B’›tËú9ôö:I¿Q±!LU“²£¬Üxhëjç7(Vßß ØÝ62ÏÏk”õÕôͺ/¸Ö¶¯.È/ÿõóJR­öNzQ%Šã?ßêÿZÄŸÈËI˜mâNÚzå'OeZØ>´eñr×·…Gd¹ÏmÍJð ëHÙëNÔ‰+Ù×™ËQqÄoâÁ¨©ž/¥¡qægë [ïø´ý£²ÏÏ#º~=@¾ÈìõûÉú @øÚ~“ dpÉrþ¤'ÕÿŸu5‘ÂàǧlÐw}-ˆ‘¹/•okÚ«_ÈmŸ/EµwŠtyt|VÙÙáSÇWí€ïöS?í¿í²½ôc‹ îï›z§×Gέ™ežZùyQZd±¼xz½Æ¼Pnø|>’2›KÂÌHÉB¢Ö&ÖÕ_94ôôÿãª÷Ûš>s¤Z‘ßA½°PÇ›#­Î6¦FMj ËzÑ­æëº³vêU;Öëõ×jÖÉkË·=±Õûmæ_ò¾²YÕ¢ÑýT“áÜ«ªfEÔü¬Ç‚Y"ÒfeÚ ƒ SäF²1$áö(Ç/C©âOH©ú•½Ü›³C¼KððÙŽqŒk~)êÄ&EïÜ…ù8:6~¸Õ]úð¶=~—¶.Á1kñ¯ Q Цúœ\„jü,.˜ŸÅS3K¹OÅ„¶X½b|ÛXxZhd¯í¥n‹‡kyb鲫vÙ¸tœäk%ÏÀ·%ϽÃ%¶€cÿ°æo:]—Qx‘ÕG¢±Ò9š&Ñ–Š,hN°ûÁ­•“ºÛƒ†Ö¥k‡µ@-MªaY]š#SޙХÏ++†`?Ŭ•2rt/£¯Ï3Lǵ ]ÞCw±L/µäq³Ä¶¦ÖFƒâÝxnŠ=Úz©¯aD¨ƒRSW‹Do©¦J*Æ,ÿ‹‘EM~à±÷¾uÂ2±°»ûA§Øîá -Õœ3‹ 6'»]²Æµ‚cøf9½lK­âë·>ÐñWo^nÇÛÒi»9—8ri×ãõ­‰Å\|¤ò¹bA'HõMVâ;¿ZôêE5Å.-¥Üyù…9Rš*‹Ý­€ÖJ9¹vû”d+Í-åüìäü<Ÿ‘óM¶fÕç}2ìM'ÞJ™:nÛ*ýÆÖæs9¤ÎN6ךJ›³øˆÍ¯™–î@D½ÔÚôëZçÑ÷ŠsÕ+Ö‡±:ÄÏ 5Õ¸m• “:)å÷(ÌKܧ`¦ÍLµq٢缜¿¤J’#ˆŽD¦—C±iæö"ÃÌÒÂùZ’~`¡=q†B{W{ŘÀÿ0Ù_—“AjÙ펺íçwÞül -N³Œg­Þq™3ËZê»G¦SâÕ©¾/YSMEK_ÏFÁ´²ÝBã‡?°l¹ÒLÆdk#9ç(‘ÅîM<æ@dœ(#\(ŒÚ/Kæ£âÄäŒîòR´Üpazs*zåØÏ:겟ë#âTNOÿØ;t&!RìÇWe—ÙûÖÈkU6oJu°fJ>0×e.›ŠáuUSéN—­›¡K;ݵ$rá7E/tDæº -¾¸› -üã¹æ£y⃪rOªO.ìŒr?0§êÏD×Ù’·j±i®°`f©ë“1¦ ŠÉ4<É4»&"P¼õI1Æ1 $¥5í­§×*×J«½ª»ÇÚŠôe¢ŠŒ÷oƒ¤¬­ö°Óýøƒá¹-Û§°$za®$øc`ðw8mñ²™ip/Fîrá¥:ä€ðÿñõž ª*á¶öôE”䜑¤(Š`DsÎ9ëýhϵçÚëœóýyš¦QJ¢jô`¼îXÂÝ•dO6G ®£Oq"Z˜è¼¢0·O–Pb‹5Ü]Füª\y6…Ð'øÍ>{L '6¸¯IVîöЬqB/NvZzR?Û¤­4s‰Ç.J‡Ñðãž¾/ë8Ýiº…Kú~ÕÞ_0m¾»N;7·Þo¦²ÝÖo´D¤Oó¥Ü©¡Ø|·Tçda†4Ö¹÷«ùƒ(-ÌKȱ¬ Ôì®ñ•ßiW¾ç¸:‡—Øcƒª²M+ÓbPî± -¹3—5¼g¢ÈzÐ÷G€ÑÙS¦Í’ãPoùSýk´¥ ‡cü•%Ç$?ûA(Ƚ>©ìzQý'úê”ð̬Õ:WAžÇ5Ž]Õü¶.”Pb©;u­@e{ëÑÅ,î\ €ð‰çØ—Äâ«mp{°ÓØ22X¥8Ê2WbWd4ç'ó—G èÎ]hÓæ´4¦ÞÅÞšˆÏ+•=§a -rœgr$ºï†Ä,PV„+ïS L‚„oÂÍ͞ĬµüE»Ü4æá}Ø5¦×e§Z¿’ï˜ßå_%·˜ŒÍó'´ÌdSëËÃXÝuú -=yè Á_L,6,¯5ú10UÚ‚MƒzoëyjPë{”-,jt¼Gäx IÇ.,IŒ™œ ·vifØ_¾çÎä¤:¶eOs¬º¯¼0¾—Õ0¾ßbÕ“uŶbõ3ÅÆ#®CéEóÍÖÇõ¥3ëVN{ú§ÊEß?eò*:2ù1ò68c¿Ok =’¦—Œ”øš·µ™vz§QöVI“ÎÕH¬Éeˆ¹b÷ê• 2î4ñ•sìáÌü5Ŷž¼ÃjJë‰ñ×;‰&e Š ŠÊDwŠœWÚiE„Š´âL9oÉ˂ʌõ@eX½GÍØX5m¤?«3úiPQgLìÁ}¶ZØæ Ü7¸Áš5Xo˜èYyçÚÑ ”&§@ÏÙ­ÓÄü…*x9ZªØö1Ó±Ú,™Ø %¢”ìåH)54X£‹1*Çø¹è¹;a;I/Ò|÷Ve¸Ã8#èU;] ¾sP_Z‡Ð+î`cßáÎ<’ë2³ œÎhQ«Ää[b®„+BrÄ -Bš+|ƒÌUÕÃÓ7åt–bm*ð)3b5¸D¡Å»‰Wú„‚6±‚\Ρ†D­A&Áµˆhyº -?ÐlwfÍ!lº»5ô¦¨+”<‚´£üƒ¤ò=£f©UÇîLÂ+ Ø¥ ZW0Ý·¯¡dvRñ¸©/qîgÞÿº€fÎó³£ lµƒGªgWƒ`Áµ8­MÛ ,é“ ðÊ¢g(9[ºµÔ ÷=™KئkBÐ;̧»ë¥0xÓ³)Ú$!NtŽ€”88A_$%Ü’¿Òƒ Ÿ$âU@2ÀØâ±ýøúˆÇùÕÌej§z…xí+ZV\zù¡3*n”v;÷05ßZÜçºE -)OƒÍ]¶µÓ—´YÚ`5ìȲ¤½ÉšpŠ‚2)\ -@1Ö 'ÁòSý‡bÑ0A~`±ÇÞô @ñlPØÐîŽù&n N Ï»: /Ï3 7$A Šòü^[È©lAæ¦4Ƴ‚¤×:6 £þ' دéª-¡”XåºöÑ,l¨âàõ'ÓDµNƇ³ã‘–ÈÛtñáÚ‚ÜÉ©£­b3A¿ hé2´œÞ'¾­l‚¡hU¯”^æ14;Kõ’Æ?s-@/@0PK’¯oò“ç7\·7•ï ¸—Çc«,=M¥u™Üžh> â7]ÀÒ;‚_Òc´MI1lŸÊÉ^RdoZ̨â&U­ƒ—Ö?¶`cœ µN𼦖A¬$ÀÔ-'Á#ŒmSÊã€É²6`TÐ «b€A6Z‚‡õƒü&?wÆÓÃùÊÉiݘY§‘‘ª«Ñ».(P—a%ÿ"êZjv -è ÎsV¬Ûåpxæ“°Pø,•"À¾‚n‚É(Áv -Ø7X}4XÂ:%À‘$“ 4—BË úÀ^3ÀÞX°Û›Øé,yaâ’›ü´˜à^©ÜÉìçÞéwá8½îí|¥56NŠªùã¶(Š÷HÿA˜q…¤ˆ}oûB»áî;Yâãù%‡É˜FM6-kò¿`O„ë³C‚Óð#ú•ÀÅ¿>I€ßäó .MÀOÊsÀOù÷Ç9<iÀw÷ÉÚ`´¼·SoOÀç˜áo¸WêÜsÙ—º6÷Û× mÇx(å -l{¨¤÷H´× Þ0.,. À=h„w"fž>nÛäÇ¿ñtùúˆú2Ä„±fr@lt Ne/Áª Ä™»bÃ\m öw} †Ã[ˆ•ýˆŽ¹/Ñ[úAò–½?Y[«·ÐÀ3Û•j[¬ÆŽÔŽM&_"íÇ-îùÔÙx³~ º¿dsÐéö/þ†9üœ+Gþ“Þ`¥u 7Úy Ï™F‚ñÈÇâÈÉä÷Ëòù0òz…y3N¶»§Ö@Þõ!GŸyÏLÙavÚËOMeŽÇ\vV(“ ÕÜŠßú$ÿiÀgß¿Úñ×øû[ÀÍø'÷šðUL¿Üoï_nVu~K§¥ ­ÒæeÒVéRÆé¦Ôúø`Ü ¤‡œÒk*Ùr6 =l¶´˜]õN½¶–7š6ä­,Ó¢øf?Ï›–•ƒ@úw“¾ ùìþ¯Ûö×ñ›à£'ÿ7¸­ýINøŠ·_ßí·XYÐÛ×ßÀž"0Ì™ 7š#hBÀè·l` ª}` µ+0ÖûO˜)–úûüþWÁýF%œôÊz}³¾±ÁîŸd_£m‰~˜“²pZê=»hwÈÚ·1½*ÞÐêÊ¿Îø[ãJ4A|Y*R2¿Ð±½:Wžáå´Ï<ñS:Oâ$ü­ãõk]ý<Îÿ}ˆÿ+‡þ?”Я¼ü)˜–áò¿ÉÝÔ±ûñôÎv“Zž./K1Ú^è°>Wnýûioôß'±?€!4ÀÊxÉbl)쥺7×'k7àÎ -ˆÊ61ãMñQŸ­éáñ±Úäæo(ì'áù |›÷û$ÿ§Õÿµ¡æÄnùÅœ¸Õ!wÞÈÃô™‹¹c†îþQ¬Uvo¿lÇB&Ú̹³^êTµÉ¤’#¶â†¯é²¼–‹Sñµ](«Ôi³øcö¨‹ðÌLÎK#KØçê"ÅÎÊ5úy* -7Ü®Ýkm¼ÐM¦í¸|a˜÷åÌ4O_J«\U+ïí}·t²VpÏž†Á;æFhæ:RX Öc…–ÅÊsP¯?ˆi‹·±1:gÃÉ=IãöE}ŽÝ—ú\ž1qõã9Zn»×³Ý’¦;%rˆW—5æR‹÷y©#¢bÎ²ps›³Y°õϼ2{z+õ¬êg£L¼Í÷¬§á­î«¹°€×ÙšCzz2áêânLô7d`}‚Ôç^^ÒÉM”ÑÊú½¢1¯Ò ½]‚cZ`ʘz¸ÛyõˆÖçjp@î_ü ê‘g£êŽ»ÃÖµí°C¾Ù>­ÓõB%¦*üQ}xº_ó°_÷å’œM;Ón2V†w²æ°×pM$VŒBeÐÔçûA¬—›NYï™V¾“q²ÆæéCº6oi¡Q…Õ£4cT¹õH+­‡VTÒ³][¾¥­l\î°ôŠWYé5yO¥×´rþB6‘è28/^q§p¿ÖZÍÒ¡dÕµ^£Ö¿d­¤çw‡íËÆYIq/'A™J¦3y›&ÒÃT½´¦µµ9·4v°Ê§ëð¶”&ÏšÚáP•OLG¹„úH‰Tw!ß'á^î–wÙ$0Tꇶ eÕ¥-ަJröÅ|q1Q©ô¦—´&ÇN_˜ÁÄñ/úÆx·iq_ãÔÙm íµYÙK1çu6¨Xx]vùÚp0̶y£fAo(«{«³œ®û†¤*’ÌÄ•¨‰ëŠ¦È¶Üg\ÙtJ鵚R6èµ%HÞŽÄÑ,ÅüX?‰¨ÓBñp ¢o›¼Í*<½Í¹MíuãªæN檸ÃUKË·ém~‘ÜŲü¢sƒM£Ù­Ju·pÌT$i#•Þã-xƒÔ1×LÇ_{­1÷‰|zŸˆJÌ¿xÙ\iÀ jL Ñk[D‡—¢0Ë#ÁE…¦@ì Ɇ_6ÚSž«-·Y3®Z)ã¯=Ól£çx¬äAæLG'&¼y<£vZ-&|8[æ¬5ö_tó½i'jø§fh7¿æ¿.öoA4;Îf _;ºofÖ(¨µô5#Õsg«È@^°¢ãfYÁ-‹¼@²¦Âûõ¬Á3¢gs›w¿ÄUëÇ—S{`å!Û¸kVê¬.L‹¤F]}‹È"­?âÎB=çÚêyMõÂL“zžù õ\ñ[Ú õ]Ç¥FÝV¸]6xv.W·‹WÞo—÷ª;©ni‡sìc÷Xš3¹ÝÖv[ÓSî.Ð%ø2eøUŽ ¹­qúø`ùþžgè¤°Òøm0皘gZBÖcÔWзŮK·}|FjéH=oëÕ›•"†9rx¶c2×zl‰): ˆ)v©‘Ha¾ sS°%‡wxÛö£~/¹#ßÚûV£ÐxV«Bˆ|cD.®ŸÙüaÙYP|¯M’š÷Ò§Z±"¿IÒñÑšåcC0çkƒdZqÌÒ·g,ÒíÞL£Ò5K½h´Hõ®fÊÄõ89b)g7!G8µ#sÇÚ‹˜*;š(Â¥,Oá÷Äx…SgÅ©KTÆ=¥<ÃÍù†À‡m¼Õúýæ ßDõE®W޹äõ+œUôXÈ˃œÉYôî1Ê©ó0n§bú`X‚°_]ýUÕÆéò „mÒ«<’y0×­,н{¹¢ *‹£âån §8¨#¡ÁÏ¿¥•$ -ˆ -.C$ÌdÖÈ©WÚ´.R4 FýGrUÖ*>,Çv©úÇ|TUûQFH«Øˆnz=ë®TcÇv%\Ú”¹C\6éî›fHŒ²Iœ~¯pl3ØRXÕ¾ñÏ2it¿Ó3h£îQIÔÓû\/H8ÔfˆZµ¢`p¼ï§a=ËÐ3s›@Ö¸ú€¬IÀzHÔž»†õ(½ ï«Î¸1SÞýê~mÅþSm5~]À…£ÛVN`ÌecýÒB¨‘Œ-ˆÀ{ºµ©²­g9C —¦€¯FÂ-JÛ …´„!›à¦ ê›±àÛÀ*Àm;¹ð2†ž;neÄü§ ”Ê¥E1ß×.(X™ÀÁó£]à©§ -6ÝJЙI¸X~Ë -&W=ªJëFÏÛvó×|^Ä k¢É”Öu'Í||À‹q28‘k9’€³2V×l‰˜ wð!ýƒ@}ºÆ@™MO‚RÕ•žp>…ôL³Nðq<øÚ&À§œ˜`ç|Ö|nßÞW€Š—À‡È® ³»e¹™³J:’ln2ŠåDZšð«óÜ”÷2ÌØ{‘i##6>ÍÊ©¼Zð4Ty„ÜMK|j¬L8P<äX@%1ÁPOðÌ¢©Wtc@„Ðô£Á†•S‚w²ÔŠT@DFò÷ÊsˆêìˆB7yaq7„•9$(_þ+·‹Ë²‰Æ¿.à=Æ6sfv_0×YJwÌ1,-8éÀËÁkòƒÐÃí $ÊnÙEÏ‹¦?»K‚§¶ ÈñVd|VÙ†õª Èš_J0k²NôÔW ^w@6*$ ÜdiX¤ç/iK0 st6ù,¤–™’;?É# AÍ„ã7\ÂÏî÷ó_° -^Í,:o8zÈ¿$%¿õa¡~&OÌ›[OI¯FX³\ö“îç ¼ €:YJ‚† ¨eüÙ µœ9€l\@ ÑZ‚lœ`4Ôˆ:*ª@Å).A;¨¶•üµíU\’€²:@ɇ- ” -(zHÖêgöœœæþá×\=1ãOp±˜Õ=™K[£©$1žŽ°Oã|¦ÜV4Ã%Ýo#Ýi»9Nr 0šý”sd½•* õúSðã5ôÓX&˜\}¤Ðž|ª€>©%@/O}@¯F7@<Ðqò= ka²eQçmúJU-÷o‰¹¹€)C¬kííÄ–Kìȧ{0JãNëŒËìyÉÕ~r½½ðóoÓê¿QºTëáA‚tœ ×MP&˜ÌëÇî°ölα¼ë€Õ’ËšÕ½ÔŸ„`khu”¹p`Ö^¡bäšÀ+lÖ7çâ;ËlQBºúeß­(ÅØÈ -Jó-Ò3¢ã'M>#ï‚¶‚ŠêpÛí§ÄÚ¹xøÙÿÅ÷×_ÜÁð£'ßùàw“ <$XÞÏ©X‚± x^+[Ç€Çk;À½Óà_Üù=Ü‚7aÝ“¶?úØËE;ÙùÑdjœš^¬:&éÿ ÂYXŒ3²9Bê#2Tï—2€?à¯û×=mþ(¸ÿ7|UݹvIPyá0C€ðB â‘ D‚ýøÆEb5"é߀˜’ Bt·Ôw„ÂÞj}#>3ýgΜ­y@¨QnB1‚3y:!·‡4šjŒ Ôº¹¸ýÇü?é _ñö/~#>6ܯ ûÍä ³wÁÿÂ[s€,“­ÉÇ•³ Ȫf9-Ä@æÈ+y8? -bû£ôŠì-o…ï‹U“†K-ÅWú’°ålÞ^;Dse+Ș—©TU4¡?{þîô#ÿª¶¿fßNúßà†?¼Oï_÷k|…’S«z³OÕ4qÔÁåÔÕˆê%pAªNzs îòy îd“C Í”cý õmd&í‰M‡³Ñv”x4‹ÂÙO³T½ÓÿGðW·ýë¶ýf6|ñUp¿æÛ¿¥ÓþJ·ÿSªLOác «Ðûì[+·€~†"`€û ÔëS+íCÝo‘ÑÿS`üºm¿ñ/ÕŠz}“¾±…Õ¯ÑöOàÀú¨£óqS/æ†V^ÂuÆãêµxÌËRì‹wɸçV¨ž¹n9<5@»<å¶Ë£2ã¼)m½ þ&Ã~5Ñ_ëê§ôÔW ý_Ïïÿ_yùG°9KwÈåê×KÇïõÏ\|œêÏÇò¦ö›Ã5¿?´Ùþ²¸çÞ\ÝÀnÀÞT&£¾-Þ¸Mñ«kºÿ´VU˜q— Üj.Bz¶˜ÇBH~eÚoäÀ7áWU.ú§þÔÿÙÌÛO&KKžM+ûxz»)öpµïòÞ\¬TÙ›â¥RXm,Ñ]îs„¿§DmDs~õÑh®mÑî¬+ Ã黉N§Ù+½žŒuå4A{ùרM…øÈGÇâ°JAùÁ¾rIîȃ?MýMø5¯¢ ñò}’ÿ?öÚóFÊ$G'›ï4_/UkºÜgáå"DŸû¹¶:_§ïúö=«#x<3úĘè÷“ó2¢ÇM~XÅêÒ›w´AƒŠÌþ©Ùý{ׯ¢ÒÓλf×¼»…Ï;Ð0sj;HŽ]’Õbb­þÊúšX¿ù«_aôOzÃ7½ö$¶×óÉ¡ß,NÆò©4&ºÝòp“k4 Âû!ãz±àN»]ÅYuº¾í@=ýØvRúµŽìgì¢-Ý ùd‰lmÊeîódý®¥† q’m†êª\;pho{ÒèÚ›ký=Ý›õµêЪÙþâëZ­Çkp-®ǃ6æ†k:.WçÚR´'ù‹ª¹‰ük]íšç¦øƒt Nr;œÙ¦- f¾µñ´R¸¯*ÕPÈf¸ÚØ1ä:ÏèU—Êd~Ê2¹Wè½Q,o9¿Ìs5¿‘Î5½S§ÐöB³5*]Ã¥û˜Ì®•tÉÅŸa‹Ðv’)ŒëN«€ž‹Og’ãj¨ü‹YÄ)k_YÓÁËôäÓAé>¹&ÞFûÜ¥µqÝm3†³†™ŒvjãÜkU%–ÓmyS‹Žþ¾Y¿úâµùò”g*Å™*VÒS1ívó1_|ÏB©8pZZ%³‡çY}VúAâtjäý4ÚÍm:æ"W5§7{?$[B"3{šõ†Ù°ä¿¬ð¬J ±/æíŠ\•G³\Ïè—äxˆx4®·ÆÓ›|WŒ+Kïù ¹šÌÄ=å¡¶ÝÇì…Û¨0ö³º9¯—?ïâ.y_Ÿ"ŽÏw®ªãˆ½ïá„ÝÈÈ\ö42”¬Š&Ó¡Øm¹ÖcµmZÇŒÍ÷¾{6DšÐåê$3ñq|Ÿãvíþ“"ܯô£¡m·g˜¬´DïM4^÷ñ½V8ÔVåí)ßóÔ.»œv µsbßñòô¹YÍq÷J`‹ïb”= 꽬 -×G?H&.Ôgשּׂ®í,‹ž^ÌAuñ4mþ‘´¡‰ÒzUÝÕ+9xÞÍÏf&®éͤŧ«ÅA˜®ºæ2·Módv3âšÅJwÐÑóÑ6Ŧ¦^}Ó¿a\TH®èGĹê”2k;ÇÛ×ääô]=;’iuKŒm¾×LÁ´9Þ7Æ ¾f8’Ú4гÝÖÝ´Ý׉{uªùfc•Þ,ÇtÕ¿¾~uç µÑi(Ê™x•påÅò-ýÚÈñc„Éú|ëËú«;NpZ}1P­ÚÁ–j¡%s”Þölµ¬ÎS~ø¨\[!†ÎÞÖs¾igîx¨þ æÐZI†3ͺ»”em¥f ig²é­Q*¤ùEÍW^»®Ê6VÎñ¶§´ 0•o f#·=ç*=¯=XêµaAJáÕ¼8\B-a*KarW`¡Ðý/nQÆjõ]ÃÅt5½{ã„É&*½êWTÊœN„8R¯¬˜»C’05¡´P$ÄŒ€/³~áWʼÇô›Üº}ìs“Zpì»tþAغ¿FX‘5¦ÙÙzŒb¹úJ‰w:Úd³ôUÍt%lÅF"‡6ZÍ#ãÐP°«›XTýØÄ(7¿Z=˯¾öÚ̵¾¡†>K'Gì­¡ˆÂÕ”€¿Ô¿X¸8OÓ.Í­“á6Çñ¡ÊîžË [ï] ¬èqUæDc¦yN¥ìé÷R´¶¸‹TW-)óŽuÉwgy&îà ßóKôƒPž~ÑöêFxíKåßp ÞÑ4ïI0Lq6¼ƒ|‹vÙäRí›…û±ªÕ‚0£´™4'Á(ñÞK|q\tz±õÇ!ÅŠÝ'Æ„àI1ÊšOîbôµ¢§éXkØ´ö˜yÔc5©nÉP–<ÚPb_¤íD ãlžpš“˜ÀT÷€Ï‘P%°|³I8£×€_S¿ÕúšOˆ,×çéLáOå¶·Îÿ E2ÂáÜé9=fª52¼Î«‘nʃœÜÛæD›qW]ho<ôõ¿i=Ý‚¨n§…%ز”å -HÞ$g¿HÚ­q„óP—pöÙ%5¦·ŸgL'Gë,¶r -!VøÆœ$+KZ[5¦}œÜïÑ~ù›Ö†¯ÖÉ{¦X>?‹Vi¸´ÅÂj”Ær-þuÍÀŒ?Õ7 *T¯ò©(gÕ¹&”ö^2¶dOyÑúªz§¬©þ ‡„ú&á0ƒµDέÅø¦Ø”Ìáóº[ÁÉ÷ ÆVƒ÷+—+ŒeߺÝGZë!*dž$oT(s ´6Œ’™ø÷G$µÐ(Æj½VžJåô¼’ý㎠-¤}Ç©‡U€ã…^Ý—:jÜËÒX}Zü:¹<™KÓT¦?‰É’¼8?ñù–…pJplõʱ?Vî -ÆfçtÇ`^¿…Önû *t´Ò$fÒ$=‘/T_¢Õ -Žô6_Úû2"ßëDÞyýVâq¬ º«—}ÿ~èç]À»u/Ù‹m¼Òoso6Úaߨú±Þœ¡íðÜã.ÑlŒù\÷‰S›Û «`«;ÆîÝ5q ­+rr`®9 9Ž{ÒtÎ.¢j3Áx _ÞÜÖìÏÿ+¡ÂiPg·«¥ÞRnšzËœz+n2}´ u–Z?lïˆN£È;aU,¶«¾9_Ýâûj9b4á³o¡›^M†oÜsªôŽÓ–HÞõ{¶î -  1þƒ.;x¢{ý|CNòðŽ4/ó7¢DGQbІ¯NN†c¬eÁÚá\‚º -@æÕ¤Îl›²q5ÆÞXèV)%05Jp~ôçtCà`§nr¯üM€ž»Q%4ç oл{Ååµóat“³6ÞǾÁ é+F.ä¼2ù©»L{xO““ê’™é - RÁñhò„žhï u÷;4™Q©Ôà‚$×~ÊŽdŒ¹àãªS±³¼“5vΈQû0÷W§áŠKsù.5ñ° -¾µ_6ªFo¶Z:›Â”H½L'=fÀk æI Ÿg¯O|ò£QOðèâ_'8> *Ég¹àù‡. 6ñ—D§Ÿ| néˆFûVÛÙ£Mù~…æ\ÀÙa'³Z†¨K»RFž¡d<Æ7kµ5“:k}¢Â=kh„ópãŒTaj‹€òž lL”ʧè ;ó× (m%ðÆ .@¡ (,%òÙ.òåŒ>zò‰K¿|«€¥‡€Œá «iø7\âSbî7 ¸1öl˜¡ËF#lªmÑ´Ès³ó¾ÒÞ}L9ð;DJ)7%f=]_|4Øú8Y®Ÿþ±»¨›@,ÚÊ F=@gðe‚àh%E$h'¯PÕ ™÷ÐÈ2iü£“|¾Ãj -¨ÅýZ¿Ñ_ð?iÀ_0›xé^SÐ¥raCsFÚ}Qxm°–3 D©’T›ê”þ…o×Ï\¢di=ª$8 ^-ÀlØn‚Âä£ÁnV{ÀlÓ0“ ˜©•KðŽÓ&š4`¼Bò>¶»L:濹Àå^]x”æOïä¤Ûð4SºÕCíÞ"‹²'ÒÜý “Ôb|~`—Sgóƒ ÙÌr’¢™I 8£Ü\!ˆWkÇðýõ_vF€+ÂsÀÕõm‚ø¸†'ˆÓ€+q~‚ÝpÙäsvYù(½i«8Î<ëX€} uoj#Ï‚BŒ·Ùé>7Ô¯Úµ¡¸PËá¯KE¥Ýâ–ÀU­tGìZs¶¤?ýãø:ÉÒ¹1ÿ£ßþƒOzÃßß`‡_\@Å;2U¥7¯’‚O5¬’MÊ-y?Yçx ™Äa)y­3,Šób³ðÒ8ûIϯbE½*´´TŽžGUO+8‚äÊÂÔèîOüïS9þo«ÑíïöïÒozÃôñëÅý—¦ËpÈ7‚÷ム_Zå"²—`sR3@jW’uÚHÝ2¤ êÉëìrv¡±7¹Ì4 Ÿ‘(=Ÿ)âš…Gµ-‡äìß`ú¦7|ã¿©»_ô?Á ÿÉßý_¿ñ·ù ” ï„@…•9PY5}Ô€ªÍ“uüø“Ò¬ -3¨b?ªt¸[5w»ÖRNg(‰Œ²ù^"”sCC§Ìý‘’ÿ€ÿª¶ƒ>fß_ý6u³¾"îg/Üÿˆ·ßЄ¯åö’^=5¿]iñ@·]èåüèQz\(½¦O€^gþ/è·íß²^höe#ÈÓ‡9Îmÿ‰Hß®3.ýº÷yø²”òø%ÐçMÚãÏ\§¢œêïŠ~<ÙMû ïp-\‚ƒ¶¤’±å¾Ë”6»p·cuî|µ_| eý>Éÿ‰\ýêÿÁö+/‡üý ÕÛ˜hd®Åmµx¡›nåTªÁr SR|ˆQ¦·ï’Øh÷.ÃÓ]v/·cl63å¼_/õÕiM÷¶÷7š¿— ôˆ-N¥7¿V(m®í Å™yžDÓì#~LÆUJà¤ÇÄô ~÷ƒüÊ¡ÿSêßżàøq%*«ã™k·có~ìÚ½=ŽÜ¢»Ú˜UaÙ€meâº>iÞšuyÌž¾¬0Í^àÒŠÈÊ2É?ÈÆxf1јò½‘èÓá¦èn{¿zˆÛÚWNÉ„>VÇ•î£3:|ƒ¾ÎÚYW?Ïôÿ+kà#5OÆhö-“õƒ¬½ë!^Š£B{“lofîðÉj¾–ãâs½ù©ÅyXE§Aƒì¼û§rî+»ë]몧\·«vÅ®y[jìklµÇÙ¥ÓFÇ×Jìât-½üô‰èÍèÔâŽ46T¡ú/ð©Œ\ûµÉ¯uÕ&ûßb^{í÷Iþÿ%‡ÚúÈXfÐÀ{™¾² œžvü®y ¨[hý m”{±‹ä†Ñ²˜›F>Q^µ6~iÛâ¶…CØrçP<ÖžÍP©§‚kÜ!í9åÝÌݨ¼X‡æ…°æPÛyuVâUW{•esÓ¨øÊ¸þu­þ þ¾ÇûÉù¥V_=ù[Çk -¾ö•CÿZWÛhFDK'¤[UÊåÃç(ÍP² N[vãѱ - ómyõmUëÐDoÔÆ»UCvçã¥íAeYµ'úà®ÊU9<ùûhööÅg2‚ 3–VŠóqÉ}Ì•‘Ûõ<ê³ú/ëªÍŒo ¿öÚqñ~“ú§Òïš§Ëûk]ý¨Ö+k×TNàØèêÃSÝNÕ®5-¿ª.éÁŸÍãåM½È”¹SQðj^öNí¼æ…Ÿz5×~Ñ.éP±àvÏw-¬¥i§0®¬'ô€ìWÎ'}r~-ø¼¯ç -¹Mßß䪶JäxØbþbq*¯Ý±[ѵ¾š¹PhÛyFô¬±û£Õ>îý:­ *>à ý}°y¡6˜–b«»r»¹‡¤øž†§¢M„·"´¼ ÓƒœY­‡9®0¡òËæ„ËÓ×¹œKÆ—FŽ{¼ÝÈò•ìiì¶³*6[db¾[MÌXÝZÅ·,±µ·, æût{—~)¼5‹Y=¤;¹FŸnm¯Åwp›ªû:,“æ-~C&Ë¥Ø\ÕŠïI©V{rÃqy$Ì/¤Û´^ýwí}_Ø@ÖÙÓ=dU„:g®SùñƒdtBKY]?‹›ï­Ç™¶¥Mèx/Žšnê³voª»¦t׉÷SÒ–#º£ùùé>ÁâðŨ|99Ý~³•ŽÖ»<ÝŒÔóÐs_)ïÖcï<›É¨Ï²…\Á¡š™¼/qÙw;e³§þ8Ÿ¹ŽãbFÇbßê–âšù^ÇMsP·Mh?èãÆrl çå\Ÿµ®[¸¿Ïšo¯ôf¨béj.–ÔýôšS®þ Êiuœ+a¥ •UðûòõO“¥ñl $ßžö¬ZÓÃÓÒdƒH…«>ö<ùŠß™¸}ÍnpC.æjÄC͆)XÙD0å²hŒë9Å@O9ýÑ]5—Ñ–í\^£_™bzÓ/•ÓÕlÐP÷ã ¥6œaO9ÍçE¥kùºa®²Î©LDÉRÔ‚ø¾.cÑ6Ì­h›I„Aºš ×ù¢g¥«Éü%f‚‚Ñl«y¡ŽÍ$´|È‚k©;éÏ 3ËŽò»Ù>iV -]1VGHm¹©céMOÁÔýˆ&U ¥Y%t AQIQ•¯ç¼%ëé|Nz"‘'YÅNí‘€xLš¾†"œËlgÚºós)Eñî9°x2"\/8fãsì»óN·¿èÂu·Ø:\ -Viç¥ÕpH¿µÁî°¶[;þÊíØ¡Cù?ˆõö†Q${ŒÆz T9¯Þ)ùVïéé»)ÉJ¦ÓhÇ´˜Ã»¼/fŠP gº€]9~Þ¦=ž|‰unµ¨w9–ÍØí½°‚Qǘ#ÁŒì·«?}aÕ)ÝÚ3îÑÝz.ZôE?FíùÒvÃK¥”mäŠú-‰æÝ7(NÍÔ>_kѝ½Ö/ÞYnMxµéx)ÙØž"¢?®B!Ø'‡å²ùÌø(m qk \yN²ìŽ"E¶¶V5V¨¸6w–íPG$ˆ(^x -;Ól”Î̱,Ý™&›ô¥žAèH,‘tú=©ûà S–ÿA(“ukd¿ÞíY‹X£¡÷ òyˆÃ§ûN/é.N(Ì'l;‹:>=½ëÑnkûMsVÎזͺ^Ë ¾dëe´àÆ5§”óLj´ Ê7Ý$-£ -wŸkR˜…•;WY‡FaVZÚ[ê~)Sé]È×uõ"ûãBf ,MBBA&FÇ^†È÷ŸÚz /n»Sœ¨ÓWÌ{hŒš9t“»Ç?ZåW´*À&º©k½ÝÔ#±f—30˜ldú·t,–`§G8›wéaÇybeåçóŽ^±=_PÉ”†½ #áèÍ6¢ø@=jÃ59€Œ 9r2ã#òsaO Aá– ðYæHà.„ð8±ÉزÚ+bôó ›¾5B«ÞéŒòŒO"û;g#Î:B$«}B¤ÌJCý°†ì_ƒzKyY•ß ŒÝOFã¿>à?¡œš8¶ewæ¬wîk{»_S¨kK¹—,òk@ Ìy´¿PVû°%G^á®õZaËá}‹ùêœ ÷Â®Ž ›ÇšE«sJCùR.9û(œ¤q凈4ásÁ"à–ðÎÀê©B·VaÝ¢@†Õ«]†[t£þ›ýÉÀ¨ -Ê.ç½ÒWÍÅ'çæþWU´Fxi*Ýü¬˜ô0h¡­ñ™§˜ˆV^äp -ïqrήÐmzº@«÷õ姯ÊÏä'rpó0H‘Á]…Ï#9·*íœæAº=Ãdda4õ‚ºz*Së©~_ý ©Œ+ …Ô ³ëÍgÓoüf`4¡‹ãõó„Y\Ô1ß”RX63š^ÕBŸ¥Ï-êS -‹³Èr 2âèÎXÄ´ï16×È9®Ùœ~æûrñu‚/$õ€[+†Ó­& ÝÓk2fˆz¹¹J*sžtS©ˆß€Ü£—HOWÒ‡ª ŽK€ “Y 2œj3¹ÁoÆå†¸¤)ÙdtAt%w%Y2›º¼žF×é‹ ””oeüf³j9(›·aÜ;‘'´Þ+íàË>µƒîËÅ2™é1õº]î©~‡€R©7G€Ñ²"”é˜eñO½$4ÝîTKŠj z¬ÔÈÙd”K6—1 ”Ý r×°UãÒ‡zYÛ -þ¯ ¸j¸Z.¶ -l&SW¸Òš4ŸHSÝ x!{È%}òsI$Ø(Vè7$ †gè1“O)^€ƒGG€.F·@—• ’zFr ʵÏ@/@¯<–à‘è¶ßè®–¬ÛÏ?ê(:w“-çËN£XãjCÃBÿÉÔê¿>àƒšöÓj‹L©cz£H_”Ûšˆ´LläÌóTîT‘ñòÚ!PYu_p»t¾§r¤qØä~XûõIÄ:ì;AK0åÖEô¥b‚C`={’àšlÞ)€…Z`-¼ °ò!YW%-€9ûÖWY®ûú²W9kzÛ“ p)tê;Aí”1»×½¥-rÓ$Ò·ìã"͖Ƙ—w“~“±dêiôø°)ø¾mAPjý||U~|ØoEP‚)ðò‘x…•Ô² Îe€W­v‚Í'«¯e/<8€§.À­òà+y¥‘8“í'-k[ý=*ßNï×\Aï5G¾k¥lî|Ð ¡¤áj -›]„m›°úiRX¥çbûW>9/HÜ yȆÉÝr~bž‚Ñ£ð2ˆ Í¢”ô’k®ôtáÙõ› |s{“DΓ‘—+€ÐÁü -þHZý¾Ì?J/H«GA™û¯Y8úú€‹\5Óú-àF‹e5}ß=0iQqÏ\D]&4R´ð]2tFÓÙŠ§À@Ô#A^‡4 7+ãƒô54د-¸ÆgT½§ ëÙi‚Ýnd)e23 ™ ·€TäÍX¯ H´uø†LT¢¾¹ò¡ÆlV"îÖØ Kl+‹â‚ßgy8FqÅœ¨1š•ÓB£]·!Þ³mVU@‹®ò„´ûí¨€:U?án6Á,¨åÞÔŠ®'¨÷5¼¬5*%Ó¹qŠý8‡Ç½#ØnªÎU|ó€Ê  õñ5—-ÅÛyNZ«oibÅN`ò3&¯µ¾$Ö a­ãöL2üm§˜¶™*`*Zþ‹e?+òbî41ùUË—4“͹N#Á: /L´€X0øP a{€!‰`ÀýŒdÖ: 7« ;0úÍNî•Ãìôì®VïmÞÈÔúV¹÷®¦_F++•ïœÀ¾Zs˜\gŒcòÅ…¦pîÁw» ê€= - À¾ÒÁ¿ñ]÷_ä¢Oð1øz›Œÿ©ñæÞ<)ÀžC °kîSÙí¾ìx¶=t[“ÝäȲ™‘ëáÕÑÕ‰}‘Y„»¶®YÕ²ìÍ‹{8]Žòa9õƒ`Úiw€Ôiþo÷ïÒ¾Kÿ7ü&:83ÀÇ;À¯ÞWÀ¯m8Á^üÆ+~Ëv?é “Ã ðÓ•øÙ²ø^ øæE¼Ž -йßgghm¬_c:T\ïPâãSQ§Væ“ÆÚZï „þé@lmÖÿ.ßö•nÿÒtïòÇ’ûWпa Þ<.ÁÑâ=éô%Tš  ÁÉ~ìñ½}ñ|H6ÙS{Üä×?ˆqÆ×}›VB+’ ti=S±-ãp‘5Þÿ¶ã¯Xü7¸á›–ð7³á¯’ûWÄ-™§÷¯ŽÊµM 8X)AÜJ-J£€Ò@Š@ àPšþ?öÞkQqeWý‚ñ]8l‚M“19g0Æ€Á`âÞg‡ûí×6tFwÏsÖ½{®yÆxPÓP.U©$YR©T|µþ[`áM£ Çv|Üf#»Ó¥Z­éÜ -ßòó<ìÜûf§;¸àïÁb'Ï÷{÷±f÷Э]>áÛe@ú= & ˆÍ# Äñ®Ó+®Æ- § êSˆ½vˆ}Ùø êT´u®õ2C_+#ŒjÇkrÞ;B™ùÄœ1¹I”û«}λe o¾!;¶Ý?åKlXÙòã.±1%•݈ŠÎë]êY_e"½†ªli…6“=-j«…Œ}·jß*Ã:Çùo©«?ß?õmKBÇ‚‘‡äüÀ˜„¼ávKn2|Ý^bc&ª½K¤ -ëØŠ+­’[¦¶L›®†FwµÖBó®ª'æCÕ7Næub®*f~m™«½5—Y×G@ÓØ%'ãP,0AûÓ̘˜””QžHàÎðz‰¢œáÝRWí<ÑÇôZ;7tWÜqÚ–ï¯×zXÉé«1Ëï–D:¾!ª\ç¼R€g§bŸ^+=…^Ï×+³aL ¨Ð(ãÑ=ñJC÷fz޳¯Ìâê}~“è÷Z\uÞ튓]çÚÃÑNÜ5 ·Ç’:qÆõ†8ÿ8ÑÚ[Lô[òªu†ç„Cjs#±/̺î¢<…j¾òx$£<¸v‡ìÔ ¬…Rúüj¹ì‰Û±Þíòc ikp°oË -Î.¨7qµ%×mÍ’²E̶©fƲpŽ6XÝ“«Wü™F­.t'U³KªA©t“ÇŽsQ–Saà{êêý¢¬Ðh¶3‘¦]A÷§pèÀ7çü=Q_sk£/t N?ÚF©©¦Ê¹&=¯•ì2W­W<9ËQ©¦;µ:WVÍfqRO9¥rêÖ–•hn˃DÿX†¦*ô†”PÕ å C EµœKicÛ*°ÇÂ6¯w‡¡|%Šåò~pÎþºjç'/–ÁDñV -è¬|N8´3ŒÎ­YŒ?5–ãR¯0CPã*bW7=”ÈòµŸ`Êq(e_M %TI…å •ŠÕbB*æÙl¶°¬¤ -v—*ç+B¶‘3:¥^Ž¿¶§ÙV|±Îœ¦ð9}U2=È+¡4´ŽTR’¿xvʬÚ'ë›,ÄñäwÆè¤×Ù‰—îž*´«¼ˆ¦“ºZ5k…IùÚóÎK²UeBík…e©¸.°Ûò.ï3sÇ\=(ƒ¬Ù“‘lЕÇ3Ýd†²c}hÅ“ddZÈBj\°œt=¤ŒO-$ÕºYOÒGv”`¯i=^‰/¡¸IbõL¨ PÒÅIXšZu€S_w(²l7B¢®1Ò·vˆÕn^é‘5¼p³H/gõ¼ood[ÐÈDàU;Ç'½”D÷FÒLîÍ$bÓ[$Õª¢'éýÄH,›‹C‚=-.ñJD³Æöx¬ž¸0Qs†óÑ ÉYžx¤[(äÃ×õ¢Žû=‹0´S@hܬ‡BRP›†ÐKÈt@pÖ÷îÇä¨é…xÆÓʦ ¸¶[tw §f2q…êy½êÎg[áT2ÇŽ’4+vRɼ?™¶ë*b.^ ³Å˜1`+±Ì6¢­Û‰q~éæ|ãH„ÏÃ9¼ C›Ø64®!tß‚‚qF‰j÷Ý®pJXŽ´¦àÁ„E@W.H ’·Œ¿Ûœ½!?©p ,ÔAf{OÑJ?,íäìÞ«ä«­S+s.+¹Ô8›“yß’‹WBù@,y‘Óì „¯‹U( ­‡‘ä&B¨±È3%$›ò"æC›ºH_ÎAŽ‚v+J*°æ¹tàtáÌìç‚Þ|Ê2 ¶¿[ïëþˆ0å|×S»jsß½phjѹ·&ž»XÈÇ‚Îr–¿ŸíT KøÝ|’as‘Ø.d#ÝL·Fæ"‚³Ú”ÕV“–½œ[ð¸rÞ€>ª~´äY'ÆÈf’3Ódæ%ÿi½oø#~Wß7hx羸PÚ½!Þqgx1 ðžÙp‘õd’™‡ÄÄ‹‡Äs Vï«èŒs«H£2ú« ;&åü¡r¹Ý4§¡D§Úr¼IÇ"Þï M^TÌ·¸«à󳵞Ϝ©Æ®\ÐüÝRóG¼åÔ$ÖçeÎ;nÉ¢W -vcžYoœöd¢f‰UÇD›ÍKé³T¦;FΉ„[_ÎÃîJ)Qqû}î%mìü^zŒ”å‰ï\P®MJ½H] ¦|y¢éb©ÝsåBþìö‡ôZqW“ÂIÉãR T{®×ö‰{Džm}ñhfã•’AÃ3›LO&·ˆá/Ál¾QÂßÖ˜°ŒcµÐ`.”æÉS¨y‡p6º«îäZK^ü—ó×ôZO\t‚!yX*ÍG·E‹¬ØEù 1«üPc<«ÞÚ½i¯öîjèp¦wý£‹nä‚Ü!–j·  -Mä…°œôh>Ò!†‡™e[‰NÄ…O®KO Ó9Oz§ØÜ"-pŽâx)˜nz«“D5枊%Z4¼y_a™Ñbx”ŠRQ‹7®žn$!ÌóÁü¹ªàæÂ#äê¶ô5[DI‹“Ý\):µy|FijNµ—ÜŠ¼x¸-Ù;§Žd´Ws#W'ÚÞK 2ÂS›DÇ«JËAžF-—gT+e¼–íDdQÆXQf˜DÑ"w–Þï&+£ƒ’eôäÏ‹—3y/³Ûšê±ãU†§Â¥$®6ƒX `T}þx‘‡=j -2˜rç¬ÐÂ…‘ÑSHŒüÅ!4r|šè,ð42Õq|º·ÞûØÎøˆK/ÄOQÝ<xm¨¡k°Úì#èÐ2‡®so6rõ¢ÆÜ¥-ÿt(³&Hó€Ï37€/l ø(BèZ`§„ìÙ÷%!T&šïÄA¼VK¤©Lº‡¢\2˜t¾“-Æ<àË­<ñãa„D‘Yy\ó%pDð6—ð@EÏŠ.…Ü­2K“c¿yÁ–²F¶Ã£¥Ç`Á(+®¾‚„6¤8S"CÖ(äð ­1 -,“Ýcb SD¶@cY`¸ÒKé—ût €4ª1'²\Á³ Tܪ¡Ü’÷¦©[p…:ûã]÷‘ˆÌÐõ9X®Õ—N"e™Î¤^d«r)BG –!2#àBõñf ›MïÒu×T€F¯3€ÆëÏ.-ÞY`th²)†Z -Ãû$@‰Š€&ë€J@ChÖÛ1@}{ þsÚ‰,ßk`¨%97¨n³·,àDâ‘‚ÀD2 ‰uºµæ!Ó·ÞÈ,2(ºÙh„œP[MwÒê¡ÜH“Î`¡æ`ÞÎ`xß°Àö0‚E,Pd,° Œ e,°hŒŠÌ-pº pƒ‹öydôÈ)=E)€n¤¢Y.ÉT¤Y8«Ú=x'¦’X&‹†?œ?ûÀˬý„yì³u¢ endstream endobj 38 0 obj <>stream -.ÓèâšbzPîz@áÐÖ}v!™Ðà Ü©!0&ÀïàwØ1æ²@‡´ÀѰS4f¥°³¿ku°K˜AˆS·U€-&Öw#Ű–QXIË•½ÜÍŸÕCók5ൿ™O -óeòV¸–kþµ×Ó›öÝ‘vD&I‰¡ÆH÷Á§Fº’&‘iiŸáŸZÿ%5@³£­Gà{е@V´ÀB²çbª˜~ €¯tàëN à³Tà=ÿà5?ð|ºðøZ+6FƒaÞÕ »™y¿Ñ¼]ßv…fR¸§|NñGŸæÛ­˜¶žìQ8Yë‚m&Û8BOî`n@¶,•BÊ!2ÂéI(1·´`âÜã,pŽâÉY@mâ*Îàإ@h¸dócõlÇ“ÛË *ÈÌ"BÕ,tŠúÔÉÎÐóM/i6´J4ã -ÄE3;òpé9zõð¥öŠF¢‡—+›ÒÉÍ“²ÐE@©nï X@åN 6ùwóˆ„eq¼Z) œÊ€bb} ¬W€bs Ð‹P˜R䥮ÒÈØwÏ¥%@ŽÝ¢CÉ$b‘ífóTe©ôãpÀW´««“Hö³noe6¸¸ã2·$<¼ÚCƒ‚R¤à4 ÜÈ5úèµ¾ƒ[‚°ô •Ý¥3-Ðiº‡Øµ;èjièE:7´šç3U@'ø Eš´Ï—€ºú½y¸•ÛdÜÇ®šˆ²H+¼Òº9Á!¢¯ÔïRŒËç>«ãh†²•>”ª7j€‰®íúÉö?÷r ß 78ß9€›çû¢7oË©`‘‹v,`„ºVìÆÃîWº]AÝŽZï$¸51–¥¢ü&Ù>¯Æ-•¬‰aøœò ås¥H¹¹ í¬b (=‹ï¿íaû~sÛ‡Õ,`çô~ûâÖ!ÇÀÃä—ï€G Úu–ðxÊ2ðx…ð8,ï#<.ÈÂgÒ(`W‰¢ÔÌËZŒð3ý 8SË\ÞM°Ý–áCÈå! Ý²‡2KTû1û÷!xëix¨Ùp+Ü`Guíš¼7pKÍ-€/h Àâíl[_j›¾t£|™ˆ|Y¿øb¾"ðŽKàK,ð‰­Nl–œÌ‚¦kñ+xÚáQ”Zè/za5ÊÎGû¾[¼Ø ;Qbs{±££ß+5¼¬Ùþ±üí ¹å‹|[K¥·™Ù…wËÀIp»Dð®üð´#½«r/´K»FìØ­y…f4Cçx(„…cRK¿¥ßÑ;H|N”ø[žïÏ5¾Wl°«7·aàÛÄÉ­qy6âdXè~h…VÓQ-w¢ãv=Ø™T+°™™)#?ç纬´èeev’—Yx³ìLœ1šŒE]™ =h3&FÄiDÏ‚øP&Â@/Æ*ß&{¼]à%žDÈœsœßI]ý1ž|ŒMä£sÕ¶viN××Ìi¼œùäÉ¢x§ªotX(⢷ž^ËÕÝdHdzPô:Rc"üõÊ,žèY3ði¬·_gi®gVX‹ÇzâŽv»‚˜î\;éR'vítÚãøViÍÒÔ¾E¨}º™g5'LÛýñ¥ShàµSW¿Ç“Á9áPÕ7\gf]7›$÷Ýä2R#i(#þÜÀ§brϔϵn—Û´;×æÎ²»‹q[ iBY´ˆédÕTÓ‹m“VfCv/Î vµ‡êï…¨uÊ[5Û\¨*^ -™JÖ˃”{þ†”!%‰Aå[ 6ãƒjNÝU'uu¯²=ývQVñ,œ@ÞH O¼·ìÐS%It ö•lÍb*ÝT¥™·!SÝ@½Â¶Âµº¿¯šJÊŽŽ+¹J7,+ák½RÄë24©´KãTwPBç݉œ¡Ç‹¢*/·…eÕ8XÓCæ+AYÈÕ£j&kŽs£lïЙ“ÊÅðCêê€ìvîñd§Â@OᔽÕ]•Ii]«û°mU4WFùÚ뙥q²p’g™œK&Åo¥W‹l5Šþ|% äŒv:˜«‡ŠÑ¬Ù·¼à T̼!#õKé«Òo¤ã”6He|™B7©³”á4wRmEIú’_$–‰º\µA\Ÿøig„·ôZ§Ô.›y´«;©«åkwÝ+¡Óœe'ónz׫ê W[ãl+bõ4¬h™RߤéÊ. ©•cJrË—ºj@RÆÛÀ$b;¤“y~èI,[>ÁžW‘x%zNÇŒ±¿ `•Q´•5·‘Ó2ƒÛÑÑR8å$¯:y«ßSa·_§úX{Jì¯Y´F·ByzPÎ].sT éA*TLI4"K„¾.'óþu=±l¬Û ö¸¶æ¯„×ã¸ïºWbõøv À=ÚJÈi~9FºyÚ¾®x"÷&½¡q­¡æ>Ì„¢=Qí[fX>^¥D7¢7„嬶þ›ø„ë$Ȥ5—Æj,ž«ÁËyY„Z· {âŬÙ)Ié4KÖL,ëf8^ uñ$E¢A,‹t³ÁTøª²áA1.‡!=R +R#„î¤NpÖ‡_Oò×µ7D¤¯3CãÆ% OI"à'2¾ž_fø“îq­òÅàZÕP„ ò±z¡ZÂߢ:4^k’§S‘'Ëš(²‡}¼’Ž·]ñ¤Z^úâzÛï¶+$'îH„,0ahíeB¨x‚„¹÷‰ùà> ÒçKH£Ç¨à)P‘\™€£Š|=ËT9SãÚ\«˜ùOzuáø•½oÐÄQ_\l¼ãœóJÑÁÐòÅ0¨åòbð:ã™M¹†:É ão”U‚¬ ã"ç6«MžGW×]‘²}—¯ÆU>LV±0´š‚`†O»¬!Ñ®€>Ô]¼1@|€è¢\+ßµo˜ -ºÇŒ¿[ûü¯ð jjÈçÍ„wܺf½RÈWñÌú±Ž‡„j3Vw¬OâÌr~ 1r¾Q± ¼øÒ­ëέoø¼ÄBK"\Í< tY*Ÿ]oHaãZÙn»9KÍüÁFbm"™è±4‰J› -’ýìÙ^zË×Ó¬ÁiÃð]7õ­/ÎUöÞqS>z%±~õ̺ Ø“‰ôpVÝl>±ò3Ëé.ÈÈÒÒÉn}(¸ýL¾IkC¡ë•ä‘2wCµšÅ$ r=** äéJ†,I4å¨öB(S¢Œäw£™†Fª´ +]'½6r ™á¾k»…Ò2u}CxAÈmý@ªhÞqã xfeΪöÅ*n¸¤1rª°f)<·ìdvÉÕWîZ¾0¤BûQ‡@òצ\ê5,Û¯70*Öí 4¢…åin±­#ko숔.c ñö¯^x›ÏÅáš{[†¹ca -5Ãç#$‚¶Çu3®ŽÄÎÞW'E£®ãDªW€GJ*ê‰<{Ïg ~ÏkÑØ뇨k¿(p£L”ëµ9ë0+÷N;7ol²¿ŸwðtüZG‹t¤Œ”Ng)c~ËC¼©kîÞV—\‹Ñ&ÌC5å‰^(ä:^jEW8vìƒ+ÜØXÙKˆX$DΆ¢T@4ï©Æ†x´ä†’bþÀT¼NpÊ]Z«?PE= -­£à2|©öûA’‹Áí€wŽIÖ‹ÇÎô!Y“ƒyhŒ ¾‰²1¹ -o7dæ9¸™.OjŽâ#H,ÖU׉U·®pº‚k4N˜6°m~H&2ji=µ«;u¢ €ºd@•ë@5µ@™«Œ L\–—¹P¾-Œ¹[5àRx£Ó]dâ®+A¹Â5-îšå ø!ìÍ%5–õNTø ¡ÛúxOLr©æNˆmĵYõÔ\§&¨»ÂÇs " â¤ Ef  qÐ>bé—0”ÌØ-°:X:§ê0É]Âö=#Ðe¬`—¼Ý…wAÔÀ-¸¤SX<”Ý"©øR zf…n µM—8Ä“Þ< -0µÉ§b ¾Ç³ã¨‚z‘dnÐF×Õsš ÞÙÔÞÙ·–÷5ŠL^µ€j¸€,Ðe\£#èË$&’g7V~7»E·š€K}Ài*ZÎ2‰t‘“ûñ\_„B·,àíL c—>cÉKo º¸E£@È;­û'‘AÖã 2úÔ_â$š¨0‹ª·® PËìƒD1ØêiøèYŸê3 èk€`ôÁ2ÏÕg w1Ÿ«jÇ“}m«‘?~çB™e€@këá|,¥JRÊö­µ”ƒX6šÎM¤@²v*1±þ¥‡ç»ä^ÜŽýsþRF:>ŠÉd˜]¨9#ð|:´4ŒoSZÁb¹3qAØiP×xýl MZà´È’7,P³²4I€¬b h)€¬£ œ­&úˆ’d¶¾p;€4RÖçâ"¯;ÕBÁb®lvœL'SËI+”0õž7÷Nq§Ö®` …F-O™dè‹°HS˜GÍŸ‘@o¸…1Uz¡'Ýú¬Osqо¨´–Ôíl­s°À h=ÌX`hÛ³€Òh3¾h‹Ä*q€¦ú€†Û'€FvI€r™ºìë(å|·-³X²cgÓ/c‰6{ÄâGŽ É‹©%û‡-|¹s¹Å4Wp†J†ç"Îh2…4á€zZop¸¯ì0Ò6­ÖžoVÒ ìM€¥ñ«Ò„æ~€e¼q ôìrž¡–ö^,&Ó ˆ)€ Ì`, ‹[s9›½b}7ò ]¨d²ÉuAÚ"t*Ñ6óÑ(îc§&™e{Yt—‹áÈ{€eQ@zp× §ÃúæˆÙÑúoÏ®9Q¥ p´=—lÖoE9Áã. òüòkFpM„˜¨Âã_÷°7rmk┞ïgòÃ[pÑ,µ’â4WŒä¦“ pbYÒŸ+VLVäqŲ.ÒÞA«1‚e©,sQžxð, b7 ø (zB}M ¶«N,{vnðÊ ÈñŽä Z “d[­²#Ž,°6YI〬2öÙj2{h2¶Ü2 ò€d¹©5ÿÍù–L"ÒD2ΩvL’ôl°^òx ãžzÁ½§%ÿh†û6ž:6˜œ+½§-£Ã>¡ð(wíx:]ž„}ÐÄÀz†Ð%@“DРײÀqhWé(“b-°JjÕjZ¾ª[ŒªºÓ,b,Èœ¤Ë´GõXöX|ÕB› -™ $ÐŽß[•HÄ ›mß Ø ÒKÂMWÖs)|-Ò`Çê Z1}£tê'K;×¼Ù1ØÑ¾ÜcahEî¶YàîrWîJQî¬%rîXÔz&PY73ã3…‚±L\¨B?ÂdO²Ý–b¾²÷êa\ëD”“øéggcW.®Øï—®msª7|ྫྷÞð |/ðPÚ Û hÛ Ð€­è1ÀV;Ö¯µ‚½ËÃæS>À&¤*`Åܰ-‘<¶ÝJ”†G-1\äüÅbѸ۹¹ ¹®jkW>“ŸOÂ3ºg÷>„n»_‹5ØÕ¾×lp~r€ßý^W‰,0¶ÏEÞûðªÃ1ðå+ðŽ*Qà×À;iº€·s.ƈJb ¢×—oµÓ–mÙƒY\å'$%C'WAf×wßïnû^¸á{‘†ïŸk6|‹¢:AÜAÒ®}«_@\ü€#Sñ¯¥w5àbKpñyp‚²œ¸r.x*t±Ãg‰`ÉJoTá$sh_B0—6ߟ~HþvsÛ×»ÒnÙ¶ß+58y·ßk6ØÜk6üTCávmÙ­Þ­Ykq -2tå Ú9C6 þA AäÚïÖˆtóŒë0/Kpu×è ÅØl‹…%EŒ[Û8™šôa ­™ 0K3a¯šÖü’[ð “¶Æ8b Q¬ùºå)a5¯\£RsÃÇÁÄ,$Úúe,ñyJéœÇ›ÉÇùLNÒRƒ8äÍb­0%ÞÛ”ÊzdØ­æs³mk,ÉÆpyŒ·B'Ðl›8ºzCºz…Çúù~(2ÄñÚhh¦“0R†§¦§!*•p·¤º«se¡ V×eBÛqë°€tS¢&ÛJÙcÙ÷½€w¯¨±ì!±O ŽW8¿?‹]7øÞ‡/^¿Lµ7äÝþÝT{C^ÑíßMµ7äÝþO©ö†ü)·ýŸPí ùSnû?¡Úò§ÜöÒ¹‚ÚU€¥uÞú‡]”1íSÿX®#¹ðY”÷‘0ŒK6ít;«ÎÝ{wçÄzõ¢ÛS¬E£^Uð”ýèsñþvÇ÷SüFENÍäƒå¢Ø -½Ô:BÈ"åʆ©ø”›oHR'T©–öŸS–/Ådè¥ËfÊóJnµ'6Y÷ÅP7BÈÍ,ˆ«Pag›¾EÙ±^S˜:Ü€7i®­ éVG$[íýù´èúÚ¥''ûl—âoˆMåê(!û¦ã¾1'ÇÝžœñ-%d‘ºUœ¯\¹Á‚œ„6Z*à Ë øÕe¥gt±2joê|UÛêGéj“Ü³ÏÆ‰„9E޵£KYÌNá|÷f†Fæ yg¬–µHйlfüñß霚¦áê¢ÐDÖrDÅh{ÆÓ{ˆ!$O?DŒRѾK§¸…oc1*аëÑž÷h—jÒG ¥º_’x…ø‘?/Ž´ È‹ˆÍ²¥àz_…évÔˆd"^,:`Áâ[#—e¦Ýd“n¬¤í:ëJ³Õ ï Éä´”/<Ü[Í Ñøàds°[ÞϓѲ¯À•+²WÕ­¹³ØXʼn`Glö®…|ë8Šô:|Š]w+$õ–Úžd‡ÜpÒU»cW¬³²ºèzC¦M%ê›y4÷°PCÍo¶s »Á21ZxWýT7¹>‘Åú†_F›µÆŠƒé=]ãt¶¨H" {ª¸mR À¥‡v«cþ ±(š­#Þîn€2ËŒŠjssgs0ÀÓ “"P%Í‘y‰Û.P=–lYdLo4æê:`Ël“±:âÍ­Î߬²ìs0ß×ùȱ†Ú |C,**¡¢r\F2™¦–…¤°,éi¶— æ°fA)vØq ¬Wv“:64™fŸhW:|I4,~y- #ÄCL,FÄS1J1³:tŽ)útYQÝÑÚð ±YQ¿‘î•«æ-~\¦Ö'¬èÍÓ¾g1eIÝúñ³ax„°(ÈÝBªsû,š¸¯˜µiW=Œ¸BïÃvŠ¥ ¤Í©c,Oß^ÒñßMÅ7äÿÝT´xìÿÝT´kêÿDÇ¿®mÿü‘…ð‰åË'–O,ŸX>±|bùÄò‰åË'–O,ŸX>±|bùÄò‰åË_ÇÒ™mÿ¬]†ûS,ôÞð•¦Ó*Å>±|bùÄò‰åø4ßü“vÜÙÞ-Qòú]ó6z–º8òû]:Õ g1âærl©“} ÊøŸ¦Ø'–O,ŸX>±|ܶE¿RLÏX¼Ê<-_«ÄïT7ëÆ¸i¨©þ½±ü'`\í]¶AýFãm™¸rIBfÿU;z7ó\” [œ0‡ßÍeM >Øï=UìòhŠ~ê Õ€õG…ëXý±Û€;VLÚYu„\ÙýbÆm¦`u ¸{ßËÈÄ´E)—ÆûXLX\¦WY }¸&Ô5n¦Ó…VÔ«C£ÔÁœ|Ø­îiåÂv ÔÑïW¾#ÜÏ -}bùÄòÆbhžìÞ¾/ö«él£gFBÿðü²]8`·«7 ¿lÙ•=:`kÅji˜s'?lÜKŽÞw¼‚̱_Ì㸚¤àw`Ÿ4û­ŽÕÞ-Œ.bn$¦©× [PËÒÁï±ÕiVÿ +ª€xÜIæH­ôѵ‚ÀÑA ”ýXïºÁÉW Ž@·ëǨë ?ªp̼ -X‚O\“Ý&ž!ØlÄìÁ}IÔåWM³ajÜz÷ Hï¥m}ä¦e¡‚—ÅéG«Ø¾æbhÓ²Q?~Åía&”ðÇ—HD*z÷•9ÿ#†õ¾îj7àó¨4quË‹&ÏbùÄòøöZ)†ÁüU;Z^ -«Ð‚È…Jå—- å5›¿gŽRÌ÷Q›²SÇò‰ñ|àÃ9ÐÇê@W²–”»†mn0Y®&Gé„íÛ%”— éd·í?h: ¼6%`´²5øÈ‚ÌÕÞ33ð!½©Â»ebÖ˜óñÅ”/Ìr\+Y]¾iÿõ -:¥ál$9WV;ƒ•Z&k¿»GJÝ]ÉÒù÷÷7Ä­S×YLx^[Ý pî£;"ÑlB>.V”c9ÝþšÛèËÖçê„äaÄþ-xúË'–G@/µD/âÿ–m!ÞX™#ôBv~èlØÚƽÚ;Wö°Ú”‹úìÄÙ6œëÒû¨;z!kݺÛQ-áQv2Á²ÀB×N¾ùzÜÆ%ŸsYRž×Ën<ÙxßV.®úM#nŸþuÔ‹·s­kç§^Þ> -ìÍÚsÔÕ9&Œ\{¯WN#éÕM…ʯ~w -zZ]Ø·í$žWÉ«6:]<=ÞNdäpõ¥þaü#ugëD¹ûåì½Ô¾…Ú¦¥¥Y‘þ ‹û µiµÁdòÐI*ö`¿ðŸ.ŸXþXÚú¼Ù¾ÔunжSò%Ï•€Î½YöG|b¡•iŠXØâu}ÐÝÈïùÀÅQKûº—6¢s‘­ch†I½t§Y*‰Û’œl._¢"{ûâJä™"ü5·”AJ¢bÏŠ¢ÙÑmEAÊ!ø¹{reͶ¿ªÊ‹)ÎÞoʱcøÚÚì¡s³Ù»cw±bñI{“Q¿ý8Ôƒž]þŽ4¿u]Ë¿^ï;bÊ.Ãüª……­•álëð<ß±Çd½ò¥è‹9u›áiZ ˆòÉe/Ý)º¬P¯È€pQ˜…ãÃùŠÛ^OáÁòü÷ãþO,ÿYXE&¿Çìcxíà_ì 7$YÝÇç Øn4>hI¯{D;ÀÙ¼úÈc¤KƒñSŽ@%¾Ç„uÞ ÛMå&–ܱ¥ýª«j¤7#›cøR`´* }µ§ôCrîyÚý¥á…mðxÐxãÙ(©!}GaTfômiüM)°ÞgäÔÚ.#Ô‰šŽ¨]a><ñÚzù§ŒvnÑlÞŒ¸î ýM‚÷¯6Ô£}æŒD]Üûö9æÛÐ/8§!eo\ôETÎßæúé˜FÆÖqï±6ŸCm¿Ôˆåãb&׊#É}mwÍ'û_æ ügÊÑ'–ÿ±ÐýYBs‰Ètþ;e5ÏK;– —úó*%ùÑÝšËèol¶Ëí?ÆZì… ¤™ý'ñùö»–^WŽ]亄_¹Î/ â¦xûþ]á±Ø]Sl9óÉͧP kÄ»m„Ïê°ÙßÜ%ôE4ž¶ K>ãzÛó¸oiG±êö£D¤ô”.\ÇÇ›9F=¦¥8³#Ù»]$¿ÚúðJBªe5 ÎôÓ¦5’w»h¨+Ï󡇷®ùF*³ç3ç§åä3¥Á+Ùrþç^£åüpNzÑíǨ†}+וl% \®c8óAÊPØŸ\‘ÅB]„(}™¤¥_ÆdÿFró‰å߆…^¯ÂʳUñ…Ë5ñq?\ý–Q;°±¼›L7Ž¿r*ù}­`Ç–0)ðâÍOw’nÞmœøj,Ù`¸”²˜ß¾ûù1ú=‹¡‰ú×ÐÎ3Ü]]»©á³m•Ýw%P?u®æåaàŽÎz'"SôÑ#snâqmožx¥Î~µu ó!kv·öU)š%wÏNŒìÌož)NrÍ[  -EŸ­Ýæþx×¢/6¤éª£¯^FÇΦwNÕàÓ£üÍ|‚þÕêÓ•[Ú‹ƒøuØ‹N–ùžÒ Œ<¾!\¢‹g£“X>/Õ¦=_¡¿§>ΜüçHà'ˇlyÙÅ^D…¿ŠÛ±¬»øC åÜy -ô¾Y4ÜŽË#W_rê(5]€ÊS_n¡%–uÙÜx%„=æL8W.ÝlT?x`äôM€ŽòÔu›KÞlû§Ÿ= ZsJ¶ú‚¹GE|:7›rBÌ΋•©ö+wqa {»ÛdÞ7~Œ÷87ÝlBß<Åèݤ|‹SA¡çy´¦þ¯Úò—Ð -"ç‹6é3~Ú…ëkGC±Ú‹@S·ü~d^ì?Ðq¸Rbérèeb÷Í&{Áª0zÁ¹mïèf<)/=ûfž[F($Lëëd¡ÝŽû~@›JXvW˜„_:öÿ ÷a+_~ñt'Ov|ìeÉèšõÆfØËtƒeÐ#ª7ÎDG/]°2‘‹n’ø2’ã\Òeñž7šŸÆ[3J Ço©è“ ³ôú|ògÔíáÝ>)°›Àcä×Ñ*=yJýB/{·M Ôs–R­­ßEü…ëBs:÷~0î3*"SLj–õ4k½u =§O´Û¥›mò*é®–ŽÉÃ'Kü㣨x¾mb>ä74®Ý;ñ^”t ¥¯ÊªÆoOGЖºi{6U}޶ꦚ#/órÓR ÞPû>àPa\²u´½«ùr§"EÖøsf)ô:ª.®…G;ó^Â>±Xj$‡:2…&™—±` H:Ö­´Í2RîUG³\Áo’§¼ú]¦{ 'Æó•ûx>²jÖ -“›g~jfÙ9Üá…$¶wvœã=à‡¯>¿Š€}ÔchÆê J¡ƒ»X?W7©h»ÛçÜuªþ~³?¸®‹yxÌÎ¸Üæ¡=ißE’îóý4ŽûBT1GÊžø#y8s^Ÿ:Ngüäáå<·=`îù|B³¨Ý5âæé·.]À1WT°ø»²{ðL]t,ÀöãHáü¼“ÚÔö·®Íû¢´aßµ™¡\7ä¯9×4—G¸Þz‘G»+Þ´=žxPì[Ì}6s÷Ûp1¤üµXÐ?A†ÿç±Ð[m\Ø|Tzˆ>.ÆÚ®;EûúºOJµp)š†ñ-g>P}šÇŒ€³ÇåÀÂÝ´±#t‹Ò“'WÞö î1\žÓ¸Û [¬ß¡jY~”“[^÷ ±ËS’¨»Íáz®ðÑð;®“œÁ¯(ç©wûö£GÃ`:ð B7/æðäáðçÝ¿E™HG²n¨ÿF‰Ѩ#ÌP|æ -gÊ·yøŸµH‹…ìn_,m›2×­Ô²¡ƒGl‚ÑUüæ¹Ý@KƒŒ¶wœz¹Ë0ÕæÚn2¢¥Ge{fR´ãazâÑé¹Q8kŸA‚~•×Jƺ3ÏG•à©}7ÆT¥4D'oÈÂçÿ9žÙýÀm}„o\RxCÚUÆÿrYDJÄžoe\§'%ÃŽIæ‰ÛÄõ}ÁáD×¶ý<æ{Üòýi›¾M;BÌýç³_7„-î{>u‡ÌÚ_šçPac\½ÙþÓ2ðó†M8ay U¯$&Ó¾uIB?Žãþ¹Z“íGíKìæuòàtTRnDV¼†wÛ¯>¥-ºõëê.¾OÛibÛe;ï=³˜ƒ#Y«¯»Sö%_OîŽÜâ4ˆúì¹4׿W-þr·mЇ>Û≽ÍÓ.7½Õ›&ÇáþsyNʸ1úòXÍÜ•žÏZðÛìÚbÚŽÿdèδÜO$J||•‹çÿZˆø?L^ÿ㱋žÏ5múÛ›ÁìÐðu}Á꓉ÊÜ臹ˆ -$KQGžx–Ø'QxJ*nõ½·¸*p7cɇǠ7»½8¡Òê±K¤[5ï¦Ì£=bϺ õ‘ާ‡-!ñýö.^<Žr_,Gè¯vôªk'ÒkŸÑø9zM N—î?Q½Úâã žõ…¹ ò‹¬•#pôÎ{øªî£`_€­Ê‰ˆgÒ•KÝ„¸Vz•9Ä~Ý1{VY]z"x@xé.B¾à§{Ê’}Ô3Ó´û™»6|¶ëL™Ó-R¤©üðy«™¾¿»NÅÃ(Aw𗵋º”çU™ˆ8UÙfû1ÎüŸ UŸXþ 'óB´¬£“q³"Z³ ô;rõSNHk–CDï|´°9VÅû&öÀý”ö{OL³¸r 2?í 5‰Ákã†ÎܺC¡þó,ÁðP¼‹õ³„Õ;Žú’¼‰Ÿ•)Õ¸ÅPÚ›=nKØ&Äí•ï"ž²JüÇõ={P/¨Ý9ôß?g{wÉû‘0ÓlðÕ΀Ÿ×¥5µSYÞ÷t%û³ùB¯CèV0oZ]dbO{ë´{s‹êbÚ‹saït»i÷YU;{g-ó~ -‡^!jý†6úÍÈ NšfÃЬàÇóÖ’ÑÚ@_hþþë“vb{"˜3êügµ„ÿc¥ëË+p¼È“Úná ’;§¥qy`8 ø%2#JþÌP¶ÔÙ<ú$Ús’Åžv(†¤Ô5ø°\¹ÇVä}ÚÓp)ÝÖ]ÔŸÓ<ê;ñgAÿ>Ë{ å…˜+í$Sº¿³Ÿ‰䔿٠ëkfösì‚rBµ 0Ï<îyÕœzÛØÆÊÌ3¥Ù¸ï¾“äzx¶kvxæe’Ð÷Ïα}°õñQ7,o¯Äs±„vÌw^}›/¥”¬ý #ì)ŠKu{Ïäû!òp4ž‡(³þÁ9_Ó×|+5Æ Í/1’øU‘¯¿—<|bùÛaÙͯ‹´²¾lðáøzt 8¶îóZí§'ZËeÚ-€oZo`$Ä÷® ˜A,ï¦È?`¡+®V³‚~ô¾wmÆÎ5ØOÇén·%óç]ú¦"{ôóõx3…Ý•ÀüÅLk‡“ýèó¶8•y­5ÝÀŽw_OoížB™¾zL¹kÎǯJÛêÖþ1Æv‹Yº–¾§£Ì$æù€dN¾É4û:fliœÉ¹ ~`—ý®hâþxåfu©£ïª[vçòÅVOî±Ü}C3.ÿº²Î,±‡ê~©ùÇåcþ“åäËß‹¡ªf¢u˜"p‰«NÏÙôb“ûÜì)ÄbUZœììm‹雟{nTŠŽJxŠÑ¡›öyÞ@:N÷rëk®x.}\_9Y2öFÜÏþ5ÿfF=Íl’^0õ´'ot‹¯Åß2€^©jNM>rϼØ`ôÕ¦žNȶ†Þ_ ©v4ÔIºáÙÄéN_gäÛ ñ›oÛ|Úɲƻ`Õþ¶êPÎËÃËÔ—]/Ï«DªQ ¼!QÐr_ä^D›œ‘Š­îæ0Ãj~\ ëoÎÍŸXþ±XÒÝ¡±lø•7dÕ»Àø"sˆîõðLÿi½-Ë¥`º—TO°t¬þ±8˳:x¨ÔHè}›ü¤(~ÿ=õ¬*ráµ¢ 1'³önû³G7nMÔòâÝÅ{¡ìªÌÍö™uéëÏAʱø€_Í=î†÷ƒ™ÓíLš{Lù¶fÃy&‰»u÷"…ŠÙݦPŒ±·­ÝÝî!2'ïпõß4¬kÝzÑíü¦™Þ±%:ü –N·Š>K¶j/ªËFtú=¿/(?L—p ÑRíøŸÀéŸXþïÅr Kcirt{oH¯¸¾è­P¹hF íé4*Å©“ _¹<…}é@Æ–9´ ñŒ–3?m7ùÉMÔÃOûß)²[÷Üì äô˜‹tœ/R÷Í=׋ãL­ÒÍf²ZýìèQWbÐÚDœ±û8rõ˜pw›É%¦;Jyw_r¢†X&IñÏø>³‘?hì>2Öxp}IïR[ß ²êÙzõ¹·/WyÍö|{hž]OšÙ…'öÚç[³@à4ÇJÑÒ5ž8h1;Ì_åÿ»òé'–O,ÿ&,§p ¾|>3jƒNùÒõJñ )—ãS.?Ä&íŒ:H ×óäç =åSžÕM3n¸˜òï/•‹“¢ìèž'dÍÚñÃ.îxàÁJå Ù Döú¡Ziœ¯]NóŽ¥öÍÎzávjõ®«ñáC½ç+ÒËÇò­s\Ì‹Óå=«sãå9­ÂÂò;¼K´˜´>ý §ØóLÀsÁÞÔ–ÙvTϼþQ-Q7L]µÔRš?*NõáÙO,ŸXþ¿ÂBø z3B‡µ1Ù,ÈIô¨ÛÌS²·œ,½˜“—DMŽŽ5…ŽNrƒçóì­õмò©Ã}gá…†[ÜÏG86xJ榼‰ÆÝu}Î^nÑNÂú@OáäèBä4ãì„màíA–Ò¡ -ʵ÷ÒRÅæ²v½Ww/êqÒRÂãΰ7;‘åÛåñ¢xÖ¦éí‘ïˆw -æ(˜ Æ÷}×<>gÚ‰ø`"gæ.õñúu/íÇMå÷zùÛsÛ'–O,ÿ,tKÍ-ê¾Ñ°hTdi˜ê±ð«‚voH½?ñE£ãbä,3zNhÂ9:>—Õ`o -d òæ?Ÿ¨o'ïeÈos;¥åh8¸“z:“Ì„‘{=µ -û‚mÈÐAeùÙ÷îÒ³‚®õ6ΙJ0g¯jažY6³rú¿!x.)™åz¿üºv›Ø®mÈawÇËQϼ“?¶óú蘟õÊÝÙc逫×ݾñæÄO,ŸXþ9XZ›Xw}­Í[dSn7çn×äìmký vCÓÍN;—êoÓˆ;nÂÐ×ãϿú|bù[c9"°s–óëß-äáÏÙjøúÙü?±7dþ‚' ^ß—´©^wÚþò¥v2—º¡}I+…øB[M¼_XÉ4/xM;šzÑÿKËôÅ´¹W·&lZ·¾6÷Êé__B_ØŠyihªyZh çg|X0ŒëùrR.æéKZ[*Wã2&¾°í½®š ­y9éûÕœU.Ö§ùõ¢YÕ4 Íé´¢ì´Ï¶` ñ§vöèkéì´}ÖjV/—–nX}ý²½×nï4ÖNeå´mýëpGøÅ)Ó0O)sw8iç³5,Yß/nOùø_=—;)ÿ²¥5õ÷;Ÿÿž´ùßû³²;Za§¬´äÜü/íÛjübNÚY;ý—–TOæ\¹””Y+öËÇ<_–†õ¶×N_ÌëÅ"öÅZ's«ýú¹ŸÐý°¸™…~±ÈŸRÊÜZˆ‹þµŸ_ÓÈ™óÏDúÕÚeocn”ýù œ´½ú/âÎhem¡_w_ÚÙ4®6÷ü†Íº´~wfuù3¦³Fp8h‹ß“ªú_Úé`sÜï›>òYýªX„¼OÐËÿŠ.6«}çœï4¸=Ëýâɲ¹7ÕõÉÜi/9üWãÍÙÌ£\´Öúº›ïݸ¯8þ[Â;$¬^/‡ë%eîmÎ1÷……¥„ô¥®þ„R·¡&O—¿„2­-&U~Ö-ešb<>üRÖÎë/ ål© ýÝyìË#m5LÉдEIßoÿ€–Ö~´ô‰ž‹Žw]þÃ$ÁËw¾Û[œjã®™†þU¬~õØGa¯ZJà/<÷Á8ÿàñ¬þSñÖÚß/–RørYk_·wÍK¢m½tþòßkmÿå¬ü—M?eÿåÇ÷’óÚSÎö×É…9×¾Xx¾,îïEæ‹õ‚±»´àÏý˼~9ÖC[h7õç ¾u·Rôýý'Dô Ù·G÷s|¹˜vªöE¿Ø•/†bývùbi‹7–;_Õµ=¼ÂÞâ_}µÿÞÍ ÛÞbÝ«5:sù½~þrÝo÷˜? õ¤¾3ör]C[éÖôþõÕ~÷Jü+zæQjd’_¿Lf“Çw)÷KmjëÄ_¼‡ùìÃKüQ]| j~Ð’y¹˜»ß¿ -D’ò›×<çÐT®ÿÀ6²¦ZØ/Ío+ùñ`«Ë¥%YëÍaiáÿ6-cÊ<üÅ.¶áu´‡±×ðc­“¾³›w5}µ¾ü)KÚòÛ6¾wìõüjàÍëÜž®¹¿4lNüÓþ¿å·/þ—:íëMú{‰j^”ýB9ýš!^YˆíÝ©å÷|øÖ¼ ç7ïMááýëÉéÁý_–7`i¼¹béZUûßaÍïñ«eøn -½Z‹¿†ñG)þ \5ôÃÕ´1þ¯/'K¥šûóï…é«xüAÓ¬EÊ®6ïèÚß×ï7敵jŽÿ¯–û/QîqÁÿý~KËQµšÜŒóÔ7ßðgÒÚl÷Í‘mj—ëáËAÙkÆ7Â|àÇ®ÍÿÎë‹;Oñ¼à ˆßz|åÿä¥ÜúLšÊieËÖЭY[ß›–d¿ŽýÉ%¿!Öh§™ýâkï¶ÃméõN%ké÷-KȾþöæèÇol7;ÙL -"ŸÖlÍ`÷JÅø™@Å;sÖÃRešŠ¯/~û“‹ÔCþo?Ô¿}r~ûã­‹”^sÛ<Ôˆ*饧ûö«Š6k@øóQàf±Æ¨Ø6ˆÊ èµný4[2€úÙ{Óí¨u¨ý³?÷Zu IÈœxÈ „™p¦ „ ô¿ß/}C½úúnú:zoÉ.ÛU¶Ë5$»†çwÖÃU•Y{KÚ’¬¿+¯ÇžîoŒ/O-cùÞÁŒ:½;¾>ûÒ¹´.ŸÒÅmœ8÷Ÿ^µ×;ðÞy¿ßÝ]ü¼uæÿcß’OÃãõÚÔÅÅê½/þ<{üàIx¹ìÜýgiëìóvóâÃ;cãÝÖÁ›­{ï}5çú§ÑYìÉ—óë‹s¯è|»^ÑíêZ²wÃIʱ¦/í^^\ܹ|C'Yxd,;¯õm$Wvl]½µ>Ü6¾Ý6Õ™Ÿ'‡½øh\Þ¥cçW·Ço«WÏeãdiù2Øv‚ó;?éŸÛ¿è×Ù“~¸øøôÃËü“nûŸÜ•GŸ–rOúiòÙnmªá´ÉI½ÿžlOåŸôîøÌÅ¥9u‘Òæç–µ2—œ´6•œörzáébÁIÝ3GÓ›ù'uf?Ìßµ¾çßé­­ONmjbïϯݼ{5¶Öž<(8©71yz1{¯à¤ŸŒ­Ÿ½NNJ÷’:íöøÝÛæãÃ7¹'ÝÞ±Þf¯õnõÍguR*‹_6³Ïô*Éþ®îòig›ŸêÌýþ—9K'uΚŠÒ®±ôÅôtÃI]÷÷áŸä¤IIÖ§=¼øøãôMÁIyþÖ¤™{ÒO÷?¿*:éNmjÒž¹ó!ÿ^œøqü*ÿ¤/×gWÏ'ïætþί•Õä¤ô\²Eiîþ»ÏóOê[¡ñ,÷¤·¶¾û“¯~[ÏóNZ›2¶>~Ü*¸WobêäôézÑIŒí…ÏoóOºm<œ>žñÔIkS|5±p?:éÁâLC߿܋²wóÃÉVæ¤ïî»þ¢É'k8imêòÖÎñ™÷êhÑ¢ÓúËïîûwG'õ&ü“o[ŸŠNºa<ûª“rk¸×Gç+~^<{•{Ò×w¦í“>9~my'eÏï¼_4^ÎÞÊ»×Ë[OvŽŸ½7;“{Ò·Ó§ß OúúÇËÏ_ÔIkSÍ÷ú~ÛxûäÏÝü“îÚS{[îÞË?éŸÇ·òNJ>™OûöpûÖUA¿nüsÿÉFþIŸ®n~zùñcîI?>?y¤NÊõKó½þYlp„ œgº¦±'WÖŸ¨“òY¢ÓN^\}9å“ '½¸xx|—ßÇã '½t®D5Íý•—K Ù{ëìøó›ZÔ~8>:ô²µ³wûÓÙJá§/Íßo‹>ýaì|žú›|ÚäùÉõnÜ.ø5=ƒÛóëñuý<ö>õóðMôéåIÐd•Þé[ûyŸk§øâÑʇÂOkîÕçâO}š®çXóç­Ûï¾~º;zß*þôËóï$Ÿ6ä˜7ñüÖ×/;¿vnïÜݻԟþ;}6üvoþ¿¸qú¯yk¥)Çö¾<~}š÷¹öró'…Ÿþ3õey¼øÓ+÷âËùüóÔ'²ðÓŸW/þì~zòÖZ{™|Ú”c¿O¶V?ýš.éå·ðÓÇ–³º_œcS_O¿¼Þ-úõíñÛ>Ì~ºùðé—ãÂO[÷o™Å9öpÜšœ])øÔÝ16ïÍÅ÷¼2s§áÓù7//ïGŸ®/Ým´Ê7Ÿ·ç&Ÿ;—ΫllÝø6ý~=ò?Ÿ§~ñ§¢îèÖ¡«]ÏÚÙå3ý·Œ³®&¹‡¹>¾ø8< æÏ7üǧm/l¼Zç?þQý·zïMûˆè|G¯ÖbÏwqËš¾÷b1òçÔÏÉ´ÇV'ìiúáãßÊ6¸§“²ˆå§Áé4õc÷ÿ’k½u›Î÷ïÝúù&–ÿ»÷e–¼Ó­Í‹¿Ÿ—3îöâVm*9­êéœÔ›à~Îûü“:ï -OJ•ÈO³¡=–¾WÕÓ)<)Wy_‹Nú-}Rç5õ‘S§ ž­½NôÛíÛÉIUë¿~R»!{¹õ_¿Óí_™“ΨÞkú´™ ¾gžTµþ NJýAjýJNJ÷’¹×…'¥ ¾tŠOÊ­ÿ“֦¸ýÿ#ÿ^ïŽ/•twºð¤ªM‘œ”m?sZnS¼É<Õã0>½ú[ô f×ß}û]å{Ÿþþ>MÛ~Á7½‰óO›ûÏ[~Ïý•»È[¬Ñ=xyË&Óý<ó›sçU*@³ºõ÷"r.·_?h°øåÿ&V’?VÿÌ|{uÖÙ'éþþêŸÙ»êGœQ‡X w&ª«X ·ßoã:Úä3Û‰kŠN¿º9ý±ðô,:jÇ'¨·-ézèæîýÑ_jˆ>‘Û[[þq¼1UÿãUºÅÅ̞׿¼ÁgÙNßîçÇ÷£˜]òÞ J¸Í Ú¿sqîèv{äŽSwðâv6ë7º`ã±;5¥þàùO¶—”wQ'ᣆ‹J_Òáßi*ºO¬éûF*ŽÖTYNÞòãßµ'­2ÿˆ.^wæsîoz¼ðþjSɪ? -Ÿ`üü^ßjýü&ê÷7•±¼;äÆë^IfU~gª|êžEÅÌ*9˜9·ös«ðPµ*Å=ά;‹m•¬¤\ejd]²¶?œ·Ÿó¹ùN›wQIî:ç­µwáãò|¯gVÖõÌ6»žO›Y×cºžZ«¢»ùáÑE:ë—œÉÀÛÚõäçݧMnÃD\p= › ú(ïÌÓü¼;X/tÛEV©âq¹·vp«ý[K×bêæìûoß­p]d••–­PÚ½®x„7:ØAy'¦›L·êºÎ±Ã‹æXÖ£µy]²q˜ÏþESÇõjõ²ÃÖqfü…®g'¯3XÔ Í­ ­µýã-üXªo•W÷oõÌ*×öÏ'Ûë(ëñئ'¹ÃC&»IÞpO¼ýÜ9¸u»jç£Þ¶lÌÕËzµÜ2Œß0Üip -¹öÒú1­^–wñ.$*ÉÍ—Ò Tº†¶egyÒÂìS¢ûÈóUÔ÷]¿o£O®%³“Œês{¬—_–RmúÛ#.ó›mœ  ¨A-Áñ’6L®û(0GåÍŽÚT;5Ñ¡ÈzK6¹WSåÕrUð(qUûûÅ÷7]!ËkÕ2=ÓVhÏhlÉ'±¾ŸxPïŸ^ª+sóãåvâ2sár;>}r*û?ìªå3±*ˬê&^ØíQ½$ʬ6M<“YuW¿°'Óå¬?æ{Éšxn¯»ut‰Jí­j’²XÂãÆlãÕ”…d"$¿g[ðâÖ!Yºµ”!EO¿í0Écž—ð´jx£ÞKj¼”dzÕrá *ç–º!5S¥éiUV ujAܧ6U¥ûý˜wºŠûp¶´Žõ5¸‚‚x çŽW©$§üf&Òð'Ó”~ð·±)Må 8˜ùä -iÎå'MévòN·ÇÔõL•_OÕø‚}ÏŸn(©¾X[ᓆ–pgö²òr¶üÖZ—ó'IåW¯_:ˆFÿyÒXïÝPmª¤¤SÙÉVz8€û{Sn­Å3¯–1‡-‚ôµ†ª®¨-K¹ÓPѵª‡ÒmY}/©ªŽ«¦Óó7ø®(­Ja(´ºTI^§ëYj/æÝ|Ïû¯ój»ì¨hå!H:XQ¼5/Úª{EñV>XÆ—ß_"£©Mu=H¡žZy•W«|œlG²½«‰ÆÄõqºµ@}”zo29Jì“Û9Nûõ^½¿Ÿs°Ìa{•hv|pZµ.f²-Îu5Mµ'# ëjòimªµV¨nø`ÖRÛc|žË9W)+SÍÏ•óL-ÖèÑ.Of›<ÚåIã^–!Y§ŒypÞ­GÛKJ÷c¯ê8%£A k3õQœåÚT^k S ½=+ëA§‡óÛ(Ϋì|Kêó4ÅÆ9­Ûظ~ùwú¼Ø Ul™­“?¿Ê3foÏÍÒÁÞŽ·ådkS…n¶ÃÙYc~«úÒ™žx' zj “šŠNvнbx9g4A¬±j]ÒýÍsë"[Rêb7íûT!=ºõê²ëßþ?U£Y©ØEamøO[íûò,¬ûöý‡ó¼º°ÝZŒŸZû!­æZŒÓMû>u”z]Øy-¦Ž“Ó¾Ï;J½÷Zpœªµay]¨<̇ó®kÃL]xØ8N¬ÎB©=™ŽÄþ'wVTWf&O%Ó0 -Û‡%$• Fßö÷»‰U7Äùé`ÝZwêºrl»|®uIŽUjîÆO’ÏRÔ#¢g¹TÉÎKFO×)?ß.—D®*L§k¸¤Â¡æö´4¯ÆŠi}i±©ZZ_Z®Ô¯¬P1Ykûß -§çÏ\+èïÓ•YÕ²²‚q}¹ìÝl[:XáØM=ÚSuB=Ìž¼ód·Ê1¿½&FÁD~’‡K•b°ú`Æ~P:ùV—ˆZ¿¯tQKD“qE5²6ݤ>[uŸžæö,¢g VË]~_<}y8¾ðöÓæøâCû¯›ÛÌ[A§ßÜÒýºt-Ö¼‚®aÅPÇkèÊWÐ)ŸÜƒ5t…'U+è -W ¶¹†®|]zµ`7kèÊWÐeZ}]¬¡+_A—Y-ØÅºòt%«ÛZCW¾‚N­ìÁºòïEk«»^C×d¸™tõžE—kèÊWЩöXë5t™ É%+Ì^Loåµ¶‹çÀ¯j˜ìRá’r#½­'nÏU[/µÕÐGîx2ìFcë7'8P5Òû~#[Û·÷èRc|6:Χ;s™âš]+Z¯ãgª,;þyü¶U%;ÆWv°òyXÕîOÅúZ­œ«| ‘«f{©žéËí_RÁÊÇ‘«²KÊ[2Ç÷ÒÖ¢¹J¾f³a*H2·§½TWmMÑóÇr'ƒ´6. -«7ƒu¯:Xœ(¿µZÅÅnÙ™íUI¦gÞmÄX/v+¶Éö’Z-v+8¤¼åfédL×´U7d³a5Cf¥pµƒe§`™Í#6_¬óV+«ù¬£­¶:½Ê*‹º½_ìâ)”m-1UkßžV\uغoO™•7–]o5‡ÎZO‚? aúí^î¤ÙF¬¶ ›]…«ÀR-¥Æu`-›]•×ñým5¾u|_Z­™È­Š—¦/‚)oJ7ÏŸ¾÷c¢è¢ä-Ñ,~~-çÀWžEðm;ÁÌ}~U×ñ­ÖÄ”†ììt>XÏVˆ^$#E+SÚ9Xë_=DzÖ.s¬å2Ñê7™x´Žr,øµÖö³v&Öç¬äªÖ–mŠù6w³ê¦þ*?nY¾^®á8¹•­‘sñÙúdòŸK­{§Bg¯ÞãkÑÙû¾S¹³Wt€Æ9Š¢Õ«Qè9×ߪQm©\GOèÐh]¿ÄMš|³à%n­ƒ µÒ4¯J+èÄ5û²5rµËœ+ yª*åI­©{UÒŽ\¹lšÊ?³qU!ËS##E%ùç£öÂ;9]%ÝRj±@®É믈ºÊ­Í;h=ªjñ‰½¶Ç~>j/¼S¼Ì®qQÇùô£E¤¡ÖЕ(.mFd2—”YLeµþ(¹¤¬1·®ÅJòÉ©zIå³Ó[/Š+»¤Æww,5Äõ’¤^Dd«ˆL—³Ô¨%"ÃËÐ"2E+R[­ÏsÚŠÈäÆùWXžSmq/Ñ#ƒk]’«Mßã­:ŒlcùIÒX.ž7^¥±lßß›Ÿ¨PÊ×$þyÒ›™ ü\V^N÷&¨C·¶2ÓbNo•ehN`O×/¼€¬8ºÔÆò±¦© ¹«[­«k{Òló Uµ®®â|½Òuu©¥£%+…[AŸ”¾¤*e½©¶eéT½Íï"å´Š]¼æ½hbRoÖÃñYz1§®|=\ûïëd=\Þ,è¿{½®‹um¬‡+›¡Ú»õpµîÒ+¬‡Ëy#há"®Î×ÃefCÅ¿™îõz¸Æ÷Žêq½^×ò-=Y—±ª0Õ²³õp}±¢a^ÉÖýÊz®àz6'’•YÔ¶¬2'òíY¥9‘­lÿòÄî¶°®&Ht¿]g¡ÕÓ¯xœ*ëH -¢Ûcê8=XÉÕ4“¡d|kÆ‹ëŠbÍóS{ÙÌxž>Ÿkšñ<}ÞâA4aa´gÿm7f˜,>JEº5C:XékeòüX¡ü©j†%­qÎïöú‹¹­7oVÇiÏ| -Ö#Óqz²rc¹Z-Öò8ů.x›VÑ4ëæW·|Z6*Ýð¶ÆÙ…æ/>+±SËu}Eªy«Ó "©{.z±pG+RnVXDZqEêÑ­/UB-–úõdEê‡óÞ¬HåãôbE*¯ë~E*¥+Rù8Õ^ÝÐ%Λק ¤ø¥©mO2š]Œž~Ö ÷»6Æ¥pù~¬×KáÔ|þÒ=½X -—û\z¾®ó¸e6ÇÊûîmô+»X -—~‘Z w-Kár¢ -×°.?>V±vPÖ0L»‚ôû“Ë>ý(œ«ZåðÙöØúR‹nQÕ‰^|(·RD±B™Vüf»6Û0M/nwp!µý âNÙÛyÉ•çUyÉYZ/nn\W÷¡ðu8•CÍ©ûå]ãòÎ=µÅåêóÛ¿ÞnL|û»¹N>ø´õfõÅæƒå«iòü[oîûjÛðw›îxo¯¯-}]__[~›¼þWGS¿²—Eœ²k±òVq/)Z£ô¾xÝYðråEºxe»Ýùúüi:øœ:©ûcnêîøYÑ -;ç`¿l±Û·å“[oÖÊVØM=ÿïéQÑI?—œt{1L´q-ÖÊüe* -׸ØÍÙÿ±[ß™°a Ø­™²ÅnæRÃI³ûñÝù]´ÂΛ˜8÷þ~(Zìö¾lÕÙïòvgoÞžôöîñoE'=.ßïå~ñI7Ÿ½ß.ÌÞÉSûÎaÑI_fWØ©§Jݵú[TÎW*~ïNÞ÷tT!óMçÃn¥#:³/ô÷¢ªs×Ëiˆ>‹{I«[5V¨e±ã -Sn³-ØùS}/™1¢·Ïz³´§õ ÕÆ¨Iñ>Vç.)5.VrQ­&§¶šç¡{¯½ÜI.oÛ‘œwªTˆ.•í$×VîýFå)’-VCªwB¶1¯¯Õ&r¥óúª—§›ÈÞ_Þ¾o­6©z­÷¨œé­öix—ZûÇU·—íã∠-§»VÝ€®<ÒÛÉjºÎâ0í®¦ËëÄ{qön5]Å÷Ãt¹š./&Øh/ݯ¦Ë[K×éÊÇâÕty±øü™öݬ¦ËdKd³¹ïëj5]Þ¡Z¼!¤ƒÕtÖÈm®¦Ë§©×•=[M—·–.ÝßïÍjº¼µtçö´±š./ÖïÀÞ»ÕtyOWEz{ºš.¯q“ž;Ú›Õtyké -Þ6ßÅjºæKú1QÜRêt5]^ã´6ÕëÕtyÏ/g6T—«éÕrOáŽVÓµ-{»š®zŽu³š®áPcâ=ZM×Q޵½š®tUÏVÓ寭îõjº¼ÐYz¼š.o´¤a|VÓ幇lﵫéZŒŒôh5]ëú¥«éò2#i÷j5]«•\½YM—·–®p?¾î;€‹I0³¿X[¯n*ÞÅ«y“ÊÆ½9*.|:oaì™ÌwΊö²é|¿º¦KºâQž^ïWWܺ¨–OWóÓmåS’K™™¼&³eâbÈ,P(Xõ\tQ —TÕTØ].;0ÕÑ%qŽÑEµµírÙ%åO¤(ô0%ùT²írËäZ,Û#ºo>¾lèٓ៛—Gæê»e•mt×õ6w©÷\Ul’w²Í]áîrO*­Uª¸Í]ÑJ®j é*M(ŸŸœlt×ݳÚ>øSe—™Ò(U¥mîZF9cºÞæ.jÔot×õ6wzí[‹îª ýyÒƒ÷\Ù÷÷Ìò‡X}Å“žMQúó$irw¼–‡níÞlÕòY8ÊÃÔµœ{ت|ò2:î<—Fà+-¤«2£¹Öz…añ$ŽÊë é,%ÓÛ˜DƹSúË–äj;lÑ™'¦×ñKS ï9ïÝPEã=Û/~w¸°+3Š -U‹ùà•gCÑ¡ZΫ<ŠVmFwùÄfåûz²ò±û¡ÞQ°äuàÑÓ¯xœJ{»æ%5³ëu/,P¥i%{îJáÖkÙÕÁ*-¬-Ú¢qaíëÓæ…µ¯O{÷V@>XO62VeŒ®¶Š3K×]…Yù&/+[î÷Z6‡z®yH—Wâ5Ï .í‰Í¡>¾<éÉÊ”ú›ÛoI4j÷o¯v0¤^W¶ý½¶6ª.}ã׺nN裔Íêlã8=xK€>N·»Uë£ÄF˜÷†ÃöæªîUXÈÐÖïì|޾­°¡R¾ëîâõ•E{Üu`†9“+:Ý·º½îÊV>va†qA‹v¸ëÉNy-×UÛ)¯ÛõDõòº7Ã’î²ë’ª¬éd‡»²ý^y»öw¸«úVs^ýÔýÂÚçI3§¨÷Zya-¬u3§V¥¡£÷¦ëlamãÊG·Ûø¯/|š»º½÷\©ãtà7›ÞÁÇéÉzΧFµ¾XË㯅mXÄTkõš µ]^;ëÛó1¥GEÙA,4™áÊL‹Q!®ïûVqS•ELÓ÷>5öDSµX•eLÙ›l;MU.…˘Vf*­P/í¶×­re¦ŠUVYÄ4}ï´±ñÝi¿r¿­ELE3ˆxïĒ𶽆á~´†·‹·ò74 s^–¸ ÷€èÅ×õ¥â·Šqÿ¥ÝéÚÚîQeeq{ì w¯¦:P£o=ZãzPøjªöÛ0_.ÛÚî±ÅNyË®qMÙg}F…Uϭ׸Ò%U^õ\¶Æ5ŠñƒØõóÏ7ýxÅÛν7'æúÚòã6&Ž¿®MmÜŸýæÞÙçyþ¶ýB­îÛÚÿ°õÍš^½µ¡+ÚMÅŽ£¿eá=»ÿ*}ÒÌz¸ÚÔåÄÛƒéPUv¶•õ÷ëáf‹á]üý¼bf}rÃ2XE{²•çzIœ¸^ÑUœ6I™êΪѠÌ.f­'NV¨›éØ÷6µŒ/IjžZUxI\’‹/j½ÊÒ¦Ö ›.{µ3‹ -è¶Ø™¥j$i£|jUá£kÚ™…Nðº)he«îÞ<ÙŪ´34u.Õª•§†A¯vﯵæuwmï²XpIM³´í¥r¦·5K«6UvQÅqâö.‰¼eñ<­èzšæÆ6¸«Í«sÙÁëÍæ=S:ëJбUxõu‹ì§Í^½¼ñ`q¼Ú+¬F* /Wåù´ÙÍ›h“5‰M¯Œëh±[þu[ïµþ´Ùad9ûM^ØíKhóÞW¸^¬U¯¥ù]q% üøXvÒHÎëÑV[û{˜­žõ‘¶=L§£ÞzÁ]‘««ÖÛ×e®¬1Îß:d§öã+Œª8çMN‚‹öÛ#{?MßÛ/lo!YÓ~]lËV¾’©­5Va[ï9H]RÃú—oÛe±ã–­ñÌ%¥¦êG£Õ)«ms)àÉd…¥œé÷\•,lµ¥q‹ç—Œ¼Ûn½P§rahØ×8w—™6ÖjéÆÕµ%k¨UºË±–kyªßdvä²Ëk¹ŸN[9¶ë|(8XÓrá¼Öa”c®¬º°ò{HŠˆUZX2;½Â*Àªk›ãÉí¬¬º0ý†ÃöWV]¨¢Ö¯Ì•’fã<ØöVV]È9Öù*À$?ËýFvU»««®¬÷‘;ZXpIMkÓã•úzšïªûMùªïÈÖͦ|©{¹ÆMùš¢ -ײ)_«Ùz³)Ÿ^3RÞ@é~S¾ÚTN«°ç›ò5ï|›òïÜËMù*í_Ùͦ|é¨_”WxQQµZ\¼«_÷ï†zÜ‹wCUÜׯһ¡ºÞ×/uk=x7TѾ~íÍSêt_¿ò]ý:z7Tξ~åQ¡üžxûûúå¾ÒwCu°¯_ë’Ü‹}ýÊg†Ô#W]îë×j%Wº¥»úÕ¦Ú -èîëW~k ±‹Ž÷õË\HÓ®~ì’»¯_ù åÍPíd_¿Üåˆeo5¯ž1•WÊæ¾ ºƒ}ýÊßüUïr_¿ÒŠn]Õb=Ø×¯<,ÜÖ¨Jöõ+ïêæÎ‚î`_¿¼e’‰éu·_Ö¼·µ¯_ùQ8Îß‹}ýÊTj•ÓÍÂ÷Æ©îë—ÓÔLíê—ÞÁèb_¿34²»úeÞÙÕöò£¾¯_Ëu¯=Ùׯ|W¿zIîr_¿ò ש©]íë—¬ßÊ+‹ ï¹êx_¿ò T-ØìëWbk—'vöã›+?JåýøZl'Wy?¾®öõ«%×|Ú]ÍÑ´¯_ɺ‡–³mÛÝׯ¼%Ïo<èž~EuøÛæ‘ÄÎÖoUhÎÇ#‰­Ì°Õ¾~åãÍÙYïë—ÍíÆÞb§ûñµ7I£x?¾˜O}W¿î÷㫲ˆ©ÕÛšçj´·_ãŠÔîöõ˶›võÓ+†ºßׯ¾Tªl_Ñ®÷õ+oæ”±¶÷õ+ßÕ¯ë·6Eûúu뫸¯_…u¯=Ø×¯|W¿¶÷ãëhnƒ·lÞׯóÅð©]ýRgéj_¿æPrzW¿¢·œµ»¯_ùô®†Z¬ã}ýÊ':5Ä”:Þ×/繤võ+/\õ}ý:[fs¬ý5OýÊ.öõKWÞ®~ÏlØ×¯´D¤vén_¿ò†¡î½v¿¯_ù‚بFîz_¿ìM6îê×Ô«ü&«ì¾~Š݆iñ&«ŠûúUjÃt½¯_úI6à”¬®-°Ïü}ýÊ+‡ÒUœ=[«ëýåœêá]Yíù^þŒÆ÷JýÐoTl¨+)µ8´[>ñ¾aÙb¦4Åe,½Y½÷'½^Ǧ¢SÍdjÈ¥³$cŒåçÇ^=?Ÿez¢»—¼öͺü1NÕÊãñù÷Ï&åý_‰×SO>]}´dÌœŽ/}ÿDZ/~<ÞZýõ1|¹77³ø{vzí|ÍØùþìáÄùß5oüÃÑ7›þöùÁ”ÜÙ¹ýôùù+÷üäà}mÊw—œ¯ÁÓ­ý¥'Kãîî¶óyíÕÉÞýo¯ŸzÿüxsüÌ=;süïä?gŸ·Þ¼zr²òëîþÙñý™ÓóCÿ|êâÁ©÷lüŸg÷§'í™moêß÷Íþý1ñÞ9ûp¼Ù¾ZÛ¹º÷æõÛñ¥ùOÆ­ïçoçïÚÓŒ­µ'kÆÖ¿_ÛÞíWŸÃ™‹ËàŸG—Ÿ¼/—î—÷/ê+-ßÌß[^ügÉÞ oÅËÞ~._\~8ãÍæÂ ½¶ºÙ‡dÖ—Þy·{>üúòw¨ë[@ò~’?›Ssì>ÎË,•t»W·ù¹Ø“ã…÷ªV4ÎM¿˜xûîÍêäéÅòÖmËùþwâ×·ño¼’ôq¼8tüïÂçÏW{zªÖ$Ž?Üzófʘ<þ¦úû/βMö? æC·öb=µh.‰·¦nÜZ^>¬zSëkSï¶Ž÷Õšë·_í{ÏÖ¿Ýz°|u÷ñƒåËow¶Â‰“›ÿ<°?Q¶®nÿz»ÿááŽ7þ…Žóö<>¬Þ>sÆœ?ñy)ãÁ§wvÇ/Ô]=ü}Æ%ùá“þ9œß|»¿Ã£~ñk‰í.ènýª{p¡zNÆòÕ ÏF8¼ÒžxÙ]šŽÿfΨØ1åˆJÿôçÔª_r8þÐ?ï-(31MµØû JX_Џµ¼µi= è°;ÆüÆÂÔß­™GÌ…é¿7¿žœ‡Æòª?«.”/î£9·nÞYÿïhÙ¤îͧ>8š\¿W›Š?Z_L>²ÖÞíÝ?xT?ß“ôù'ýˆ3æÙ,¹Œ³s{÷ÖQ=m>ùrmÊÜþ´ôµþÑbê8ÛV N[ŽÚGî3ƒWý6w¼]›²c{Ѿ¿7Nþðѹúª ¥m-p.ÍQïûx-Šô² ØÏôëç”w/ž/óòÈÊÊsò:/öLõ{2ü}Eÿ|o/?}}bÓoßÌϯNÜàOçéK¿åƒÝ$[>'gùLÙá½[ÜX>º{{åûéôÞÖÝMçgÊGꂽ½ÿ2Ý´ÍëY»!]æézÃAkS]6^O®;û8xÛ[¦ßl<úvo<òüt‡ûfü|ßXÉs±fNDåéàÈ©gÂå*ŒÊƗ狪ØG…øËÞrTì¿ìæ—ÝG³ô·÷fé]þòÉZ YôܾÙñß¾9êÑC>y¥Ž¸¨¢ôÏýåè·'ïøoŸÌúÕ|¡ÚnwU] ßËÉ7;õѧqûpëpöýëóÓó©è^N~{ÊÎíµÀîšýwý‡·õ|íW0¤z‹õ 7õ Õÿ¨Œ$;û^í/ÕOú5*6WŸ sÛ|0C;2ã¿}³’ïQ£ûÝßÕO§éjÜ«·µ©­¹ÙÕ;þÛÅ[O·>? -Óí#åóê먎_£fÜüfÔâˆ]kª^¸¸ïO¯Ü>³œÚÔæ?kçoÉ=Îṁ®s7v”îÓOoü=zñœ>¸w+œ|õmýãã‰ÕóÉß»o*ˆ ÷ñïè Q¦sÓn’ -öãYíÇæã/½=g··ÄŽ’‹ó‡Kþt¹îK?Ð?ý åÕ’fµö8ã_;êØÜ»9ž©û`1ÚU–çŸSláíEüÈü»ºuüïÞÊûÜX|ˆÜvýþ€*·ïžÌfüØBxÅoÈ{2Ÿv½»ó(x²¨½àÂ÷{Ëïžä<·’²ñ‘Îɯµ_·N·'¸ztìA—jSÖÆâûycyûÁ‹ SvìÉ•{Iƒw­)–׹ɸgº·ùÉ¥ªóÕ·ô^ªµÿI v÷’S)5ø«¿~ûkð$ÕêUiãW3—õC¼È¼‚àÌm/9;ߦN7..îÍe{âª|ß]|Zo¹ìX3á´eu³djmr#>Ä×Ûé¶•:ÀÚrêæÖþ¶©2HÏ"X‹LüÞÁŒj,ÿ·º·©7Ëÿ-¬¬ª¦Jœöö©gÁÎÇ¥[?÷â,XZŒZãÍ/+©Áë\\1 ŠÒÒbñôè!€ž~ÅÒØtõþÃj× ûb͇Pïnëæ6T¬+6©d7ùvncoe¹ÂÓ(³Jªnæº)ÓæÂúÔ|⫚_YluGwŸ%°ßÿ2ƒt<Ù\ø³näÞFŬä -ÓT¸¼µ½³6_ëMmjîý…}žúæöÚùlôÍ­Ÿæ|Îûq¢r~t8Ýe;þy<Ó]Ûu–’–±ÒC|z`t[ƦÌtWÆŒÓÙ‚2Vù»ó - Húê&²eì“¿ÔÍÓPí¶¼P«zj¾µ6×üD½Ws{o¦²ïË¿†OKùYÉ× Öï·|Ûß–_v Üßÿ³ÒÝÓØ™|°\ž•ª~)»Å­öl£é¼]îWvc;÷_Y-®ábuÙLÕÈ;ÎÕÒÞÝÔ!¾|šê®øò}~>®‘K¯¢äþ˜Å…ªá"Ûo¼Š¯“þbW%âëâ½¥$+SÞ²EM”>„·žkâ…×Ðì-¿Þt?ýÏçÒÿ|¹÷PuT–zâæ#u–òØ¢º—(ºx}±Eõ&Š(ºx}±EÕÑÅë‹-RŽ¥Bˆ×[Ô± ]œ¿¶Ø¢Î±ÏñY®)¶Èe¬g‡-Œ-Ö¦’èâõÅ©$×£‹×[T+†¢èâõÅ£Yjúk‹-Ö¦’èb~l‘ÇãÂp[†(OîÌèhÞæ‡‡3ªtÇÖôúOô•£óEå˜Ô½83‰kª{“i^;~Î-fŽžíÌ*3䮢ú'ýqp¶¤}Òôƒñ*g¹¶ Þ¼h}ü{÷ÑòûWKjÒÇúVæU(Ëž¼³ù8oɕН4oùƒ½(ÀØàÇTË Ô½%y/¾²ëãå—¾£¦æqìpÈq­úowÖ?Ÿ<üF'ý³•—í•Ù$xG.0u.Rû. rðý9»#5·Gy·ïçØ³¥(†GŸ(„¸ýÜP?ºÓïãcïYÑXÙËÿkµ¶3N8¶üêï¯ã‹çÿ}ÿïtl¡v§¶üð‘iî~;Ûº8>~sü^mœ}ýûûøôjlelùáëõGwãøëÙ·ã±=²‘”‚…¨F7=Úœ™[u‡ÿ­münŸìL¼ºw´ñ¯q°š*ï*‚1ýçÍùøÂŸ;µ©ñù¯KSã ‡ßÞ/X[¯Çgv.øŸ:à‘ XfâŽÊÝÌMl_¬ýÝœºûÏÆ“Gã¯FÃÏo¶ï©Øöñ«Ë—ßï.ï<|÷dãÝÖÛ×–¯Œ3UÇÙ—ØO HçŒ__\XW·Çgï>sÆ—§ßò86µ§Æ'7‚åñ…Ù8õñøüË[Ïøþžñ÷ÇÚ߯žîïŒÏO\°¡ipÛš×…"UnÆEëÅ™²Ÿš¶5êŸ|çPâÛóÈžÜRö0©+cfiYGÆ©g;ýíçñ\dnTªÒæ¦þ¹2Ãf»£ÃÖ<ï{׉ñïÞÊU\d$F‘vÐmŒ2&íªëŠZé@yRÔWŸ'ùSÔ€Ú¾—ÔÖŸÒgÞ^_HÕ¨ŸÒçÞ~´Äm¢9ª¨?ÌëúÇËŸT+l¿2µ}UeLÚª÷yM£ŒI;€jÔ‚*{º¤Ên9ÀØp<½~¥ò+Œ-æÕþµ©^-æÕþ<µ·c‹yµ?ÝKÇójÿ”½ôhl1¯ö¯åŽu3¶˜7²X›êõØbÞÈ¢š5Óӱż‘EÕÒééØbÞÈ"7övl1odQÅ„z:¶˜Srôüâ]·qZv¶É™*U•Ïß{½ù­d îî=MOô­¿ùâ¤#‚*uâÜûû4>Äç©ÆCüýòâ<=$ùÎzÜ4$yïàw}Hr/gTÓ9x‘>„{¶ÕxûÑûä¯õ˜Q4µ–rDå¬ör[‡ÓÖìï5Ÿ˜fÔ¬™ï7Kf£Ô£ó¹(íäÖ<7HÝzéþ–ÉyªÞ»‹m«ñÝÔd4eícz8ð×Þ³¢áÀ™ùìp Ý‹ «>¸tÖþHXæ="«_¨èf8P½ô]gÁÆåëzD#´q&ü}ÿ¼ž [',Ì猉6dýàåÌL”G÷Ÿ7Ì0]¹L†;¾o>¾,>ªjIÐM¿×£óaáiåiã±S=–›W”J¡^ݺ,r¦èG·¶ç»¹†¤0нtxˆúðr“Ò;E4‚½ürOc&åWešs,.ÕËôLúi¤Í¢8j £ä3IfßFÙ0ûL’É5${kTòOIÁ6v6^©oYqÚ0­A·BôöoÖ^&9vð±áá$ßì°€$>·ó2FUÂRÕk((c]ZüL::,céÚ'ãz ÐTÆÒ_XLÓ‡h¼´¹ÆY™SÆÊr¢­§‘ 5ë¶RN”MËI[|æ²µXé! -,¾ÊãÔýÊÙäit8»(]7ge\¿”"õ@Û½ u~+yü@;¼<ÇÕp t/¥WQÁ>ǧ7>¼Œ`]~Ê  ê"'æ’RY~ õ¹ù*j¢ö¯!)•z˹*¥²^)Ûo¬‰æÊËe…kÐC„õr»Zyœ$Ø“+ëOtoòøçñr¨ï5ÔN1‰"Ö¦®/Ž˜D9¢x]qÄtœþúâˆI±¾NââˆI‘íåºâˆIÔ¯6u}qÄäxú×GL¢ˆµ©ë‹#&QD=|=qÄ$ŠX»Æ8bTïÞ¸¦8b2ЍëýNÇ«Ž"²ë|±ê(bf„¶íqĪ£ˆ|/#VE¬M•Œ#Þ©Mñpááæé·ôPamjŠR^_ýýÃ_p׎¿ÿwº{ô?Ç5sLÿgÐü§Ž™V0f¹.ýÃåÔÝ/µõÝ1svl÷´6u¸üðâj㿯Wÿ]üÏØ -'í?ÝÝ{´1¶2¦¿{Hß½36CWcÒ·é£YŸ<¤+<¬cIûÿ«ö—þ›ûK^3¢+8©ÑÕÐ7þ‡þñ˜þò“’þטiŒ={ÿÑûÆ¿~U³¬%30½±EÓ´—<' Ç~§ÓŒ%Ó ¬1Ë\òLߢÃ_rmƒîÍXrBC¥¸KAàûc_kf¸d{–ÍiÎ’å8Þ˜é/9pнd–5f:KX:Å0,‡g/ù¾ípšµå}ÛòLâ:¶›N1—B+ôõï’4ËQ?ó\ÏçƒDE§s­Ô¿éGÁ’ãê 7éæè¦v9-p‚è@¦K·@·gA¥x¡ÃYÚ¶™JùZ³\¾)?ý-Ÿ2ËŽn–;f…K¾ã˜™k°%Û7£4ÇöèlòÎtÝ”?fK®kØ:Åô{Ì¡ØA*ákÍ¥û³=/•æ9K¾íFYîûæ˜oÒWÜèi†žÒÏ|Ÿ¾¤ž =!×ò±ÀX -ìŸÝThÙv*…®2% ß%i޵仦KE}É¡âN ”‡Fèù}ò¡éÚl‹rŸNç,vÈ—à†”Ót(ŸnÒ øæèzÍ tÇ7j!Ý*gF·J–ô[%‘+ç$Ê#ú5•°$Í¥œ¤j'´Ù”øw.Y5çQèÓ•†ªRQ³µé²‡vUp9ä'®îÈ2#Ó!÷CFhTz<'•D?¥œ&ËÎ|ž†©!=!ª©²å:Ù ´5P‰Wµ¥yz¤”膔ÈI!å‡Nr|›ÉÕWdGŽ­-Ÿë|KÝ'ªËe«æ®’¼€}T*‰ê|ߌ'ÒÃ7Ù·ÐI}*6úæ}²jW°Sn”>SnŠêkà í»,‡<?+×ð•¡ðóç¥lóBÕˆˆ“¾ÖÂí0•’›!kÕî,p© ‡TnBU”=_ûvªÑ¨…b¹Ñ-Yµ&rºšu•GvÓ)&e˜É›¤qĹÃG -GÁ9Mç³µTl.Ú©³rÏL›]Õ*¦n6†W .ÙeϘŠõ¨67uuë™Ã\ž®R©3cʳpÓÍb‹¦$UB¹éq÷•‹/Ÿ—JúªË´£:éï¾j„ZT¤Ç™N2¹¬™ñO#á֎¨†—R_ët"Wo^:QUaž*‰ª¢SåÚfÛ¶t’åVTŒé^,)*ôÀI®¥ €’<î'ùÜp¢·GÕ¨tšfe§Ë–黺‹­ÛåzSIntJ3´è‡õ4²S}‹:¹®NRh>¥ê¸©[rM]×$w@Oš®QÝ;uVBum·PRn¾xn¦xùQw%Iúšç™·eÜMSõ•ê0ÖïL¢­ƒ»éDj´‘QÑ ‘ Õm!¯ëì¶CŽ©{ä -u UL‰ŒM‡©œú!]³I-TCGí¸Ñârºaš$qÔÌÓu\ê{™IÔâÜN'ùÚOðO“Dî?Yú{Q,˜»l·»))ÔY¹áiê&;uö¨-®2€{ TSp݇­“¼'”DNͲÉüè§–gEÍꀛF| -ªS¢.õ$ôY©o¥’ø§–¾ô÷̸¬q#»ÿf`hÿ§’ O×â~ êâl{'”bšú—Ü V…-IâÆ”éG¿Œí(aú¡Ž/¨ÇªÌ¯Ãq¨Hú¾ãDìèû²9¢EîŠ:P:,—ê³RÏöUÛ2Õ·ånŒƉ5?ì()Tql˰u£ž}Àñà¯T =dVWòˆ„ÅC:†@׫šz©$¾ÕPõ¦R‰\ª™NdÃrôCÝW檔 i‚ ŠÇX¶Ű8¨d;Q…鱇³LCw·’z–Z™”uNº†æh©Ÿ"ù^=Ÿ8‰]>'Å JÒ÷oÖK5¥.e'y¶²®Ê9$ÁInk} 'šº‹š|ÏÑ£È:‰ÇtmÊëh|,™ØÁ÷ïéàŸÕ%›°¹.Ò5e§§Ç,â$þ©m$ßs㚈“ØÞè¢vß‹ž–¥'1Eý@ždD ­ÄÝXkÜJ¯Ïã«SV—Lã{ðý̤0;>}¦Ã ¦ÂZéX:g\`éSDóÄ8ƒMÛF¢‰bœLÔ; ܯsô³h¢—™hîJ}¦˜*!FÜbR3Å8)0Ã(Š¢¼z:©>S,¨gŠ©’jÇ)jZ_™éÉcº<úN“™b6‡íõó¯Ïk²Þx¦j£K¦Š©B¥{fÉT1¶±hÄ#N´½úL÷x®X:©>W,•Ïã£Å#`Ñ\1[‘µ£¹blîä#TîGsŸ|«Ñôd˜ÍñÖK抱?‰ŠAò=ŽtñÌ3þ…í„ñìÞd¶»,×T=¸xº˜íÔ§ÇÇóÅrò.;aÌáö¥B!qAüM‰v)5Óu’n r}£—C©Ä0žbÔ.㣩¸ž’BÙ¦.ÄÐc®h©Ñ¨~j×§GXQ å¶bär©1Äw@'7Œhò6‚Z`Ä3”m3ª©TaØU9îéù'6Ïz³¸‘â°…èÁ‡W]ùžJôã'Áýž©®:‹z:Õ[Ôº‰c&Ü.çå‰|}Q8Ï‹:üú>BíÔR$ËQ÷[ŸM¨ºÌäh©Pqt×WyǓ梠¡+N -ê²ÕÿlR\¨S‰–žöÆIQ|€c¥tv>gTID)|±žÁ\¬O¾ÞUwÍñ‰§<朗¨‡†â4~N`FÇSnŒó(tÍø[žòFœèê°x<盓L=À_œÃ“ï<;sqN Lê[ÔY«G…Õ ®£§¬§‡§´‘ÇÁŽEÛÑsŽ{‹¦H8v}Fw%=n€ð¼²ÐfDƒQigÈÅJMàL%rƒ˜GS²‰®îzÑUÆ\UÒ9x¢ö{£õm}ÉF*2¨þæ¨øx½ð»:ÊšnéÎaý[ä[|# -5©‰VÙ$?PS Ó‰<µŸý4L/¸Š#ì|NÏrR)üKGGÓ_³ãùÍõÂ. °¢r¤§±óm*ø¤ë=mÇtõ—¤Ë#€á@º>ƒú[ÒåÀ`"]Aƒ%éò -`0®¯ Á–tùПH×OÐpIº<è¤ë#h¸%]¾È ]ÿ@£%éòàæ®s Ñ•tÙp}H×1Ä’¶½Eº^ ‚úMÒ6 ´?‡ ~•´mÀ #íÃ!¨ß%m£0¨HûoIÛ) "Ò¾‚EÒ¶ -ƒˆ´ï† A’´½À !í³!hÐ$m³0(HûkDIÛ- Ò¾‚UÒ¶ ýŽ´Ÿ† A–´ý@?#í£!hÐ%mÃЯHûgIÛ2ôÒ~‚†IÒö ý„´O† a“´M@¿ í!h%m×Ð/Hûc6IÛ4ô Òþ‚†QÒv ý€´/† a•´m€4Ò~‚†YÒö ’Hû`vIÛ8H!í!h$mç ´ï… Q´€Òþ‚FAÒvHû^IÛ:Ü4Ò~‚FIÒö7‰´Ï… Q’´½ÀM"ís!hÔ$móp“Hû\5IÛ<ÜÒþ‚FMÒ67…´¿… Q”´ÝÀM ík!h%m÷pHûZUIÛ>ÜÒ¾‚FQÒv7´¯… Q•´íÀu#íg!hT%mûpÝHûYeIÛ?\7Ò~‚FUÒ¶×´Ÿ… Q–´ýÀu"íc!h”%mÿpHûXuIû¸N¤},²¤í®i A£,iû€ëDÚÇBÐ(KÚþà:‘ö±4ê’öpHûXeIÛ?\'Ò>‚FYÒö׉´… Q–´ýÀu#íg!h%m÷pHûZEIÛ=ÜÒ¾‚FQÒv7´¯… Q“´ÍÀM"ís!h”$mïp“Hû\%IÛ;Ü$Ò>‚FEÒ¶Hû^IÛ9H í{!h$mç ´ï… a—´€$Ò>‚†YÒö ’Hû`VIÛ6H#í‡!hX%mÛÐHûbFIÛ5ôÒ¾‚†MÒ6 ý‚´?† a“´M@¿ í!hØ$mÓÐOHûdIÛ2ôÒ~‚†EÒ¶ ý†´_† a‘´-@?"í›!hÐ%mÃЯHûgtIÛ0ô+Òþ‚]Ò6 ýŒ´† A–´ý@?#í£!hP%m»ÐïHûiTIÛ. Ò¾‚QÒv ƒ€´¯† A”´ÝÀ  í¯!h$m¯0HHûl$IÛ+ Ò>‚IÒö -ƒ†´ß† A‘´­À !í·!h$m§0ˆHûnIÛ) *Òþ‚ú]Ò6 -ƒŠ´ÿ† ~—´À "í¿!¨ß%m£0ÈHûpêgIÛ' 2Ò>‚úUÒ¶ ƒŽ´‡ ~•´mÀ0 íË!¨%m—0 HûrêGIÛ% Òþ‚úMÒ6 ´?‡ ~“´MÀ° íÏ!¨ß$m“0LHûtê'IÛ# Ò>‚úEÒ¶Æ´_‡ ~‘´-À0"íÛ!¨$m‡0ŒHûvêIÛ! #Ò¾‚úAÒvÊ´‡ iIÛ +Òþ‚¤%mƒ0ÌHûx’”´ýÀ0#íã!HRÒöÃŒ´‡ IIÛ ;Ò~‚¤$m{0ìHûy’’´íÀ( íë!HBÒv£€´¯‡ IیҾ‚$$mw0*Hû{ºiIÛŒ -Òþ‚nZÒ6£‚´¿‡ ›–´ÍÀ(!íó!è&%mo0JHû|ºIIÛŒÒ~‚nJÒ¶£†´ß‡ ›’´­À¨!í÷!è¦$mk0ŠHû~º IÛŒ"Ò¾‚nBÒv£Š´ÿ‡ ë–´À¨"íÿ!èº%mc0ªHûºnIÛŒ2Òu]§¤í Fé:‚®SÒö£Žt=A×%iÛ€QGº€ ë’´mÀ¨#]@ÐuIÚ¶  §¤í -ÚÐpJÚ®h¤ë굤m -€Fº>€ ë´]ÐH×ÔkIÛt}A½–´MÐH×ÔkIÛ€é:‚z)i{ ]'@P/%mO²H× Ô+IÛ€,ÒõõJÒ¶ éº‚z%i[Eº^€ ^IÚ–d‘® ¨W’¶%ÍH× Ô IÛ€f¤ëê…¤í@>Òõu+itýAÝJÚ†#]G@P·’¶!ùH×Ô­¤m@>Òõu+iPŒtAÝHÚ~#]G@P7’¶åH×Ô¤í@1Òuu#iûPŽt=AJÚv”#]O@P§’¶åH×Ô©¤m@k¤ë -êTÒ¶ éz‚:•´íht]AHÚn´Fº®€ N$m7ª!]_@P»’¶Õ®/ ¨IÛ €jH×Ô®¤mÐØ2`¤ë jWÒ6À¨ÛU‘®/ ¨]IÛ £„´½Ã' >Òå‚Ú•´Í0ÌHÛ7üÄp!] ¨]IÛ Ã†´MÃo 7Òςڑ´½0,HÛ²´¤óT~ÎÔŽ¤í€AGÚ†ûUÒÏe˜‘~¶Ôޤí€ADÚnIÒÏjØ~žÔޤí€ACÚf]ÒÏo~†TUÒ¶À  m«Ã(ég:¨H?7ª*i[`¶ÓQô34¤ŸU•´­ÐÏHÛç(Jú™ÒÏ‚ªJÚVèW¤m‚*CúÙ@PIÛ ýˆ´]BðU­~TUÒ¶@?!m|V¤ŸU•´­Ð/HÛ"ÿÕÒyAU$m'ôÒvÁ—µ‹t~CPIÛ ÒHÛ ¿Ö)Òù AU$m'H"m|[§Hç/U‘´ …´íAðsÝ"¯ÔJÒ6€Ò¶Áßu‹t~BPIÛ HÛ¿× ¤ó‚ZIÚF¸i¤mêI—Ån‘Î?j%i঑¶9¨ÿ$]&»A:ï ¨•¤m€›DÚÞ þ–tùléü‚ V’¶ni{ƒCÒå´¤ó -‚Ê$mÜ$Òö –¤Ëk¤ó‚ZIÚF¸I¤í ¸I¤í \I—Ý"¤ó‚Ê$mÜ$Òö ‡¤Ëq#ÒùAe’¶ni{ƒ†GÒe9F: ¨LÒöÀM"moÐðIºL3ÒyAE’¶ ni›ƒ†S(Ó”/IÛ঑¶7h¸…r AYIÙRHÛ4ÜB™† Dö€$Ò6†P®!Hë¦mi¤m¡LC£®›²úi›ƒFO(×Ð(ë&Ê?ý„´ÍA£)”ihuå€~DÚæ ÑÊ44JºŽò@¿#mw„2 Šz]Ö¤í‚Pž¡QP/Ë9ƒ‚´ÝAP,”ih˜Õ«ò À !m{”Ê34¬ê…¿`‘¶=jÊ34lê•¿`‘¶?jÊ24lê¥Ï`¶=*Ê34,êµß`¶?*Ê24èº.ß À  mƒÔJ(ÇÐ ëº}8ýŽ´ BP¡ Cƒ¨›òãô3ÒvAU…ò šnÚŸÐHÛ!µ#”]hP$åÓè'¤í‚ h%íÛè¤m‚ h%íÛè¤m‚ hØ$í×è'¤í‚ hØ$í×è'¤í‚ h˜$íÓè7¤m‚ hX$íÏèG¤í‚ hX$íÏèW¤m‚ hÐ%íÇèg¤í‚ hÐ%íÇèg¤í‚ h%íÃèw¤m‚ hP%í¿¤í‚ hP%í¿¤í‚ h%í»$¤í‚ hÐ$í·$¤í‚ h$í³4¤m‚ hP$í¯D¤í‚ hP$í¯T¤m‚ ¨ß%í§d¤í‚ ¨ß%í§d¤í‚ ¨Ÿ%í£¤í‚ ¨_%íŸ¤í‚ ¨%훤m‚ ¨ß$í—6¤m‚ ¨Ÿ$í“6¤m‚ ¨_$íV¤m‚ ¨$í‹V¤m‚ HZÒ~€aGÚÆ!‚$%íƒv¤m‚ HJÒþ€QAÚÖ!‚nZÒ~€QBÚÞ!‚nZÒ~€QCÚæ!‚nJÒþ€QDÚî!‚nJÒþ€QEÚö!‚®[Ò~€QGÚ@]§¤},£Ž´€ º.IûW€FÚ@õZÒ~EÚ'@õRÒ>EÚ'@õJÒþ´o€ ê…¤}) iÿAÔ¤}( iAÔ¤}( 5Ò~‚ ¨IûN@u¤ýAP»’ö›€ööAU%í/í#í7 ‚ªJÚ_:CÚw@µ’´Ÿt‡´ *’´ôi_A”'ißèÒþ‚ (-iŸè-Ò>‚ (-iŸè=Ò~‚ ˆ%í ׇ´ ’öƒ€ëEÚÇ@4º’ö€ëGÚÏ@4º’ö€›AÚ×@4z’ö{€›EÚç@4Z’öy€›GÚï@4’öu¤}AÃ/i?EÚA4Ü’öqy¤ýAÃ)ißè¤ýAÃ'i¿è¤ýAÃ%iŸè?¤ýAÃ#ièO¤}Aƒ/i?è_¤ýAƒ/i?èo¤}Aƒ+iÿ ¤}Aƒ)iß ¤}Aƒ'i¿,¤}Aƒ%iŸ<¤ýAƒ!i_L¤}Aƒ!i_\¤ýAý-i|¤ýAý)iߤýAý'i¿¤ýAý%iŸ>¤ýAý#iN¤}Aò’öC€áEÚ¿A$/i?n¤}Ar’ö?€Ñ@Ú×A$#iß ¤}A7/i¿-¤}A7+iŸ-¤}A7'iM¤}A7#i_]¤ýA×+im¤} A×+iÒ~‚ ë‘´o€iAPï%íW FÚBÔ[IûhDÚ/BÔ;IûÈCÚ7BÔ½¤ý!í!ê^Ò~Êö‘u.iÿ­ö“u.iÿUö•µ/i¿í í3!jOÒ>ÚAÚgBT]Òþ:AÚwBTMÒ¾:AÚwBÔZÒ~ºAÚ‡BT.iÝ"íG!Ê—´o€^ íK!Ê—´o€^!íO!ÊJÚ'@/‘ö©e%í ×HûU‚´¤}\Òþ‚ ´3Ë´… Q—´€ëFÚÏBÐ(KÚþຑö³4ª’¶}¸)¤ý-¢¤ín -i A£&i›€›FÚïBÐ(IÚÞ঑ö»4*’¶uBÚÿBÐ(HÚÎ@i AÃ,iûi¤ý0 ³¤íúi_ AÃ(i»€~AÚCÐ0JÚ® ŸöÉ4L’¶gè7¤ý2 “¤íúiß Aà i;€~EÚ?CÐ0HÚŽ Ÿ‘öÑ4È’¶_¤}5 ª¤mi_ Aƒ(i»€ABÚgCРIÚf`öÙ4H’¶WD¤}7 Фmiß Aƒ i;€AFÚ‡CP¿KÚF`‘öáÔÏ’¶O¤}9õ«¤m†i_Aý(i»€aBÚ§CP¿IÚ&`˜öéÔO’¶GF¤};õ‹¤m†ißAý i;€aFÚÇC´¤m†i?AR’¶=¤}=IIÚö`Tö÷tÓ’¶9%¤}>Ý´¤mF i¿A7%i[€QDÚ÷CÐMIÚÖ`T‘öÿtÝ’¶1e¤ëºnIÛŒ:Òõ]—¤m ÚÐðJÚ¶h¤ë굤m -@‚tA½–´MÈ"]/@P¯$mKš‘® ¨W’¶%ùH×Ô­¤m@1Òuu+iPŽt=AJÚv´Fº®€ N%m;ª!]_@P»’¶Õ‘®3 ¨]IÛ €ö®7 ¨ª¤m@ûH×ÐÿÏÞY@×u\{¿i__)iR:qÀ33333ȶÌÌÌ,3³e”lËl™™™™;v.¤íûÒ|{ï™9g]]Ù’Ž}µÿkMb©o­çèþ´yöð öøý»Âb±X¬g“ßþƒŸ¸Žß¿#,‹ÅzvùíCøð‰ëøý;Âb±X¬ç“ß~„¯ã÷ï‹Åb±ž_~û>|¼Žß¿,‹ÅJùíOøð±¿'X,‹•pòÛ§ðác?~ÿN°X,+aå·_áÃG¿X,‹•ðòÛ·ðᣎ߿ ,‹ÅJùí_øðñûw€Åb±X‰'¿} >~ÿ°X,+qå·Ÿá“|ßì³X,+ñå·¯á“|ßì³X,+iä·¿á“üŽß̳X,+éä·Ïá“üŽß̳X,+iå·ßᓼŽß¼³X,+iå·ßá“|Žß¬³X,Ëùíø$ã7ç,‹ÅòG~û>¡üfœÅb±XþÊo?Ä'´ß|³X,Ë_ùí‡ø„îñ›m‹Åb½òÛñ Íã7×,‹Åz1ä·?âzÇo¦Y,‹õbÉo¿Ä'´Žß<³X,ëÅ’ß~‰Oè¿Yf±X,Ö‹)¿ýŸÐ8~sÌb±X¬S~û'>/ÿñ›a‹Åb½ØòÛOñy¹ßü²X,ëÅ–ß~ŠÏË{üf—Åb±X/‡üöW|^Îã7·,‹Åz9ä·¿âóò¿™e±X,ÖË%¿ýŸ—ëøÍ+‹Åb±^.ùí·ø¼<ÇoVY,‹õrÊoÿÅçå8~sÊb±X¬—S~û/>/þñ›Q‹Åb½ÜòÛñy±ß|²X,ëå–ß~ŒÏ‹{üf“Åb±X¡!¿ýŸóøÍ%‹Åb±BC~û3>/æñ›K‹Åb…Žüöi|^¬ã7,‹Å --ùí×ø¼XÇoY,‹zòÛ·ñy1Žß²X,+4å·ãób¿9d±X,VèÊoLJc ‹Åb…®üös|8Î`±X,VhËo_LJc ‹Åb…®üöw|8Î`±X,VèÊoLJc ‹Åb…¶üö{|8Î`±X,VèÊo¿Ç‡c ‹Åb…¶üö|8Î`±X,VèÊoÿLJc ‹Åb…¶üöƒ|8Î`±X,VèÊo?ȇc ‹Åb…¶üö‡|8Î`±X,VèÊoȇã ‹Åb…¶üö‰|8Æ`±X,VèÊo¿È‡ã ‹Åb…®üö‹|8Æ`±X,VhËoÿȇã ‹Åb…®üö|8Æ`±X,VhËo?ɇã ‹Åb…®üö“|8Æ`±X,VhËoɇã ‹Åb…®üö—|8Æ`±X,VhËo¿É‡ã ‹Åb…®üö›|8Æ`±X,VhËoÿɇã ‹Åb…®üöŸ|8Æ`±X,VèÊoʇã ‹Åb…¶üö£|8Î`±X,VèÊo?ʇc ‹Åb…¶üö§|8Î`±X,VèÊoʇc ‹Åb…¶üö«|8Î`±X,VèÊo¿Ê‡c ‹Åb…¶üö¯|8Î`±X,VèÊoÿʇc ‹Åb…®üö±|8Î`±X,VhËo?›œßŸ=‹Åb±X‰-¿}mr>~ö,‹Åb%…üö·Éñøý™³X,‹•TòÛç&Çã÷gÎb±X,VRÊo¿›ÜŽßŸ7‹Åb±XI)¿ýnr:~Ö,‹Åb%µüö½ÉéøýY³X,‹å‡üö¿Éáøý³X,‹å—üöÁÉáøý³X,‹å§üöá|üþlY,‹Åò[~ûâP>~¶,‹Åb½òÛ‡âñû3e±X,ëE‘ß>9ߟ)‹Åb±X/’üöË¡tüþ,Y,‹ÅzÑä·o¥ã÷gÉb±X,Ö‹(¿ýs(¿?C‹Åb±^Tùí£Cáøý²X,‹õ"Ëo?ý2¿?;‹Åb±^tùí«_æã÷gÇb±X,ÖË ¿ýõËxüþÌX,‹ÅzYä·Ï~ߟ‹Åb±X/“üöÛ/Óñû³b±X,ëe“ß¾ûe:~V,‹Åb½ŒòÛ¿ ÇïψÅb±X¬—U~ûð—áøý±X,‹õ2Ëo?þ"¿?‹Åb±^vùíË_äã÷gÃb±X,V(Èoþ"¿?‹Åb±BE~ûôíøýy°X,‹jòÛ·¿HÇïÏ‚Åb±X¬P”ßþýE8~,‹Åb…ªüöñ~¿þ,‹Åb…ºüöõg°X,‹ÚòÛßsŒÁb±X,VhËo¿Ï1‹Åb±X¡+¿}?Ç,‹Åb…¾üŽ8Æ`±X,+´åw<À1‹Åb±X¡-¿ãŽ/X,‹Å -}ù'pŒÁb±X,VèËï˜ã ‹Åb±B_~Ç_°X,‹úò;¦àø‚Åb±X¬ÐÇ,‹Åb±[[°X,‹ÅJ*qlÁb±X,+©Ä1‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±X,‹Åb±¤^ $¿ÿr¬¤W@ ˜‘d©ø2Á€$úÌ©‹ñHFŠ1¡¬ ˆø•®` ñû¿‹õ< -ů‚Pœ|øýÈzyQa~ðÿH^„0/¹Ü¨pñko¹0âM‡ßÿ±¬`傅΄ññÿ¯·œŒØù`:^6¢Â -ÄoÉΈ…w:üþ/g’ + ~+õ;›Ô÷­Œè|xÓá÷>ËC.XX¨0€ ~/õ]꛿7)Ñ1ñÐé`8^pY¹PXH*$Š¢àU¡×tÉï½jb¢±àˆ¿,«¼°T($І? -½®K~ï&’ɇÂÃfãE”  E…Á„¤á ÔŸœ¢ï¿¡01|˜xXè`6^Hé\Hsab!©L"àóÿóŸÿüÔ_í¢ïÂÿúgI‰ „ø°á¡Ñá„ÃïŸ ËÁ…0Š -„B"A<¼ z ô¶]øÍ·à””!’²„‡¢Cz ³ñ"ÉÉ™ ’ -d‘ ÞyçwAï)¥0þ„ß~þg¢„Ñø0¬‡¤Ã0šá`6^¹p¡ÌaAÆBPAP ÈCŠ)Þÿý@)­Âo}ÿü$H¢ø<¤st˜ph†ƒÙð[®\Hs!°c©@(ˆ "eÊ?üè£>}b~ þ—?ü)!DÞ3ø < ãaƒÃ0:~ÿ„’§¼¹sN±@c!¨@(€ @qH•*uêÔiÒ¤Ik|/MêÔ©R¥BJ Dñx€õ ãA¶L‡ ‡2߲̆ä©v.ȉ(,€ -pX DxH—îÓO?MŸ>(£.üFúôéáL—(AD h@$Âx íÓaÂ! ±Áhø'»Á\üNr!ˆÄ‚¨(ˆ hÈ”)sæ,Y²dÍš5›.ø:+|;sæL™@D‚|< üt(Ó!à†ƒ|ŠÕløý£J^rq$‚‹?H.L, * -`²eÏž#GΜ¹råÊm|'gΜ9rdϘ"@Á‡ÂCÑAŽEXƒ 7³á÷+Éb0”#A?¢qab¡¨ÈˆL 9‡ú8Uj02‚­* -b¢p‘¢C‰’%K•*]ºL™2eMÁWeJ—.UªdÉ%Š$E - `Cˆ‡¢ƒL‡„Ãʆ0Ê£0I&Wƒ†Á¸4‚ֱȖ=g®L †C±Ahü^ó(ŒFRÉZÃЉŠ/€ p#Ÿ¤J“.}†L‹üÀV eË &ªµjש[·^ýúõ4hÐÐ|Q¿~½zuëÖ©]»VÍÕ«UB*I>°HGÎŽtiS§BÃl€O1͆@ƒýI’ÉáILƒùˆÁD`.²f—X+Q©¨TÌ!Q¯~ƒ†7iÒ´Y³fÍ›7oa¾hÖ¬iÓ&M7jÔ°Aýz@RíàÖé(/oî\GÆ h8€ ô)o½ fCGƒFRÉÍ“hä#èG€‹OÉ\äÊ“°(U¦\ùŠ@EZµ D¢i³æ-ÂZ¶jÕ:<<¼M›¶¦Ú´iÞºu«V-ÃÂZ4oÖ!@€£By¤£xÑ"… -äÏ›ÜJ–L‚ FÉl(ò»ßþÆð'l4[V0”'A0D€ùˆÎEÞü -XT¨TlEíºõ4jÜ´"Ñ:¼MÛví;tìØ©SçÎ]H]Å¿:wîÔ©cÇÚ·o×¶Mxk >Õ‘ŽreK—,^ àÈ— ±‘ú2mØÑ`£‘èr†”’ Á Gyê‡wèFÀ\€)XÔ¨U§^ƒFMš6kÙ:œºvëÞ£GÏž½zõõAáz÷êÕ³gÝ»wëÚ¥s§Ž ÀâÑ >ÒQ­JåŠåŽ¢… æl¤OGf¢ l4]¶Ãô$h0БéÒ . -"sN„°h T´jݦ]ûމ={õîÓ·o¿þ 8hРÁJðçAп¿¾}z#àðhÑé¨W§vM0Ê•)U¢˜`â i6k˜¡Ä•=ÄžÄ4èH î?\.Z¢š‹ê5ëÔkˆX`)ŠÞ}€ˆƒ:lØð#FŽ…=z4ý{äÈ#†6lèÀdà€þýÀàAt4kÒ¸!˜°`8ˆ|yrf'³¡ÐÀ0ÔÅhøý Q9B ÊI(%1 ™³æ {Q¢tÙ -`.j×mШió–€PѽG¯>}û4x(,Œ;nÜøñ&DDDL‚?EL˜0~ü¸qcÇ)#G GB€0 `>­ÂÀt€å¨Y½J%d£h¡yÁldÊ ¡ñÆëèOØh$œ`(ObŒ4é2dÊšâÎBh/*T®†æ¢I³°V„PÑoÀ@€bÄH@bü„ˆ‰“&Mž2uê´iÓ§OŸahúôiÓ¦N2eòäI#&ŒGBF`@ôë ttí"àhÖ¤aý:µªƒÝ(S²x‘‚ùrçÈ–Y¢ñîÛ¼¾¡ü  &#ñä{"äI„Á€ŒIÎ<w—\ÔGsÞ¶aѨKPLœ4eê´é3fΚ={Μ¹sçÍ›Z@ÿœ7oÞܹsæÌž5kæ „„>F>éèÓ«G·ÎGë°æMÕ¯S³ZåŠåJƒÙÈŸ'§Dト†æO¸¦‘x²%%2ö”žD39rç+¨sÑ¢U›ö»t',†ã&DÀÄì9sçÍ_°pÑ¢ÅK–,‰Œ\ŠZFÿŒŒŒ\²dñâE‹.JæÎ™€L™<1bü¸1`<†F8ºwíÔ¡më–Í›4¬W»FÕJå˘h¤† EøÃh°;I4ÙÁøƒŠ[E³dƒQ¸XÉ2å+I.Z“¹èݰ;¨˜6 &€ˆÈ¥Ë–¯X±råªUQQѦ¢¢¢V­Z¹rÅòåË€’%‹ sgÏš1}ê”IãÇŽÓ–£7`#¬Yãh6$k¤O›êcáOÈhp š¨²g«{ʃ< ¦$Ò`”(]®"ÄŠ‹n=ûô4t8b1y*P1w@L¬X¹**zõê5kcbÖ­[·~ýú Bð§õëÖÅĬ]»fõêè¨U@‚x̃tLžpŒ6x ²Ñ©}›V-š4¬[K¢‘/wö,?E¢Œ†Ãøý£ 19ÀP!V=S¤ü(UšO3f†£Œ²ªT¯U¯!qn¤ï€ÁÃFŽ1 °˜T,‰\¶|åªh@pذqÓ¦Í[¶lݺ5µþ _mÙ²yó¦M“u1@ð±|Yä’E çÏ=súÔIãÆŒ6ÙèÒ±mëMÖC4J—(R0¯ô'Êh°;I\¹'%…àILƒŽ¤nƒ&Í[ .s1aâRµzM̺õ€ˆ­±Û¶mß±cçÎ]»v£öìÙCÿÞµk×Î;vlP¶nÙŒ€ÑQ+W,‹\ pÌš1urÄx`cð€>=»vjFhT*Wªxaò'Êh°;Il¹‚¡bOÌIÀ“PJBIãf-Û´ïܽW?äbx‘³ç"+VE6nÞ Hìܵ{ÏÞ½ûöíßàÀƒ¨C‡è_ìß¿oßÞ½ÀÉ®ÈVÀcýºµ«£W‹Ì›=sÚäˆq£‡Ø·W7FÝšU+–-Y´`¾\Ù…ÑHñ®r'¢¤Ád$¼¼ÀÀØC ÈIÀ“ä-P¤xérh06 oש[O´£‹i3çÌ_¸±²qóÖX€˜Ø·ÿ pøÈ‘£G;vì¸ü¾säÈaàÙ€Hdžu1k¢V.\ l̘:qü˜CöC4Z·hÒ N ð'Âh`¤ñ¡r'h$šÜÀÀlUÄžbdÉž+_Á¢%Ë@„IÇ®=ú L\LŸ…æbùÊè5ë6l[±,@HÇOœ8yòÔ©Ó§OŸ1_:uòä‰@ÉÑ#‡<°Ÿèضu˦ 1k¢E æÌœ6i˜‘€FϮ۶jÖü ¼¹²az‚îÄh0 -w0dR±§1À“”‚”D p$½û6j¬àbñÒQ«cÖÛw"> -Lœ<8œ=wîüùó.\¸h -¾‚ï;w 9€`=öíE8ˆ¨Ë–,˜;kÚdB£o.íÃÃÀhTF#7º #… 4˜ŒÄQ 0dìI9IñÒå+ÉБc&Lž\,YFæbóÖm;wïÝw©@(Ξ=wp¸téòå+W®‚®I៯\¹rùò¥KÀÈyàãô©ÇÓ±oÏ®±[6®_½jÙ’…€Xƒû÷îÖ FÝšU*”)Q¤@žY2R !òVNNGÀÀ¤$5†¹òBNž¤FM¤Á€cü¤i3k×oÜ»c×Àâȱã'O!(.!×®]¿~ãÆÍ›7oi‚/oܸqý:@rù:ÀvËlìܾÌÆêUh5fN8nÔ0a4Z4®OîÉ€@à Aerò?‚&¨ô&š–1°î‰`ˆ£,z’F’ ÁG2nâ”sæ/^Š\l"s!°8}¨(®"ÄíÛwîܹ{÷î=Mðå;·o#"7®ƒ¹Œpœ9} ÇÁý{wmÝ h¬\¶xþœàO†î׫kÇ6aMԆĜLF¢Ë *ˆCR±gî|bTªV }®„_|øèÑ£/4Á—>DD ãÖMbì†DcûÖëÈhÌž6iܨ¡útÇ@ƒÈ(Àfpš`ŠŒ¬9òb4lŠž¤W?HUÑ`Ì_¼låê˜[¶ .Nž> NäÊÕë7ÀVÅCâñãÇO@_j¯?FB²qõÊ¥ çNŸD‡²Æš¨åKÌž>y<@F;I†‹7áÜ$Á (öÄ£UÛNÝz2r¬0àH6lŽE?B\\¸xùê5Ä©CPOŸ~…úÚ|ñôéS$ðãlܹfãÒù³§O=|`ïÎm›7¬2æd›aÄ.¹ “‘`Š ŒL ˆ=ë5‚£s÷>àI %Aƒ± -ÉŽÝŠ‹KàEÀ‰Ü%,ˆ€ð è[Ôw ú|ƒ<ž€éxøùg«—/‚?£±w×¶-b¢W Í˜4^z“–V,+²V­žÁ•®—;ÒÀÀ‚x HJ ölѺ}ÈIГ̘# Æ–m»ö8|ì„àÌÅÝ{ˆø ¡ "‡ïQ?á"€Àl4nݸvåâù3§ŽK2Ö‚78c⸑Cú÷î¹I“úµªV(#*]z T¯ŽsšRi‰Þv§:†e«”@ìÙ¡kÏ~“DL )ÉÊÕë6ÅîØ³ÿÐѧΞ'.Ð\€µ*  ™@ü õwýIBpkÜ$wd7Y¿vÕ²E›DŒ1¸_Ï.í[7oT·F•ò¥ŠÊ—+[&Õ71:jÜkM0YÀ2TíÍ·­`Tª†I Æžý‡Œ7qÚ¬ù‹—G ƒqðÈñ“gÎ_”\|ö9:ÄlR!@ þ¡IBp_ƒÕøâ Ix“à ݹIäÂ93&- p&f”Õà NME†/Q¥OŒ÷í``R±'†Óç,O³" 4§Ï]¸|ÕàB`ÆBP!‘ø'êG!ü£ÀØh ÑxôÉ@orî̉£‡öíÞ¾e#8“ÅógMÃrFÿ^];„·h\Ït&„3á0#ä¬p¹Q®Ru£m'ˆ=‡?yÆÜ…àIÖCJ²÷À‘ãäH î4¸ k!©L ÿ2%ñ 6 ã)6⌫—/€3ÁÔÄi2,9k&¯&<;“ç–3ú|õ5 ŒŒuL0&L™9wѲUàI¶ïÚw ÆÅËàH4.ÈZ,L&þ­IÂAh Õøî[´àM܃ÜÐGîÛµâÏUËkQšŒš¦ÉÐsÖ?ð…“”'ïÚÁhجU»Î= )¡Øsñ²¨µàIöì?|ì$Œ«×oݹ÷@ã±T(&þcÊdÐ £Þäé— -“qéÂYˆ2öïÙ±ucL´‘˜  ÄDoš“ñžÇ¨ß?Û—Z.i Í|RÛ=µ(pÙÁ˜8uÖü%Ë£c6 O" :HS .ÀZH,L(þOÊdCñ·¿‘ÉxŠ&ƒ¢ éKö /‰^¹`Õ2DøÙ¬a]šèÂÙqc8㯜™$´\À  -×»)°íž1 ¸ øÔÀ;qÚìKVD¯Û„9ÉÑg¤ÁGâà‚°Ð™°²FƒÈ&£ ô%×._EZ"ÀH`.QF€ÑÖÆŽ={bˆq÷>Œ'Ê`8¹øÉ"I†22È 0(Æ0°*NÁçêcL=LºŒüòæâ‡ïÛo.²3Iy€A… #[®|…‹—©XµÕ1(Æ`¬Ý°Á8qbO1È“HƒyªÉÅÿsr!Ð&ÃÆã/dðIY‰LWW½w>1Æ0ÁÈ!ÀøÀØ«ÂwNîùª,d¤Ï`*^ºBÕZõ›(0&`ȤD„_eºzòØ¡ý»·¢ÁŠƒ\®vn׺¹ˆ1ЕH0dÂú'µp‡Wû%ˆ<Ó’÷S~,jâ…Š 0ÂÚvêNéªcãÖ&%×nB² -!†ð$”¨:¸ø¯)Ód¸€uŒ«ØF;zHT>£–-ž7K¸hŽKÞ?*¦]‚w,Ua“ñüòJK(_¥Ïb¥ÊW©Y¯qX›NÝûPà Äž˜“Ob:7.60dºŠm´[׌Gq$cóú5«–Rã}ìˆÁý{uëH.ó6«¼n'!d÷%*-|U2 --U®rz[„wìÞgàð1V0 [¥¤D†“äB7&Xࢤä–KR2“’~=»vÀ’xxùo,fÏœÁ ö%ϯ€i‰(d”0ê6jÞ¡›c¶ˆ1vî=t À¸"“’/Eˆ¡Eî\è&C‚ñƒCÔ·dE\Åž4Ý7l`Ÿ2[¥ûŠE -È¥|¦À½ã60˜Œç•K¡Fud¾Š®êu5oÝ¡kïÃÆD`åÒU´ÇN½hCz’@\H0̬D$%&%ZìIÃ}jƒB ˆ=Í•9³fJonçb0X¶ CEŸfZ"  ›µnßµ×€a£#¦ÎZ¹rÈJN -0 [±§Í“¸raÃÈVER"bσûÌc -vÝû÷êŠ!F#bP¶ªvsÙÀ°¾SÀd<«l¾D>!-¡|UT¸ZµëÒ«ÿÐѦΚ`l±€ñ… Œ\XÀ°$%2ö”qsN‡Ê[=»t×B {¦¢% TÇ`0V®Ñ'uK°fæ«XúìÙoȨñSfÎ_²bõú-;öPði‚aÆž¦Á0qøÙB†Œ§O0)qª{îˆ5B YÅå-KˆaÄžrq°Å•°/y.Š>©i æ«XÈèÑoðÈñ“gÎ#0¶»‚¡…ƒñóÏ?{Z -)uÏc‡÷ï†ØS„tƒU,DhÞH„ÅÄV.b¤x“ç»& ÆóÉ;ú¤´$»LK _mÓ±{ßÁ#ÆMš1wñòèõ›·ï9pää ‘” OòóÏ&¶lUÅžTÞÂØs ]€ƒ.½whÓ²iúàI¨ŠAå-#Ä ÷ 䛜60˜Œg•i2d‰K>´óՎݰÂ5iúÜEË£×mÞ¾ûÀ‘g°ŽaC 1,\x![hfì©Ê[æ”Î ±'£IÓ`Ó«)Ñ“h±§=+a0žY.A†5úÄ6Zµ: E¾**\Ë¢b6mÛ½À8oC=5ƒñóÏ:60̤Č=EyË 1ðŽ¢™¬¢'Á1QŰ…ÿk{¨•ÁxV¹öè³vƒf­Úw¡|•*\Æ®ý‡Ÿ>ùÖ1,`8B ²¾e”1dR"êžFyK¿UÒMKVq[°%Yu† F(@Ñg^Š>1-i×¹§ÈWD®Z»1v×>ãöJ¨ŽáÃÆ… ³ŒAšuOŒ=ÅÝqCB K=¼ÖÃE£$%=œögbü–CŒ„”wAµO¯¬ÒÌWE!cëÎ}‡Ž>§ñŒ1‚Ñ­šIÉÉc‡DyKõaˆÑY éˆý}ò˜õp«'á#ådˆè3›}RZ2rœÈW©ôyìÔ¹KW±íþè±7?džÙ)Á)£î)Ê[8¤3T¬NÁd»ö0lÌÄésD¾ºçÕÄŒ‡ØD“®gã«§F¶J=w3öœ:‘f1dˆáì¬:< Œ„“£]‚AÍ}ŠÎ»¨}Ñ'¦%f¾Š®{ß?3*[¥a š÷”±'–·õ³ ˆË²gÚÔèêI8'I(2°Ä%jŸ2úœ0E¥%¯Ê -×Ã/$ÿÐëAa)c\¹h$%kV-C:âU1T²j4ÐÞãœ$1å2^³b$‡¸Dô9ÉÊ5c!-¡BÆM*dà<†? •1¨S"’3ö” Õæh1¼åÚ@cO’°²&¬.AFq9’AC\}bZ‚3\§Î¹2‚ìˆc}Ke«bx])‘±'mà°5Dˆa}A«[‰'× ã¯Ö ï–àHFŸÓ0úiÉ©s–àÐ糂! Ÿ_h`ˆa Ü€€uÏÔAÃW°Ša†úŒÎk\ÄHÙ|‰=È kÍzMp$ƒjŸ³.б¤%ÖBF|Áõ­Û7¯ÉÚÎXJJæÍšªbÏÖ-´%Z²Êž$QeOX]ƒ ,qaô9’jŸ«Öêi‰‘¯:Áð >²ð‰M÷«—Îc m cPR"î¹cì‰ ä‹6"ÄÉjOâ÷õå—Ó—¼þ†j—hA†(q7yƼ%+ÖlÝl£9òÕgC–1D§D&%*öUÅ0’U—œ„ FBÉ5a¥™ kÑ;ïc'NµOŒ>Eí‘–<+¢¾%Ê*[Åa HJ¨î©bÏÂl!ÆÚ({’„–2zUœ?µJ†dDÐHÖ>U·Dk¼kùj`ü]ãÖ Ñt0°…†Ùª‘”àö-|~bOqiÕ#Ä`O’°òHXS|@mA•¸h$c÷#Fôi¤%ÿòŒ>ãCNcì§2†™­ÒŠOs1†íÒªxïÝy£„ÁH¹?µ™ l—èA†,qíÚX‹>¿v>ㆬoaƒöõ•ë¢[¨ºgAšê£F‰#YeO’ðrú•°Ê™ 1¬#ƒŒIÓe‰KÖ>èó™ÁxdƒÊtYîW[Ù0öÄF‰=Ä`O’(²ø³ÃªVÙ.if‹–E¯Û"G2ŒèÓ5-‰ Ñu—­1¦CMw£ŒAëṳ́„î&RìøB ƒ‘ òê°Ê„•î£a»Ädˆ‘ #ú ˜–ÆÑCF} ËÃÔ‚Oë“Í*ö”!'«‰&›/ÑŠŸ"a•3zA Ö#'ôÚgà´$80dás™ 1Œ¡íÞÂØSÜr÷1üþ†Š¼‹Ÿi>•Uq1“!Ú%F¡:ïjŸÁƒqY‚! -Ÿs2†ÈVõm}Ÿ8n¹sˆ‘8ò.~b‡U$¬5ê6Ó*Ñë6SAw9’ñ¼`œñƒ²Õ²j¿§Q÷ä#ñåêKĬ8]"(Vš?ÅLÆx=È0K\îµÏxƒAó[K ­Œan–I‰WìÉ!FÂÊÓ—È+%¬FUÛ%F%ÃdÈËh.ÑgüÀP…Oã ›Š"[¥¤Ä{rˆ‘xrñ%bZG&¬àK0aŪ¸ 2d»äøi 2DƒõÛï¬µÏø€qç–ÇÀ‰Ï5 - YÆh¬-—;ÄÕê­×8ÄHd¹ûYü”V3a:[o—\»L4§s†Yß¹>3[•ë=ÝVo1 -7_bNë`ñ³BÕZzÂ*f2¨]¢ÿð 2<*Ÿç[6à5´9fE\< ˆ`X{­Þb0Rî¾ÄRüÄ«HX'͘·xÅš [wí£vIÀ #^`ˆqŒ5+±‡`ȧxÀF”1([õLJ˜‹W_"‹Ÿf‡•ªâ4øI3F%#`aã?ú<Ÿ.`¨Š8>ECe«¸C®ÿ-Çž‰)o_’6}fUü¤«JX£b6áà'µK܃ —è3>`Œ1Á iŒBªŒ!²UkR±gb) /¡i,~ª«‘°bU\µK‚2¼ÁÀtÕ †ño¹R¢én¶Ðþô†5)áØ3q§/Ái£ø‰+—V¯—Uñ+׃2â †l•`E\{ñ*¥-[å#1„/ÁiQü´$¬bãRpAF`õ£Z%ËShnÄŒÄR<|‰JX7Š„«âr&#@¡GŸV0Ä—*pCÕ·<Ê Fb(_"&?…/±tX)a5.*z.`ü]^8ŠŒŠeKŠŠ¸ù|–­rR’˜ -àKÔP†ô%ªøIc\2auÌdãÿ\À¸âF¼Ÿ`ÈŠ¸ýÁ+ãù#ѧ/)/}‰µøiKXÿîåKâã¬Ô\ÕÞáµ¾„öNJYAø1”aLë`ñsÿa=a dã¡ vW`ˆæªñ@³z;ñUÞ,Ÿ -Ú—ˆFÚåKpVÜLX=g2ÜÀøíÇ õ¨&Mcp¶šTŠ—/¡Õ:1›¶Éâ§ž°ºÏdØÁø×jÕ’¸»zåb0`¤S`ü‰ÁH2ÅË—LR—›EñStX&¬Ž.š±ƒËãŽöQÛÝ Ù*a0’R¦/¡û%zËéKèzÉzáKdñ3pªL†Yá2Àø o»Ó…#cm`0Œ‡»Œ¤’Õd¸ú’Zº/ÁÉÏí´Z} ÝT °: Ôw7Á8‡7Ñpx•˜Ç°Aãfë[I'[øŒ/¡Fš˜Öyðyà„Õ½ôùÍ׸œíÞÜA`Ä -0¦K0Ú—1Y–ðSÍ»õK¤/¡¡Œ}‡ŽÓ´Ž­øŒÿÓkâ´µï&‚qüо]táH‚!º«qÁõ­D—=ü|ío˜3Á.y‰ô%ÇN»|MÞzW¾Äd¸2TíîàÂÅ)»b7ÅཛྷùTm÷ÚÕ¼Ààç®UŽR†q¿Äè½Ë‹GXã2‡2¤/ÑŠŸ. «w…KÍöÛ3&1¸o > Ÿdó%Ðï—xû’Ú/Á[ïî¾ÄQÈÐjâÔwÇ®ý»i ‚FgÕDÓ³w0Ø“$¢\|‰¸÷nÌ«Þ;öKÔ€Ÿ¸ªx+®â§%_U… *}ªöê‰#voß¼× àM´ÁýztnÇ`¼rõ%oÉ»Št¿DõÞãð%nÅO·|U•>e{õÈ=Û7¯_k&9¤_ÏÎíZã· FIa-‹wó,*f‚qŽ‹ú%v_òE°¾ÄVÈ0kâ{¶oY¿zÅ’y3§Œ5¤Ï.8 Œƒ:®`p-ÉäVèKVïK¼ ªôIÍcþ¬)F íß«K{š¯„ƒ: †Ÿò*‹;| ÍqÅË—x2žˆ -–>E³deäüYS'Œ: W×ö­ŒBžequï=_âÑH‹+_½dÔÄWF.˜55bô°½»vhݼQšó †Oò,‹«*å*Ûf‚-5.O_ò_íµf­ñn䫪…¥Ïµ«–.˜=-b̰½»uÇ»«•ÅõÔa0ü‘KYÜ×É«µÑý9ÇåêK܆2,A†ºðùªîÓ*\¢‹6føÀ>Ý:†· 0J`|È`ø"÷²¸xÔ[ìP¡{ïx¿D΋~ õÞä%ÿ ¯>Vùª¨paéSuÑ:¶iѸ^Í*åK+(§Ä?Tmw#iåQ7[¬´C¥ÞU´úê½ÇíKi‰l¼ãpß *dPéS5KÚ„I0 -9Áø=ƒ‘„²”2ÜÊâr‡ -ÞU”3ÁAå%Jö´äꯪ|^¾˜–pQ³$¬I}\œB—ší`üŽÁH:,eÈqÚ¡‚÷ÞÍû%qûf·ï4‘a2dé›%8Q¡tq#}Z¼>À%q_d/eüÑ,e /Á«Ø¡‚‹Ú„/Ù¶Û˜ãòö%&ÚòKZrN÷­Y¹d¾,}R³2ŠÎ'n»ôÁ{ï0~(`)Ãh±â\ú)î*3ÁAû#úTi‰l¼S¾J…Œ £±ôÙ®U3Ñw/œ?wvÆ[ † -TÊÐËâÝú ’;Tôû%Áú[ô)û«"_¥q`ªp™¥OœÔÉž9ƒ/1I­8K²,ÞkÀ0±Cż«Œ/qDŸfZBwÊW©1l QúÄš8íàJ¥.5Sâ FR)ÎR†*‹÷2Šöqá½wÚ+î*ÆáK\Šâ–´ÄÌW‡êÓ­ƒ¬pQM\Œp1~ɽ”aN‹eq9ú…›ÅÍ»ŠOž~§/1fu´n ŠË´dñ<œáÔWT¸DéSuÑR¼Ë`ø#·ð“Jòæ‘Y7Æuäfqº÷N3ÁsÎŒ>e·DKK _ÅBF›°&f铺hŒ×Œ$—[ø©—2dY¼ƒ,‹ã•4}‡ŠçLpѧ--™2aÎp©B†*}R{Õ\ƒÀ`$¥ôðÓ6•¡nQ)Ã,‹›û¸lw]|‰Gô©wKdZ"òU,dT(SÜ,}Š.ƒ‘ô -¢”!V‹eq³Åª|‰Ëý{‰Ë¬}Šè§ûÄ<°JK0_¥á>¬påÇ -—YW`üšÁH:¹†ŸŽRF‡n}p¬(‹ïÔÇu‚ò%ŽÚ§(Š‹n‰HKp"£usQÈÐ*\²&Î`$¹\;i®¥ Z¹r­Ùb Ö—¨ CÕ>õ¢8¥%“!-éÓ]MdP!CU¸´¾;]jf0’Hî4qñÝVÊ­JZYw¨ð%®A†%ú”Ý™–È|U2D…Ëì»[o»ûýƒ yytÒ´© ïRF0¾Äv•®¼kÑçÚUÔ-1Ò3_Õ*\T·õÝýþ¹…¼¼ÂOÛT†YÊXk”2ìeqo_b)qYjŸXŸ=-ÅUZ"òU­áZáòûçúòì¤É &r*C•2–[Jò½M—ÑO—}Rí‹âs¦‹n ¶Ñ(-ÑóU¯ -—ß?¶Ð—-ü´tÒð‚‰6•¡—2N¥ ÷kžAÆFWDô©º%FZ¢òUïB†ß?¶ÐW€Nš¼`¢MeÈR½ê}I”2·XmA†Qâ›͸$CDŸ“ñÆ;vKdZbËW _ä?µ¡`킉˜Êp–2·XA†¥Ä%£OâêGcŸõji‰ž¯2>ȳ“&ÃÏ2‹ïÆT†vó(p‹ÕdÈÝSÄ›Í"ú¤¢8M÷Õ¯%Òê¯jùª£Âå÷O-9(P'ÂOì¤ÉËŠn¥Œ@-V C5XEí“¢OQoPÛš–ùª½Âå÷-9Èb2\î7ËðS\V¤%*²”q%ˆR†[Aã}Tâ2£Ï}bQ¼šœÎ$n)ºä«\úL*ÙL†~ŠûÍZ'M]V ¾”ádP‰KtÞåH†ˆ>±Ïô^ù*ƒ‘TÒ3V×ðSë¤ÉËŠZ)Ãó«k»Ä 2°Ä%:ï²ö)¢OÑ-É£º%œ–ø¨ág=ü¤NšxÀÄ:• ”ád`‰kÏŽÍ뢗-š3&Å»¨è“¦û¨["ÒÃ'y6ßÕ4?­CÁŽ© ÏR†éKD»D2D‰kùbÙyÇÚ§}Ên‰––0I,[øii¾›á§Ø•¡wÒlSqú‘°šA†¥ÄEw¼B`FŸF·ÄHKŒ¤•[Æê~Š]ÆPpPSV_òýwF»ÄdLAF›ëÖ¨ìˆ>)-á|Õy4L¬ágk -?å® ÑI f*Ù°ªv‰d,Ä K\m[ŠIñ¢2ú¤é>÷´ÄïY2‘½abk¾Ëð³‹~ÆlÚ.V¤ &J ëõ+ô c¢2TçkŸžÑ'ç«I§@ág.{øI÷›cwÑ#½×ì4_bIXÉ„UU2TA%.Ùy·EŸœ–ø#׌5`øiìðÓ.˜x•2l «1øédˆvÞ­Ñ'ƒá<&æì§-üÄA.Ü•D'ÍêK, «Ö• -2DƒU [£ONK|‘gÃÄrÕ¨~Ò W4„?v‰QÉh…ïÙ¨—ª}Z¢O#ieo˜¼n6LÄì§¶Arág4»/1«âz»Äd¨W€èÓïXr‘kÃD»zD³Ÿ¢ù1mŽ ?ÍûÍA„Ÿªø©WÅåLÆÒ³§Ú‚ *qyDŸ FRʽa¢]=²5ß½ÂÏà}Ée5“±zù"K%Ãï£ ƒ£Oå’±ZÃϪbö“®±ªð¹œ´€¾Ä(~Þ¸rñÌ ¼v´aÍŠ%4lT2Ì K\–Ú'ƒ‘ÔrÉX­7ßµÙOUý”á§­“ö&ãßÿþÑîKTÂJƒŸ4“NA†ê£™wŽ>}“3cµÞ|Ç«GÆì§Ü,ÃÏ8:iš/ù‡éKTºªâfÑØ-ÈàèÓ?93Ö?› ºùn.WÍwºG …Ÿž¥ ‹/ÅOê°RºžVüìÒÎd¤t 2Œ$•#c5&æÍwµEE5ß-á§g'ÍÕ—ˆë‘ý»Í„•‚ŒfØ.Ñ‚ K‰‹£O䙱⼽G€7ß«GŽæ» ?ƒñ%âr³ì°ª„Õ2h"Ø+È`0’^&â=ñ~³¸z$f?­ÕÏxû’CûvZÖ°Æuk 2Œ¤–a2Ü3V9¯C7ßñꑜý´W?„Ÿî¾DŒqi +nCÐÛ%dø,ÏŒU5Lä¼N_œ×1®Ó|wõ%wo©â§ì°ŽIUñf iðÓYÉ`0üR€ŒUk˜Ð¼Ž¼ù®f?ã?Ý|‰˜ÖQÒÀ—˜Uqc&C2¸Äå“Ü2VyÃÄÒ0Qó:âæ»#üt©~ÆáKdñÓ–°ª™ 2|Wœ«Ù01çuNœ¹põFá§kËð%jZGvX›‰ª8~â‹6dø¯¸2VGÃDÜ|·Í~º‡ŸÎ~É=Ý—à´ŽÑaU «üĉ`2|U€«wÃĸzwø©æ¸\|Éây3¨ø‰ k=¼D V5“A;ÚdÁ`$±g¬rU05Lƨ†‰¸ù®oQq ?]{ï_¹`Mëˆ1.ºD «â¸ Á2“ÁAFÒ+pÆJ«‚Õ®6­abÞ|7g?ù5Çeñ%²‘&;¬êYOÎAÆ+Jþþ <.¥7LÔ zªab¾G`Î~Ú›ïN_òØîKD#M?E‡UKX“eñJ@%ý_Å9lôXeÆÚW›Ë2,ó:ú]E›/9&j\ròS?©Ãêš°& 03áöŒU¹+­ -¶4L\çu”2È—È~‰¨q‘/é«ù*~º$¬¡dMEâa7®=V¹_ÇÜÕfk˜¸]=²”2~øþ[›/¡~ ú’þ†/‘ÅÏä–°º}î¿t(éá°› =c5{¬*c5&ãœ×±–2äþhkï}–›/¡«£*ª`ÄD`@ùoæ^ä²ôX¿ÍÛƒk˜xû’bŽkù¢9š/‘4£øiKXC o(~eW<ñ/ç>–a^J£«‘±7¯ãÒbř૗hSÝn5.åKÊè¾$9$¬îTXyøü¯w$±Ù0L†­ÈeLë=V‘±ª† îj ~ÚËâæL0>Ã%û%º/Éeó%¡ † & ž²ò‘¨p8M†QäÒ§‚mkÜ —²8Ý/Ñ}É´k+,~¾ò « ‰_{JçÃG"üm&CË "MS5^«£”¡Ý/¾dÒXÙ/1ò’ é\ŠŸò¿?aÿÃý” “ -ñùÿ¯©ß€´/5>L:‡ ÝdüÎ0æX†9¬õXká§Øà·w‡ô%ã±÷n÷%æ­÷õ%6.,T4 ~«I}Ïààà -GÂþ-“\Ö"N=Öà2V×ð“Jxï·.s\ZK5Ò”/ Å„Õ I… Äï”~oüéw": -Ž„fÃj2Ä$—s,CN=V#c5vµ9&Ž ?,e¨²8Þ/Á™`œãÃâ*/qó%!†… A…Dâ÷¨?8„ß•€˜tH8œ †c’Ë(rɱ c*8v×~Ñc *cÕ7"hWÒ¶ˆ»ŠãG–s\8,.k\!ÝH³s¡ca@ ¼ -zÍ.üæ«ÂCÐ!à†#áØxÅf2\‹\òëÌù‘òAc»¸gøéè¤ÉR†Q§ëè¡ýÕW²ð%:Ò\˜X@ðGÐëBo¼ñ†üÓëø]"„ð¶ƒà0 GB±áb2ÞzÇQä2Ö¨¨©`Ùc "c56µÉU²,.Z¬Ãö¦™`1Ç%j\¡ëKl\sa`TH(ˆ†?þl|CP‚x ¿t †áH(6t“¡ÕÅi’K/r÷X·¨°3Vg'틇¸vI•2ä¸Îà¾ÝÍ™`­_ââKêãñMN.È\X°PùË_Aoj‚/ÿò—¿H@4:Óp$“aÖÅÍI.G‘˘ -þüQÀŒÕÙI£© QÊÐ[¬-h±¸œã¢~‰éKB(ÈpáB˜ ‰Ø -¢B0ñæ›o½õÖÛo¿ýŽ&ø¾‰„Š‚Ã46žë¯ë¨‹;&¹,E.sJœ«~Ê ?K)Ã,‹W.‡3Áný’PÃwJ.¤¹0°@* -DâwÞ}÷½÷ÞKAzÿ}ñoøÆ»ï"!ˆ‡ CÂaŽcC?]êâ8ÉÕ'¹ÔâOyUM{e¬Þá§^Ê0Êâ¸g‚Å—µ_ÅO{>bpæˆÂ©@&†>H™2凚àË>J€À¬Ç_M8„áÐØÐrØgþ {ÔŹp*øÁ£Çqg¬æP°è¤™¥Œy¢”a¶XM_ò×ÐËKŽDãc …Pvâ}@âÃ>úøãO>ù$•&øòã?B€h=Þ6á á±ñ¼fÃÝd¨º¸šäò*rÙ§‚…ŸZ'M”2¦ˆR†V—3Á¡——¸8‹×1àTX "©R§N“&mÚt OIø§´iÓ¤I „|üÑG€Ò¡à@6^ÓØ°»”gú+[L†¥.NëÅŸ­Èå1,¦2ì¥ £ÅêÙ{OŒÏ+Éd3äHt.L,ˆ -€‘ø4}ú 2fÌÊ Âg̘!Cz`$mšÔ© d ‡bÃt)φ†n2d+ííw­uq5Ée^}{,#`ø‰¥Œ¥–R†¼F î—Xæ¸B—¸ &D@‘!#à%kÖlÙ²ƒr€ðßÙ²eÍš%3’ñ@8>LIp˜†Ù€<ÅÅ¥ÄûïìÒJ³ÔÅI.­Èe˰g¬q„ŸjÂO•2ª”/%Ëâj&8¤|‰ a00Q\ ¹'"±@*2Ù‡œ¹råÎÊK‚?äÎ+WÎàt€íHp|ð>ɆˆEŸ×lXM†½•¦êâæ$—¸úçæ%{õÓ~nÛ$¦2Ä„Ÿ*eXËâb&84|‰ 2:d.>ú8Uj02"ÀDîþ$uÚOÓ#9ræÎ“/ÁBÀDñ%K•.S¦lÙråʓʕ+[¶L™Ò¥J–(^¬(àtä8Àr á§‚l€O¡pK_¿5’”x¢áb2乓˨‹ãÛÞú$í -\ä -~b'mìðb*ÃVÊeñÐó%↌¿¾ ñ…â"C¦,Y³çcQ Pá¢ÅJ”,]¦l¹ò*Vª\¹J•ªRUªT®\©b…òåÊ–<€Ž‚ެ™3fø4­bCä)Òlüæ7¦G‰G°án2¬­4U—+9!Š\¹¼ÃOì¤ÑeÅ‘ƒûÊ© ÏRFˆù ˆ0À`¼ón -“‹l9rå,Š-T”+LT©Z½zšµjÕ®]‡T»v­Z5kT¯VµJåJ€ÐQLÀ‘+ÁøÈS¤K‘ÑFüƒ 7“a.ž­4ª‹–ש.®Mr,r ?E'ÍœÊp/e„*äIÈ`¤üHræ"ÁˆEÙò+W©V½f­ÚuêÖ«ß aÃF£5jÔ°aƒúõêÖ©]«FõªU*U,_¶4ÂQ0ÞÜ9M6 OyÛb6â†f2ôî»ÞJ7œi%ìj­.þY€º¸söS«~bø©uÒÌ© 1-®•ÅC×#zŒñÇ×Á“@„#•âÍ…Ä¢jõšµëÔ«ß°Qã&M›5oÑ",¬%*,,¬E‹æÍš6iܨAýºujÕ¬^µ²€£p*‚ ÌSÐ¥ ÙPhüoüâP‹É°vßÍVšºáL×÷Øëâî«mö3pøiNeØJ¡åK¬` '!ƒ‘&]zð#ÈEá¢%J•‘XÔ­ß°q“fÍÃZ¶jÞ¦mÛvíÚ£Úµk×¶m›ðÖ­Z†5oÖ¤QÃúuk 8Ê”áØHy -„Âlò[a‚ƒC&Cußsä)hk¥ÉºøK]<@‘Ke¬æì§-üà ?RFèøÍdXÀxÁ ƒ‘1s¶¹ó"¥ËV¨„XÔkШI³-[…·i×¾CÇN»téÚµ©k×.]:wêØ¡}»6á­ÂZ ŽJÊ–.QŒØÈ–òáR0Úpó(qÿPÍ¿³»ÉÐ[iz]üf\uqïÙOq'Í ?U'Í2•a+e$ÑG˜8Ò}‰†ð$©Ó‚ÁÈž+oþBÄEåª5j×,š‡µ -oÛ¾C§Î]»uïѳWï>}úJõéÓ»WÏÝ»uíÜ©C»¶@‡€£FµÊÊysçÈ–\Š2ºG &Cußm­4G]ܻȥ]=Òf?á§­“†7B¨”¡Rо”ˆ1ŒOR§Ë)kŽÜù -)^ -¸¨V£v½†Mší:têÒ­{Ï^}úö0pРÁƒ‡ 2t(ücðàAôï×·O¯žÝ»véÔAÀѰ^šÕ*W6Š*7Wö¬™„Ù4p-ÍëŒ'Þ&Ãè¾ãK½j^|0qÕŵ†‰ -?ÍÙOUýt†Ÿiõð3dJ_‚é*Ÿï iÒeÈœ-gžü…Š•,S¾RUâ¢yËð¶€E÷ž½ûö0hðaÃGŒ5jôè1¤Ñ£G9bø°!ƒè×§wÏîGËæMÕ7ÙÈŸ'g¶Ì?…hCx 6⇆‹ÉÐv°û.®˜`+-@]Ü£ÈeסíŽFóÝ-ü´wÒB"ü´­÷ÇV6æc ŸfDOR pñRå*V©.¹h×±K7ÀbÀ !C‡5fì¸ñ""&NœDš81"bÂøqcG1|èAú!Û·iÕB²Q¡l©âEÀlä³IÊ)0GQqhh8M†6°CÝwµGÅÖJ Xw ?1c ~¦O«uÒBЗH“¾ä lLAð)ÁOR¢tùJÕjÖm€\´sÑ«oÿAC†=vÜ„ˆI“§L6}ÆŒ™3gΚÿ˜1cú´©S&OŠ˜0nÌè‘Çзw®;´m֌بT¾LIi6 Iùä#lüéx ñЧÉ;z÷ÝÖJ \·Ï눗éꑘý¤æ»^ý ÔIKâ2e7¯¾&¢OÈJ Æ 0Š–,S¡rõÚõ5 oשkÞý6bôØ 'M™6}æ¬ÙsæÎ›?Á‚…  æÏŸ7wÎìY3§O›2y"Ð1røAýûôìFl€Ý¨]£jÅr`6 -B´‘= -4æ®L ì8ºïÖVZà"—¹,C,[TÌæ»WøZ4õCþ¥n2ð­Bø02 0ÊV¬R£Nƒ&-Zµíع2|ÔØñ'O>söÜy .Z¼$réÒeË–-_ÿXº4rÉâE Ì›;{ÖŒiSŽÑ#‡ зW÷.ÀF‹¦ ëÕª^¹™ÜäQ(Ø EC# ÑÎdÝwùR¯6/îU÷j˜àìçz1û)šïq…Ÿ!†åAd1äðÞ¥‚t5[μFÕšu6 oß¹[/àbÄèq&M>kμ‹G.]¾b媨¨èèÕ¤è訨U+W,_¹dÑ‚ùsgϘ6yâø±£†ØØhÕ¼Iƒ:5Él. < -"5ÑìO‚4¢û.Zi‡ãn¥Ù3V³a"®‰ðs¨\‰wø™ôf‚ÊêL¤É@_‚AFÖy -)A`4jÖ²M‡®=ú 2|ô¸ˆÉÓfÎ,€Š•QÑ«×ÄĬ[¿aÆ7Â?7¬_³vutÔÊË"/œ?wÖŒ©“"Æltnߦe³Fh6Ê—)AÑø$^hgëM9,füh`o¥™{Tt“\ÆjÌëÈ«G4û‰OÛXªŸ!~Úœ‰a2R€ÉÀ #wþÂÅËT 0À“€Á4läØ ÀÅœù‹–,[±*zÍÚuë7nÚ¼eëÖØØm¤ØØ­[·lÞ´qú˜5@Çò¥‹Λ3sÚd`cÄ}{vëÔ®u 0m”’/7Ÿ|øAðhx˜ c,˜LF‹ÉPÝw£•æ¼áìÈXµ†É®XqõH›ýÔªŸfø©_0ñåãL@Y‰Š2ÈddÊ–+_¡b¥ËW©Q·!€Ñ©[ïþƒ‡?i*r¹|eôš˜õ7o‰Ý¶}ÇÎ]»vïÞ#´{÷®;¶o‹ÝºyÓ†ukW‘‹Ì5} -ÚÁúôèÒA˜j•Ê•*†ÁF 4\Èð2úX0˜Œ~†Épvß=M†³a"2VZ.®Í~Ú›ï!~º½Uø—7ibLø’¢%ËU®^§AÓ–Æc"&OŸ=¸Xµz-`±uÛö»öìÝ·ÿÀƒ‘Ñ„’I†ñc¦ø3UZa2J•¯R³^ãáºöê`Lœ:sÞÂÈQÈŶ€Pqô؉“§N>}æÌYЙ3gNŸ:uòÄ àãÐÁý{÷ìˆÙX0wæÔIãGG³Ñ±m«fêÖ¬*ÐȩРç+ ÏÜõW“a½I&£·ÕdÈçmd÷ݵ•æž±ÊÛŠr^g`ï®âêQh7ßQÎ0C:eÉ€ £]çý@‹1oÑÒ•«c69vüä©ÓgÏ?áÂÅ‹/]ºtñâ… ΟDcG»wnÛº ÙXºxþœàRFЧ{çv­›7®W âP@#¯Dã} »?yEû {š ùV ÁdÄ=°ã‘±ê uó]mä²Î~†Xøéµá_lŸÈ] D5Àd€/é3pØè SŒU«×mÚº}rXœ9{þÂÅK—¯\¹zõšÐÕ«W®\¾tñ8} -á8°o`#jEä¢y³¦MÑFß]Ú‡·h\¿V5LQ -ÚÐÐÞ6µ¢ñJ&£±f2ä“Ög]<@ÆŠË2b¢Œ† Ý|76r…jøù ¯hŽîòäÈ[°Xé -Uk5hÚª]—žàKÆOž1wáÒ•kÖoŠÝ±gÿÁ#ÇNž,€Škׯ߸yóè6œ›7oÞ¸~ýÚÕ+—‚ãðAbcãº5«–/Yfc˜áƒûõìÚ!< âÐj•$†¾ÿ.ºí×µ(T/“¿"ÉplEð0ÆŒ_YSÁFƪ5LļÝ|WWB5üt8mýMdœ¿gR½N£æáh2†™8mö‚È`1bwîÙøè‰SgÏ_D,nwîܽ{ïÞ½ûpîÞ½{çÎí[€Çµ«—/]ËqâØ‘ƒû-bÀ¥,š7sêDð(ý!Ø€8´@£e(©?N)Ÿ²¼bi{îÀ±Áf2è&Ž/7L†¾­-“¡_JƒŒ—e¬²7LÄգР?d·¼äZfr&am!Ê@“1sÞâåQ1Ñb>vò̹ ÀÅ›·ïܽwÿþƒÏ>ûìsüûÁƒû÷ïÝ%:®åÃqØØ·{Gìæõ`6ÏŸ=mÒ¸QCôîÖ©mK‰f(YÀqÓÓcâÖÆïí/ /“Q®rM¼|ÔC3jÆÏ6°ã~þCë±ê«µaB¯ôšWB.üÔRçeñÄTZ²Ú¥×€ac"Àd€/Ù°eûnãüEâ°xðÙç>zô…Ô£G €€ãúÕ+—. àSvmß²1&zEäÂ93&=l`Ÿî”¼fÉ€1ˆö­Qè/ƒ3êòÑ «É¸ŒÉ”±Ò² ׆Iè5ß…¼ÈH#–¬–Â0£YëÝú 1vÒŒ¹‹–GÇlŠÝµïÐQãÒ•ë`/îÝ,}ñøñ“'_J=yòä1ðxwn£á@6N= .%vÓú5àQæÎœ2aôp ÌPòçΞ9CZœµ}Ç…Z¶2â¾âs˜ ¯ŒU-ËÐæuèæ»¸zrág2äƒ1ªá’ÿŽÝû9~ÊÌùKV¬Y¿eÇžGNœ> rñÅc€âéÓ¯¾úêküûéSäã1Жãî[ÈÆù³§Ž!³±amÔ²ÅlDŒ>ÈD£L‰Âˆ½2õž…šU_`ÄÃdÄÆÃdh÷XÅT°-cíÛ½£c^'DÃÏ_C¾>Ö ÂŒQ¦ÎZ¹jíÆØû;yöÂeãÁgO‰o¾ùVé›o¾@€üŠ`ãòÅsèRöïÙ±<Êò%óg}µªV,SœžROŸF{ÌR5~i€ñ&Ã}ÆÏf2ÔT°-cíÜ6 —eX&¡~þÂse·Sg/\³iDÇOŸ»tõÆ-°`0  â»ï¾ýðþó»ï°O¿ÃAl@¼.ÌÆ½àQÖE¯X²`ö´‰ -&õkU­Pš6ýË%¼b§‘%ÔPŠ—É8zÒy“ ŽŒUM›=V™±jË2dÃ$DÃO”{n¨“Œi³.‹‚_ÃÝàLΠɸsO‚ñ bñÃ?üMééËñåÁÆí›×®\<æÄÑCûvmÛ¼~õÊHDc,9”6aMê׬R_S—f§°‡&A˜ ã¾¢f2Ô³ïöÈåœ -¶f¬¥ìk†Ÿ(·z†¹æ_¼ Ží©QˆŒu›·ï9pœÉ•ë`2>$Áø¨ø;è$øá!àl<¸.åêå gO;´÷vH_ktlÖ¸ Qª(%(iíoàþZ=~aÜÊ~“ñ}@“aŽeèSÁfUd¬ø„‰ÌXCµa"å^ý(U:øa«uš"@ÞD‘qñÊuˆ2>ÿâÉ— ¤âŸ ÄÁ$*h6._rÐØb¢1°O·Žá-Õ­Q‚;\Äk 5 £ñ+ Ï`2\gü¬E.ÛTðv½Ç*3VmW›Ù0 ±ðåÖ7q¼YHYëø)³,ÅM«»7nß{ðð‹'O¿þ†À@.~üñÇ¡àß?‡dbÑÏîß¹uý -¢lìÙ±eƒ@#b̰½»vhݼaê¸=‘Þ!£ðî-›?Q¯=þ:aM†ÇX†š -6{¬FÆšYe¬fÃ$Ô|‰½ ïØFofÓ£…8Ÿ??r%Žaï?r¼‰ ã˯¾þö;año!À–ƒØøê)º”ûwÁ£\:ú„†ÆÔˆÑÃôêÒ¾U3Ê]ÑXg•MLËÓÙ -Ä0–"—qÕœ -6z¬Þkˆù} çóÿàúœeXÛÎx¥ÁÅ+Ölغ‹žÀÅ8ƒÈøæ[2Bñ” CcƒÌF·o\»q葃 H^'ŒÚ¿g—v-›Ö§GB -ŠPÃö™Bø|›8&C=¡g®Q‘SÁfUf¬¡Ú01äž¶£sF 1„v­.‹^·eÇÞC¸ çmñ¾××Hº±€Ÿ´¤ƒà 6¤Ùx(˜¢ ë׬X2æ”ñ£ðõ¶aMêÕÔB | -W[Ç«žýµ1I’ &C+rÝÒŠ\æTprÊXImx -4ÔJÍVíq§bãJ÷m"ÅEŸ?‚8ã[¬$0R‡dCš @ã3B㙓Gì0tõŠ%óà·r$üð;BVX—B|öç³54Ô†ø˜Œ/‚7Ö± ¼ÇŠïôÊ©`gÕÌXC { ñêÿ¤­t/XÜt'ø>ÈÜÅ+V“;Á÷éÈL?þòky±GHü?”Îú”¿A´ñõW`¬!%40Ö€ä5zùâ¹øÄ |Àß<¡F– ú2¿ÛÿäËÁŽ=ÒÞ&ãbP&ÃRä2Æ2¨ÈEkTÌ©àÀ«ßŸeÂJ4Ü݉ÈN¨s‚ëVqæàÑSç.á3¸8¥¹ ^¤ÙÊÿH.48d6À£`°qèuÆîí›×E/[4_@ É÷Èò˜©«*…Š'Øä~¹Wm«çÝL†±H%“á(ríÁ‹j,Ã6ò«³¯­v'Â/#m¢ £}xømÔcÐd4på€p'ø)ÉÀ¹Îš kÙ¿{Û¦˜(¬%EŒŠ’цæÚ)õR¨|X\-rH “¾ß²¹¦Œ‰kT´©àä± -Ù*ÊLËIP³‡ÑPd.HÓlÈ`Р0ôôñÃøìéÚU‘¸Æ›¢Ð6bÁ»L] òÚôÞŠºbùœ&Ãu’‹–µYŠ\´Fm™œ -N«Ý¼ö†eà¯DÙJzâ*"z¤¡ éM~2¤±ñ£6L4.Ẽ};c7à+†…Ž$ÞJ&³]ÐîO߈‡x¬¯8Ûš¬ñ7².nNrÙŠ\bŠñ Ah÷Xu¹¸‹Ñ(oÖAÇM™5?rÕÚMÛv›é þJZÆOºlh@úJÏ[çN;´—æá Cc±àÕ®e“ú55bLñÐ^Hzq\Ýʶ¼Vá’˜ÄÏdhuqG‘KŽeÈ·ÕTp(g¬BÂhüÊ~ Þ¥ôD4\‡ŽŽpÖ4>þò+•¸J£ñ“ü½´²ñ/J Ç y½qõâ¹SGñµd|•lÎ4jkŽÔb^áOèÉ ­½¶šŒ²–éÏàM†{]Ü6É…E.s,#™d¬B.³•Òh|Jmµ’åªÈšÆÀác'͘·„‚Ðý2•5 ‡Ñ0~/­hP¤‡•ÆÏîG¿x–^Å…,¯€§ª†zBú50üšxÑS<Ñä0uâ_Ë𪋛Û2æˆI.­È`*ØïO1Qä2\i»t¢Ïü‰¾šéOòÃ߬FCkr+6þmtº±®ñàÞ­ë´Ì£Ð5´èhìðÆ»¸ÊŸ¨©Pñ §C\—Òo²zrk2Œº¸|¨WNréE®d”± -9Œ¥'ô;™o=—P™kÏþ.þäžÍŸØÈlhhPŠo¬? Ü•¢PÚÍ+3´øÕ?yS¼¡lЛ+2oXê7YŸÑdè×m“\£œE.ÛTp膟$»Ñ•g*„A¨ÕŸˆüÄZÔ»¯\Ðl"~C)Ü{ £P -5¨ª1ÁâODÿäcêŸüY<çùªxŒÿvÚLF÷ç4Žº¸G‘ ãžÐÏX¥¬Fãwfì{w Åw§t¢ò“#f~òô›ï¾7ü‰ «ÚöPð£ ýè××Ò®ÍqŸPkM¾³NþD lüQ¼­ñދɸ &ƒæÅ]ëâÆ$—k‘+ô3V)Óhn„Š+ñÊŸ˜£Âbìïà±SçÄZ4ªwyúKŠHÖ[>N†Q¨x5yë†Õêq\1pIõõmþ×^_Ç×ÇÅNJ늄2§Q]\¬7'¹ìE®?$“á²ÔÜÉ$üII¼¬†#wñòÕ¸ýŠÞ ±Ô»~tî¿rTÌ(”:ÞçáÓØ»C<¨-ü -Â2®‚‘?h€èÝX­¶b‡.¿Çßdª‹ãzq9Ée/r…~ƪd 3ý3Ey©ÌKñµz×ä™ó—¬\»¯ùàúÕwŒÔÕÓŸ¸¢ñ…˜Åy⦵êå:ëÃÉb”œÐøóŸèYzz\#•s+Wð&Ãu^ܽ..'¹Ì"WrÉX¥^±¡0Vü¥¦"y!q+¾…ý‘ºŠþ‰›?ù¯Fо]…˜ºnÝàúØ:üªŠ ø\#è¯øNÓû)Íû•j‘ŸÚ—Ì(—Íd˜[¤É±9êâɰÈeÊéO, »´kñ.©+^wÀù;*Ô¸N©«ò'Xïê#.ýTQE Ú¸BÏÊÿõÍ7é/ç+l]ä÷L&[iÖºøZ½.nlËp¹B>ü$½bõ'¿Ó6ƦÅûjÎÔ•úñ²µvãöýϼýÉÏ?»$ÿá»o©àu÷–éOVâ­ìŸXŠä´;_ä{ ô6=©âŸÒ“¡.¿c2¬­45/n«‹›“\Ž"W¨‡ŸBš?Ñò™ºf¥Ô•J¡f¨—\qÜÒZS¥Pý“øùgņ#Ô0ý‰±ÛF­ÃRûòˆ òˆÆ;(ñP“jË ãj‘ß3˜ }^ÜØ"­×ÅËuñdUä2eŒýé Èß²¦®ªµ†÷ŸÕ½$3uU¥P‡?1аF¡ß‹¶«éO6Ë_W¼ÞAu„Êr(4=>hð~Š÷Pâ-È dÇJkÆi‘_`“ñ“Ãd­4û¼¸¸ál\W4ëâɦÈeÊÝŸ˜¥P -5jÖÓR×EºnÁÔÕ2ª!7TèhülCã?†‡—«*ä›—Û¨2 =;ÏäÊ ^¥üàý)ÞÇצS¥Q¬˜»?õËïñ2öyq¬‹#œÚzñdUä2eCC¾d.-(†Bõ*ùÊ5–Ô•J¡^þÄ‚†ù‚þN®¹ªwpù¸Lyºd_ÈùøÃ”|2%‘ÿ>êB¶|”€vZ·ry˜Œ@­4s^Ø-`‘ËïO-I¤ü‰5u¡FƬbT£ZK×5J\2£èO¾qó'?{„øP®¼áAŠõÞðÁûå%ƒÌôÂëG(ß]É_]yèÐ ß11Öã"?µ/#h“WL¬KÔ gu]1¹L½L¨¡ªäÎÔ5ÞþÄù *~2r.B¡ÈŸdÃÇS§úä“OR¥Æw…³ÉnŽ0ÌG žÁdX÷¨ØZiF]Üc’ËïÏ,‰äjUr1Åc„3暣ä¯ÄןüëŸÖ7Î×ß]—A¨¸´†‹É3|š6MêÔ©Ó¤ý4}&zwENÑkiÓ4“qŲbÇÕd¸ußÍ+&–Všª‹'Ã"—¦8C G•ܘ -µ•B]ýÉÏü 5¨Î¤öôT“ä¸À ÑHŸ.]ÚtéÒã õ¹Õ®RHLzâkiºÉ¸h+—g+ͼb¢Zij‹´K]<…ŸB¯x‡©P£z=u²yÇÞƒªßüD±¨á BÑŸÊhdΘ!}úô2fΚ=WÞ‚²òÖÚx`Ñò(AP&ù,ØÞJ£yqÑêâÉ*c5W¨‘_.N°ÝgTS<üÉÏòs™É¡=êš$‡ ”n&-hdË’9S¦L™³ ø†—ºí ßdOÑ;&Á› óVšG+Íý†s²3¿"Ô°TÉÇɱ4Åséjœù‰›?q}uÚ„Ö+ëóåΙ=[VP¶ô²pió†”ñŒ³ýç"?“atß…¹r¶Òä çdX·Ê#ÔxïƒS¹ kS<ÏèO~ø^+j•Й²Ú -—jà(óåÉ•3(gn£X©r•[•c'io²:Þ1‰ÓdXºïÎVšå†s²+riÒC z õÆ3©·Ö0u¥R¨9oâýü:µãÑŸT)_ÐÈŸ7OnPž|Fɲ•ðÝ|`Q{ù]<°hÇÄ¥.þo}[Û-K÷=ŽVZòª‹[ Ô ™— (†?¹ø þıêF]¤GËÄþ®Ê€FÑBòçå/ˆ`T¬ZKfÏðW˜9?8“á9°£wß]ZiÎyñäFÀPCK]:oA;E· Üü‰§Ñ0wÝ8ÞÙÆÍ x‘6ÖÅŠ*X°`¡ÂEŠ—,S¡JºbŽhˆi2Ð3ÎÁ› ÛEVÑÎ ¾•æ÷çäƒ\C [•\v]å߸ÏwÅ„^Tƒ˜8»=A\Ç“*•/S²x±¢EŠ-V¢TÙ -ød3|CAl–C“±ƒ^~§7Yƒ1–½ûn, Æ+&­´äi2~á=û§RW­ëj÷'F~â:ßeGÃö:êËë¨b&ýI£z5²¥K–•*]ÀÀ÷ ÛÊÝÆÊd5^~ÉptßõeÁÙ2é­´äX·ÊjüQ½¢•F x©ç-¬þDå'ú|WAècë…jŸŒ¦¥­š5¬[³Zå -åÊ–)]ºLÙò«T¯U¯qóðÝDâ¼@™Œ3ç¨7Yã0XF± ìèÝ÷Jeå“dÝJsÈcöOÎ’«®«~A^÷'wtDêö¦hŸÐh|Ë aÝZÕ«TªX¡|ù -+W05£ÇÝ(9B“K&ãòuûËïî;¢*otßï%“¡¶µÉî»ãŠ ›Œ_ØÑÐRWk)T%Ÿˆ[ÐÊŸ÷EŸÝ*¡Oõ…‹ø+¬î@Ókk ëÕ®Q­JåÊ•«T­V£6€Ñ²m§î}ÅÂAœ-Û±W™Œ‡Ú3ÎÞ&Ã>°³vÔ¶6î¾=Ô0RW³j<—$ýÉ:œÁÔýÉwqù“@™«¸˜„þ¤¾ÑØ´Qý:µjT¯^½FÍZuêôè´Ü¤™Œ{ö—ß=güÜvlÝwn¥¹Èj˜³ä¢Z¨˜éO†Hbퟨû'žA¨× šá¡ArBÁŸôëÙ¥}xX³Æ êÕ©]»vºõ4nÞ -¥˜Ü!7©Ü5炃5ÚÀNÝ÷d\äÒå–ºš]×\ù¼ý‰šï -º¨áX/šrGÓðA}{tnÞ²y“F ԯߠa£&Í[†·ïÒ£¯xÛÍ0Æ…E×7Y=,”u`G^d­Å&#,× ¬¥Pǯ‘úﮚï -¦Hî¶(ëŠè¹nމZJþdØÀ>=:whÓ*¬yÓ&7iÚ¬E«6í;÷ÀG§Åk<4Vf_¾d‹oÌøé;ôì;wßã«?±ìâ±æ'‹å#˜b^8˜¡PÛ £!2W$G2jè€>=ºthÞ*¬(¬UxÛ]zô0t=ú·Òb2hÈ/“3~Òdȇh`‡<ºtß“s+Í!ïR¨«?1ç»Äýk‘>stream -#ÇM› Ñë…ɰ_%h2,3~[i]†>°ãÒ}çŒU“GêšÂ¼nløÙóÂbuB0E Ïr×)c±¾µhôíÙ­K§íA:vîÖÀ1_#_jÐèñŽÞO60¼füÜv\ºïìKHŽÔÕÓŸôǵ&4/L/¤ž½¨¿û@A¨ÇN$±m¹˜55bìÈ¡ûõîÑ­k—λtéÖ£W_cLÄÔYóͽPtƒÒkÈÏm,Ø9ãçØñè¾ûý±¼r 5\ý‰z3 _Ó¢ûŒ¢égQÃm)’yûdóºèå‹çÍœh Ô¿oïž=@={õé?pèˆ1¦Ì˜»ÈØýs>Ðk½Þ&Ã:ã§vÂz_de“!„?õ.õß²h¼Ï(îÇkE@A¨#24¦…źz|(/š®tiÍ(j„:jä"½"áÛh¬^¹lÉÂùsgÏš9cÆÌ™³æÌ[°xé -c÷þÃàKÔÊ×ÿGN“A%xm,XnØÑfüx`'(yÖ»Œù®Jâ­*£H.ŠÏ„š÷\Ŷz‰Æº5«–/]²hÁüysçÎ7áâÈe+£×nؼm×¾CÇNSk]¾r›Ø `2\Ç‚yÆ/òö'j^¸ŠñÔšVÔÀ ô†- \î²ìu»yõÒ¹SÇï46­_½rùÒÈÅ‹-Z´xIä²Q«c 2ÎâsÓ÷ܺ4?YL†ËMûX0nžwŸñãVš«\ý‰šï¢û'²H.‹KÔ(·„jíø`2Wµ’C D#vó†˜5Ñ«V,_Z¾beÔêµë6nA0 È8Ùº©ÖÚ~w»|×X°mÆ»ïÞrõ'oË— sšErQÔÐ&5•Ðÿ¸¡.˻П\¿|áìIDcç¶-›ÖǬYµ***zÍÚu6oÝŽ`? y‰¶ÝÚËd8n&Ã},˜v‚”ÝŸ¨·q>¶¼÷«5Äúų㬄º õrç…3€Æ½»¶ÇnÙ´a]LÌÚµkc֭߸ië¶»÷i­ ùøN“ñƒ§ÉpŽ»ìøý1¼ˆ²ù“WÕ{¿.E±“fФ†„~þè‰ëL¨[cMe®ÂŸ\»tÑ8¸o÷N`có¦¨M[€‹]{ö:z‚À¸u×x°+h“qÆf2x,øYeó'úÛ8T$WE 1ùg BµJhPFC¡ðÞ¾qÑ8~äÐþ½»wîØ»uëÖØmÛ‹}dܹoyD#h“!VÊ›ÁŒûý!¼˜²úg‘\,ÕÐ'5܃PÌÕ±¡ ý „ˆÆÙS'Ž>¸ïžÝ»v¢ví.9vR€¾DD2–Jk\&ƒ.¹ìñ³˜ ÎXƒîOÌ·ÖŒ§‰ðýx5©¡¡gô8Åh¨ ôkù~ü«—/œ;}ò8°q`ÿ¾½¨}û‘‹§ÎH0 /yòÔõÞSÀ+ΚÉP7 ´±`Ø ZnþÄRÔ“Ž Tì$GsÿËä„ÍhA(=™¤Ð¸xþ,²qä𡃨C‡;~òôÙó—®"/q{«ËÓdoJ“7 \öø±ÉJ^þÄ,jØ‚ÐZ~úü•ë·Ý2WÆ j ?1иvåÒ…sgNŸ}ú¥ü íq¡aõ%Ïd2øòÑóÈ5Uw äL(½.!nÇG©Ì‡òâ.wÙÑ ³l€á}…Â?€¹øî{2n`Äm2ŒE*l2N^Ahª´ô¹éE‘¹Æî{Ý´;Én5rW4ÐlÝ8Àt¾EsñýÀ 0\›¬l2]ƒP#s팙ë¤ó°Üµ]ŸÂh(4àWžÌ†€è",‹€`°ÉHj¹ BåL(e®t›Qô\Åè“g•»~öBC±ApB,$.`°ÉðKz*ÚñÒhˆ‹®F抷OÄh{¹+н$;èR¤Ã|ýOâB¤«n`<ƒÉx—MÆsÊ„jí#síÚ›®@‹r×¾#'Œe+Þ#¡64 6¤C ¿T\u F&£›Œ„—ÅŸˆv¼Ñ>12W깺”»hɯGÜŠ™ ÁÀ!ðø§€âÿ·wàQVé²÷îóÜ«fÝ]×u‘6”@(¦zB‡P„ „‰€  Bé HAªTEDE@QÙ½ÞuuË]ö¾çœ¯œ¯Í !d -ïß'˜|™™Lù¿s¾ó–CmA}ad ÿÈÈ“‘aAdT’t&¡4‡Gºrb®Êå®³ÊÆàz)¡k0oPs0{¦ ¶`¾PÃ'2vñȊȨtù»r¥[ð‘¾nR¢ÆNi3>¼34Ö Þ æüñ7öÃ?˜/ ŒÈ’¤I¨òÊUŒ¹v W®âr—:»KQ̨†Ò’7DwH®|¡4"#ÈRMB…+W1æªXîš·h)é ë«Ñ¯Âÿ¾«õ†èIì è Îw+Œ l¤R)RMBµË]påJ–»èÌrv—ÿbF•5¨7Dw(Ä~¡öÅý ©TŠÔЮ\ëÆ4â—»¸ì.¶F®iõ«iø§¶†à ]ýKc DFÐ¥†s•—»Xv«KÒI 5„Æ¿ï*¼¡ëñWwUÆ@d[z1W9[8¾£”ÝE×ÈßÚºSlÏý“4þ}Wí ݽ«vF% »"ÜŸ´W®Šå.Rb@:þ‰käB1c€Ðà¼aèµ+*Ø{龤såÊ/wÑf+$»KN Õ ¬éEãµÖPûCLjŒ’ïå.qœ¥„Ö|ACë #©r;ˆ˜ 2¨41Wn¹KZ#')¡Sg*kŸ‰[tû†F`Þào‰ÈùZîê’HJ(é-\ökHNkb×iÿÐðkåÍþ¥Óû‘ ùXî’ÖÈåÀƒÆ\4Þ4|¸C{ DFÈHO -Ù]v·ÊÖÈV¿¤m Ã5ò{"#´¤]îÒI kÆÑx¼¿Š‘JR^¹JÙ]Ò9XSEã}–¹"2Â^úË]¹"°ÆEã+ˆŒ“Þr—¸F®¬‘h¼d§îJ†<˜øCÆóˆŒª’ñ¹"°Æ¢ñl¿$¹Ìµ² ¡DÛÇÄÂ8"ãÊÇ9Ë#§51¯-s­ h!ãZ`5&ˆŒ#Å•+·F.Öô£ñRÞŸNÿØJA†Ðaœ+~GdT±ø+W5…ÀçRxÌû«2þ†ÈùƒFdV±¦ ݽw2^CdTµt Ak1±R4žt e)< iÞ_%A‘²R-wq½»¤h¼\æ*ÖÆŸ9_YÐW¹tñ‘¿;ˆŒ+51/”¹JÐ8ª“,\1h(ÆÕû˜ 2‚*=h°h¼*…G UžŠ@ƒ[WîÉ*íc‚Èž€Æ)ïï+wá©(4tñ'­Á‘4žäSxXW71ïoq²ð½CÃW¤­A”q4Þêl®Ìû““…•†eI÷„ŒU;¿K»¥!2‚#£h¼œ÷÷¬÷§—,¬Wapßȸ$íɺt‘°)¯;ˆŒ$%4h`Ká!y¬¡†œ,,Wø-K -? ȸ¥EƋҦFÅ#m`MN4Yx$—,\9Ðð‰Œã2Jù}LU+hüSÕ”yB²°š -ƒ@¡2V(‘Ñ‘QÅò ±¡MVTø*Kª2îd\U#c¢îÖGˆŒ*‘AÞŸ:Y˜¯0¸Ç²¤ÀqûÖW€Œ‹{„ß…=YÁ‘AÞŸ,,vá+ *X–82Nÿ€"c™¼³12°_Æ•6/% ++ h¿¿u›·ËeIƒF È˜!mã¬ÙúûeT…ôRxX²°²ÂÀGYÒ½AÃ2Î2ö3dÌ“v~¶qFdT¹|@C¨0 ê¹~¬,I¨e¼sÏÐÐAÆ÷€Œ/2N}Ï{›×®('Ș@‘Ñ·gigDFÕÊR…ÜïO¨0¸h¨ñWŠŒonÊÈØºqõ²7æ•N›8.Ÿ £O""#X2Îû“* ôÊ’.++ …†odd¬d̡ȎȮt“…%htôY–t¯Ð<2þ.#㺈Œ] ³¦M,Fd]VpeIzµŒACƒŒd|rñœ€Œ•å æÌ˜2¡(WD†×È’|@C®0 eIúÐ<…G¯úè[V°xþô †Œå“™ñ€Œ¸ØúuAb¹ËWY M-c Ð0BÆç {dÌœ2aLîðt MÖdü·q®rX–¤¬e¬4ô!Ô8Ÿ?sâ°€Œ2ŠŒ‘™©2ÅÔÁ߃"ƒ -4†IµŒFÐõgRVÉm(2¶¬[ù&AF AÆ@@F{5d0AdT­ô“…¥&¡• ?È ]a—/.›5}Rq #¥Ob§ø–n›„ :ÿDdT©üAƒÕ2ÊÐjï ZdÈmN;Hªs)2²Ò>Ó««ŒŒjˆŒàÈõùZÆl©– !@´Zñ‡ ‚E³§OO‘‘Ô‘|é–%‰µŒ­åZÆû†¸Ê¥_ã|Pì¤2sjɘ<m¼ "#¸ - ,‰+€ÞÀwM>«¤g@Æd@ÆP2j=ýÔ“ˆŒ IÕµÐBׄwÔ­VüCÃ0/˜«q¦Í—JÆæe aÈhîDd]>Ê’t ±^ Ë?ÈøQ]°(Ô8dä L‘È0#2‚-ý²$U4ß5Á×LCçÂÕ2öqÍ—ƒd˜Á•qY’²k‚¤‘û-%ØöÎVã̺Â&uï,!C*XDdEA#Ç74ŒkO|V±‚E±ù’Ø– £)"#4†TTù­qFdIúeIbׄûƒF€ÕG|[„6B¿¶ê¶ˆŒ*—NYReA# RV°hÜ‘,éC£V%@#àê#½gDFðåNhhB®24T«]÷W°ˆÈº|C#A a!”ËÓ -Ö4ÁxÝRãê#EÁ""#$ä Í5Ðàò4Tµ'ª%rÕGÛÅê#EÁ""#„84Ää®÷•ÐøQ¥_ªJ ¤ê#ÂEDFð4Æ©¡¡)X“ !J·”à2WJà¯ú‘\tyÂåiˆkRi¼&íO4†ïR©ú(ÓGõ"#˜ -àòD ¹ÒŒPV{¢­rUA#€R‚ªAÔƒ†Á‹\)^õ6_ -)°Ê Q*Aƒ–Æ6ö™, ãMê#DFˆ)hˆÉ]¬`MjÂcÔœC†2/˜•ø­>‚Å‘hÐ-qhí «re»|@Ãhï#u)^õ‘º`1ØoÏC,chˆyBíÉŒ ñ/Èò‚ùê#,%aù†FÇn½¥‚5¡4^ÝX˜ß–‘É÷viÚRãê£`¿9µŒó4(4Ä*WÚO£|ÅZÒî5ÖÛ,IrÆ?´yÁ|)Á$M)"#ôäR?i¥¬G¨Üœî°&m-YCƒ m^0WJ€ÕG!+ŸÐP4á™óz¹Øüœ4ÐÁ%ùù.%Àê£PUÐÈÌ&íþfIÐ8qú¼ª*‰Yãÿˆ/xdÜáó‚ÅíÒäR¬> -iù€†Ü„'·ˆôeÝÈ…mŪ$.íêŸ<2nkò‚¹R¬> -qBCnÂC 1iš´ï Û–ñš4ta˜ÜK B\†µ'rÖXxæœÂkÂЊ¸³Æ?‰1|æc)AøH ¾ …ÛÂ`ú,²Ãš°—«2í'Tÿ¸—¼`,%ii ÁúiMxHcaqß¶—+-0Ðä -Sj0ckR’Ÿ2/¸PÈ ÆR‚З²`ïÜ%maÀvX›]&¨s…éxBõw|‹q./x„Œ¥!/4”»¸ÖÈд*I/íO°5†B’懣üCƒnË8y:-0h¨2x4`@¯¿qȸ~õÓK|’Ÿn^°z‡EDFÉd/W²k¼T•¤ÊàùëÏÔÌ22øŒ’äÇå {Õ¼à–Üd‡5a×x1íOŒ§Ð€¾~†+Vª$?Ì 7ù€ÛšíOs…¥ .OjOˆ5ˆ1X¿6 2X’_Ÿä‡¥a Ch˜mn®*‰Kû“ƒñ?þôÓÏT?)qAJò[¬MòCd„ô¡aª×íÏå -«3xnJÐ Öø‰ƒGÆ)I~˜Ò†ª”QHû£Ûh)ƒñ 2 o0cÈÐfìp-Æ]6m^0–„¢ÐPl-–2²´?9ƒ‡ÅÕ$hk€~„±„"ƒ&ù}dägŒÆEqÐø/ 4Úuî!dð¨ƒñ4¨5¨`,á¡ÊØÁ$¿0”.4t3x„`¼ØW˜@ã{Oþ÷/à¿Ð±ä;B’ßk'ùû@©¥ EÚŸŒŸK‚ñ[Þw1hÜù3µ1Œ%"2„Œ¾ù§*ɑ҆*ƒ‡m Nâj˹Æ] Ô 0AÆW ÊŒÒü“ëŒÈi ÁgðÈÁø©3q5 -o`<küðÃÿÀ$ã»o¿&È`I~šŒ®_0&ù…‰4ÐÐÆ+âj 7¾¾ýXãÏà‹?ݱ„"Ã0c“üÂN"4ô2x¤`üØ’©¤×í qó°Æ?î|ÿÝí¯oÐåO å~2v¡.Á4TÁøŒgqµ×布Œ'` 1ÆM6Ë0ÎØÁ$¿p“úq5Òí«Œgи~ók°ÆwßÿÝ··¿¹uãK ïm^·‚¶åÂŒ°V q5ÒNƒ¹òÐø -¬qû[1ÆõkŸ‰È𕱃È#W“–ÈWmÜJâj×¾¼A¬qûö7_ƒ1¾¸z… ãèû{¸¶\:;ˆŒðQq5¡È•-‘ иòù×oܼõ5èÖ¯¾¼Ê#ƒöØÁŒ°—R\Ml§¡\"gиüéUb›·nÝc\ûüÊ%i–¡×– 3vÂP¾ ¡]"'Áø#ž9é“ÏÀ_ݸqã«ë`ŒO/_±äÔ1 †=va%ý¸Zúâl.q‰<‡-‘“z5c'Ï^¸tùÊgŸ_1>¾øÑé\ÆÎm[.ÌØ /) !TÆ«–ÈGÈ¥''NŸ#Öøô3Ð0Æù3'ÚÈX³\Ùc3vÂYJh<¦Œ«±Òyo €ÆÎ}‡ŽÂxrñÒåO®\¹òÉåKÏŸ=uüð4cgD=v‚ýÂQþäg‰\Úä·”ÆÕ?yö£‹üøãË—?&Æ€±äà>6ËÀ;$>®&¹*–ÈåŽÓ œ8uö£ ÿºpþÜéaú¹{;¹0vød»Þ`¿l”éBCX"ç²ÈEhlß½ÿÐQ°Æ¹ó.\8ÿÑY0Æá÷÷îxWDö؉ù\"—³ÈiÇé·ÖlܺcïûûðÔ™³çÎ;{æô‡Ça,Ùµí F=v0c'줷D®É"§q5€ÆºÍÛwí;xøØ‰“§NŸ9}úãÐþÝïmÙ°JD†áÞ›ˆŒp“!4š9ä%r«7¾ûÞîý`ãžaÊŒWç/^¾jýæm;Á>räÈáØ»kû– «ß*_ÀA2v0ü! -,‹œ@ãµ…å+Ölز}çž}:tèàûûÁïnZ»r颲ÙÓ9d`ø="¤^"W¬v Yä4ÞXºríF°Æî½ûöØ¿ïž`Œu«–/y}ÎL~ ©–È«]bw&—^™»`ÉòUë6mÙ¶c×î={öìÞµcû»ï¬‡±dá¼YÓ'‘ÁU¿Sd<ŠÈSùZíb ¡/¾<&¡o­klݾcçÎ;ÞÛ¶åõkV,}cþ«3¦”ÈÈP†ßCd„«ü'„ -И9Æ“«×mÜüîÖmÛ¶m}wó¦õkV.[üúÜÒi“t¡\Gd„Ÿ|&„ŠÐ(z~òôÒ×,^ÖØ°ió–-[6oÚ¸nõÊeKÌ›õò‹Ææ ÈÀð{ÉxµKÚþyèÈüq/L1{ÞÂ%ËV¬Z»~ÃÆ7¬_ Æ(_XöêÌ©‹ rd`ÃŒ’¯Õ.’EΠ1~ÒK¯¼ZÖxëíÕkÖ®[»fõÛ+–•/š?ç•i“Ÿ/R#Ãï‘ Ÿ«]4²G-yqzéœù —,]¾båÛ«V½½bùÒ%‹æÏ-}yJɸü‘Cd°Œ ¿G„ŒW»$h¤UXü”—gÍ¿pqùÒeËßZ¾|iùâ…óçΚ1u"L?³† ‘A3v0–2\íb[%õHNIÍÌÎ+z~ÒÔ³æ–-X´xIyyù’Å‹”1^šôü˜ÑÙ™ƒŸMV"W¹"@†«]q6WëÖû™éÃròÇN˜üÒŒYsæÍ_°pÑ¢E ÌŸ7Œ1yÂØüœaéûõfk€ Œ¥EŒ B…™FRÊsY£ -Æ•€5JgÏ}­¬l~YÙksg—‚1JÆŒÊÊx.%©YËÀ†‘% W0ÓèÕwÀà̹…Å%“§NŸY:ûÕ9sæ¼:»tæô©“KŠ ó²‡зW7† ¿G”4«]¹î}úÁx22¯¨¸dÒ”i/Ï|¥´´ô•™/O›2©¤¸hôÈaéƒúõéÞ™Y-˜±YÒ_í¡Ñ¡kÏä”ç† 9ºhÜ„‰“§¾44í¥©“'NW4:gøçR’{víж… g'íj— O«„N‰½Ÿy65#+gtáØñ%'¿8eÊ”'O,?¶ptNVFê³}{'vJhå¡ËŸ¸+VDI³ÚÅ–ÈéšFó6í»ôèÓ¦Y#ó -ÆŒ{~BÉ /¼P2aü¸1y#³2€±„\±º¬q2°ú=b¤—Z­&…†ÝÓ2¾cמIý¦e™›_8f\qñøââ±c -GçfÏ<°Œ%ñ-=öfºÈ@g„µt—ÈéLÃêlÞ:¡S·^É)ƒÒ2†ÈÉ]PXTTTX0:7gÄ0bŒä^Ý:%À«5NXþ2v‘ ¾’Q‚›iØÝ-Ú´ïœØ»oÊ ÁC†feçŒÊÍËËË•“=|èjŒÄÎíÚ´ W¬üò'.ŒGˆ”«]24b›Zž–m;téÖ˜šž1tøˆìì‘ÙÙ#†ÍHKÐ?¹w÷ÎíÛ¶ôÐ+V)b‚±´ˆ‘òÂU„Fíz ›­No«xjþ NÏÈ:lذ¡™CÒS=یѥC|+:–4¨[‘yR®vIШÛ¤™ÍSŽ`ä~)¥NKOOOœ:h@Ê3I½À ­›»læ& I‰2Ða-hè \¹6µ©±F¯>}Á|6å™ä>=‰1Z¸íÍšÆÆÔ­]‘yŠÒ@ƒDOàʵAã8«ÃC­‘Ø£WRò3ýú§¤¤ôï¾èÕ£[gj ‡Æ’z¤, ‘yÒƒO6©±F‡Î]Á½“’’“““’z÷ê‘ØµSûxb +\—Ðé'KåBdD”t¡AÇ2Õk´Žo߼ѽGÏž½zöìÑ=±kçŽíÚ¶jNŒÑ„Œ%5ª‰ ㈌ˆ’0 ¥ãI£¦ÄÍ[µMhß±S—®Ý@]»vîÔ±}BÎd,yB^þDdDŒt “PO˜5noËÖmÚuèØ±S§N;´o—жuK¯` ˜d°±‘ÒƒŒ'0Õk˜­v—§yËÖmÚÆ'Å·mÓºesËNŒ³ÏZäºÆDFJ GûOjÔ&Öhb¶Øà-[µ&jÕ²øÂa³ÄcÔ©“ :– 2"Q<4Hô„Ž'0Õ Öˆm×Ìjw¸Üos"¯ÇM|anÚ˜ãé?€1ä+VDFDI èÇ©5ªƒ5ê5ˆmÜÔÜÌj³;œ."§Ãn³4‹kÒ¨a 3†8–+VDF¤I ˜„ -S j˜†7kf±ZmDVK3sFýº&ÑòX‚Ȉ0ÉÐ`“P¸>¡ÖxºF-SÝú ÀMšÆ™Íæffs\\Ó&c 0j Æ “ i,AdD˜”РS b ˜†Öl€7b5nÜ„¨q£F± bµkVŒ“ yú‰Èˆ,IЦÔ0 ­^³– ¼Ó AC&°E½ºÕ«=¥2†8– 3"Hrr—8Õ ÖøÝïɈR³¶©NÝzõê3-Lµk`<õ$5F´` DFD*J=žÖ€¥Zõ5kÕ6™ê0™jS_TûÃïc<&‘‘RAC°ÆoaDo< æ¨Y‹©fÍÄ0’<ÁŒAgŸÂô‘yâ ![ãñ_ÿ°A¼æ¶_`üšãùº‘‰’ ¡´Æo¨7À jÕª‘ÿ-ž”€ñÈs“ DF$*Jk Ù¿{Ü!ˆØâ ò$‘™bÎ.]‰5(6˜7~û¸ƒé b æ 6’ÆÀ±$bÅ[ƒPCÄxã׿w0ý†Ø|A€ñ›| -Æ@gDªÔÖ°A¸ñ«ÇÁLƒ-¨/„)#ò%;ƒYƒaƒpƒšCTt4G¨/Љ´Ö ÞÌ!ëÑGÉ8B|A¦¢1Б,•5$o€9ˆ=¥žx„Ø‚ù‚ñ(J´kPlPosP°ÿÃÁhŒ‡@¼5Dl€7ˆ98ÁÑ0˜1Ð,Ù"6Dsp‚’/d` 1"[Q*lHæàõK…/Ð…¢¢ØÍ¡Ðj}ƈx)¼!™ƒê—Òwÿ¡öã!P”Ö¼=Ø¿PúñP(Jå Ñ’~¾xX¥ñ†léç(4ÆC©(=süBÏ苇Mü‡eh ôÅè(ÿ -öSDIè ”‘Ð(c¡-PÆBS P( -…B¡P( -…B¡P( -…B¡P¨à+&¦Ã¨Œöi…iÑý;GÇÄwq¦ÂÏ}Ò - -3ó£{DÁSOøbÿeÃw}„Ÿ¬¦nÑä_öSÿâh«Ùît8œ¦±Ñq6—ÙërxLqn—ÙêvyL9Ñqv»ÙiñØá˜¾ñÂ7vÙk³8è­œ^—Ûç°›mV7¹×ì°»œ¦!Ñq·ÙiµÚLq›ÙæqÀ7N›Ùår¸àˆÇluZᜳËãtšâ¼n³Åît‘û¹¼fyp«Õn¶»]ô1-9b·˜=<=Ëìñºá~V‡×ìt8½¦vÑq^«Ùêq“›yáU8áÙxäövrËë¢Çë¦w´yÍ››þE«Åj¶¸6rÔe¶9áuÃ1¸¯ÍBîks˜m·•óÂË´“gb³›Ý^x/Ƚ­6øž=ê°“hµÂ´[¬ìÞn§›<¢ Þh«‹ÞÎ ÇôI[­ä÷6rÔî4Û¼ôÏÀ»ât“Wâp˜]ô•[ìf‹üÎé4»m^'½«ÅÎFþœËbv[½nSb4}'­rS—Ûìðzè1x-nò6[ÝVx®ä9À›åqYÉÓr{Ì ^¨Þƒü5øÎét¹È!wp’Ý -ŸžÅæb¼½ö:ìnòä3s šËê¶QXུ’Þjø¦8ü)j/0€Ýî%·‡÷ÖI?bø¬ôUÁ{ì´cð$,ä£ ïªÇÅLé´ÛácÒØtHtztßh¯)¶‘©?ÅwV°5õyF´K8“Z_äô ÿŠ'ù¾t„œ$1©ò™“ªwnÀÑ -žpÏ -žpÏŠž!p׊ž#p×û:KÈýïç<¡÷¯è™BŸ¼Î¹BÞíÙBŽVø|;WüŒ!¦¨È9C^_ÅΚ˜Ôtøê _Íâó Ûg )ÌÊ•–_ljÎå§±oc㻘⋠-sMl„ÉŸÙÈÔ,©0?kÔ0SSø} Sl|Fnzfj|ojÏô‚Ìü1™©Ý2‹SÙ -±›Áœ‘±ñ£rG™n=ÙHã‘ÙCN=3¼;ÙÂec”ÅÔ¾‡Æš¬SwÓ€ASîß'šœn.«“XÀeƒÌMÆ,'|€.'û´]+yprÙé'í„—OÞ«>j(·…œé”ªp»‹œµ¸§ÝC™ê²š½;ùÈ`8 ã\ð‰ZÜ6ú©x½ô$ˆ|²^›Ùãt‡Ò>µÄè¡À$Åè«“î+"‡+üš2´¯Š­ÐëÒ}ŠäñF‰Ÿ­ô¹ö‘f Ý”) Äb·ÙÈœàu:É‹pÛàIŠrè;nµÙ´9­vxü$õ;§‹v݇c/Uý€1©=83:ïéIÃIn4Q.¡­ÔtðÖ„ÀS€3”@(à'­ópì]Ö< òI»Tã]b‚4LL+ÎÌ‹‹Ž‰é•6,39?-k$L ‡¤É4¥•[˜V˜™¿1 ËÏ,(ÌÍÏ4 ÏKŽÀ]Ä›Ã,³gÇèÿs]wœ endstream endobj 40 0 obj <>stream -%AI12_CompressedDataxœì½éneÉ‘&ø÷8?H¶ï‹Ph€¼$kÔC•m]B¡ DER©hÅ’ˆŒ”Fóôãfö}~–{/3"S]U d(EÏ1ßm7óý_¿üõ‹›/ÞÿÛÓ‹xí®?úÑñÃÓËï?üôJ¡W?{󿛝?~Ðõ“+Ÿ¯ÝxéægíxñwO¾~ýþÝOõO×aüñA¾þñÏ_~üxuüÓÓ‡¯ß¼ùÉÕ2þò›×ß<¿}ñôæãËWzzõçë—¯ÂF–»—Çß½ÿo®þ·à|¸òá§Ip_¾ûË˯¿~ýÿÉŸKlqÀnßóî‹×ï¾¼}ÿÿþôê…ýêEpñ*•põ¢´ñÂÿýúWO_ïßj×5å"¯†k×Kïûë–|“®[/y|y÷þÕ7oŸÞ}üå‡÷¯ž¾þúøþÍû_ÿôêø·—ï®~þòËñ——W¿zóæý_¯nß¼|õçØ‘ü‡‡×ožÆàß¾ü8† ótó3þpûÍë7_üÓ7oÿíiLKì]ÀñŠò·_\­ü,àú‡Ÿ½_?}ü8z<”¹>þü÷ÿϺcðüø_~õôåk]1wÿú`þðþ«·/?üy|þ"”x]|ÃM±_ÇÒÂU®í:T—Çzåëîjµ¯~óôö«7cöuš|®×yLV -×yó Þã´é )_—šÆÜ¥à¯Chs¬×1ä±tã‡Z:¾Yfôé/¯ŸþúÓ«zÿîɦíæÃÇ_Û¦äœý×þò«oÞ<}øí»×ÇX¼€ºÍÛÏßôÓ«0¿xóR§K¿ü×^øÍË_>}{áý›o>ê^mla¬ËãË¿=}XøÅWOï~óþwÚÇ1kc=Êl»æ¯JkWc£úœ®‚k³E¿ü× ABìcšô˱¶¿øðúË×ï~:¦sl×›-û?~xýŲê5\5û⺭þ×ù?ëíøÇOïÐû±ÛŽ?_íwýó_vïß}q|ÿVfÿk96cϼêÍû/íoógýËøü›¯l úûÆBýòÃëw‚óðOú—ö‡_¾ùfüé?¼ÿ櫟½ûãûÃXüæÃËW㵫_üÛÿzzõqz–Ÿ~ýÍëOãÐõ“ga}xº²?ŽõWþÿ·}÷ôÇq -—Ï zÿî/OoÞµB;!/ß}qõ?_~øêÛQÿòÍËw/?\)|b~|ý—ñ——cŽÜ ìŽòÕ˜0ýD_Ù5ðÌ «?}BC/?þi¬§w_|=qÛ¯ÛŽìÛñýú•ì¾W·¾ùúOW¿yÿþÍD»ýÓİBåýÿmüR?x÷‹w6A§-á…}Kƒòü§ke¼}¹…ñÇÿÌØ/ß¼yý凗_ýéõ«s œùûlÉþö9ëooÿíý›×_¿]öÓ -òË—>¾~õæé×ûúãÓÛç°Élüñõ»/ÆFTê¶ôöýÛ¯Dø¸úõŸ^~õ¤8?þéAßüõD˜ÿ0(ðšÆ¾xñ ñõîêöÝêïÿøáå¯]2ÕÃË/ž®>¾7yä'‡Ý«Û/ÿrpwõ£?Ü~WÌ«\åñ£Ó¿ žñ‡³»ýzõÍ`‡W!_ùbþe¼{7GÉΞïÿ?~xzz÷_!Uý׫_|xùî˧1Žóð¼ÚxþáàœónH~.¹ìŠ«®¹înÜ­;º;wï¼óÞ}òÙ_}óÝßø[ôwþÞ? IЇbHCl)¡B =Ü„Ûp wá>Vïöa|?°oåßÍÀ;ú=Z(£¥<Ú‹£Ý0Zw7£'÷£?ÇѯÛÑ»>zÙF_Ëès=cþ0âúÃÕýÛqŒñvŒ´·1î!Ž9Hc&˜ß]{ós?fé(­|}ÌݘÞ1‹yÌfšÆ˜Ø±eš«cªïÇ„ÇÄߎéïã=ùWƪŒUëÆ:ùêÊÃXµû±vDZ†£{c5ÛXÕ:Ö6—tËÇr½X\~`LÉØ -·2б1ÚØ ul“<¶K›&ŒÍ3vvz{iLߨU·cwÝŒa4i5•ÃxOþű ýØŒ.>Œm9æzlÐÛ±QoÆvmcÛÖ±yóØÄò/Œ-=ŽLx|¬ËØê·cËߌßäŒîÃÆ¡ˆãhøqDœ‡e,â86·ãøÜŒÕóƒšx£&n,ås‡ê»œ©Ãs‡ê»œ©Ãs‡ê»œ©Ãs‡ê»œ©ÃúPéùxxâÅ']|òésÀåÂS/>íüsÿ韛‹Ïí¥çðp¼øÜ]|î/>‡‡Õ¿1©sÓþÃá®BTAÊãdÅqüíÜ¢uÄëf±v¬ƒ¢åAÙâ pþè”b½2je´J)Õa*#SF¤ŒD@:m2ÊDºdTÉh’Q¤›’‘£¨´È(‘Ñ!R!£AFŒþúíQÊ#dç°¢:FsŒâ½µ­1Jct¦(‘‰Qòru!mQʺbTEiÊ$(JN@LŒ”ôA^ë †ypèèÃa°kçëk1Øø˜°ÁÒǨ„ -ÍìÞÉ2^{9§ÿ.o>—·ÑÝ?\Þƒ|.nàùèF?}>ƒrä?úÔÕS6OÞGw¹¿>?ßßñ÷óý íê¹yÖ?Žcå—ß|øêÍÓÕ¯à囟v¿œaãH1-ÜôpÓÄM7müV9×r´e -^Ù_„fnºyUæÙÊOo¡OÜ)Û}Pnã¡WDeÕYYwUýÂ4ŒeòGè÷*9>”ç’*YŇ -Õ£«xq« -ÈQÅŽ{QCTqʵƒj#Q¥•!èTx©ª˜4jnT=¹UQçN•”{•Áª*^Õ• RR¥%«ØTTÛS9ª«s£ZŒè1G·D›¹‡ÌîT«ñ&°¨ü£½P<ö¯êÓLÛéjF¸QùNôž£ê>wªÿÜCÜ7ÑÀ„Ȫs¢ -mR±1N1;TU“šªJ]Õ¥U™ì9ês§Ï=ê&@6jÁžCSaN…ؤz–=OÿõùÜà¹ÏÔòUÐøÜÏgÅûJXê~õ„Í36Å¡Çù¤Ý“Ožræ©Ûç0lŸð|Â¿Ãæ—ÿþùö°o³ˆåÃ9ýýŒ¿×á×–±Xiòk]~Ñæ}žý¢ÓS«§^?vûaîó#v÷vu˜¾éø[÷ÛÕ6ê6g€¾ŸØ„›Žíˆ=Å=ä±O¸Ê\Ù[€XŽýî šÞƒœ* bˆª f5=Kb"#ÁêG5Ü©ÙëA NÕ Þ‚¨–ƒ¤V²¬'¼ª A,sb›»QS‚Äœp§V¶{O¡DxµØ‰ÍNžtPÓ]Vó<2=vštGíŒà:Ú¿;}îõ1ÕœZŠì»p³Ò’Ò»„ÇhÐHÅ<¬Ï žÛùh[‡ñŸåßýêYŒnõøÍvÏØž‡û¸yÒÙ'úsXýRxÿ®ÿ~@øÂ~6ÂÎâ@øŸ¡ø¶a„ÔO‡N\âµÏµoÏü]õß‚€vÁýòÃy³Ã'½ý9Úñ¯¿ùJ7ßÿñ£EJ^ýîõ—ïž>~”ÐÃË­E„S^‡ù??ýhç€úîÿ7#+˵«¡_åz-ôe¤ßþªNßîµë’}HYæÙ_·ââUí×µúˈ/qÏgVå¹—>g1~ûîÝË·O_\} Е«pxb´ø!pàÿ¼ÀÕ Žª<¨ TzB÷ÐæÚдn†ªsšÁýýÃáa,¤ºO“z=«º+ÅýxT¯á£¨¹•~Ä'lž¸yÒêÉ|+Mª¬4*>móôÍs³znùVª×q£‚­±ûµB6}¦+”³ñLKÛQÙÔ¹t§Ÿõ¸Tõc$µ¼ûwÿ0¶ÕݘåÛ±ÍúØnul»ü=ƒÅ™|ø{ гøð÷ ïáï$(žÖç nä” þ³æ}e^Þ$Ž Ž ý^L/¤s\o@WI×q•Æqµ§|yW†µ¯»Áo¡ß¯÷ålï‹ô>®˜¶ý[{—·þèëúĵ½q|à¬8}ê…§]x`O>¸~á¹ù„çöô9œ®žãç>‡oyáîsŸÃçòÂÿ`„[WÁZ^¤`ÈÁS6Yxí60‰Ødâ£Æì]utÖ啳Žîº­Ãî¨RœvtÛ98îœwæ¾3^F \VæLš›<·xTZ;À»Gß=ý|3˜n!~õ„Í—ç0‚ëùÌS.<ÏÜ?ÿùwEè¿Ësø–?Gcý³ˆ‡OŠRüŒçû"< £<\Œ¯üŽÏ@k»_«}nèÊb­Þ!¦Ü|Ij\¢ª-vüáT1üì/MVeÒÇ«”Wúï -È·®†­†¹/3ºs*æ€nìÂÿ‘¢½R?•«ûV¶6›ó´:‡•P]§0}»Ä½­ÏˆÉŸdÖÀh69ûg–È5Zu5\5µe5_e5€F5b5šº™*q?L-ZfÓºQI€Y^Kž3½Àþ7ìO;Uk'ÕµbMQ—QÏÎE©§äZLU7}è…^–z=&=­#Ÿý¥Z>éŒÕïz8ëßÉØϺXtc®ùýƵûÔZü”~†pâ³øÌï¾›Ìù›Ðua­8l\xk'ÞÚ·vä…“ôõ›uúú=¸ô¦Sn½sÉ¡áLn(|ýp69Tý}Hc7—ߒȾ¤²/ÉìæÔìЃz?;=t•ºË=œIÍ—D'·½‡÷û¸ð\dˆ¶Ã™Ñ=÷ Ïrß]ŽèaÅk×i¢å™4ÑM¢è>Oô°I]REgÎb¬Y»™6É¢ë\ÑÃ'¦‹ž&Œž¤ŒÚ$_f—ŸJøž &ûöK‘‰Ë.õo+¼sâU·¤‹vá<Þ©˜rZL"ªuuŸ§­GñðI¥$èn7‡{y.Gû€$í¾)(q.Cû~UVb),Á˜yóž?粳Ͻ™}˜gï47»ˆ¼ßvìÆ‘;\:ug3³×.Ÿ=pÈÊþ;æe¶gíûçexʾ×ûž‡kE¾¹ÖDœdœ;i!æ„Îm¶Ôýî¹ã¤ì·ðäõÍ[WàNKð†õ~;¬ -< *h¡ùÛÝw3Ó¿Új’`/¦'0n¶$u1?çÿaÃNu²ã¢“jc‹¦UwåÖ[7¯Ü”ÜÀñ0™Æšq¬6ój;?ìxÈýF"ßÅ ì=7ØÊ)sÙmûÃ…ÝžÙ\b9«Sq8Ëž?Ïž•OÙ?ý´|öáº(}®Þêùdê’-8IÂt B—ëî\Ù†/=÷ÚsÂïç ÕZîVAÜŠaùñå—ß¼{9äcü –£ë±óÓUK×>—(ÿ_»ÝüF»ê1Öþ›þú?Æÿkÿz•®~~õ/ÿꮾPø?ÿJÿ¸Þê/†éêqü²E¾á“Çõ÷òË;ünüYlÓîêçãÿR¸vµù«8æ¯I“} -DŠK¸¬½_õ¡tH øp\ W¿¯Ô¡îÔl4]„‚ñûßË÷£ñ’üUp×Ù·± ýÚŠ**PŒ­_íÛ<Žoþ¸ê‹”ÞpÓ—äìoèKì×Åõ´îKò×±z¿_÷%ÕëAƒÚìK}‹c÷mîû‚y^w¥ŒkìJ)Czת+¥]ç*º?_÷¤¦ëAgGêèXráj× ûq9øÿÏë¥_­^N×ѹ6W/ûko‹9W/É­rY–o¿ ÒÝœ¯“s«£/e.â¾iv˜ðõ"²K\Dti½ˆìWq¿.ë.q-Ù%®å¾é}—VkÉq-Ñ£õZ²G\ÌÝ­;„%e¸¤»v—%]•7U þÚIÿc¼Îc…t¯uë@¾^71%Ä00†¸üŽÉ%ŸÚu cˆ"Œ!ºÑ¶Âßå›0öˆLÃ|'ŒÞ_·3aè q «»Á¼Â  .×qH›1Äþ¹Ì’ÛÂ!![Ð_§qÌæÝlÓmåf÷ñÁìù‚’ 4ÊOÑ¥]§OÆ2O Û#Œ}Íc ±kù{s=ž±ÚPÁæ€dsöq.Ø_n¾ƒÇü}Õ΄¡/s”èë~<ûq¶zÝ[H›qÆ1´r=4´ºün{~=̯]hq³ùÑÅqØf3ø}=Ìù†@ó÷¥™ BO& -ôt?ŽRò²| ëÓW -'AcÞzÈmîøùûêTÆ“C/ßläà!1µ¡,‹DR]Rã‚0¸Á»klÊ´£ôH(Lò+!H„e³5~´v|GŸa—¸vìÒzíØ'®Þ^®]÷‰«Ç>qõ.é5¿=Ü›:+W`™2ûôáfÑ›gˆçÇÿôô×yÿÉÐâoÆÿþù¯‡oõ÷¼ò«ªï‹&±$Í¥>fË~©½š²(³âÛuêƒY^ë§k<‡wQiiøóAU_¾áă×üº¹>–"…o]ã.ÁûYË×¥qa¸dŸž§ÑJ:éÒe° ~}2¬ƒÁ ’nÉ=–¦r‚äxþƒªè„ ÒïCΫot‚ì¹Þ÷¨VàÍX$ÀÉñ‡6„®Õû¶JCj‹ÅŸ YÀ뾬\O$Ú¨\%êS:iü§ŸLÚ -¼îË -Éðv¤A‚ S8it›½ô}Þ`_\ïB}å¤ÑÁ±[Ë'.à öÉð®Qy+žNo¼–Ì—“Fðû‚äx×赪o'*µ'.à öÉð®ÑÁy$`fߨÒ¨§Ó»€7Ø$À»F‡—ýÉ9â¨øøNŽüÞ`_\ïÎ鬓Ø·pH®žœ¼¼9¾ ’ àU£ÿ巇ߚ”6ĺŒö)¢[ø~¢ÛÐ+zÛ‹oA)tíCÎU¡íx~þ¸Ç'bÔyinp2H€nPꔣuÁåë^(èO/”Ef_Š Cí -ê“1•¾EÑ…_¨$0ôŒB‘kèl)jc(.Wæë”C11–°A¹ã=$"^0[7)@3Þ}¡¡zëk˼!¢vƒ‰§êõÝñ†“µ¦‡hã:™ƒW  Ÿ}7ìªØ‰\€½ˆÕÈš !yC4æ0¡{oŠ:t3ëÞ˜á` ÝÍo¨î6¾PÈ·¥ ´Dz]kâø¢Xˆ0™¾'ЗĉÒ!ËW6„Ž7ÑøèóŠ¢”úJ¶:cFj´¹ë|uvì¯ì˜-ÎqÌ¤Šø¡8ˆÎA4èÑû8ã Á^€c‚ã ÷o@(zÊøzM¿9ùQ„1=9Å=„µ!L€ }D)ì}´ -…Õñ‹3•gýìæ2ú– ¨Ýã¶{uEwx˜àA¼5×ý”GãuÌÖ\‘þÇ NÅTžñÓSEÏZ(©‹-'Wì“âš$\·à#€®ûhÀâ‡V{Ä6æèêztCp%\§ä*¶§Ø Étà0ÝÀqbî•¡"(F_m@éÚ\¬ÅTŸ$¤ó§@“ˆ—S4À1fŒ/I7± ñfªÉ¤&[æ0ñ¢säìmL¡|@ƒ4ÛÞ±­±GSɆcÈA ‚N¼I¶åÆ—yÐCĹ”€c²œ]ŸG£Ãã$×\'Ö${ë¬÷äÙµqr¼O†bÌžŸàAª 6›è‹ ¬ã´b·ÆD`ö¶åóu{æ\õXä˜:xÝx»Œu0°º" öÑF7èÄPsNæÓýêŒP XÂî'©­+ZÏÊ…%WeÌáûm•V˜[˜ÀTøfޏgÛ++%ã麘ŒÞy 7*¦Lõ|­Äá`ëEçAÞ[µÃâí§I* 6ð8…lC[£´ƒ,öl]!µú*çàÀwyzFO|MÀ”Ïp5Z¬à"Z‰SîÙ€Ù7à` Øiœ­É ¹â£±¾W$¬ëb¯à¦Ê—tTc¸ìÁÀ6„J›Yß¹eN)ê¢:”¢LYŽ­œwjf!™ÜdøujfÙä±¾Ù‰½5•`À\ºž@ÑÊ€w¡OG4ç²’¡? ©d Q-€#Ŧ–•#ßN“0‹òU¬CÑú›Ïæ“P[pètêÞºÝÄöÁÏy†J2-²“lÜB\©.ÑÄ$Ìë4+¥jv6›#)XIèC Æ*t_`3‡¡ªâ ´­•+& —y|0‹ÿÆØdâ~n&4UÀdj°¢²ýì<€ÊvرW“ öºÞ¬±@Ú'i‘®6_›OÒ²Úµ‰i<<¥¨JjMÀ€JRÍÿVÁqEjKñ^ÙWϱ‹1 Àš –å’õL«‹¤v¬‰¼Úú$9¢?ÉöR D’l|À,„l¼[Mú³¶†˜ àXݼ"YŠ #2„Q P)ÐøWϘõÁ{ÒÕÙ)8½ rá‚ïP2ÜÜûΉ—,R¦ 1šÈ¦±G†`HãâPž¦LÓç»êg7c2Ìž:€aÐêÖ=AjÁÀnhWhLÝsеy#6Cúi½£¸u„T¦ŒÐð:Ȇ‰„i|¥¨Ð”SíMDÇÌ“z:5Ð’Tê 8,Ê–ßBN..Â^>D+ªƒö$ÓÀ4¼5ÞtcÏ6êŒc9ƒÃ¤·Áð®úST/¨¾4"0n7†á<™¼èÄÝVÍ+5·•ä©pהLj^"=˜ü\Íü:Wc¸:;Ø¿ÍSU²òË1kj6~ °dqØ€³Hx/TŠNÝ“ïm£¶RÐÛeS‹ëPºé°A-ÂÞæ!÷y¥ÜÖ).f³w)ÔóU!Sw‡ÀØr5`ùRp·ãfmXƒóª)½òJ±‚…=ó{@-ìLâHñ&Q™녊ήê~–žú¦ma7/oæ„C -I|L|>šG‚iU;”̹Bw¤$¬¦æ@ C"€ -ãݨ›O}‹ŽæÀÒô›š‡Î ¬ñs†QÌÐaƒGS+ߨ˜i¯Æ‡³£ýý%LÇ®Ö&rGDýxs+Áå#`Œ,Í.LgÒ¨¶†¦TÓåq‚48À*¬âä‚̪úËbb¨·²\ÐËÄÅ‹TØg9ݵfn#á$ØWÀI9z„ÃÖ•Âìcç`+6Œ¤ÄU0«6]ò22Øôêt^ËÌØ n—&ç­¾œÜVª²~—FÁ²¨uƒÉÖÆOàg8Û`Œ²âXU¹=Œ‰º¬[õkbNq’Yظ!æÅdZnˆUÔg[­²{JGˆ÷Û99k£ ÊE ‚ÄrÅ€Fáp¾3Ìœqª7b|1ýH »¹‘³½M•Ö— f¢«džÔd/T1äöè„;áUœpx*•k< -pïø(X5glÚ¡¥S|  Øo¬Wƒëqº$Ôl²µ”13´‘êìùº¸˜Øæ™)2cEn4¶æ8µWØó{ çÀÃÙ¡ç‹óÝ2Ý C‚œ„¾Øt-FCa)`?€*MÎù†”MÆ.•fª’YŒ­Á…”5æ³íc…ÜÙ»#õ±ž±‹8ÛodÓöHbë x–(wƹ;E ‹Ïç—ÈJLfÄ%E–æE;£“I@âoýêÇ?¹úçÿ ÅM¢aB·¸˜îÛ @W™yr"-WIÚÜ=²m¼¤·ùx¢·‰Ô]#nÒ\©Zb§f*ô) l‰ã -…A‘¦¸ä"#†¦â ké;¼;&µ%ñFPqÏvÄ IcRx©&1÷ƉÍÕCíB’Íb:$ž|œSLmŸª‹3;‚ŽLdT3kCÌ-«@ÔÄW°-‘L\Ÿ»…,Â4òÇüp%öÅ"ãmiÄh|Q·„!ˆÐ­&ÁϽÁð*å’Œ–¦ÐMÕ¹¬ÏÖ1Ÿßc½"Càš¯ž~t ­RVrµ@Í_œ«r ç»s“˜Üþ—xLšs]Í+A½CÐl2³æC1§!Îį†k@­b=ˆ«8ÓF‡Bœ¶Š…siðwnp´ç¸ª&‰÷Q½º44 "ƒ¹At\ovsÂ8Ó\ˆ8Á>R˜;FJ´&‚DF„i[óÖ -—§ó£™Š"ÞΉ ›Zhi_‚u #"cÎÇߥ5 3áTO€jóo–{QŒõ(‘Ç|U£ÑaÒÑ[“jÆâ^žâ–h£àžÈè@oSnt›ú…BQ›E'OÝÃfD¹_zÐSÙFLÈêŠd«0­}2„€‡`¶ÓdààÙî@õ汈ýæÅí –Fxâãš)…û-!ûÐ3ó®%¤w@Ov§7_ÄÿÂ<Ä=$cj˜€æK¾Èý\9Oâ»Ó§V6iºŒÅ¯PVÐÚbåŽO“o-öAŸf4²h?ÅÏ“<¦àFór2EÛv<%%1¶Únƒ2FZ¢æ#ÛÜ$¼Â;rã\gz!ÝÜÅJ÷8ÛJ! Ê€ù 0S¿Y˜²¬>M{c”}v±o¡f5á[ìž,霳¦¶u`k¬Æåª‘¢W”ÃӌƯZÚŸÀªgîˆoÆ\Æ…t'Š+µI.G‚“¸E -¦v×nñÕìAèfªILŸ‘êM%å ©”šs²©76š æW0NõJJ•p-ŽƒAü…æ9—y†AÛï:€àúdsˆy®Óä/>’R`£.R‚ÎJ¾+± Bü˜bðIFØŠ>2TMÁ¤çCÅZ;µÁP1)ÔOFsE_5carêÌ|qµšØÄ ÁZ“ ïK4’æ–ÓÂ'}Ê!dê Ö‹0V¾zÆb«¼f²ý"@0(À™·ž„PÈ('÷l¶Œ:ZOš ?š‰ -qú»EËš$J?²˜ç3¾aT¸J"(ÈEÌ9þM…Zqt !Wâ³Ë>Ñz+.’f†y·ˆ;ø@ªQº‰–>Z„‘™Ÿc^$ì*¥wR’Xÿ‚eëL•>¸fa·6ß)ÓE©¡;æÀÁˆÂÑ0HÄ´\z[[î.Qû -åXÍ*§¹E7…I‡îÛj È /x´èHìùºxÁ¼-u ÖrÍ´4û™{ÐVá{u›èn©3z‘’  -I?LÕb«Yœˆi`5s™è'Ð/šÒ‹#>Vsè -å ö.RGÃùZv§§ ‹7«·c3£¾›n†Y앯­‚¶üÄ´yd1–Y¢‹¶’2:[ð#À<•þ 3}ËxK¶±æh@É/šgàÎø9sJu+MdD\­ -D´»à…l*–Áøk²BéAk”(›Öúéræ…9jDÆk°.¨ûpó ÐÔÅ'A Jpj˜à%Êd7CòÚ¡ à`ÜÅžšÙH¬‰k±Ó„–€W—UE5`4G«¹!%Žïzï@ßL*U`J$¾{À˜ÿ‚Ð|ëB¶m£TwÉjmÙbk ,zÁ1–N½ÅRÛ¬¾pS’Š·bñSÔ@ZR÷””TcÀ½,%cmÌêŒz˜væd™*.–bH„Sðí´ׄ¸è aÓ­a6ŒRú –¦€šLj}¸D¨Tp$¸ö™˜ç:-)rLÖP°xQh‚ *C2ú‰Ûj1ß=Í -x<›­9ÀZ}œ$N(ª>§¢4¥<‹V V¤´­*ã•VÍ|dP&$MÔ8šàL±2Œ™™`xµ³W:cFscD­™AÕdo¦ˆ’¢D(+|n‚ E„KÖ¨ï¨XyU¦0â33mC¾$†´ÏWÍJÅÖîä’­¢Äi,ÓÓq$ùjS࿤‘$:º½%R08Ž;r ðãŒJn¬~°«‡YÁf‘Àjj¨®ÃQk3KV³DšNzð¾âËå~—æJëgÀHŒW¾:騉 &˵Ì^´@¥:)ö½xß‹j¹ üu¥­À=Qg5§-“óL÷P¿78Y¹’¥}/ÞõÂ# ë‘`zÔ·`‹‹'\›l­:ŽÍŒŒa¬* -ô‚à}/‚…yrÐÁB·àFåßtËÎ}HÄhš—ùfZmom.öøñ¬>‚c þp¶ìÔ=’ `É^#x0SÕô÷àh@S§âdzÁw…Y¹D@JßàPÚàím(ÀÇú<ì*öàÝRÙãÊ" ±;¼]¬-32³#`:XʪIâÑÄ!™•#Óõý¾x¿/Pàád³)Û‚ÅTÁb3ï"¯’óZD×Ê wR’7x×`q¤Ì’\ªUX¬®È«fåA£«0ó„JdTÀfa4îDòIS]rÄ‘#Áyв%Ï%X ··RŽlUçJª—ˆßãxØ‚ÿ»ÖÌ=Ϻ#0fsòÇ4ÃkV`Ïßãíæ]%ÞlÞÉ›­¯qÊ„t hQè{ò«øôxU…DÔkÛ¡jégn¿ZqžOâ4ëÒh±¸b‚5lIÁLÓ8yåI)Ïpïš’F«[h3ýóÖLoÞ—Àâ~me‚sÖ0‚v%Óú‰’ -)"“Á¤eÿ&a‹y±È¼iNâòãsà¸J!÷×à&iLÍWeFºÖе‚¢¿: ê÷ pÓX¥añ8¯2º5dI©\Zyjk©$øµLñGrLÊë™XÀëæ$¦Þ„KàlFK‚¹ef4t‹›¢¡&‚¸Æ²Í™¡ü/ÐMkHÛOû¶€“¥9Iµƒ¤„%f@F¨PžKÒW:‹¯U‚bKßXÙO{¢€¥1¦*'7Íø+0!(3>9ÊIÉc›ÄË„ëYReíóÄC>: Æ58ÿÅÂMdþce”YÏzà“3³Ì ùñ–d3cדì¢gÆ’5U™?b -ëÈ ÑlXƼ,!ßi–™œ8ÚêƒFkÞª¹ «c£™DŸÒšE¶éŒÕDÆ97ëVôDª§s_Ußü\ü,ÂÚE&…¬‚òç,µÌA&ñÛíLÇ$5"Ê1ÏÃÓu,éLÓè -cÌ+Í@Òjz‡Îøè3và)kC²&‘vV¾BdY©o@߆Ž]7Ba"·UL«ÜÏ|¨¤ XKP·V@R!€a¨z É‚‰‹â¬°@ZJÚû‚ˆ"ôÄéwC8Y -K’U°ìoãô9÷—€,÷´$ÅHXq§¬ÙÑ-?“¬T^™Îg–Z“ƒ? %|¬Zݪ[®³*‡œKñ‰O÷¿q¸—¬½°¢YuA$ÒÀÂ[•:¹¥¨¶‘”v´Z½ëŒ¥£¯ÿ‘³6#hËÞ‘†:ÄHÍJt¿‚®ÛB‡£ >‡X%]ǰv[6/{³<ÜÃ(ÊÔ:s¿€Ó ¡ðNBL–xU–œ¢ÔY%s¾ÅÒÀ2m³Ì¯ÑœeûÒô·«Çät{ô@ëá!·_HºG DÀæ’ÎÀ+œWy‘Z64J­%6¾4ë‹d˽•¡Ì쉕€g0Ïøu1N üB±½Cƒ?ÔÍ}¬ÅL“Žø\ À`| ³ê´øY›)d ²ÿ¬c„ñ,ßÒ©uKšŸµQI<¯ ÄikyŽ:Y³³HRÔ´ÎJ†H©¤cþÌV™}e¦_ -®Ìç ôE&fy ´eð‘ÌœI<›!ÆÍ ³˜õÎ&×5Ý›Z)˜K‰Áð,ÇÒZŒ”‚Æ4~Äö¼Ó[¼'óð8 uùàÖnÏ·'`-qCß[.…êA+\é4o!G_œt@{<ß»Q˜/·íÆLÔ_O·Ý…É’æ 3äKCZï^¼º§Í±ÓLºíG_ÀYܽðRžo=Á -¹n†œ­Ý€ì ;µ<^=‡›Îj6ÛFð;À/ƒ7žà~œEsÛOï¼Á>-—À›FOp³ÑD#ç¶Ñ¼ÁoÛeð¦ÑÜhÔ»+än±Ú‡B˜N>Šf½m¼fÖQämmÉð×’PôÔ0°x9¯‰·Y¸” cºcéØè¦/Yû4~™{\ú]×~6GAK,uæ%ð ”‘GY«Í˜ÉI:õlô>KHÂyKðΧ+þ•:+°/H.€'’?îÐD+Õ»o†â=š‹`E²ïâïí¼Ýa{S™öØ/€dßèïE=ß}£²ÇB?Á~ $ûF'xר–wªqߨxXÄä¹Ã~¬HÏã>m´™n°o´šì±Ç~ $ûF'xßhYò?7ÐPCžXà{kOŸ„âjU¥i¬¤¸bhgc'$Q0¯i/ïU)Uº%T xC¨$À‰5*VžO…|ßèNæ“›ñ+–0'º§Ï¬Éšo÷J0žž6Çb3\Ák½GЬ¡é ”@Ë¡ŽÚfvœEeêý3;áRÞ­nÈ@µ½f©ø3è¢Z@UŸõ3¤-³#¨oµÌ4Émw³‚ë#aæí¬·';^²‘gõ·lw 9Za©‚®íV+tÃ~€åÍ-*(`0°–Ä7§Íù¤·ÇýHÕòÄÞnÏÁ'`-¸³Ü¹’}£’»’f›±õ²@Ùd²X@¶HË!—yz#Wà EÇ]Ÿ¥ÞÑ)¾f'ö]Ö\–¥ÏŒ,˜XzupÊhˆ€Vþ<ÁB^ ®)[¸Éñ¤ -Þ÷n6·ï^´²Êsgtˆ‚çLWD’¬ ¯š[¨@–¢C›Å{ˆÓÉ—ÊFð¾{³¹}÷¼•6"ÝEÔ8aË4뢮‰xƒ|§© ‚ ‘û~¬vHœ2R…Áêa«8¨+K%Ö}Zëê²çÓæ–u/ˆÅ¨\IoQUr´™é©%¬”‡˜[7Ô,[­d1 ûŒ}6+ -¶R'°‘·íºsªò7»Ynw4f~2ßÎ -”¡Ïî 024‘ÞâH¬Çó=Î^àæ­QêÃìEp¡nrÞ˜gä?ë¸/ˆõœÓÖéãù¶hN¬ä¨Lxx °Þ/Ò¬T92Â$ëÀA)dŠ__Â$–¶Š¿_%h¬Áb•-`¨/̈û/­œ•7 +£»ÊÊoò6NGÇa»ëÂÚ†ö–®ÛÂ+Ší¦^Pl^uºåþÜi>'T;µæ.àö¤K÷˜6³q -8Fomï"ÃuØÞ‹ƒïÎÙ•q -Ô|x¬ì-·‘½®ÝÃNoWëÃï¬s­²TâÆÝ*ã7Co¸\Ðf¯ÏdÑVyéµÒó†CæÔ[b›)D x™»U/.€·vsùCžW8Í)mó†õ3wÞñCADÔ£y峇óE/¡}xgÀÛ§/_¿Û€üÛwï^¾}úâ*þäà®nÆÿþù¯zyƒÜpo7Ýÿó߯/ÿcüð¿è¯WéêçWÿò¯îê y÷W‡É¯Ò8¸z¥Ð˜£4vŠäÖú¸…ænU‚WߟƒÍ¯ß¡S¿8X§üÕŸ£7ñWb1»¿5Ÿ¬b¹‚5zØ€jÚS`+¢{(°CÀbê#©‚dx¿BªíÕœ $ -1pOîi´d‰–z¤ÍÚ’jZïš„eo -ÛQ Þ_GZ(Õðzó’ -a™ -öÝîŽU°VUÖæ¬8€Î¶†ä -ÉjÙ,ŽÜ’1A„%“«‰W} -†7Zµ‚U$··SÇD¸4kñ›)›jß׿ìz©ƒ¨`­ êE#À<ÞTYâˆeì'¬âú®†Ùx5MGß ’ô…`… A-˜^-vf0Ç•°k€Ïm0Õõõ]jOi¼‰ý*å­Z7¨FÓ>ܥЃMNëy ·]¢Á”uÏ%.è[“)ײgÎCµ»Í·C.VúGAÌ·l©«ÉÙ©7†3Q«®rr°w«Ýù­›ßç–`zäøˆÝ H[Å»*(°ø„h­.v@ož·í°²EýY¯–•ÌÍNZ™q˜ -Ns´]X/€èÁr|%>Lš`5È Ü9…âe@5P#» R!À¾ ÐP; 0µîJyC[[ñX*ÏS“«1Z18P*8¯k¶%A'ĉ—;Lܰ‘KÎ} Õj1`-S†7µf(µðŸ-LqxM ŸÛÝË®ßMâ[‚ÓÜ1yâà -`)ÏŸÇóï®°.Àý2J×|P+¹ð géomFjªãè(f …i .`"È+ A·w€ƒ%L¸¼]¤¼gÔFb°(ÕÄÈÒ -Îë4FÑÍ`@½ÒÊÞÍ‘ýR^£À”-ÇÀÞAÖÞõ–"¬oGI{´áªh¦@¹@ÛÒ¤t¼%~cK‹5pë…`«4À¸§x õZFšPiTh2mÀ õ¨¬¶?O×k–QÆÅºV°fYn¶ƒÖíÞtª·Ó€.X¬ÒÝ`U¬AV9Ê€¢ôÛ£§àfB9šÖ{³Ý$¦ÿ¬+¬È×#ÀÞ£ow‡… ö£ÇáìWgñ²Agu®õ& #ÔZýÜR.0˜¹Ô€†¸hm:·‰úÔ¬…i'8–˜ÀPÏý8ñú@¼^´mìucÀÑÒmÎ ޏÁfÌÄjÀ-¶¥&!S -½›pÌiPòYD r5ÀJa˜+ßõ¶ô'@ÇÔ?k¼ÃÙæÑžIÀ“¤N*X/{X£Ë¬U— 8Ä P¢a@½ÃC9%ìU_L‹4°2[i9>…q%´Æl"Rgº­µÀUnïb¿(€úyE­Ê©«å˜ à’`mЉXr!£¯»Ž©]ËÞj… -NZVtÿ{ŠÍb7ž„l|£!ðv³ð ¡UØlÆCÇÞ=]ŸG[¸„Scú=bá,îWG¢I(ud`“W …'°ÚÑ:Å{\tÏò·´—{#ã FÔ’X‰C!†NàíB“J3*ê­ÚÂ[‚»'m–;Œ XMú¨ž‚áYâóqÏÆ7=Â\­)Q@­šÀ|®[KÕÀn7BV¸Èbæy`5ÄM†Ö;Œ˜&;!ƒ°õçšc?ºÕsÕƒ®VH’pÀšÌìdïbîkDîxS*{#új2JƒÀcwœ0½Øæ]2J¾@iÐ-×Ð-?ßÔ„.í“fé;(—§Z¯Tl«Ùní3Xƒ´Áû©iH.Pï!¥T1\ѦZ=Ø¡­Ér¡0(±<Úv¿ ÐÈF}×® Y6ú‹t+LuP6Vaš4Ábí¬z+eˆÚY Ô˜xš"»Ê ÷}°n%Ü›¦@ž†DuÀkÕöªÅæØ÷¢±&}UŠÈ–‚Êe5£âÙO3G°yW¡š›­@o¤KEÐDöº²1ÈH dH¨TÀë±ó&äÝP±ÚæQ`‚„!ª‰/ï²Þž©K‘7OÙT`ªMͯí4g»ãàk­;ß:%ivÌ.un,dž´Z”¡M­Ÿ#ž‡%/»Ý“CT±ÀÖå´õ¹Ý•~0ƒ¯g‹ßP ý΂éúšítŒIœñ;UËZ ¬™l§ -ë/w<N‰t;¿Rí?c§j–›c3±<_Ð8lÍËþÑR#œ½,ЀynÍî3 &Øä÷¼ ˆPm``¡"56•Aò<ZJ‰’áEQ6 ­`·PoGWà$ÂÑ:+;o³%óÆ Z+i¡Ý¢nÂ.ì³ËÄ(0`#©±È°Û R®T#º½%Ð…-"Ê÷ÜÁ{œ´ØœdÕÙÉ£QPk{8dR=ÿÆ».*, -x$ÙÝ"€ýe‹`Û!¼DM9¥FhdNlbo;‚:Q„÷Éë…?³™ ¼ÆœÚ{n¥aA¬iÞK¬`õ«*Ø’>ì]³E‰úA£ÝS“ݱLK³#Ôʨ(奒¬Æ–ng›É¿ÌåYo‘²gÏF‰v(ÆY)ÓvLŠê)Ì8ÿR‘s®'öbOa~_¦ž[:ù_„ €%=¾×8.~¯¡`–`"'€•v*¡§ZAk ¨`·©Y§ÀúDuÓæÜh)õˆ€NfàDÙ©©ÄyÈ¢ÉÖ˜À\¹«PhÊä‚9×TÿU*È`NVõǀθt4«ÚdÈé£m ®vŬâš{ŽgzÍâ#Bu,-ŠÊm³QþvÏÏôv@Õˆ‹#HŒ˜œîDñPˆ¼Ã`Gƒ àäÅf%á· œêáZlKVLßÕ §“ Õ۾鯧ÁG§Ô¢íÉRì/«éR5.UÏ]ãüÞÕÄ¦jšSPéLpŠIšÌMÒ$ެ‡½EüPŽ3«—9c 5H5£¼8 4øÈÖVN¢!m஑ڊ¡DÓ”÷ôÅIû%ót´óÅcmBµUdµJEKQÀ£psÚ(\RïÒ7tKù,€Õ±[°Cé©[~ŠÒB_[âöô&Ö)-æÄªfp–@YCÇzs(Â[€a¨–FC‡¢.åŠÏ[4€º9LsÔ[¼G‹z‘\õ¦ä[ E´èW;º¤ƒæ<)Ðez´CsXuÉÀ3Q`»¨Ó@üu‰žœ²6ÛÊM'MÉi‰ókn;“ZE¤§ŒÄä/|Û½“Q$öé\h‰*¥sÅ™°úˆ·YjÔ;¡¸Ȱ›;ÎáEƒºÝñ¢wš¼%ž¦43 l¢FÊ`J£¿Å»Áö®á8I³ÀŹæL¬ÒñŒ¬ˆâi3¹FÁðÉÓ@í´ôW†7Où£‚¦+P·Å_§À°ßË|× ±Œ™À]óË‚0 -Ã[àè[‚jOÉÅ€ôw©1@¸O‘}xäB(,"g@¨ÉÞr>.,ÝÙŽA@”*!âÊIß<Ñ$>©ƒäýÎC§WËÙÁóô”Áì1ßZ’@KX:×§liÉÂI,$6ÎX ÆçgœH3k³Mâ“s(ÇâÅ\Hi¦\áôÓçQ -Þ\ˆ(6®`9J©pŽGå Þãœ|ØÓu#RúÕrŽûS™€žÃV7%€ðCF+œÀÉ×âÝö®±m‡\²=Ò]5£©ÈžªÅ’ÎLG>Àz ´c®ÀVÀ#;èÅü6ç›ÃA/C`+–”0°w;DÞ +Iˆv2EV`Ñ”¸¢u_Ñ‹f$€bR 7¨‰sçÚÇ5(*iÄo Åü$ ¸2àúg¥ÿ8>ÕŽsg<’aw"VJX“±±“DkÅ ˜$Të| 0í~îi$Pžê°Å—jL£Þ+løHSó…¡AK&ypÓ,]Ûafרy Âp*ÏÅ"‹ˆÀ55aÀþ­ï&Ú@Ô^H¬4 -À -svl¶œUŠ—Ñ„Ð-ÑÀ™Æ‘dF|BóHÑS ¾Åõp‚äè”É4ô-èMíg{pœG` ¤ÌbÁáp36Xgå™ðÌv‡ Ù¬×XÇ6-aqb ª@œïƒÝiÅVPS’õûÀ.™/f¢¶£ -î«AŒÊ RÍRC¹3zÇ.¡¶^™ñLBz´Ê¥õ@ê¿Ø›“+“7òW7Zf|u´b -@A0Ìëìlj»ÃhU¸²•)aÑUÒC-h“…»únMFÃ6›”dódf•«XE(’¼[V‘Võd÷ƒ=’™£ÕbÀgÇz0#¾³›˜a0”´ žÑàÍÍ0j½Ï -ñsÉÅ*Çð_ §zŽð«xò&âB_Ç6(nwýrZrðiy‡fŒEÀ"hYœÎÒ!1é­È×f„8_ËÒã]s+aÁH½:½À!Ü©Åé7½Ýjx(pQîh¦=Å»°äd¶qÙ -ÑOެâÙ„’Ÿ&0ûJÙ€±œ7XSæíöëŠL‹@`ð—ŸÖâh×hYœÏ—Ý« ptÓöÌÙÎ1ÉRt×·Ü"…SèÔìu+Æ -°NÙ69Ú -$г¼¨zÃÞ<Óƒõ¤1lf%@®Áfuòë¨AœòÉç«wg€`0³Ùió”3…å' ¸*0 ¸Ïˆ)ØÃä:Õ<TB~‡wCôqYèÉæUæ÷»¶Ø ž3lÆÊ°Þž? 3‘CÁTŠÑÌ€°é v7ïø<—’Œ‹üGVÒ~{Ò5®ÍÉ}Â5CšwRoÿÁ -Ã8õÝb¼$б§HÁƒ]q¶«í³92s÷tÚ#Í2~yƒÌØö„Ze_ø.xp¤ûu€jL pwdM«-´ [ŸÅ[€5ôÑÀň¾dR4CšNäi§‡‚‘«Ó%k,jºá -zËXhvÍñ|ǰÆr ¯48¹ß Ä–Úk$‰4\Pe@ð.TA<âkˆ¤…W¯`š&Š>—½‹/ sfªgw¥ä®' `5?¨°<ªö¸ãM©rsÔä~zà­ñ`“€ð‘Ré ¯¶ ÕbJˆ ÎÏ+_DrF³Ÿ ¸ŒTœœnª m1c,ÑÀ÷É+Á_*¶0óE²«kGW5iÇ{ä Ü}™à“ÕQºÞ>šC€žCCŒbf Éê–‚ÁÑ¢#]°Ð( Ú¼:»‡Žssí¶ÇÜ\½ÃSn·=*²Q—‹z;=HIC æ(ÒÑzft—~oªÍ¹ýi=“9¾Ý—ƒª¾N´×9cÞVÀ€FX!ÒK«®éÌƺu»m -rœLJ=íÁ’-P2C|©3[€‘ÇÝh7lù|Sï>§}Ÿ“’[’v`º3ì¾}ñ¨8oéüFú –U`À¾·ä~æáÖù·wW„… -¹•q¡¤Q’æÕ~Ñ.db1ªiÈf5†o"ðG{ÖÝ¢•ät˶¦& áÜyîê0åÃR70->Õb\ž¥Y´$V\`@GšE•ÿ¤Ç9龓©a†“׆è5–Ð$@çÎ|mf°—+8`nK “€›o(8ñ]/A€Æ1Þ¼ÀµA´o7£`hÌŒò®mß3CàØ$Å‹(ô(¼`2¦ ÀÞ¬ßÒ5ZÂ#÷Xئ[½xË:yVŸ¹%â _H}ÙÞn/¬LÛÚEÉOg:,þÍb¬fL=,—ÍʰlœžB‰¥ty:šU¦!^&²4²#õ…ÎIRžbÀ‚<˜fŽy"È¢Æê]¸®´±Ö'V®GíËÀ2²3E£,ì‚QŠÒÍ 5¥Y!"èPO䞴̶š‘‚sfÌ0,± --îæCT¾¶ô©ÌÒ˜Ì@iÖ-Ä|IµÈNqzID– Š÷fGܨ¼k- -4ÓV”2÷TLJ(y"HSó‰D‹çw51}Õƒ…÷Émš('Ñ-Öí`v¬Ó>+(ޤ’>ð†‚ ×Û£‰—û»-›®« - îiÑ*ÉÍÓÑV ŸȾ2ݵç‹ A0‚¡ÔUvC𱕥NA¿wœWOÈ›®@€Ô+i‰€ã:¬,ViQ–)|hº}ï§2Û,`ä‘`‹\“·Î"E‡!í6+0h@¸ÎdZèëð’Hž,ESª<4æÝN†: Å#YZÀŠIcˆN‘‹}13Á¢#d@^Ò››ÑYDRé½Î{ 6¶”ì&{Ó%n8q¾†Éûlb+³©¤¯²FD‚F™¥öܨ{í7¦a¨J+€t«7Ë»²ï¥¬Óz½ ¶Î(°¼±ì˜Gª¼Z“‚ûÌÊ4ú’Ý´gt³U0ù9¨¹Ù“Ë»m Ê}»/r`˜æÚ¤$àeµa` 3ŽE&ðD,êNE¨ù9õ\Œ}Á›¥,R8J X^Mª9ŠÌœ…–Ú«Œ)踂VÍáHÜ Aºž¦e+3̨Ï8Û{3Ô's"3Cm¼‹°`õ"7H ì'ŠêHK ,“bµS­¿TbQԨ̜>e• XŒG¸|®eÜ-œ!¨™‘¹ -NG¦ßL¸aL5­™ûu€—êg©7ImÎYMZRc§ +M9«Mí(›•â`%Þ6Rê¶"¶6f÷`r<1*Poå,c€Æ”* ‡à®l¢?§6€<ÌAwÜÕÒ–u2Kv-rÎI9‹¥Fæ>BtyZdëlr£3tç¼–K si¸`/ë`Õé h)ê¯U9ç˺ðT¨¸I=43Ô©\yœ÷*@ò¿Lg)e(ìÚ»t2«s…:«Àü‘,¾k~®––¢ eú—T•T¦,%xR™*CZ•8Ty/áäpR´€!Giî+!2Y¼¡M&fÚps§$˜¦962×Ù -Ú5ʱÜàKZ’ûƒÙY¤?$Ïp‰k±È4Â\"SV ìCðh„ÉOt!B1Õ#Ê¡a"]Sܺð7´Lë“Ê¥ñ;óâ jhA,Œ, -š[Á}Êí)D™}\¾FR´´£VP›bp6«Š½ê¬Å8så]˜÷fý>ÕF ËðÔÊÞ-.Š+Ü“™R^¨’Ëš~q&gƶ*èÖ"`I"‰7#VœÅ -Ê%½P-½"Ɖ*TÞi‰pÞ«¡Ú\šÑ rýgé»Àó"·PXrfAæMÝcµ§µªXî&¹¬eŠ@‰!Ìö}. G§*Ó ûN&S¿@aŒJëÑ€ƒì +¯Ò¿äúÀBà¶RݾOv·©­+K{d+älï}“Nfe¡àç×tÚKÕtl+ý»W”O[u([ -6?OŸk€Æ#Ày/T>¿7J a!®ZB¦žlÆÏj+3ûbÉî“20ž¹j84 ùdœåZ¤0Ab| OK ‚Ì &ƒL\| }JUëCäêvî ­>ý ¯UBA)ô_«@Ïï#c|=ËóµiMHsla¶-'ɃmeT{É…u8x²#½wj¶šH:Ö¾X’•‰b´3—êÅr–\fÙ‚KRh•‹@ÏV$‚%¢ôN[63h¢U°$Pʬ$Éežd`â(3mÝ¢1Ÿõr£EÐIÐpôÂERŠKÒ\ž’¥Ôôh̼LÓû“"@FÅiÊÕ²ò>×ù¹/@gÝ}ÓÑ´¯e³Ù·³ŒŽÆ~zƒ%,¤ó…úBâæ=jÆ’ðŸùn‚ºšTã2rÅʵê©sì> ñâ ¯\A-soÀ]é>:ÓÊäÊôOWŸ3Oè¶PÒ/Wôƒ0M4 )*7¥¤sý^h^_ÖëºÝ”m»¤käFš‰üža«~Ú9ô"T‡•ᦖ0Ò`bûOfÌõÊy¡Lè—|RÇÂ+RÛŽÉ»bØt@ÃRgyÎÅÏ2qrí%X´Ç•ò–§ç“F}IZôp¹i —H €PZûWŸ“šïqYî>³Ï$ŒÃbÝ^·Ê‚“Þº†ŽIœð|¾6;Ô°“ƒ9;}°ð"{ Aã–¥É_'¦Ã2}UÂ3¨QV+”Å\Ã;76%·`nI;‡”4X¢èôà¢ÀaXJÙyd_—0íºRêòhØ:á -–í6pÅÖœ¹¸qÖšóf*¤–Ž€ ócDÉäUVšHó¸35‘`9,Š’,Jšj˜P·¹°yjùaúÔ„ä„8Ò•!ÄuØöJ#²PgÄɬ:–W3S—Z:)¤. íR@ -¦Q mDPoouÄÐÓÀÀ_Ý™“k$nA¥¦Óã †êi)ZMÞš0‹:5FŽŽ¿¦9‰ÍÊZè*V¾‹Tmi‹Ä4NCÕ¦:ÉL/«ºPi*Ó«­ÑîWÜdy72™‘£!MeÇ[Ý Œ+1qå\Žëð饰˜lî¬Ün‰ízs_24¬ÌlFQOF&s“@ Öga< -j—6p˜q&8Jn‰&ðÓµŒòÆ<ŠP7µúµD±îY±ŸÆ…ÆÂài¨ÝÎnx#‘s&š ^–Kf<"*`”uÜ6³]ï¤Ñï+Ù ë» - 9Ó0x³üÙçmµ0t²#n:v%~Δ_" ÖMK2È!V8 Þ3 ‹…ûuPz³Û—q:¬³u† úéNðÕîˆ1¤Ós ßsg Šº¼zÐÃgÀe*6‚%:ÔÍ¢c‚` ¥«|r¶›}_§$ËVXì•np?©´/ÓüåVæ:ÏcO‚4V‰€¦¹1GoNfö€¾ùG€›E¾K -iDA_|°Š9„§'˜0K¼Ù¦¤QziƒÖ©¢ pÊUCÑZW Öäšç,RŒj˜ .ó°ò·ò^Yk«²¸A"Ÿ¡1@vV¦"3“¾uË×J¿UV@8‰Hàð´É@'‚Ed–m=$ÎBczµ¼Ÿºx R»C\¶Ö®º/ËøÒ”ë—ó•§ñ~‘+ä*˜D‰1yÚ­}šñîÞ´–G€—ÃÜ-ŽÞûYPð-òi†Ä€)-ˆaÖcN&냃#é$™CÓ^œf^Îcï6•“›Y¿I¯žJØ„3ª[Á6ªY0XGSv’;9ââà÷KyƒÿŸ½7]®ë8²…Ÿï€?ް¿hÀ5­_&=Dß a¹»Ýqã†! - º (™ýô_å°²6pE€å ê„Ã6™Ügï²²r\&vt4ìó€~LÌÃëãÅÜG,-GbÚþÒƒØfßyåó$E‡@Eá‚p!kžíf‡&¥ø šº|Ì‚hÔÒIEz´€5Wó°;4¥ÇI`M˜<í´ Õ„:BÏÊÉ ê1·Æ`džú•y«œÂIíÀ™M]a½Cˆ^Ð?JXàEú™’&|ÅÈ!ù|ÃK7*¥”¬ÁEP9jQÚ¡D©ÒM´:kºò¬Ô‚áä%NÇßM…ÑT.¯ÅÝAXÀ!€yA‹:Í`’vÖ:­„Ú— dèE~aŠØü=ç(O$(o©aô{@ø…r|°hgౕ9ôê'»ÞN(?wâßÁ–Û° Q-‡Ïá(zK­â$ÝnX„|`2̱çñb¹ ñ:Ÿ›rG1¦ˆ¦/:µê‡#ÃÙ+‹SëDÎRËœ D@ÈüºPÝ$ƒ“µû„0<Ì~ryù®ÞtFê,®SSŒ}ph×|ÒºpáásÒ -o]´}&Îübæ•CÚýɳÐ$µ*Øswz¨\3vì4H·W²/©un…Q€Ó²]D\޳ÕtɹÄBˆ ¦GT݆^ªIé~v“brÇ¥ÖŒ)ïc‘CŠÂ¢“ÚØš8é -í*’uÉôPØ9—ÍvQi#/ÈVT¤úÔ&q8ÊÎh¸ÊEßN.¶. „9àù¾.XhŒö>«Wô8:Iã5.,DJWÀrL&†EÞ3}N‹iËŠdx;òä™Û&‰*ãùëÎ)sqqL‹ÀÁqžH­¾ "2 ½ô¶tý}[¬ŒzË=j—ÌkâàhD6‹Øf„}TQꨀ.YGÐN=B…¡äåª:Òm ÕĪòÜŒLmè)lСñXã."+à6td¼4é r(öV¼½¬Ú$M\\²³ì„ïà‹“”Dû}´«ÎÆš+¢{7 ŽÑmÊL*!‰B Ÿaq2„d+ó÷ÝGÂ2ð©²pýlötöëYV¢Ã©Ÿ1OWºœw€˜iv³eB8€•cw8aó¨à -š"ïŠiãÁB÷Ûj”ÜVlCä¸ÿÕiDD„{Ú–ÅB;0Ü,ŒSæØï“”¾á÷Ø-C±¢©¶Š`º¢ã0¯e„L'äý ãV6ð æaM‹†» Wd|U¡Ã‘¿Çn%ß&‚ÈÓ,´KV‘$|¦ ß4• -ôf7­Œd5÷îV02àç½¹ R†©¦v+<309l6 Ô 3‘D’ cœä«úŸgäŸÄ3R`8ƒßÏÄv}¨€ïHÔB3 R[\¬Sh½d’µHq B‹Ÿ:gœïOºâ%r±GU8ø†x³@›m JM~î¬N”’}¶Á«=®ô D81Ç?ñùyM7fÓ˜b4ÛMÅyyð¡?Ù7œxÙeXÐñc #"­wŒ‹¢XëV\;È!Rãkwd@R„ó[¨“…(F*sHqn8Lm-é2(ˆ2×þ!ÌœTBSk"¬¬hÂô`8Î[Èq©ÎÓ&T¼…"´² –GÖØ7 )Áw; èûæ‰B_ç,ÿ"HÔMYà/Ø·6ÒûTo~à"¤CÄlþg«ï'j¢nkl瀱 çu‚f€Ôœºá÷¼¯òû¶Àã [ÆUÇŽf8?)]åHŸVH (BŒ9(º,Ñ•´h8| -£6öÆ*Ì0ƒY -1)¢À¼^Ö–*!3A°ûà ˆDo‹=ÎR·6 ¤+@FÐ|§ºãÎ×–ÁtC æy¯j,?ZT Òè-C"˜÷ŽGáÁ@G·V1 ç•i (ÚcJÈ97%kÛYq=H=Na¤ÓlõØ›)aAâ…Ød‚iR*\ëM5Pì$æp3{fÒ†u@V1¾„‘Áí;e­L_àœìAꈾi܆ˆ3í*ÍD7u»€£@¡+D7D‰0x õF•Ñ"kÃdµSÕÎ;R² ᑊàž,TìÅÇD -Ù¬€È(ØñR©0ÇVcoKH ²"Ö5Úµ¢~„#%; -æÎG²NÀÕX<Úbh*©GXsÞâ™Fo•øYÛIX¾f×OòÐ,Á²#4>ÄœMÍÑÎ[]¤TÆŽ†Cê”"Gó’hÑV’À:&€üÍ(âO«Q´Æ €L”ˆ¤cÁØ€Ô2&†(°¸6çˆÕ4!ïÖ Ò26È‹ÈPüì·àƒäLâ¢I¦‰ZÒŸ‡<«º¦3t#}–«ñÞqçˆ^å¸h‘Bu -QÛ˜ÑÜJšïus#eÂäæ‹`#m×LÏEp&Ÿ-üh€³ŠÈ­tµYuWœÎLW¹ªÙ8óÙ1(¸ýTIO•ÖˆœÉÉŽˆÔËò R 8xìF¢'Qï§OŸÈSYMI¼Ë»YóV]Xwafû97ø÷¼W<ò¼@vâc%¢OЧ²iÑÔ‚TµèÀZg 7Ó ¦ŠPÁ„§'²© Ú¯‚Ì0o$Ñ€é9k)^gFÙ49*A_Ú×ÓܳY¬¹ì 8¾5zkƒŒjz`h¥£{@™c*¼ÉšwL˜šir-Ö*œWŠ?Ûï× -}ňX©Å¦B1¨Æ¿Å²–“¸ˆ1}(A1‡iJRÓRéÙšQŽÉ’ûHÉÑ*ñØ•jäŽ: )$bf3ñ¹¯oS“=þDd0~˳S@rZ&л¹¹WA6’ªsÜ®9A¹0Ç·[JzŽõÄóå#[Üì=¼å¼"gˆÙÝ´IM¬àg+ _OªÄI‚‹ï7Óñ´†gek‹ƒA€á(ÜâzcüÞébYå ¼´ê&haR¤› Hªd¢˜:„€4»•‰v¥u˜ŒÔ}H•«‰AÏ-‰’N@Q-7'ÇU;Á︵JìØD;TeÃD§~yÝÎŒªAÔ·1`b¬æ¸ç¬Å«Ðc(ît20qnw…ÔD忀ô‹*ÊæÍ¼œÂ"5“È¥ä»ÒD!í…9a‘[S™UT¬‹C4…8Îp0Î+Ú^Þ‹«~X Þiõƒ=Ÿ¶™Áùñ{Íì…¦N[Yp…¨bŽTX²Ó V—F~W Lî[¶êa _Á&hæFªêçÑÛ' «S-ÉÜHºç[º: yî$ djñP¬ÙT°¶PÉø-‡žÈÈSN‚ ³6 ˜%Ú@‘›ŽÌêLGM’×p¤ä¦¸½ m··ŠF¸Øèâ"d8Å3²¤œàeâè€Áx0}ñV¼ÚU–Ô^4€:\ ¸•F›PûŽ¡ñ4 — ¦›K{§yPýëW²Î‹«út¡‚æ'ÞÐ[ØÓŠ÷* V|2¢LDé ½Ò˜2ÖÌŽÉöRn¸"Ä <«uò›M lVzcW êbVÐ[‹„ó•äÁ•Ñ»Õåw–¨VªC‰@ך Ë/hT¡Ë ç3r=®{’¼Z$é¡}™—L„*¡©#ˆzµ©š¬¡®X€ì‹·&ë®ÒgŽª\ù¨mæû ¹ÞTJƒ*¯gGËb""¶ß%]ïÅ.zÖ‘Rk,Ÿ&±‚¡~–nÐÞ5Cö-QCYÁÐ^ÌN'BT¿-í§2,ëóHžæc¨ õôÞ©÷kʦ*uŠ€GýbÁ -PµZœ/ C\èQ]Ž]ÎîîÜ ÏB?è³ "†ê7-¸`ŠKWEfƒRïwÔ–.xøyš£Ò2ù¨e}ÖUÕÌ+×¢!L ØŠƒžT·®¬ˆxÝró®šh匬÷RÃji©wWr N@~ºkZìLëìäÌ••µJ‰€xºŽ÷jÂøRÈuÁÊ\Ȉ®Eò —à@qÓ'‰¸Ð/´ã }¨#–ax1¤äÜ>¯‰®:S—$Ù³ xbÌ# #²aø¿Ü ÇÈj‰2®SæßW–åY5|¿h“^­I0U4£Ý}Ãñö(¼¬h}2R¯Do¹ül×ÞãfªøEëò¨]-ª!l±%“¦€ÓÔ¾ê͇ILè,פw9+zºe$ ò lkÐ*°šçîíyÙ¢C­®¬½@/Ô€³4toî7×pÕb±8fÚ–^Dro{»UÁ\A,Y½;”a¦™% -*¿Ï 3<õ¶å[:.=] -Cf/³<;Ðó¤0úˆÞR‘½Ø$³y ˜“s¢q¯Ï^umqÛkÑ= W ÎÄ’<Ö@"Õ¢à³îð Ë 5Û¬Xç*sfÄÌjœ -L–ûƒž…¨m`鵿@ËÖÔÁÈZýª§ì!_®âlÝÆÝÞ³GøÏæï½\Šp€©/Éæ€Ó&Â̸$7e‹8G­Hý¤æÖÐ\-ÔÃ„Ä •ˆ:ϺÉcNeDÄ$yðq†&LžU(ÄdÔÃÓU­£c<œÞFMñ ß£:œuCZòý–ºéYŒª!–@ì`ÀѸšVëÚù)JZ¨‡ ŸAÛÚ­Û!j‰Ö+š®£!.yеW[”ð†xÛõW7N™½ØIÑÑD}/ãâÀ6¿à*…ÕãŽâ䪖I*ý22^`Ý©y„T=Öéìm©Ú4YUSšG8ž¦ -˜ îØQpÜ´_žmçJ4îæMd©gÅ4Ã…^PÄ𣖠×á ¢±ê5f9ÌD, Û#%k% d4,K¡‹Œ–FÏ_OÖ"qÕõÈ-V·[‡Ìö À²± nT­Z7xÀ¥*á{4ãDDªk§Ð`¹u­ Dޤ$ݔć“Ì("s8õÁÜîÕ5ò€­:–‘EV@ ¯i+-¸@"ª—/:œ -¤6\ ÃW·½Sû$RH/lL@Ê$5‚íØÆ\4nÍYY:ô¢àŸ8éX³«329>-Å瘱M6ë´…²ÂžPDÓµG:­Îà½õ†ñJòÅ$è&j7òÄ€ˆfD­£D‚²‰aæ;ÊèÐå£; ¼ÐáE) 'àüïëß%õ—ÆŸfºˆ+ÐØ¢µt -6Š›IØ ¨Hbe&#{JüP@äÎV!,—wgP˜ N¸o3¬›i”ë¤Á¢ýTBæn5¨1nCVqŠ8éH^acÔ[YpÕŸÕÊÃ%Ë÷¿¶Œà4aÉRf5 Š`7<oîÈ-W°ÜÊBTð.2R(QódOȳ‡gE5¥AWg€m› i\:aïÕÞÇR:Ñ#ނ־8Àèñ±µ&ÛNj¯äØj‡ κǂ;Ô9¸–ys¬4͡ƞ™SJ~ݲ—¦Õ`y4z¡‹i¾À#ÅŸv¬BT3( m%`¥¨Ê@ ЉëQªâZ^µ ^ˆšXNL7+¾ -5ØÒð¨5Q O’3)¢ÖEq%êM†^à (Q2L…5#I'®.~(vn ‡–ªm0ŸÈÑÊ?|µ&”/Õê ¾«ú,ÝÔ¶èU›üÈÊ mCÀ¾ÙDô(àè0,öÛÙl)‰m˜®«ò$Pk4¦©b±ËæÂåáå#(ћͅ±…Ù£6b®êa×¢ - iÍ]ÊWm^~¶¶)(ÕÒ¶ù]P"gÞ2‘kE^ê fgMc™ˆÞéìÊxk‘’¡[L¨ÒÓn!ô¡q†B1=\|Ù£>Ù•GE´H E ?#úg²åP੺Äv¢+¥Ü’ÖòV¨^L%¢_,5z&ãŒò°Q8‡N›a–ZŠ<_ åX‚°[¤bQÓO2*â©.Tʶéäf‡ªDFXê"R~™T¥+“Ã)(1.‡Æah¨mˤ®¨œ*t Ÿèï“tïæÀYÁ·†~¡ÏZ¹-µÎ._˜—k(šâ&4ˆ\­Ý¨båQ9¸ƒØ_×ÐÖëözu ªÐž×†Úu.ÈM }ˆZÙZI–/'A‹VûžÌ\Dm07=СQ‚³Ö$UÅÍ¢h˜}ß#³j"x ¢øÉç=R3hgkš=|X®äÙ ùN_%7âšÃðsu -@ØÒoiq1愾5ͶÔaÑyY3è˜`…/ÆZõ%!”'6„Vk.î0/+';ì—€¤'ŽÔ¦l¿×JZn-›÷y²lÙx q„âDYóÙ=̱3ˆ.D‡0Ü„yõÚd …vQ¥àÚ -zq)1  ÞiÊOeÌ[žhwRò˜èÉaÈ@lQ ß/Ò‚U/PT!~Öƒ¨=]i_–èw† †$²4ª˜Ã‚7’!Úhë †VQ«Â®„¼†â"¶.s ’0Á*ƒŸp?PÔ‹ÁÚѳR°Ãüé ÚHm°h`ÃüdÖ'9œ(þ§Ú^’–¸±é­çƒL#4Df‹¼ ‹!Ïd€„ú¥§ ë|FÃZˆ(¼ ˜àÐB"jqP] e€T©åÅïØ™e”òWËAfàà¥Ðñ:ÌP#I雨xÖ <Ë#i%³Ž­4“¡¨¤'Û’!“Ð#µ.0N(ïÚ˜F“vv¤OÜ’ ¤VhHºˆOßò¥Ù˜3½:Ó—…ªŒŠtŸN_CeÍ@ùªaÑQg¼ö†GSOþ˜ cÏrmxBa-ïOÖcŽ”¬‡ÕÚx þl3à ªÀwd_‚­ uý€%›ìûUêÆ(ÊÙ ’M‘†És'¢ÛP\»P5k—‰z§juÿÑ´_LW¢]®A_óˆ÷3Ú·…æ -‰v»Ñnìë5¢gšSøÙœB1­…ˆ¦qm U× U•M£E‘­1Uј„¢ëÐ}Z³à‰¨eé]êzðÚd½É&’¢ Áš°K»ÝÞ.•”* ­Î[£Tfe&Uêwð}à6µyY“fƒ¿hS@Xy uš!ÈòMvj6­¡JS[xc4 P[ccP_—ÀaÝ€¬+„x :šµiX†ÈPÆIÇe”3z©E  fJsÕpflhx9‘(6û0ƒŒ†t³EãƒA±º›}jµñt®†Ú×DÂ_€œ­Ï¯¶i%âlÓªø¹(ç k]unÈ¥$ã&ëüµñ}˜“ÿ°ÃPÆæg÷éb}¼·hÔÑ7Utf—<;i¦hÅÌœú-´°…kƒ~¯ešMUL+SÌm·T¬žÑà²Ù[?¤¦=åYWuº´Ü½ƒ*h"ˆ†¢®«¦HÓÒ„ö”‰ÚEV%êTÑOÃÍú»b]:h¢]iœ+ðrëRKKo6" -š1Òô.Ô`Q§2‚ûò¨Ó–$Í€%) bi;Ô -µÍ‚떟Ѭ*/ACNP™Çì¤XéÐQšßê´9|â.íÝìfùI×q]˜-¡íÕµAV¾Ú,ân]9šÀ/¼ÜþÖ£ÉÐ$9­U3×K]àþ3nºME[ÈŠžŒßšh¦#W*Y„”Íë*¦*3n¿ÜLÂÖy{j3Þjî™(l³î2›ƒW⋵Upm!¡?©QðÓ ûŸ©ÝÊF‚¯eÍ9m¯´ÛÚ>êFÓ *úJ'Å(ÙF!B¸%¬W¶&»BÓt»ÔëÉioœÆ¬¥Z†f+ 4¿´ù¢[²LýÉê°)‚) ¸X£¥5™·‘êÈÅ~ò/#™ªhÓ(šª4x‚Ùª²¾Êia+¡¥‰Ú*D\I}‰o˜Ü³€Bœ@qËñëuTÝ´â¼Ëœãb÷ºU„sî[Ä\°­­·nà<دhäÎ¡ß ­KyF¥ 5¹JØM¼¡ú•„í™5LTÑÐÏÒ[Ñ…Å;Zî:$©Ì’6kuÚ"™²ÍŒþfG ý*ˆØ<ú6‡fñg ÖÔef]“t4!#ë±!¡‡Ï°G¹‹6:'¢˜RßRgÍfà‘ˆˆÑæÒÎëšÂ’@ÅÕ½ %ãTU‘ˆJT‘Å5Øÿµ'±J` 4@(J$nª)æºõ;×G=Ò4QI:;úu„ü[^tC¶†@ü¨%|ytâÐÖ]Ì” "ädi¬¨0SQ—éÚAj"­ùPŸ²nk\e¨¾Es³ó‚dØ‘®kà/ÁKAƒªaŽyŒœ'y¤DàWÎÏ– D¥[ÍY‹VÂÒ%µáåÈÈîsiNf–õ MHdꡊŸÏÊ fÙv+Dq ´Ø¥¥Å•­€¾Í–j¥T¸ ÍFÄ´røxÛ²+éVŠÀ<*f»x …º7ƒ­#áv´›Ÿé°TçèæYŽx¶h¹t0ª¢/|¶jJ hú‹LH~ჰHÒÐ(£[•PâƒâÉw+Å¢ô„ŠÔU;-”ö€HG£H"ç™jA¶md0ÚtÃ%?­­6 •pVÞ5J( › -Ê2ø“5Æ‘ˆÀo–ÁJiV§Ð&€O5Œð.eø†o¦º–G>õ¹…`6p­bÍSºpÞêêE؈ð^;©(¬©u`{!gÏM­È3A‰o„æ›]LÄÞq/³®úRW¶Yæ@«mDDtÛr·Zæ?=Û‹ŽiÄüD1¸…—®_Ä“m±ØÚ±ˆ“°ѧZPwæ;Ó“RtEQ”åºÎ$ kÑÊäˆØ¥ö”cà¹é¹Í»V,¢j]:„,"ñ!ÞÛŽT$,Q:’Õ0[©2¹X]A÷Bþ !ó•{¸X Ë̲ƒOi@‹´-þ”e [ÃVE¤ ª?‘‰R5D3|+mzp+á—Î}K»t BWCrRÀjå W?;^Nön/¢Ÿ ?f‡ -ˆZèP5»°G­{É·kæ ðŠ0 "Ћïç݊״[ --­vÀ %“ÐÀR$ “‘ÅÉ/€ÚÓ´ØéÎaˆ8E臖«N£“AE«e D6}ßè¨6y ‘rGÅç,InÍJ›èºJÌ ¢ª»5ñ_ Ñ pJh\šçGP -CN–… %w&jRjj‚zÙÝ&x 7.îJœ¾€Ž–ü¬žº†(8ç^ Œ/§Æ&è˜)efÌ’žH -/pžšÅb 3´fen³h·ÍîëU -#±3€¢V¨¦>’kêºÖx5£†©³§¢A4ÃŽ5«¥5(Kgå‚ÅŒB-S¹H:„Õ´)³Û‘³Ú&eá%èS‹.Ü’gÈmâ^Ðä›ÉŠ–¡n*‹·SŽÝ ý<¾°] u°bq¬û‘Í\+^ÔÿѰ,C0•7lm‚cŒ0ëµuƒÎ''PgD‰Ãt¨ß‡Øô‚åЂ™p@“Nk«”Ö -БuzÂkЗš=P’SÑô’ŸÕ~Ú- #@$ÇòÑx¼Õ8ú:Ug(‹%‡y¡ýQYTÜÙJÃåT¦€pe‘²G!uEMèNrt¹¶ÊF‘_UÏT@ç2Ó›XIRh‚ÎR¤¿5¾€”¼Àw uëY¥Ÿ8ŒÊ×`F䊨d}3ؘ"ò¦ý‹ùûlÔWÌxgë QCou& Â]u -,ÙÓ(Z·Ì·ÙϵH¹™Ä{f&k‘B&ä£é}±0?%› PÜÆ-¦+lÚhµÍÚ·ïBÉÓÞ׿9”p©n¾:Û¶¤…eΡ^Áž #—¬ºZcÀ]?ÚÔ¨¬_©ìÁâ/Ïâzj3¼ ÝÖ X[ b°‰ (œ ZZAÛfÔÅ øš’¾ ×°[Jˆ)y«ðnH‘5çœPo/ÐÎm½ÈY’qWµýîÀ ЉªÏ³W ¾k‡‰:![Ýw_ФÂZˆp#ϬW­ñt›8ÁuFÏ5ë©fú]¯1{ü>HR5‘µù©S­OG¥¥tÑšG²ù™%ŒF‚MFhYÂ61´ätMtwY.0x[X7ì¿;Ñ“~†÷¨ÁRH£ä²ëKŸ¾xN>®x–% Æ5ý;VéÁ G @¶wCGLF`™ë‚ ·ì«£#ÕšÅNžE(ž3L¼E;Ù—”ÎX O¾,ÉO¨[˜l-‚ ÁÛP«Iœp{`y”,JcM”ë`°ð¶@Û¢‡N¼m¬hqÑ<Ò´\¶.TèH®Ùx¦ù–™Òââ"eMh]´tul⃫UšpL…ÕRU‘ dN'1ƒF³ÁV%GÛ"+ÃçE.÷¸9£ ´&¨3Í &#ò}Á¯íná'gkMK¼\4AY“aßFÉÍQQVÇÿ÷ï}ÒêÈ´áŸ](øZe‰Á”´.m%ɹt¨x°ÅØØ3ÓBC4`ƒÞ§À3›#5‚_a–©PY¶(†/Æ ¢ª©]üŽƒ’7¡ÒxjÑÃ ßøÚ‘BÎGcòl8Gó‚ˆëŸ¯_¸FÊ„™2Ó=_ê PfæàÎ⌸‚z XpôzF©>«;~:5vé¥(@¦ p·|3ò‚b0‡]2-äc(V#ÞY‚¹CÉ*¿¸†[$ /Â+¬€HìŒF¥1½Ä¼³¬&m·nH5o.`V‘$‘Tœøà€êŸ¢%I ÍfÍ’$‚5š‹(¡ Hjþ¼3Õ{…Tð˜fýåÙoHhYêdo ^ArÁ‰èýP‚Êî×êêSÔ_Lµùá…’]9‡Ð{Õ^Dâ©TcKpöT¡ÂëŠÎV´k  W× Ú^`­ñ¶ ìÈFœ%D[Ôˆ'ëC(=Fœ¶–Nݲ'\Dcgn™„fÛÑ.e< -Ãݵ ‚l 8ƒ˜€/· #&^ê¨7o*Ô9v˜¬T ¤ùi«ýð n¾/ï7$‚p¡n4^Aë+„èœÌ -ÓÑö—àíâ8ú| -ôíï$ßyÉ\˜$„\xä“- Џ¿ØÝ`A*·¼d2 -d{~«1Êô ³h¹"àR×l~?BCúÌ×êQ"G±uÞ`'î…’kc2’*üc¥õΊ³ëD4àWöìm7&Y­2Å ºö¼˜VT€:r*ÑO@AƒW¯†%„ÅÖTY°Ž[F€õ ‘„l)F縹Áp±67K$4€ßõ`-4Ó±`i6Àƒt.F-9Äî à½#ÓjáÛ#s–Úoý­6üüqægN”àÄ›´L¼{÷І:Üñt¥»ýBÉÀÝmR?d`ºÈ©Ìâ¶0È -éþÑn\5ÌÇ­ ½6!tÕ¬BBs.C¨vŒè`><533ކ”FélÄw¸7+,C ðÇ`ë¥älXYŸa£'CFo³Çy^šÙ›Ö!êÎÒÎô ä2;‰\¨o Êœ³ÌbP·¶Ÿ&ˆ³L[/Ž:x×`Íþ¸””´¥²µ@ë¦a¸™dÅ š¸äfÓ\gm5R6·Ìì´ñæ %ÃäPŽÆÝû4»ÑKo´®´%X=í“l‘ànY!N"} æqÉhS8dÔ ²Ø±ø€áìp[µ£í/9²…ËØÍÎuó;ª©{DT` "¸*у×qÇ·—úóœô¥æ,á*üüÎçOl;šG -;»±h:XÃÞû"‰H!(¸1ž ÔE*=[-³J•-Þc€¯ƒË¸ýùélÕÒ•)ЦòühûK¦î;Á*¹ʯâRp[ŒFDa.ЬR&™¬2dtyQœ{Ög JÕµÁ–ÏÏSW ЛŧKÜí5.$0‘ LŠ~m‡N:)÷%ÍŽPF&rƒÜ4k,9ÔÕpO1òÒ,* RD÷R_€['Xlr@n+ msóL;e—°ãˆ ¾äôæ+¨ã‘÷!JBÓR‘„«%;z9ò -šL*] "“Ӌ̹j¯k€6µÇñuȵ³ØBÁyŒÍY&@eœír½©·±Y²Œ_`vEr.?"K -côz¡ †Ù{róÕød°öza¡vŸH ÉÒ;‰‰p½š›$.¢c쟆‹Þ4$êäÞ¤oU4,aD¼­€bD‹/E²¤Ã\(Ðô Oú\×¾uÉöÈø/õ52¬("w}Ô÷Û=YϛһôÚê"NÐ"&+sBÏ5~¸¦è­ß÷E!7:'ÈPõ׬® Má³j2=èÐu†e ~?f`G˜âŒr+®òwoOu -XZ”"®Ý¸„ÉõÔ;È€|Ž–ŽOžš˜t[Šz˲a -d¥ø=f¥ˆDw[&Äù& €7"⃵ˆi®-’¨Z \-Ú2E ¼°^M‰²µUÍ|H°\R£^ævo|k˦‚ò4µy8)­È›ˆ Í9á;ˆMÜIRúýT¥Qoª<”Ñ×P³SyªÿPÆ@0âíaÙxõû¿qñ㟂BÓQ\ËD½fyË­W.–¥ŽÕi*!Sp|îgaÛ„öÜ6~%´•Ä3>¦’cüÅB&^À§.@6Ï©f!îgoáT­ØßKûÞ—ú‚ 2ÛÍQ&D›#@i6E*°‰+ÙÀe¡XfóÙ!M_K‰8ï@IaÜ|+¾Ö­B`y -’ Z²K¥‘Ò4Ÿ¸[½+ -Dt…7žUE~©–rÀ}´ùý“9°Û/ÆÀ²Fáà{ˆØw`|‡à ÀžxdD6G¥r)‚ÔI!È¥NZ7ê4ÓaÖÄl[‡¯öþ}¯ïÿòWûÿÏwüé¿ù·öÅï._½¸~óú›¿ßÜœ^_ -ñÅé×g—·È¿ü÷ËËã‹ÓWûLÝäýä~µçö3þû÷öÞŒÿóûŽÿó÷·ã/ÿküá¿é‡ý´ÿÇýÿóÝþ+zò¯{mÜoâQ  Aá鱺Ã2éÒb’–äÚ‰´[À|É;ÈË—\Ž/ÿyOç÷¿Ýó<ŽZ’¶ -¯P (äÏãHE*„&ù VjöM}±j§$°Np,ãXQÐÜÓк -ñ±Ëµ±*\¢<Ë%JƒØ¢˜ÚƒHY:tkwã:Ωʧ8ˆM/èÔP#%sG\"òÅ8 ƒHò:1Ñkk!&rë ½€$ ƒFÈ©‘$Ä g&Š3y£`Ò1•0ü,W¤0Ù1ºž¼€ßYÕIžúÜ0qè‹^N~"á)4Ï#åúÁA¤ä™uN­Ê$‘å2ÿA¦ ‰Éžs™È5ƒÜNˆ9D!f -#œè †$å©’³ƒÜüòllJ”¾·L‰?å9ê‚ßsŽSå>cb'{„‰ÜE¦5þÈOª[N6À‰*ÊäËõ…LÌ•¼ò¤Ã ˜ðŽn0yØ—øç¨Ðm­0NL¶T"œâ-W%»ù1v^ b“z Ù,ïxª¢‚‡¼çTOú츻£2¡—®ÎBt6JtdÞ¤æŒE_@‰×ž6¡²%œ• •73ë.0ìi*Jήa‹L—J©^0”><©mz™Ì9ƒLÅTÊñIl}&J³D&FjÇDÒÔeƒSÇÏ„:ìJ™-áÔü>UYX -Ió‰­ -Å-/èrRx bÎü­À xMvFLX&¦a²(sQæ¿€ÈtcϲõÂo-…‡ÅB¥FÁXŒ&gƑБ(œ“&m‹Dä˜(•gð‚,0=ú±ðV²xXêjçÙ¶(!T‹ževµëjW‘ä/Y €;`0ÙdÙSAŸ ©a}ˆJäà؈Muþ˜”‡1‘=T².­}÷jg„aÊçd²$û31g[mÃRÇË -.bí‰Å¡?2?/SgÉûf"å1a Å£™Ê²ÛMTå¦Â¸5ôê3Ô»€Â( ,ÔÆå¢ÄTÁ˜®¹¢Ä*ÀAƒÌ˜ÈMÈQgŠÝÜ"ŽH]ª÷dô¡èeFdáç ̺qSÑÖ$—õ P1wÕg%3o阱V@‹šåŽ¡À¦ã£M5WÒœ2V¢ìµ†³„Ș%BÔ+‚‚n-à’òIw¢`½)¹6¾Ð™˜dY±—˜Øô’¦Rží9V/déÄÄàƒ¾€Ëꈘ(,¢D.[<Ñ=`_$³kvr÷rªÔ¤Œ…3‚v)bA‚¬ -&³Šïé«ÀoeXG‘DY®¹$ýA/ަ²B­ß[s]GKbQÒ?n“&@h¼²-{½M¸åŽì,3ùSâãg"7Ï‘»×Õ ‚Â39V< -ÙÄ]š*ˆÅëïM …&¡)‘"I!óKTíAÀÿ„1¨ªˆŸäÂFL€÷“ÉÒ‰5u•8YU޹ ÃS¢F¬s¨K4 WpŒ\©:é#ÂŒ1”}㥼T&bŽˆ7F^bb-µà.°s€ÉFIlÁ9È„¼8¯B‹õ¶®EÉY>¥=ò‚*çÉÅ险¶I6\–›q™ù‚ÀQf—p7>Ö$mމ`\!Lˆ$ŸOT˜0æœ/²œˆ½ØAòFCÖ«|f©… -O½-x—tn‘û\60Ô?’†ÄTôQ r„ÜdËÙÑíYÑMÉWš!vøIy¬NtNŒé:]Žk -±b˜ˆqûÛ †å•ÌHLì5„;Ä J zbמìXõâÀTÖ+¹¨¦›Ø¶ª7g‰ó’’jKjËÐ oc$’ätAžä;IF@ñš¢z81Q̪cÕ“žŒ‹ShKŽˆ}­Kä\¦Ë­âIY¥0/¯AÒ¤¤AlRQËSÈ”ÇW-åÙs_¹ñ,e½ÓچŮQ€ÈB’]˜YCOM¨³JŸ³pK3Vy &­%5±† Œe'Cg*$©é˜œ&òë4ÿ8,[›Â8^F+øýl s‚Þ˜WÔ -½ATWïXƒ*Ë)/’c?È^Z2ÉǼˆO†;“Ë’VK˜ƒÁ`§fÍ*ç9½•(0?KJ·\í)Jàiü¦IaŸ0R'1ÂF¼·õ"GáÂþ¹•8@.cÍLå04]T5 éHùF™™£Eå–»®ñ©éši.œ˜a 0X¨0•¹ ÓQaeѧˆ«a«³ # IÊÅÚÄ÷r <åtÇ8'™ˆIjŠ©8ä…¼€Ú¾Øè f’ £ò!ßOä…!\5“”9äŒ^å¼M|ÜbÓ%ªAõ²¡ÀHé`iö¤|¤º$Uá׬ƒâNö ÚHýB3È]‚-ãO ¾Åßg`sçeáñšRdA ü‚¦5“Íà -»ÕøÜFÇ*a³f½ðÇåÐÞj_(Ü÷™ÉCû±Ë=F/ÄîÔF.à·ºÄ7¨uÊtqDÚï‚Qçx |%È1 µ\Èc±šÈS†e‰N…¤]nª–¡ðp“qðPO’´"ÒŸUôñ®,àÌocà•ˆÔ³¤Ö†ûO.æAæÎ`f:‘Q<ÌQqHò’jˆ¨ c ²åd²B!0?s„Þš¤rŒÙƒhD,pLP° I€ŠGЋNöÏ’4c¢¯¬ SéJ”—T›Œi"u¹2.Zc¹=8ƒÀÀ“̰ì\!"ÕñÅN!®è‹,ô%>TmK5 D$B>œ”ÇN§ñ‚Ú-IX“µ>Ål(ÅœÉòdL²xuŠ1HÆoL²§l"ËêAàcŸÚDáYd¹J²“ëa ¬Z+éAf®¡ß“3¨Š5 ³òïÇ}œDÛË\ÅOV©Š”œ¤Ö€÷1"¦24‰AŒà ™1ù˜±È´dU„þ¸[œ9r–‚ÎAL -ÖÇ¢1“h‹=EAÁlN$hò’¬Ì CWÕ £oÉKëõô%'~1¦¦i+üO;2PµW(1ŠL“AÌR "ÏT@Aòƒ"¢„¦kíy­c‹\ðÜ>Žjx$tøR_À2œ?æÔ)Ea Q†¿E^¼§5!·´æqñ=64ß(ä¬ö0 òšp®k1¢Ï–7Êd¯Sè¹Ø³)(±ˆ† ĂɆnWq”™qéNÝg(ÑY9ˆt}Ê-BS¨ž‘›>6UG„•l“­OFY×Ä¿á¯LE@‡'g -1 ÅCˆXTöÖˆÀèi²'±xpê#àg{¢‡6V$u„‰\×`&a2Ã]‘LQÞßÍñC™QæI®_Ø ˜ÉpPâL3¢x¼)džœ~,_Ç 1R±BVr׌rƘk…Ÿôú$WmB§fŒÞ+6úäÄ H!_cìg"êL˜<fW²js<©ú‚¬fEÇ&Z‡›'vqËïÕËE'2:¼”™ùòbÄ5Nõ­(yˆ¾¨ú;Ëhž­k"…bÏïV"C† ‡Y™µú´r]åh‘l¨Ê/ wG*Ó2ª|(¡!Aá9Dy¬¹´$ÄØa!GÆ^äg›D&daØaÀÏb -ISªYfÇ7 ­w©­=Mtei=™ÈAYDÊLlóäæà £)$5­¸íX"ìz’üþzÿÀOB)’„×Édé9Ë£u‰·œ›–‰qGYžú)*±7N¢¯ñ š:eR”ÖØ<‚\8ÙQ”E× 3îjvYÕ!'q®Æh|õy0$mŒÂD<§ÝÇEóbñ ¹ˆÇ’%‹-ç.^ˆ2V"ÕA|-O”9Q©É =+oÒ#Œ0Ïc"£ƒÃÙ•;__TN­þíÀ¦U¢®31’Þ# -ˆj#â«Qž‰šÒ/D¹è0à­¬2±EóÕd± x¥Ë®¸¦D'póB å7óåå´¾–¯ƒ\d9ÄÕRøˆºdæ -„ÂÛÈ^—ú­1@&£øOx&ÉèÊꪪ4oÞ"²!šòW«p.ºœô÷û`¬—"Äî¡Ô;ORÕ¹çµm)ó!|`¾‹Y¡§!Ì.ŒÊTˆ.î9[•È·§›aÐ$ÜŠpÕ/kkækÒZ_&"&Efÿ0(„´;&{žDvN[Ž)3±5‰ † 6³h¬ÞF¤Í/K/}”\&š±£öš<é>†F x2³V°c^UF 8ÒLô‰%*t"ŽIvA’)dñ$Út½1&nõÁ6pYˆ2k -X‰Î Ç(™²5ˆNî$‚«,§ÀK &Ç ¾E/×rÕS¸ª¢ËA,‚pÝhÓóÍ…‡BtQŸzˆÉ%5BÙ=‹p9+“©2\Ôe^Á$w.Žú)gþBÏ.4•qlï‘q™(M8ÎÃ@LdÅá#“ŠGˆÜ¿™‰¾ ±¹å NÍáÈmÀ‹>+-8ǰ•ƒèÔ^c"÷!¢·h~6Q2°z:‰¦æ9w«Ê]ޤ!Ízæ-Ñí8c -£RޠćTÈk)vmU…›ç%ó£”{Á>¢®Új#°‡*F¼Â5Ó+iô…­=ú±gWc!uQ"'„p#áÃE:ˆ'œâÅYsëÙ™”BXëüY“8úø rÐN`Ë·Î$“- )'œÇó®LLÄ/þtuù—ë³Ë›³Ë¯„Ì <ËØûÓwô/ÞÉ?}þøýÙùxÍÞ¯íûÿº÷ë¿ÿñèOW¯NÇùµÿëÏoÆ;¾Þÿå?.Î/Ç¿ÐÿP.Яöÿeoû¿~|þ†ÿÙïÿúß.onÿãÍÛïøß~ý›ëëã·w?zú·7×_¾9?½<9ý >ýÙü÷“oÎÎ_]Ÿ^ò¿ÿöìäæìêòøúîøä ¿þ÷˳“Aù±áýòf9‘ù8<Ùˆ ^¹>ûòÍÍékzlüƒ½q1«ëÓ×oÎo0¯UÍ£¿=§/_Ÿþþúôÿ½;ðöžS#Q¿ª©Ý™Äí^¾¹øóÉÍñ÷ôô½¦V5·åðoOìõÍÙÍÉ7;;¿÷Ì.¯>ç߬j‚·¦ñ/?öè¿Ü‘/¯.¾»z}v³JxõÝéõñÍÕõ=·æìrU›2G{Ñß7L›ÏçWo®ONÿp}üÝ7g'«šÚÙ忤Þsä×+Ôyèï94fÝoNϾþæ¾÷œwî«ZŒþöÿpöêæ=ro½SÒÁߞѽoëuÍeã~þÇóœÇ?6¤È«÷\C¿üÍ¿}ñ›óï¾9þ¯j&4ðÙbÓŒÞÜW³¸úò¿OOn^\½¹|5zqõžMý‰gxk>?*-ýÛÓ¯ö?ÛY{ËÚûêúx(³çº:{½³÷ž½·.õ´æ^ZÕÜvæÞÎÜûé7egî=™¾3÷væÞó2“væÞñëÒ0væÞêͽ?¿yýúìøòÅù›Ÿâã¾1d})“X{l7Œ¨êðÉ^Õ´6×7¯~{úýÙ1ýþYC·'ð cáÏ_}õúôæÙž+žÆ‹Oì­N$lžW÷ÕSÖub^mè)¯î«8®l"oxà?ÿîôäÍùñõ™cô?ù¥ý—«³Ë›#5¥V'uîËÃrnU¬ðÁ&ÄA^ÛT6NçÿÜw*auÛò?Oï‰xÐÕøzø?¿yÏÓ»{ñÉuÊ·ç÷ öœ«(>8¹:¿ºþ×¾ßíz¦§“¹3C½H~÷ï®.O/ïï[×Ô6f±}–/¯._ßß–«œäœÄI¾¹þêøäôó“ã{óìºoOàÓ‰–|¢‚þgZãF=&ü³^OÏû£@Ÿª08?»ùËñÙûî«ç( ޝÏn¾¹8½YYüt~F².ÜзïQØf¸mU3¡aß™È}7f]ê·›;ŸçDâÆDÞã0\+k¥Þ<½þú§¸7·|•þºÆûÞ²¥á‹³‡F“>ÝX¥ît½xì7>zNTZY²ÊäD­mJʉ:+›Í‡‡4Ö6“Ë‹zqúýéùçß¿ºúa•Å0Ï/eèW×ß}su~õõÛ5^Q2ñ×ålyÛþÕÙùñÊbOdׯÑ;¶U$=?ÎÛLÔ¼>~uöæ¾9šþp]@üÃïOG¬­«’`'Öž‡ øpa¶.~Û ³O&'õËuiì͹YÕdsÞ×µ-›þÞ™¨Ï<uW­ÿ±·äÑÕú_®ì:yÂZýuÍìikõוõs«Õÿ°ûue×Ò#.Øu¬÷z*ï^K¿={ýÝùñÉéÅéåÍ¿[ãÝô0þžHú*dø'tNî³¶ƒ²™sÿ3¿.ÛTª_? ·y]sy¤æ[½—ß_^žž~z~zrGÔoV5³ÍIÜ ö}Ø$ÿºªInNâ©çW×<ýcÑŒ¤ Lb={óxóh]øc¯Öu ¾Ç¨ ëÚ—Íëˆp_›Çíã?ûô·þ¸ªYcŽ’w?1¤ÇòãÇ—gÇëLýÿêìüüÞ¾¨ÓÓÿY—JF{§ONÞ\¼yXlaø_®kVËÜžÛPØNþxõê¾3;?»<=^WâçœÁ]{õêìæìûûÎìú” éUMmNaãÞ¿9¾¾wîÍùÇoß#¿úË_&pÇ9JýYî{ѬkF:ô;Ñ‘÷åHO'èºfÿsœðÌŸxÌ÷+¨¹7XÑO}°nOæÎÅ~}uñ,ë9dà·'ssõ,Ýð4ì*Å—ëZÇZK+³1a-­k_¶8ïж.)²h;Y׆ì’aV¹-I†i«šÉ.æÓK†9YW¼nרð¾ºÀºü¢»t˜gx1=âŠ]—ÔøÙ§Ãœ¬+ÊõHMtýçäÞé0k;(H‡Y=.f]Šõ.f—³K‡ù({óxi].ÑÇ^­+âPA×µ/Oãwé0ÏCó^—“á±âae‡êâa]û²‹ÿì`õ~TèÜÿˆ®Œ±?hïŸý.¬-¼üóÜ…5âu<>åi³Y~öh†~eSzšáÚ6è šÖ5‘3|yuuþb}Ùé»>¯;ÐÆG]Á;0°gÕ»àAxZërÞMëáJä}Ä÷o™o¿X׊ì`hwm'Ñ"Ñʪ&³‰¶®ÙI´H´Óë«÷•oîÚJÚº’ª@û±íNCÛɳ<{¾òl]êÈ -äÙºä¹Ê³µg’|@Œ] Šñœó²Üã/Öµ:»c³šcSWÅk;6ëZçyl> $­ëÓ‹«÷Aí´ž`VO†¤õú;ÂÒZÕÜÞ…¤õ Œg¿ï? nßçñ¿nü÷³ñçñÿŸØ_Õtg’ó6lËÌv°aë‡ ó+›Îᆉ"ð×÷Éœ5† 9pz|óÛ{ïÜÙå«Ó¯Î.ÏV‹\Lãç[ûñåÚz›?²úãì깺-z’BuÙs(yyuñÝÕë³ušU«ÃLþüæ=?Y…Lx`¬å}"ä¹ZÖ'1±æ¨QØ „!ÖU¸÷$áøúìæ›‹Ó›•qÜ£%Ãjåøã%Ä» 8šoßsV¦¾ª™Ð°ïLä¾›³.áðíæŽ¼'÷f­‰yOôp­¬•x‡îª«Q]½Ú[{Õ¥÷ŠEþåì§ç9?~ûÅÊ–ôçV%·9Õ÷úN'î¿ÛÏ댋ìÂÉO(,wáäO8œüŒ3=(œÌâ‰ÊÁ}¶>Qµ !ïBÈÿìíBÈÛ¦µ !ÿóNØ.„¼¾@Ä.„¼ú-Ú…oCùõÕ›×§G„5¦±3ãlivfÜÎŒ{nfÜÛÓóó«>ûúúôôò³!yO?ÚËÙ×WŸ}vu~zóÙõé«Ï®®/¿^×¾îì»}÷ÏžÑcí»O³µð¹ê'ÔeaU3Üy?¦Õãyys¤Ø¥«S®ŽÿçìâÍý1Hۺܑ6úÛË~z>ò¢¬kV‹á?uÉåƒlúWb‘¬jmÛvu6ð‡ÛókÜž-&ý›ë¯†2øùzZ­ ’üönOî!—òÓ`Us¼;;º—0ÚË«K¾ËžeÕÆ6Ì€!V¤ëšÞ­ñ?HAùü»Ó“aÈ_ïüN;¿ÓƼv~§çæwR/“øÔ Åî§ßiçwÚùv~§ßiçwZruo+÷üý]þé6îΉ¶s¢½kY^«}µ¶2¼Oß‘†•ÿÝ?¾fÌýý+s?mÌbû,êY×iݜţ½ˆkÛǧv#KnUÜô!>@]ùtÊ埗¼ÿ™Ãg¬u³>áùŸ¥P8?»ùËñÙû.çç(>UøŒ5cÑ|¸lXeÈøØë -Â=;c]Úø#°3V6‘Ÿ-vƧww®ë¬ï.Ïç¦î<æÞ\¯°ÃZ›°ÃZkíp§~:Ü©uÞÖŸæÔ{äá?= -ºÃœzwFóê€\ž5æÔ;áŸaÜ|‚Ø €7°.)ü¼?õÕëÓUæ=èð\ñ4H\Ÿ¾ZÏ}úi¯ÞskÛ\ÖÕó’†}g"oŸçDÞîÌ›ŸÌ¼Y¹´¹‡ó)îÊÚƒuÏÒðüü›ãWW?ìŽÿ¹VçÎ4Û™f;Óìž諚ÒÎ4[¹²´3Íl.mUy„i¶²‰ìL³i¶3Ív¦Ùýö›Ó÷×;΄è~±ª5Åèoóòg¯î_™V6%üíÝ÷::ðy]³y{w&÷ÕV7“ á!NuqvNÕ;5þöæúË7ç§—'?µv¶ÃGúx,¹ÃGú±<®gƒ´C zv(A÷GÕù4Au¾<~}úûëÓÿ÷fÜ(ïÑ%ׄ©óÕõÕÅ}S€×U~/C¿=›«{Of]]FhàwÅÏ íèÁÚŠ ö^‹óÕõñÉÍñùŸ®ÎVV*ioüPüɺ ŸíøÇÕŠ›;s¸=ÁË7 öý½Ó‡WÖ²ÿÝ ÝÙÍÉ7;;¿÷Ô.¯>ç߬j‚·¦ñá¡“•%NlV¿ÝmeBC¼%p¤¯ºögÀ²6Þú™â®<<þs/gëÍñÚJ›w®Ö«õQòúfÄÍr"ëaG4¯UÍè)Í÷)ÛëÊŽù¹™;•u ›òh•uuâo§´~”Ü·²D”'È­YÛ”•[³²¹|pfÍÊæñay5Siÿâ=h7;ƒogð}¢ß§7ZÎó”&ߺʹŸÖâ[×ÜvßÎâûé7egñ]í,¾çhí,¾U[J;‹O-¾u¹Ìwßê-¾DÄOo}2›”ôºMÚ”Rx±.ënx±òôOðâÞòà - ·¢ÙlH„²â?y"…ïøýùÕÕûtçõK±õ5ú~‡àºwóê&´íö{ïΦ_›I›ÍýòüøäÛÏö…tõÝñÉÙÍÛ]›O['÷é8D?}­àgîò]¡Èx„Ïw帥‹Ñ½eûkôx¹¾ízh•Êo¥5ã‘vQþÉ}2¿=ãÆÛGê0_Ƚ7Gpê•••l\÷Çÿsvñæþnü¸.«þö¤NÏÇ@‚×¹®²ÆÅðŸ:õ }aMZw®ƒUoÏ{âÍõWÇ'§ŸŸßÛ¬X—Œ¹=Û“;×KòàjšMï‡oÖ†vpwwœ&Âg/¯.ùb¾ïU±ªnÌá_¶ˆÅ‡ð躦wküÒö>׆¢;uïIÔ½UqÅNÛÛi{÷×öÖÚZøÓ×ø°ò¿ûÇwW—§÷¿a×ÅÇ›³Ø>Ëg­GlNb§í>m÷g x^òþgXëfý\À?U¡€®é«Ú'‘Ç×g7ß\œ® èÑ’áYeIÞ[6¬Ò·¹üö=ÚéJµXö‰ÜwcÖes|»¹#ï©ú^ëDâÆDÞ“9³Ö‰¤Ýݹ¦ýØÝÏmÇsm®×Løð«seòíïΕé~u®l"?Û«sפq3Zw_Ñ¿úLÄá…g¿++¾™{Ànîˆ>ö¦>‰èoëÕ}:<¢OveÁÓŸ Ñ#\ŸÀeÍU<ýã™Ë÷ Lb=»ñhÉþ‡1×ïà¯B°ßûì¬ôºÚtvðî-Å÷þgÛŸŒ²ª)c‚’ŸPÉj­®',Yß´¶dœí >žû!úóºãÓ‰9ÒLè¬ümmÈ »,¾gcY<&à¸ò[èá©|Ÿ”¢½”ë*D¤z°Z±÷$ª¶_YáG(Û|ÓR5Èß®/_õO€øüý›Ë“¿®ñ$?ÀËvsüåÊîŒmN6å<Ä}ö×å…¿5ƒ‡® .ûÃŽËVÉe‡ußí¯k^â³;>['Ÿùg,Î>¾"ú÷qûÿþìz];ÿH=tÍÝÏÖ"nøgÂj?sÈjÃã ×Y’õPÈ.ÇôóW,Œž*§ñƒ5ïðoÎÏWµ*SD?íÊ<¬ûS^Y[ž'èþ´¶)=ªûÓ_Ùl>¸ÿÓêfòa þóêêÕ××Ç+ÓvíŸæ£Úþé¿ù7ï¾øÝå+kE¤L”/þtuù—1#f9ò‹Ó¯Ï.—ÿ°÷§ïøIþéó·_^ïýòÅÕ¿Úsû¿ÿýû{oì?nÿÏ{”FaßîḬ́¿¿ù_ãÿ=H?ì§ý?îÿŸÿëö_Ñïÿº×Có5í”Ã{­ûƒ”Ziaÿ –^CÝo‡Õ§XöÂø—Ê~9ôãqßökóû'{î0×âkØï‡­F—é)G÷ãx¬ûñ>wØZ©¾ïÇvèèg/÷üañ9Œ‡Ò¡scð‡±„Ú÷C?~ÿ?öÂa÷¾„ý=½¾†’|Ùãõ=„ñD9Œ.…ý0¦2ÆT{MãkÁ¶0¾þý^=̱åºïóa¯Ó)µh¨9ö}ïëxqŸ¦‘¼ÜóãS½ÕýxbŠmü½Æ1´æý”u‡©Ä1Ÿ1ùAê±îÓdb/™ÞØÜ˜ßXÁ|iû«ür﫽_|1˜óæÿýâ‹!¥ÆBºÚËB¼xñ›““7½º±¨Ä/¾ ö6|ìÇØó1Áêr¡ÏæTZ{éÓáøn¤÷q/%Z)cˆí0·Ô mW.cÝúa.!ÙŒõñ©ŽG¼Ãï™6.ùœͰÖ ŸV8a¼`ìÇØËzØK‰i¡§¾´çƈRîã‡ñ0÷±X´Í™×ÊA§<ö§ŽG’§ÐÞDZAãÇÕÓk÷.l[¾%ÞØƒ-Ût{ïîò6¨m¬Ðø¿ÔÇ(ÆZ:Ÿ‰Gy”ÄN.&Zçbw…¾@,–˜Õe¼Ž(ƒ‰B*ž qp!Qb Ž¿4qÿ|tãdìBñcLcÄì;{òÄü“ˆ-í÷˜9þb1qŒ¸N‹t(c>Çk¬ ­¦Ìý ı¸f³ÉG{[XhƒÍ69qƒY7Ùysa6Öncq·ìÀ˜ÎØãAˇƒ0ÄÆøRLSRM4Ü;‹óT[ðï{}ÿ—¿Úÿû² _çÁŸ¥ÓdÇ¥Šc;Æ šc.OC¦¶ñd©™¤-µ 9íÿ×8rã8ôq²˜VÇ9ËŸ‰×ùhùK$Ú¥¯ŽÎ&-ïxÛxÊ¥Ú2ïîx/-av=4~$øN‡h\žO2ñ„´°ãä Ï|¦‹ë‰-…B›]SŒ32˜‚v½Ð -7b©('7Žm%Êà¥ÓýÒùÆ””ÒøÝ rHÏ  yÁ—]èô‘!<Ï;ëôÄÂulÅæÈ.ö¶ŒsŽ[b,’lÙf)Ó½(\ÝhE*3߯‡‘·pð0Ÿ;Z÷!I*†!%ÓGaLוqÆ÷I€ñ¯Ý„CP‘Œ¥Õ:îÆZ˜yÇ·±UãºÍqH£ÿÚ#~fñ=¾ð.^éôšž†0žk_òXûÁ=¹§JcÏ­Œ¥·rt­Ð3cð4ºñâì›sGòn\qð3®¶DòV&I£k‡­Ð|ãa )°Ü{܈©ò˜mäÝu©ÇÀ»s"±0Hc!*“R"dpÀP - -rBh Kl°Í&gmã¿|ÆIl&ç‰÷;]Tk¬aMµ0mÜvt‡ ó| ‡Õiðc'¢£ÝÛÖh÷gÑÙ&Æ"’éAhtÑÞ’¼`J¥£J”šø„ š‹Y^•\ ü¹ZkäÏucÃIveH¿Ë$hé3ƒ3+e ¼°¨ ¤…ÑAÓk>uþ§ÖÇT^ò”õ¸“8ËU.Ïó«ÁG"pÊže˜¯zƆnjϬ!—s~׉ŽÇKd™V‡²™+o\à&áu}èZê½ &R4Æwù+D E§7†Þè 3C -O/dÖe| K}¬€/ªË”¦k’Y`Žk#†&Ï4¾ Iç $ -¥ó$Ç×ÛàlYý>]°|Y5Ý”ñuz-ѱäÉf¾êèx'WdhÊÔ8éAuìM{bݸ8éýL?Noñ8D_9´Ê‘퉡üóB’  |Û–iăÍ;:Ütµ³h¢ñÒ],û–èxǪ’ŽvГZI–D -¼òtDˆcc»Cæ›IPÆq©÷Úßqh6ÖæñÛ\Â- ½e3¶mÙÆÆnnþ6)$Óy3I+~hûÎwDáÏeOCJtPd5ièmèDc5˜)In_¶·*Æ”âB"“ÌŽ,ÅUh³X§¥e±>Ô':~Õ0’ý…dÿç0ê¼ è -aNç+$ìóÚ· kÏ‚„·Z%p ÷¯<)üòƒ·iáÖ=¡ß„¬'DqŸræŽqüxYXHsb¾d…›Cðœ‡Sô’…îjÞàyW$u^ètç'ÕøÎgÅ€ÎíT ©VR†’—‹ãõ±'ƒ@ú- ˆuÉ{“XpÐ{|‹òLìøØ&~ËXC^ïTƒ(%ã‚e“­F‘Ññ{Éò <6xlõþ=ýFG.âÊWŠ'e锿µC NʤÝLd|²4¯¯qw„êò¼ãh·<«BÄK ¹ûè—âˆezO*³*ýhœ¾±¼œã±ˆIT^“}C@Žcѧ€2thÞEe踥†œºžrm¦Dç¤$Åäà iÝéf…´&‘ž|]ŠtbE²*LîÓ²«ój e)7ïfèPæ%C„îë¼‡è² -$ÐæeEç;$6(ÙYBœÙÿã¨ú–óV¡pWplˆ–-̳É`w8p “n2ò«o=Õf:“4£< ;ÚüÁe±ŒM»ËvűsWò\ìmNwÄ×t!ébÏ)ÒUo)—¶J« ‰¶!ó6ÄâS_GC ’«š¼áãS´w¹üâøœÖ*ˆž6ÌÆD~^Ÿ#ë‚QDæÓÁ¢¯×ÖEÅÜU_g“ži^TÆ;ŠÓÈS!‡;OÝz×=ÕxïnÛ¦Â)Æò]µtSuÝ¢àÒÞùæÞ`{›·MQÝÔf75Þ-zñSí ãÐÿÑI¡!TkIº ­ÉõL;DŽB! lÿ{ÖÉÏÍ4¹Ž:K¨Ì·Ïã-+‰‹8Òˆ†¼%<’k8&~× ‘DùŒlW‹ŽU˜Ô¤º=ΧZ‘NÒ‹Äç´Ë‰ÂWccSä[d\¦c\ûÆ! |O§]l{{†¬À*LÇÃ\çá{z &õëTºœCÕž±Á­ÊKô2©‡ö±$D—ê­÷Œ?ä1‡La'ûëÞÒ€ItŠÍLgÕFGþû˜é=ã>ï(ò¤dÒŠ˜t²k¤þû(z“èÿsýt| úTÛv¦ÿ %ÇöŸ÷r™ÚV±‹¼³+öªóùñäÐì~nÙý²Î´Y>òQ$?*{¯x·èï­ª ¾üž¿æ¼º_™¨MÍG=‘o—OéŠÜ¢è–-hã»ìÓ¦Âñš¹iôTRgoòNŒ}%ïZ•W‘ÈìenÙöµ³ÉKûÖÙ¶O1»²Ø¸¹œæÝõ}ª»c­’šÇ÷ ¼Ó{´ââƒîCKÊk&õ1 ã9GŒXšÈbu¾ç(ôéØ3~åªønzì¤u J/rg”îøÒw\ò(E"ôyŽ89<Äa ÓÏâañŽ }’Ùdè{»ÎÇ6æõqÜ w‡vA¤~uvzyCÐfãùÎg7P˜Øñ5Gy6 44¿ÿâë=ÿ‘ øËÁ8“í]kƒ32€÷_\Œ­ß|1v'íÿ‘ñ÷W{åi·ìå›/ooØÝ|½¿îÑíon£§möðæF¤F~ ^ÉìH[!§â$J¶ª‘Ó°”DY(ã*¥œrÈ÷Bá„„F¤;ï:Úûrûäf\ŽùÃõÙ«¿œ_žÊÿò×ÓãóÛÿª)h]N$ÂH–ø!/øãì®ú"çÈC¬:Æ6.J[qŽóÄÆf’š;®š£-ïûxC÷C!Ò@}x‰—œ¾¯¹4ì– hõt)Ò@³&Ý ‹S®ô¡_HtðhÛ;?ÞøS8d§FG°”. S‡!5„ⱦˆp y´³0QLÊì`¦ *§s÷eoØu,LêD¬ã‹Y"Ìôñ¶ü¸ 1Ëû‚5äÖŠ ?u÷]‘Ye›&¾oRe?}œt¦H¹4%èI™cæÉ©LŽ` ¶ -GÝ~ÛÇ7y£³#ŽàÜKæqб -yû±ðLÎës!¥a8ï8ïŒ+È•Äã8mã…‘S†zêÆr6RS#‹þ:åÒ×%3õë6WŒ/(cyðÅPÖ˜tçeoØ}°n%#g(Ìã42«pRu ÕËX—‚Ä4D'Ü,\1„×ËÑæË>Ú°që~»GLœ5°ÚX¢3³TŽdztÙmù{ î`‘>l –”%ñ´î¾î3ðœ‰ Š¨Ñ¥—Ø…JŸæpšGÃ#lœÚ7F˜y*t9UÒyùm$Fî¾ë£ š.ôNŽKÏ>'œ¾M™w|y7¯Ì$Wx€ªð¯X È)-ixÓGó`Ýa ðéâk{0q.EäÈã^ óÇ-ž à„âÕí—Xw¹ûªÇaØhÂ(E\èüé8d4@ -íÉøØDdvaq—ÔÁ{Ákz÷E“3§å2÷dÑÇÉ-JO`.^µòpèw©Ï|*H·_õñxc/Š˜íœyÞÉ?:ØrH°Zùp·p­'†oÊäŠ >ˆüÐÝ}´{²›ºSò)1†“Ñ/Ç–¢Œ.¶¦Ñ¥Æ"ƒ.N7ÄqÉT -C£»óšÈž“ê}Š’ÍxAŸÚ\æosÐ[¸”ÜÌ¥½0ø¡ZñIMtÂwÞõÑFMnÅÊvз{‰ÒM鎋£u $–¹B€Ärl[n¹M"‡_‡ Ú(2MSÝxÝ;g¿ö…³¤åó”ÀpAß×ô úþÐDd”â—çjIâ<¥T„^»nÊ~´Á³–4̨¡{11KÂH»s]F*a@{ðhù 3‹h雯üˆk?´7ŠJ3‡P1É…Ü‹MïEÊÅÔû½4¢0ÖTé±L>ò©&n¼ìã ›¸X˜¹#ÐÝOŸw” FßO9«P§Z¬8ŸSæè›“ \·_øO9RRÈÜQ8€êÈò:ÒÔ»ŽTC<ú 2‡dÃ2sDgËíW~´áûÛ;vºnŸÓ6_=j±ÛHvdlÓøIb'õPJïšÔ‹/·úKCú•µ‰ØpÛå°EçÕCê¥øh,´'³“>0LÎHŸmû÷Õ½QØÝÇs Ty™¿8yÐÛ_¼ûíuœóÆxëíê¯ý8Œ¸iˆoQx¶zT·;÷6mñÙ.z÷x¡.hÈ”ÛKBŸ”?JéXòѱ]ƒ›)¢y÷锪›Í§_œ<èå/Þýr£ÔUÜzùm֣ݛjíÃaÛéÚ¶[öù[•ó{¶ŠB§¶<É“7—‡á¼<<ޱSãŒK¡êÝÇ}¥ ïØ|\·êž/ñî—/ÏÕâå?¶UÕåßG)„.àë~pœç³¨.fÊÕKŠ œ_/¤Æ/ÆwþÍÇ¿ßÜœ^_öÏÏÏNN??9>?»üš>þ¿OßÊG3Æ;þëé×g¯o®9d÷7ˆXLl>ø»|wu}3Cxquu~û™KüÛ³W‚ûððÐïϯƀ÷?¿¹¾úv¼cÔABD_?$ªȧ,§)!8€’Š(\BÅO õ…*º+…\‡…Ò¤Àƒò¶Ç¯J:Ô‹5“R·‡žÙ³dî–ΔÁ˜b2R ŠóŸšvI¾¢z°Ð²gM3ã†x(E¥T¡ÞeŒ}°²'ñ3>V"-\§Í …¯RÁ:Ž£æºF/sK$²7hþO$ï5¹*ee–¶Ua¬EµC¡õTOŒ\öNaOžâa1ÐÂRégd‹-qêy)œCƒƒŒv†6¼ú´-b‰ ýýþØ•@Iõô#/¼FwU$ÑX¤$'A˜HøTRœ(Ç9^£‰¼Ÿh•Gçð»hÛ( òÕßöó˜1+ýd¨ÄË™€> ÄQ¡ Iøa%Ô¡Ù¶>Œ•ÿÞ½+ºt”(•»äRE„× ­‰ö>8¾Òß9àE»DnÆñGz %¤‡^Ä„¨ü¥«V2ŠSâdk#¥‰)PHŠÑþSQU¦ Ú´*UEÙ‰‚Jl”%ó0QZ5‘F¾Êæ ìê¦ßŒ…{×%™~Ó$dœæhóZ—DÊ`m•xŠüOƒßc¶3܃œoJbå|ý¥N…²\' ‚ÖðÒa£˜*ýhì !;Dª±¤Dø±Tö‰z)Fç¤0œ’yÉð¤õæªtú'¨E—݃rc¼E i¿u²èsÃØößx–n€ÂìSHÎÐ\™>–%ò„èµÉGY*}'Jx†L‚¡‰òF7rB$J1“ß8Jé©D¡àï3É•Diuƒ{hŸÉŵŸK‚Ö`l'ÕKÓkÇêò:q1k¢ S%pÌeŒ„’RùGYnRÈŸ Æþ¥0™=sÉÑ%2n³¼ØQ:…§ -1*3¥"áGˆMYBŒÙÇ(èïq¬"¸$t,%«uôÊú¢oëA;gÃ_ RÊÅóÏÌcð!°ÇSêF$Yæ@™qƒ”ÈcÉ+Á‚†pƒbbB£ ‹A`6?áõ& Gî’^š‚÷ºÊ,kL‰$GåC½àI$}™_âÉIÙ”P2¯LpX -E˜À»?˜—œ9üZÒi(¾ 7–>Q…J 28O¥[4U•\ëK!ìy( -ŸáœI›Q8q”›=e={ú£ˆTŠ”;˜2Úð1a̤1ŽMË©TžRe¶"R£œSZ*2€‰@7tn*=Co¡;0Jm¢|§ÑAÍ`3voœNNÇ“<-4VNÉ£:%Ê(£ùtÉj'É{ç ‰sIàç,Ë_ú¸"Åj‚üˆJ„Ôª£… -’¼©Ô›*¢ºçEÎÖ\|‹Óµ1ì"™¬usRï´ ½—H -äB²8ò¶öTè–z‘1€ø7äÚJã7CÑ1e*¹­2Î÷$C¬BA爀K†@çà´œ|Õ O¾Þè685SP¸šüàÁ·O’-âL¾J$Ιd±D 9š—}%V7ø4¦ÍWÐXh -eQf8cå,‰ït¹s&fœÒÊW!×IóeÂûIw;)$ã6u.%¾Û3]Óƒ¡¸ò„~3Îe{2øG®Oº•I¸EÊ2o‘ …$"—uö¢?ÊŽ…£“ð½I?»™ùÜÏHñHc«c¹9%šÄ:eÖWx'0â äñÆÐ"TÉmÄÁYY•£PS£‡±cRaW·Ó&Qî»dé¦<~È9Ĉ dÆ’ŠYë¡dlw>öQ•>A b¹l¹ -¢CÒ²XºM¥Î’tT>µªZ(Ë’ „¥ì­“?X4JNK -”‰é‚ä½×Y¬PYLÓ*bNr⬗ßQ¾'+Ð’‚¯âMp2]i„V@ªøÐFåå‘à†b©¡gÂ2Ê‚!ÕOpå)!aµñ‡q—j¢³'à‹A¬T‰ ¹~2J [Êrx)%o\¥^ë€D t²¨Ž‡“ ‰Ä)¡c‘Ò2˜Ð Á°"ÈR žD-U±’5’¹l¤¦Æj QPÏ*y:6Ó9K¸²À#W*Ð)J"ƒG’‘˜Çsû8úCÄ–÷7lÊ/~‡±ùK…Ê=Þ·Þj™ 8LEÒE©ö€xç$‡A¤ Êx%!BøR$”x'êPbÆ`=v1¶%ÓžÐïXˆ9MggàÒl…‡‘Žtù5)ò­™†<‹1(‹2³']î«yEû¤þƒ~ÇúV¦Œæ&ê%hÜL@hdngLñ¨ìIÔ™d^xºÊÿÏÞ»ôêr´çy¿€ÿ“™,¢«ª»ª9ø™dh8Û2,„”Yúÿé뺟^$7“O¶ÁMCGäªýö{è®Ãs¸ÏitùYwr"¢ä'ÊÉWd»†’|A™&Û- ”ÔàÞ9)ŒPž“âdG÷²D|þlGÞû¼ùT„yfv% /jê~ð»Nxƒýì ìnãH> -î¾ñŒ©F«ËF0'§Ä–á§ ¨¾ÏfzçœIxÏ+ÙU¾#©c÷Ž4óDC=TbC"ôDrëÿ?d6¶ïÿ×ûwÿwß?qÓ§ÊBê͆ç&žˆ%ufå“â”×NRÏgq/`‘·1™~ÆÊþñ»{&WW¤ž•œ,ÙÐe™­ÌÉ;Ýl¶˜ÐdFCB‰u¼!/°Ùp8RN¦ ‡Ãå>E¸ohvEö­q'ÛÈZZ <‡›ñ3S™m¢â×"Iµ•Zä]]!èÏsJ7噦˜ç³ñL‰áÃ_ÓQ';avn5¤IAöäËì°Až›ž;+P}"8 ˆ÷ßóg¢–ÄZ98@ŸxeVéI"’}=}=“¯æ‚áúó¶èÐ÷œc#H÷x~fîâkŽL:ÉÃÏØZÁœ`Y²¥+¿×zžÐLvÔ¸Á«…î“×<ûÎmåLiÇg+¦®1’M „BÀÞg`Àűï¤MÌoÓŠ†Û®%Aùì.¥~â~îóó·º;®6$žÞy¿«È ø'‡<n…Nšo³øº÷›•Ï„ÊDb§ûbœv‘40¨îqÇ-!ÞÄ‹œ <R¨»'ƒå¢•g{[z‹ªü‚ö'FÏ -M:àéœO’ÇЮMisP:P›Õ:ÿ–sá³æÑ24Ù‘ùˆoÝܱ–ÝC¬âs3}÷Üš›ôíÍœxîŒTµæÈ#‘œ¨úª Âùð|á ý¿_>kÎ<ü³d 1AºG]±Žg¢=“H´“ˆ»È4›´Ô4ž©8ÎRØ‘¦w1]7ÚjüHç-›&G·÷Y 7“þùOV -›ãó-æÎ¦ìÒ¡h=Ywóryñ¬Y‡5TV aØ4]î\¨8Î ËXÅËI±ÛÏq[zs×›AvtÃW6 ‹W«TM¹¨§<öü"IønDÐ Ùv1õݳg.6Óu½›Ùy ->ý2#ä5d[ÉÜvDr¾ÿ])¿€`MdÏi<îó—Í춨 -wánÞ¥Iz·Ï -鞬$ý¦3˜1î5RJÏnl -¯9]ž»ê <ŒÁÓ޵v®yv¹çÜðÛ[Ìb]¡¡á/FÈùyÊ&Ü«š«ÙÉ(ˆ¯+(þzÄÉH­ÛМžSΡCÔ&™,c×kP°`·¦ê»9¥«¤ÇÂ"{7Y4ÇÑ|RÁs -®äΓu–*1S™ˆx¹yP°y¾ Eâv䶸.2µqÖ”#U:©Hpˆ±ÅÔ>&"ŸGxK]ÓÅ 7k…©×%LRÊBe¦‘0Ïg At Ý;«ž³øÕÛl϶óKHÔhHT7Sw%®suÞï¹Ï Rh³‹F¹ßNÎH´˜¼™Ò9G°N›ŠÒ¹>Ø£÷ŒÐEîKînY1Tºøà5«vùçp%m‚Vë}VfEpÎÙÔžpÀ~"<ËñŸñH`ü£¡2,öŒ<›?ÝO”„dv'M`céÚŽ¿SšÈû…×[æK\Ï~j÷ôº{ïýý×±ø7ÌFõQWÂþÿåßïær={ΗÆ. uC‚ ÚÇöb ¸2iÌD‰¯){øX nl7>ß‘°§itÑh2b»••¼TOò4&FxVÇšg–®š2Ë›Xù«!é3µ·ŒetÙ Çv.w/½Ý¬ÜÖýŠ7Çö%°·‚¸–Cj™ÙÇmuc“å7Ä™3èN5Šç´K ʯi÷.]¼o‘°õ¯6¦N[äúABûÏß;r@£”Ä(MãP¤{ÎöX¶r,¤.ŽÙN/±Ùp·ÙMBaËPÅüVE^cÚ»6RÒ6¼š¹´ö(V‚ö€¢ø‹–z Ò™$«¨~DlŒ:ðÕ'ÉùL%Š–ÌvœOPÎrmQ9 £ô1¨óë8¨Õ#c3K,ã¡CXÏá… ;ðá‰Uã*«&ô°©4xšòÿBè`P¬¨ÝP>Î`ˆbG8E -T¨Ÿï榢ˆ ±ßU_–û8­Ì’ª|Çç¥sçÄxÛ]€,ýлrÍJssRÔ&'£jÕ¢ÚH{ù>Žô!ˆó(XUZ a"šÝ=UÁs¥dNÓ¸”*7Ä”4ÇsÛ/ŽkÚ|À¶ô¬$õ ­ž!;!-¾î¤â¤›·¥¢uFEœŠ6e²FX:| 1¥mªZo4”(ÆÜàf‚£yÖ5rxÚr›tÏauµ÷t‹Ôü£ŒwÍ·«ªZyk¥áIy_1jŒi‘òñF½ŽsnyaÛ+²Ù'¥nzâÔ€íå2[²"ZÂÆÔ7¬å²­é€B;Ð"íW+I C#ƒ“yÄü?"ÇødnÉ î],T;¹Gxq\Dxú|/zÂD ¥Ž<¿†EÍG?ûøZªàËmd¦Y§ÁO‘ÂÇ’¸1ü‰VúH¿ -Éé’ºR} ¼ª;m8kª±P -Lˆ;Œnêb}±ãÓ–aß P·‚~J À ˜ýªËyYMR"€¿_Ý¹È HóîDy¹Fì:óæèi™[€0öµš¿SܹG”ÒóíD…P’#á˜,7Ù3ê©íÜ÷géƒ)BL%†TUðç·Ý:9У¥Â*|_â.ÃÝ«!}wÑY_ô‹2`µmÀŇ¹åõU›í}§©Ã€jA(v­ž¦°©Åé÷á„$?AÔªíF"˜ˆ,dz^Ÿ•›­èNQì~WƼób=›“öËŠËgmzw,ÝÀvòf«Y x¼ð¼î”¨Ø؉‹?Ë¥ØvÜünT….dI<_? %ëçÎ=°MÞ·fîÛźÜg(2µf P#Ã\+Z‚Ì2êÉ<"(à›’„‡QÑ_`l°¿‹5‘¦'Ãu×Eswy„½Ë(„â‡Ò[¡X\SðùmE;¼e NÒu&«ÐÒ¿±`÷üÛ=Ãä¾X‹å¢yæ‘©+}_Ÿó›‡vä5nZA8|6Ìn1ñùŸ›'‰E^ê"¢:€ÎÑá"â¾!‚²´ž|¿ñÊSâ,ãÈ%±$Ef õŽæ8G¹ƒätçæ14®+CÏ2üÉ¡âî‘õïTDé ÐM0«¤y»¹©(;éA6Ë1ýòWi`ÞGJ¼­ÝûˆÚâ?É`‡ÙýU=Û U*0 X ˆrE­!Ôø$ÚF 仨ÍŸÛo¢vg.(ÇØ„ŠsäTôWwïfƒ -Eg~öÉP²ìFFb)fØ-ôb§Ù³õ8­(ìqC8™WV£Ç;„‹f6Ua7ÚL u˜Ddê¢-ÈpWå'döËÄ„w¹¢xœÅåÎDSyͬ÷·gÁ Ï!uÖQÛèîÕ1_w`Sœº;°FKÝñë´YiSîܦE'Åãkä‹D¼¬éجþ1»{Öˆ*Ï¿ØQáÆ¤ºT;Ÿæ@3³“¿¥¯=ß­Ÿü¸ ük[¼ví–ôxGŠ·ðoIÎí“hÏ–Ù|·ØL/l¶ßʼn'Šåη™Ï¾Ü®@­VOéBÊ&[#1Ö-ýfÔæw{B•ØmUìÃ9bã¹—ç9j:&9P²z9S]å39‚@ळœ¨ Xc·òsúÆ…x¤lsRâÜ’QèÐÙîq'»a™ŽS]Zuèî  ˜‡‡,·3åÕi9d—¤–`´{×ER ®BÄ­Iâ6i©"y®<ëÞr‚ÞP”…,›[Îü@GãJäû,™‹úàQè“]Î"«äö¹(`9ÑIl”`IqÇì’8‰I‰ Ì]ùP>!”#ùC™’¼!5{"9Díö+ä™ù&*¯¥¥¬v;jšòdiñÒG^´±Øˆ éFéŒfÕ<â×å\g¯YC0Úe’¡Ùò+,>_â"³’éÌ";‘ÝZC^k`â@¦ß@» ð{Bù+‹žþÉÙq§è‚åd§2Eݘػ‡pìÈub›¶xåBý K&“Âè¨6q*|j^ÞBªÝ¾ªUÌgn¢m`sQtLãªÿÒÀ2V?á†dÒm¢Ób‘EVÞy´$(ŠŒˆZ”Ÿ©'²Þçˆÿõ¥•`?ÕÚ¦P0IØÊÆð£ÿ$ê'”K`ºæçlçöT³79a{¤ˆÙ"wo&y*ÔšøLõ{Û¥{hëCÍòY ’Ò)¾í­×TPénBTgz5™ëˆ>?¡Í“3s¥"ÛwÉÈ å<¬”bÔF÷eÚG}÷–l݂ŘéÆÿ}_á<×_éêiY{ÊS­´…=†¢ˆ\@?r”æ ãB#®Àî¿®,}Ãòc -M~•“‡ò£Xée™ã†kH*GoPÝóeŽm+8±€¥=êºÆÏû˜„ý7#d5­Ê ?AµÝ×. ƈ˜äó‘;¸66£ªµ„®õí²Éž(—™·ôh•¶^—wgÖÍaÅlåÐ}ã /<ïj}©zr€ú³ŸÃîIëÇþ‚ÁV>­¿U½Í$»Ô¶YÑg’ê9ÓO; -³+¿Ž{¹]Ùç·bGÙ'™Õó›©è/z†µJ´Ê[&¯·Ñ‰8ëÈ÷3³¹+Mð*Æ ÉS.|B¤‹¶9–xÐ{^¯÷à -CGI¢ÀÚ6OHí¼«´oH`8íGan8²õ.˜Ž -.WkÝ9¸L€¦Ÿëó—)/Ë/™|Øœn÷;2öÐA\ÒÒOù%ÜñU 7º˜TÔÉrŽH%V¡AÜíÇ)› %Ú¸Š]rº[Z”+Eñ ‚U<Ýçêë/ñòVÀÓQwhÙž§ #P©& çøfC±)•®êný‡¸Í(qkD‰ -) x‡tˆžˆÙÜù2'‹ýŽ -[Aéï°l¶­kðãwälìêÄl$îÃòoj8.Pº7‹wI;Suœ#±QYuQø9Ç #Âú`Wî–/©æSiÅ„–øŒXЪ ôÕŠKÎÛ7½îVޏ6¥\”ø¯|Ë6(çé°î' ‰Pç]A\#ý/òÎgú‰zþä(jUeŽf*8Ë4”™å÷æèæòë™0QsE¯çZ±k1€üʈËÊû@ëîç]÷õƒøæ¼RÚ0õâ=Ár3!M¡å‘¶g=LL/àª/›¡ùaæû”Ùbu„<Ázl¼®öž9u2¯r°C§#“0è%P-¼W°ZoOÒ˜pÃU; É·…U„׉A„÷žÚ2Ñ@ëù« êq½ðê;fv%I͈eÅQ·† •Kê-˜YT÷cµœ¼õc¢O„׋«u^UéÖßMYª Í xÙ|ànIqx ¤ ãE› ¦ŠÔf:aLÁ‘CRuÞÓuûâÚ›èY:´Þ~‹”B‹rä/%)BMåy„ük˜ ¶—C^„—4¯4”¦¬Ùçíl°ž -0¸%Ñd ñl'ðGIr¸¬s~ ï}D¿º¥ÇÇ‹×a?ŸXh¿PÁõ»œ…‚_õÊàn4©ïÕò Á’„V&ýÓóÒÅ?FÈ «Œ žoÔÞ'FK[ ˜ÃVçªcµy‰À÷(eäÂÛ7ÔåwÐme>ÊaÛ¸_7¾p?Cωi)RÐŽ_ÿž„‘£j "{ÓA1ø"{]ÿy†‚ŽTÉvÉ0Ðj¦ - ½ç+W›¢ì8[¨&úäö܆”¢wf MŒÜ½FBö"·2ŽOèèäŒ *üÐf`žyÕk -u†7*w²#è°/~œ€em: ^ùRv|}íiFÊ0º%ÕáºFc 8]ÈGæQúúøŽ¢9öô'‡Oðc&'£·.Bß=XGïoØ©#Mëz ­ˆˆÙúÎt93_œà™¶˜5²_¦K ØÉä(#äj–09ú啨‚ÀršÅkjdV:½ œÀWqHzhJsrØ9»{¤ü×(ŠêŽæ Û}Ji»"^oè4ÀÔõ¶Dv‘íuݯœƒÂ)µ{÷& ÚŸýçž9@ntÙ›žµ®¯ O›>MÅݱ@üC ãYù&õ¨ìd¶ùÄ¢P)°oWÇXNɬRÅóp‚WzÙHZMñ6 ñÛ< $Ñy+$O½äÞÃTŽòS·œ3ȈV¿h>›ý>>è”Õ¯ÖëTÊöqÐ圈¬÷.úCøŒÊQ¨NÊd»ˆMÕîµí¬2±0øá:Û²:¬ÞE”¹ÒæÃ¡ÊÈÈo¥Ä8„OéRàDoÎÓñ ceç³ðÁš•}´ÒˆqÒßU¢$¹Ú{^$ìzy¹ý ì9ãõ1£d"½å¢<ž¸‡¢É¥ñÄH}‚‚³+õ:‹·Î Ÿ?DâŒöV\§†9WYkM<[´¤.‘EÃ¥#•Xêô`½ÑŒÍƒdXl¹&-ÛÈ,ND4wªylÀG`YnÞ› -\´ÖW -à‚roMÌÄA(Üéõ¨&ºaTODÝÏ8…Òh§~>Ê–YÙt ›±‘}‚iwÚ8BûâÆH;S3ù½XÕãÆƒ¢]'Ó[Ù))êìÂÊÐ~a¿mÖ½ÀCŸ"|ž\6r ª`-‰ÀÑþ˜mè°ŽG= ›¤e<«qgÏ-UZ\ Ö!ÃþðtHwøéÎÿ`ÂBâ„ÐYL;.Ëù·æ!ßsš¹4—&ò‰4Êî*ÖÿâLR×¾ŒÓÉ5v_WÌÏU¡²ÕÏØ2Ñ'?„‚Lx¿ìç¡gßiŠ¡)@®^ç?Æ: -í48°2@ˆÚ@s”i™ÿCï:×k ì¡Y]þŠˆQƒoßÿ.-ü†˜³8ª¼‘ÎiF[p Ó93q±º -Ôá·iHÂå9Î¥Ôêâ÷t•„(r[N rÃË`û÷K fȹ²œ—ST -çC£ÆC’k~Y-O†5"ë]¸ÛMN1ZÔŠDX‚޳p–úëÅqAa]u„~Ì®{™Ç¿,xÜԊØIuP¿ÔúlºÑµ’Ã~gnÚЉìY¡z…a ”$w]šgvaŽ!‰óò£®Ñð>3¦÷!(ç~êQ3sNmæöØŸ´)Kq<¾Ú/äj#W™ØñΊ?×uåÖ(£°Ïø‚bx¬Ø®»ñE)}`Ö<¾3éüºJ, i´þªï9ýH'ƒÅxí–ø+‚,N#6ù‹––39I%¿u Ô'…haÚEͨ Ø*¡/б3œÌ!Ta0awÂ#^-KÅ}7˜&¶-®´°!nŠT ?ž -GX¼˜îÿH4ªSH¹2Ò%¦ `Q’‰õ-µ*¨н` þRcì,‰ósí«ˆÅm$Úà¥8€Š#2³«ÙY½?êåÂÏ䨎®Ú"3åxÒr -«´$qE\0îvb‰zÌ$®ïíæÂžúá¥ßŠ¢q–Vo¤ßò!•¯ o"™ø°Šj§ ÐÅ{ä¹ÔíH_•.«ùŸÌ«A´Xà=g‰ß0rGYÇnëÅ1:©*¢pQ«åQŸ°÷²JWGOO5w|oÅrVñá_‘g ø#¢|R™¾ŒÙûöyv êãuS¥èp¤xê  ¿ë}Âi3ˆŸéAPýÖæ®D‰ør¥(¯Ú·íhZSW-ŸB¬ÛÃ]þíÛbë[ZµzœkY´MŒÌ^fØ8ÞYAé˜ÓbmšbŠ¥zÌšîô€ìXè &bí †5mMx'‘dkÜ vhÙgT%dë)á2¶#õC>Ür -š#4ßMl(îvÄb?¤´vÝõy ó¨ß5f²]P9­"+©,ذڑG ÃìPb>Ânä9`Ù£KV ¨’ã”8Å¢ µkÁ¬ñÄ;‚ô(Á{6{–§Êǧ¨Ô>#Vd–#9›MÇ -¥ëzžÕŠU(¬ŽTRj+€éˆç]—}ÐBY{VƒØ¸zªlêupÏ-›NÁÞ>ÏWâ.(˜ÓÖ×Üš6Šc³- @ÞzO· -DX?>ó«vvÚÈpòÌ©-:w¯ìÇö°îl¿ÆÁ"­n/»²®„"Ì 5.A7p›iD4Lz-õØ7hnYʯ±äY.|ÑA> -é+!Ýò3§Î2nçN2åh¦g!OHu“©sdTb H7íRç`VãRZ˜±{{Ýx5¦lØôRíe3oGÿ×E]sâÞ»¤Œih\&=…µ²E´@™¬…,ãe9ªG !nŠÿ¦ µ“3`½çã2L`Rîê( º=%ˆó¤CO„õ X‹·œž“ -·0²T³®»º\#û5à%Jy4}»H¸KÏJ B’Ô AÃa¦ ƒ–ßMœåÒ< CK­Š`Ѳœ;sädì}ÇÕ©ØãHW΃²£3­ Yñæ•mÚ?R§@·YŠæ‚a¶”îpù\@ÁŽ|~è™l…WXCvSäaêè!ò:ÊàwoÓ!õRo“Aqüãú!¢ÛÊÙ˜H¼\'ãÔÔ± ‘^Baêð­~íÛ¨ShmåfÚ ûÊU”éæËÚñžÚ†·¸ª¦vÙe4kê§UÙåØ={™§Ù)áU%âùUúþ‡T³î\ýþíÿþ¿ýÍø§üÛÿôO?•ê¿ÿ¿þö?þãþwÿð÷ÿéoú›ÿóo~þ›Ÿÿï¿ù‡¿ùÿîüûú»üJŒõ¿áúÿðÿùÇŸþþ¿üÓ?” ëóýÿá?ä{ÿê[ Ùú)=ðo¾¾°ÿæÕ0¯ÿÊ‹Ço^Ü™VåÕço^}‡~ýêóÏÿþûw_þóßÿÃù«%D-Ç"tÊ<¶†H_è€yë?F/Ê ÚÞ#õ¨Ã,&Y°´í %'N*:]Ï:¼Êµ—ÆÚ[wä4¡¿ýBfè™+ZÅ)B©…ûÇ,6èx}&Q¯"FhÒ÷·™è“ݵµÜ©8“®õB‰H­ ì€,$‡ ü0ºj6Ì9!Þù-Î)º@ŽÓ›Ú¨géd‰áAUG© Æ#¿@¬î@ôõüÞ”õhÆØ¹˜‚‚ô¨Sµ¬Õó™Hg±ñ#]0•¸ô<Ä|¯ˆù™û't_üé.ÒÿQ‹ô—jêõu5õªšþ©¼Šö€ß©v\¥~ÆN­p ­\Ãð3žÍ¶9×®’kWu½Ôá;å¾uû(‡2­v«»ˆ_#çÖÒdäÀoª5¥ûMÛé¢È°ÊüÑ®?¼û–Ñ©¤o·dÙî%J¦ DÀ¿\éíŠ)8THäüÿÀÓ±jh¬ ØJ¨ÞÌ7µ·úpT0DÏ¡ÄÜ9W0ˆ`‚ZËöÆëx}X¦sçm¡ˆz‚ûÁB+½ ;=+œ9ŤÁÿNI`ì Vm”°œÖ7Ÿ›µÝ¯Ü „º ÿ°¬gM53íÊS»ÜYöј‡4*ÑàŸ­[QLëÞ ë›Âif!žøQ’‘½è¼æwKѦht@ÊÞ¬y|íÒ&úµ¤ Iü–ð.ÚY¸t‹=§1O‰C3¢ë–Pû+Ú@䲑ý›îyOÄõÛóÈ®çnÖ¬½ùVe;ÎHn;ã½¢Yx¾5¨ÓÚ"†ÃØ–Ò$4Fòlå4o6qÆ*¥-Ø‘#¹š@Ž$Fçk’ -äºÚœ©Wî\×ó{ÌÆ_}¡¿š/~I½kùm¿ýµ÷ʇYõaäÔ˶b¢M” q;³³Çñ|3hn9]žF«§$Å“Òá -Viš¢ááSž ™±;Š³Ï—`FêòõÛÀà™:8Íýr¥ç[®WVþØvÆÜÉ; „7Y§ÛÚü‚ !T‘ª«§>’˺Eü¨ñ’ªô¤Ê’sïëE[•¼ÖL´ÒÓÚEª£ìqìSìpáö#a*Îh"ªQÿìdÔfbaÒŠüh«x -N«¡`ÛÇŒlø³eœQ£üKk@þ=}kÉL(¾.ÕIÏ­&‘=MàOûþ#|¤u„öË/5ç3§Çg?[×µ®J§ýYIgö²–ãJk¤‚ž3µdÒ5iÌ#2yÔºbî§ °%ÕX§†fçaâ«<Ö)Ö% -M<KºÄ_ž'ê$X'gÛ›´•õ¼¬vAÙpQÒ¸7ðkŠqSD7îÙ"„ä²yz÷‹7q|g©}bÙ®ˆ˜Q¥eý:êÎ|Ã/¿?ÿòGä$ó_J¸³Ç·J€±©[T8š LAÖ¤—¶q`§Ëd³Lž{-,açŪÛ;œ¢ÓÔÖÍ„©m¦}©DÒp_Õd)BêûAE¡«í„ÎIÏ9®”ò´æûf +Ümñx´@´Y-ÅÁ ‡¼HnIÏâHÚ@¡™f¦PmOùqf sWëöê΢$s¾´l;í± ‘‡·Óà¶„¹3_²¹ˆt Ö#êoÃn‹ó-‚„!®À >'?hGy²!BtEa¾UÆy~‚~qý@£ƯçšV²øööÊõ¡rÞ¬[’¯‡À°JÁ5/ø ªæ×ñt¡í’ØôAžÑE•¦dŠî%¥$s®Wd’ìöˆ˜>AÈÝÒ8PnÿÑy…‰¹í¬HÈâkR–§fÊ#>ÕŒ[ÖU…ó\²S -ý*G¨hJ—Â÷UÚB-Øg8­‡irä+_E&ˆ;1õ¹–xžRGtËCO‹ºŽtq^#8€ Où‚”#B/ã -wJ%(î Ik;bM*$Z‚})ÑãåÔ‚¼[ôTaƒ!¡#2Wªõ‚s»‡Ãš …v ð´Woï5ÜNÓnß‘ãZ‰7fuJ˜°1ÝwwÊüÍVXó:pQKi‡&×%?%ªA3æ»È æL&J½4µ&TÛ´ýJgHÝQ¢SŠ]ùœ”S‘q¾[à¶Âr¶Gà‹»¤Q·ž£~´á±²s[9´vÉŽf¾fÂFâjœêƪҸk‚F“ŒPŽf02¤4-VðX™»<*éçxª¨'ǤçèAˆãº¾V© ¾@1‹slkjÕ¤„ÅMp§ É’¸g -…LÊ{,¾&$ zŒt€a2Ò´£=H±ý)jÞ*gán©Ùó”Ho€üÂûÖ…É~ -RèVÙñy>-ãt{hBûÀµÅ¡rsï^ºpv¢~a­ÚÇp -'” Ç>Ê Ì :GêØ±-éÄÜ% «.J&®ù+“{Ù^ü5¿¨ ˆØ¬dŸ*Ãf[â6ƒFc•ÙX‘Õ]ˆ.·€gÌGÝKVîûRÓ.¡û˜·«¹Ê{k½@¹ú+ÏéHv¦éRÐÛErÛGT,jCbïDŽ­²i¡ì®.JS¶Ì>LNú*„FWa|äöƒHF}r^iB„Ó™é¥ÝxG¬A£ Þöx}jÐc‹¤áÿYZB¼X*€Ê%þv(—1-VwœeøþdV¥£ ¨Ø-öLÂ&+F;]¼&&5ô߯#N"8Š\Õ>‚ñî¡ß|¿vE¨`ŠºóãñÝÓ?g!§·`kÌy"ÙÖ -\jÌe´…õ%y¦¹,Ç k†¶Ðú@qE”A¹ŒjTÎ(î!ðá6›¹½$R˜a‹'»C%¼HÔÒbIܱʛŠ9Õži QK‹Þ§‰ZVö* ‰§å*¶ó Ö °¢£±gÂ¥noEQÛÓ´÷HˆC\p×€·k”¨q½/ƒ9óDB¶K9ƒ½öŠr‹Ê†åG£=¡`³7*Š0Ò)ºwÞÙ~‹ÿehp”cUÄ‘óÙ2ù$›¢ïÛñö¤ñ¹H¾„¨ðÍw”nù]0ú-ñڣѥ§?òs¸Çš†bN7Š‘¥õ†åÝ9äxnÇ ÖBƒ$7¢öŠØ2&"Œ´ãr­Y¬ò3H'8¢(7Ì;BäÀ((Z=\‹“.­̧íyE}Ϧ̥¸©[–(4IŽjwHÏ=•4=a•»±àæ#Ò9؉„W> -…3t%8Òè4ÍØ(ŽÈz "^˜.| èòú:M9G¢)Öß“5žVÛ{A ;D”¿P0‰ÐWØÄër™–§ 3þVùÌ»øŠ[sÍâ(úêQç#…sƒÚsÝÅd ©÷%w Y‡3læS¸˜#÷¬u<Ž»Õ˜ÜÄ•ÝwRúr‘:¡ŒŒÐ5|¹iÑó[8ßùŸcI0{ ÷t‚qÙv’%ÈZ›!">qB¸¶êö|¤ÝÞéíÏ#ù²– 5A†í6þ€á -H±' †Âh@Ê„­ý1SúÐ8i[ mʺÐÔOJ7Ó[}?^!ýç@œa¿è†É]<ãÃapyþ6äúÀö+@Mê®PµœÇûö4mÁØê Àß#V•w|,½&E[Z,G^“N endstream endobj 41 0 obj <>stream -ëvuL¿ìêopsõæÒ[÷*iá™k]Wè ÄN9QK5×ýáþ~–¦½ZË–d厩Ú<ÊKÚER‚#Pq=êEä€Ö[O‡(<Ä6ƒm²Š©œ$¤d§}hêðÜïõ.¤ô.Yj¨QK©E7Á„ÏØ§?T¦½µHè@Å?î"[=½X-†H¬b(8,M@ŸÏ Nl¡r êhŠá Pc¿ðSuþ¢$fHµ©±£ÒJÙúî/*“\À6Úu³šB÷î^ªô;Iƒ×tKqü#…»Z/þâ™Ûô,GõÖÕÆö¾‰öÖŠ¤çÝ3׎ɻMoã¨åËíæƒ,ø~_#*fÑä‡m„À3ûéïîi³,:˜ Z€tþ¿Šp׌ -SÊ^™¥ˆ`S½‡ƒs+„ÿ~ !C®ž(Ñú½mIši¦ÌoaÙÊZˆN¦µ»ˆ>q×ËÕ‹çvW‰ÓßÅmß2¤×úªé?5Έ¢† z§®\aâ3í¶*¢ê+ÁóGЂ8<0¹Á¯ŒAˆ.ô>Ó_‹4z.T—߉|»u3ífûà„õŒÀIöîWþ)?š“&ŽçÎŽü!H1 x&®YÞÌU°»%¸€Sp÷h…¥f_‡Õ¬._Rþ9¾Iľ¿¶Ak)ò°&•pü™€.–+ñ Twiî–¨úÝ| »f´>Ç/ŒJ4"êšÝRœŒâ€’}¾Ò6@Z¸¿³ãÁšåàÁf.…~½H’qÆÛ“04{=»~`á{bà@Þx8*ô3ÑË‘¾¼Üü#vSÙ»•®9—/jŠò”ƒ¦Ð¥ fÝkŠ–æøÈ ÚØÆÀ>‚ñtK¶Ù -¡"±ÝA|þ™X ´,ø"<Q‹p„ȳXùÅ~+.ô1é;ÈÛPjôŒDü†°/|âT>¹éGÜÙ²sÆÖ©Ð EPQß³´qò=ë¡\•bÃÔ‹®,,YÀ 8½pÒ_NyVdšt“QµýŠ+R¾R~G˜׫ÝÉŽŽü6剗_Ðäð¹fT¼‚¢ñŠÓÕ¹k:(nƒÉòY|§N8 Pdd¬ÏÚLNgBò‚ÜäOî„ôQ‰'W²ÞVŽ!EVô|OŸl±$_jñ5bÝÈ-KG×g¡©1'ûðãÕuõ.xÔȺ¹m”#YÉyÝ…Æw+¿è٤Δ*™ ° žg@ëÜe¯‘h‰/RµÙwu#~ÙÂiþÛÚX­Åî4Õ-•íÛ®uäYXR]3á>g˜N¤|+C¾ñ™Š @¢1DqbFkåô‘ù}dò—*Ž(•¤ÎŽ0þ‡ê´@£øÙ™þí…þ -B™'vmµWÎâ½tÅ’Iªƒ°C÷„Ì{”¯×GÊç)­o¹¯•'*O$ψjø3Ýî¢C†(¢R«õS6¨«îߪ‘¨¼¾¦gY¨£í­«/‚’J4‡ªñ¨ýgD¯÷è]ªv;#²ÔSãâÒÏgCegYg „‰È»ö%´êè‡l/[äדýÍ£C3½Úlu>ÓJõ.ÓѦáÕÒŸ“`èÝMIТÂE'1²€ìŽÑÔKä3Ýiø˜{MÜÈ9¥®ûº!‘¹»aK¨D°`A7Ø ì°U»>s0%pM€ÃS8Ê~ 6IÛÅp ÂŽ ¥qÿ tÞL…yÇN›å(¬Fô÷3sÅ9’=˜?…c3ŽLñ*Ù]|J¾ÇÙe±“òØFøÖ -äâݰ‹®d|¡A,Á ìé§½ÞkòŽe´äU•¢,ypíTªU{úE'ˆíY§âÙíè±+£ò¡ -¼-»›…€ãô€œæ$^Ñ?ÕD‰5Á3^½E¡gRÀPžÑz‚dÒn ÀÑ£lÆqäF¨ÉÏiaeš—|è¹m½]µtqX1¤¦,phBµf/ÃÛP•°YÖÖŒˆhà´´ÅšçejÈ){BLÎyM¥ -Óö”‘<¶y½‹ ç.Œ‚(¦S¯»JOÖ #³YU@³Ä’ …*ª’wšœP‡¡ÜàçBªvO¹z„ó¥þ¬C°œÕjµê¯ »û¨Bb—îíßJÐKUê¨>8ê‚%gö!¥Ø¼Õ¥…ì¶q -ôo\š·rȇÀSXÒST1øûˆEMY›!OuÅEšP’l‰¾Ís‚\`Ö /Ò˜ÕFеç¾â¡`W=•ò+¼rÞ³Ìvʹ Î.³ØåN±‚$ãX‰ÇØçÕ}L7ŠY ¢Áurkg'?¢§1¿áKQw¢K9fÑ-…âŸßp,c(®Jò[+zÄá -%nEOù`1ÔSƒ$TF+’n;Eà¢rL»¿ÂS( x‰½’'úaÊ€¯QyWÁ#ÖL!f…G…0÷ãN,‡ó õ޹‹æÕ¹ÊˆŽc|ÜÀEY›y ^qhìx$Œsõ¸>:°Süô,zZ8;k®0kÁg)/£Äqþ¤Öí>I•Mq¨×oìbà5ªBs[ÞÖìØ‹ZŒ`¼ñÂq$2/6x®™øõ>/Š?²ˆˆg~#u þf*zn ‡çŠN†w‹Z ·¦mmIZð@[Ëùl_ÓêèˆDؚƀ‰3D?nãmª_Üàñé¥gSÆ)c«FêЛØz±É¦-”JôFŒôYÕ]>Ð@{8È•cD -ç5?ÒDÑsà…=íL -0µKí(։؀‘|Œ—N„…ÍIÑ‹ž°Û³ŒÈcœ V–¾©ÀÜK{¨Ø š‘®}_9-Ý>žo éàŠª‚m;ùÛ‰ -ö -HAL˜Ë†\±B’ï8Tµ—BÔ rJ9’Ú m˜j9¾ü‡“ãu2 -ÿ­äìï:?h†Zjw²ÝÊÉ Ç²}îa‹Ù_g!bôˆaÇvÎÚ‡b¤ÖNrZòÈ ® "¸Ÿ›gÉMïÖ#Ú¦ÁÆ^Ñ©Rýt °›ßAÿ(²ŠªÈÑ6òãúrE7iÝ1‚ôËb$ÔFŒò/*©Rð%ëÅI"@ìö^»”ÝÌ0 øPmn$:’xZ? OÑdG쫟Ô\ÐÐNq˜ª—à·•«5i3ø[b˜³E¯B*­UJ#H’"„BÌ%×–,œKÞvÌ„Xwé.ø„éKC0§ú8gwê*÷C‚¹3JHÜpŽBìi =riJLħ:SÐ~½Ï -}ÛOÂSé%h )õÆÀ¡ÖíºoFJ9QÐå†ù‚•óGÛ&/˜{Æq0>Š•)xF<ƒ´¯ˆ_Ñ*¯¥ü~ßWaŸλB(¸HŽx­®iŽU+¸Q¤½¡é´yM‰v;²ºöÑ$æ¾E§i³0“ˆ‡Ú*¹BF£RpG>%t†£ ]¼GäX½„/ÉHüDHÃéxøš%ÈŠe…âíBT í~ùÀ j5µ6SŸ\œ$g?é7‰ËBþû—£uð_/upPAü¯Ö:0ïý¯Ö:8ƒþ•W__)#Üõ{Ïß¼z²^¿ÑiÚ2ŸL~®A}+eðX^I€çÈæÁ– àyy`5‰íLvvÉP+‡yÒ·³¤…Ÿ-±iÍ}Ç §8` YØKuy¨HµëEЧ>¢íea"CWÎ’>ÒAJ…¢Í VB"xœ>Çî9›ày·øx¾ò¦öFÈÃì “8çomÓzĉž¥LkÊHäÈi|Çšõùj3äûŽ1$ðáÑ×ËŠ~%¨ÎŠ8d³Ýœ§n¶,)¢;K¦Ì(²nÀ¿"1“ÿ¹Àÿ;(¦ûøª¾…¢3E ‰®á  想eW~ÜO´ae„Y®iÚ5+i~–eý’ÅRMõ[c_êM[x阱ƒ¼4¨]¯b׎\ï•l"@ÁŠÒÿuÄU$F諘–” -P§CÀMLÈ– ¦k¼ˆ< -PÁ®ûŽËdPñ#©ámË:2š‘Ë<ä! Rû )®ˆÊ9 Í¡ÐQÁ2 -ö .h¼×ÉÁ|̉môs%…»¤´ÍvÁ©€[íÒ¥aeŠ•>¢ûÀ+(ÈŠý½Í.#}¼E[óÍ)#ÍÍo¤˜¿!J²K"O|jAÇ÷^Á¾ÚW ¹#1×w˜Ü¶ñ³âë~öϯ)¼â¹-míÄã&Aîéß_iÁbΕjÆ—ïLÍ»#¡¦Nêùþ©£ÄLÁ…¿·ç#«Fv^kÃÕXzë¿^ß°oÂw%ÛœvŒoO|ðbŽU>¼!@ˆ -žš‹Bªö+6†sÄ€TS½kE¿&Ç%]ñTdÃ` y˜Îv­<ÉÙÒ‰øœj3¶ÛÌVzzß ƒ°Û×踂àŸe¶òówª…î;¬é”Û!Ûð'þ–÷‹'¯£ûQ -_ÛlÅEx–¡=8X{<_T>°hÚ«Ì\æž1XJgŠè ! ë ¶0Š»¹®@®2Q!6옕Ú]e»¼Š L7 -> iR‘¬Ø¤Þv,jZMWvdU¾¹msî TS¦Ï¹‹Åäì&Dk–/ËBDb°>éf¥¢½Ê{Þ‰³ÖKí,[<ðcH¹èïw½HÀúÂ$üúé»÷V‘áÃÖE†”öïAõ`•56'XÜM×ÁW’ýÀ‹ïþ+¦3¨Ê¿|íá‹Ãža„i±^Ç€ÍrVǘï+ÊlKŸÇæÉ§îk»Âý–÷nËÇg31}¾@àç|Bjƒó€m’ŽÒ}nN*ï-ž„¢¦ì¬Ô«§võ›¿…4ó(Û¨g/›‡MQRP³åŽ ëb7k›Ù’Þ5L%x/+<,å¢,*è?NKÓ„öù:ë¹( Žä+/úØTi!! p¢¥œ%Òc° ÓÆt7óýÈD`M©pvÄ=óÇïø&¨#„‰Ó¬oPæo {ƒÑQ(›¤íI~U¥à*ás --”¯ªå~ùî­èœ…^åŠ;ÔÛk©ÃÐÚÏBŸ¹Bg/Gš-ªk× KDvÝÝJöοÙ\þ´üÿ§>‚bÓ‹¤íðLwñ¼Qð¸ÝœUC’ŠÂ!7µ—† 0F*ΤU¦«¼Ó1õ‡Óf‹Ä›‡0é«Ý’h 6ÀÊÀæä‡ÇgD™ä¡eÊr vEÛ -ÛÙ®j);,"ìw¿ÅIп: GÎYÏܺ8»—’RòA:™)5×Wß³÷3%¾ö‹o'!…K°cd¨]%Þˆ‚j :Ê9Ϳ㾥žH ±MãÙµê5뢤}–¯ÒÆG¼çN - -¢©"É\£x3[3nŠî‹£ä7ô‡1ÌA~Z=UM¶`m1䣎74B„S¶dSß|k”¡SL³Sп¶Ñ×>1Òê«Õwe¢ç¿ŒíÑÐAÊ]|™æÈ ÖN½ÕŽÔ?[M ƒbIM'¥Å³¥ÑàÐs›~rHÓÚ¼V?6p)šN* Þʤ¨pª_¾‹Ñ=Ò…ìù$»W@)}€c)BœÚX3$T\{ö;ßWjI‚e Ðj¤Lï4I¯TDYKÚ¨@Sœ•í¼‚òB¨{ì¸AÌ³ÎĽµ2ñiTî¸ßÊydòœIº„gëzfÕgHŒZ,Qf ÖM@[$êÀKq"bÚ‚*±VbÊùd­VSpå&*]ÅÝ…Šr¤énC‡yÕ•+w.>À†P`œ8Ðtz¡¦Ž‘þˬ2ÔiâZä"É 4/!©žNôW®ˆý¸ãß5#lÄjMwî -=¸+"p´ˆÆÑ£i3,QºY# AÄvèÂC¡ôKŒ…gœ9ìõ³ÑçŒQ÷/R³­8µ[:gxé-jì¿Ùp¾¥+=ŸeétßæxÐqŸîû·›0½_ázy•ÒcwkWyÅ_¯i‰žî$•²ßº•g‰Y„£ëövï¯Çr7,Ua7®oxÉ”ÅpJÆñ]z -pQìúõ¤Ç£BVÚ~9É߉E¹7 j©dœ´˜pÍ¥Uç1¯N$ôG€Î˜æœ+~G³Éñi?¡{š\( ßq‡ÑÀ/§±=Èî¼l·v×nCÉtÀ®€«¹È=, g¸é £U'•wöu¸R’ˆ`†x—»¥FÁ'³ÍQÖ8÷çìíôCš„%ÔUéq<'è½Ü =ý÷D]ü·ËÑ {ól¤œ!ªP­šASÝ%TPÒÊ . ¯¾`°'uÓ=¼Ä,ÛmÓ]]–|áy)·5L,ð“Á‘úœÁfu»§++;ªP÷Šð­?Ú´p`/‘¹²ç7}E.[âÖv©=Ü‘é]aî5á%¸™`gEn­ËÝ!´Ã8áG'¯{hž—¸q‰ù¡¼ËžÐÏr!2„J±^ã¸=R¼²¬ ‚‚36`£(ê_~¿Jþˆtü˨#Ùù6-n«»ëøewu÷QœÜÙsvɹդch²ì-¯í–@º¥¡sV߀yl‹Ïõ3²~ÜYÏÊ&ÿ­õ_ÛóûåùÚ˜±ú&S„‘#@]Ð:ÆïR~dBîd8]¹¬–FÕ»ëŽè‘éN¢÷ÊU~žìxÄWMÃ\×™ÌáˆV»¼Æ/½¿>Ùîrþ–ó.gîã§’‹z@8¿Ø}“×ÄùýRö@°žWå)¥Â|˜=TBÜ•(fÃad‹ë/σ\W܆ل>fxjõî"o¨$ $Jÿa‹ßVJ~r¨H›â-ºÆÌIÝ‘;_j×ÈéIñů :ャ®3½|V n }F»úCSCóñÞK¤ìy9òž®²¥Š"& Þ9ýçfçói½Î׺”Ï— Ë- .äÍ—³S¼_'ûkûæ¡ žu(äåÑ.¯ÛÅ+b íG´`„CgôB×9ÂY“ë.…kÇñ“caœ#dnU곬³A$ß3*9< «{>­sxÓ¯¢s=ò‚<Òn$`K(â(z ónݹë=ÝE§éPBÛÕR–/¤“‘3—™£sš¾VN×qf -»œÔyc«º,…Y\M»Wì K嬧ӨÿŒJí§dú€ÁW—]~[ËW²Îœ²×åîáêÎŽN÷\æ7$ºÄ”‹šùÊQ6×èʳôFéq*Bñ ÐKk}“MïkІ<³Pq<šº “å=ôüžESB§_ÅŸK‹ù(æNã»h Fde†GÕhKªO4KJ¹R#h -&µ~hÁëV ó¶áík¡!³»í"Q( ù!ª9NÙ}´PWï1R}nçó>*–ëȺ^dc‘Ozè”y©þŒ$J¤Öà’í’ýû¬ý•Þ¢P›4y=£Ö0%«¨¬@~}_/É´i[?–")ÑôqÄëV(ÈVU\¾Kxª~\3åÜ%[GQ€û(åȦìÝ\TJ‡³­4ZP-×áWZßí7Õ?Á—¸ò¿øû¤°äÍhïSlêó‰bmÚ. ˜>Ò :“xìØEà“1Å.€x~ú»%Uð»%ϼzóÜ›'aÏó -M>OQ‚Д'·ð>ÛåÝÙQ»²gd©Yút«ëìñX¡|ç'n|˜\v½"Œ×~g‡b, -¤ÓÛg…lö=Uí5YÑÒOcörÕJG•™ªÌƒöT4b˜ÌÒ0PRZ™Ä(7¿V1Mgzîç,ÝÖU”ü8îøŸñ%°°ÁȱhoUH¦âgÏÀ™.ilgVèùjb)û}‹tÆvÀ³Ã Áz—, âŒE£k–é#y(bËÈo7’?$²üSóÜ¿‡3ÿAEkºR(-È-ËÖó³P1bmż)Óì+Z‚Î7_õIfÞY‡tÊ>?bs-µut£´žw#+öçÔüW÷09w¿Vû£õª@š??§ù„,´³ÇWZò(“Ì6ö''ZAÕéþqaç©3ü.+—ÐÒ.e~€ÊGj{à;¸¯â¾Z(à76V²½À’éðxGz£µ×{EH–\¤vÿ¢¨QEÜúÄÊØ¥*ÓX9ÖÛ§í+*á²4QZ!¤íлJt!!¥óšÛ¸{È%çr§lÄU#N>ŒÅåϼì…åÅ®Ààq}ÑX"Ê7´½­d•¸_z³"¦£4Brh©tšh·²׬ Ù¾A”•ÆLþE·›(ùRã›§N5Ê3„3F¬ }ñ®$¦çNé¿Ý¢v[Þ£\qA5.>-0Úð–ϵÿ¢¤QIZœS†k ?âÉäS )½øKG4Œs¤ý é2æºì.ÙH™Í«•~]r/‹a§â') ZÑ:TPHÓWr1k4nGÝ=Î-gÉúu²ÊK‹’uGœ^H²‚V®’žŽ´C$d[ļÂI0¯HRGuA3–Õ«#Ÿ §®XÑ—­‚ÚïÖë7Ì=K]Ñ͵Æ¿ºÙä |õÕ,<Žó%:XBߪlrµ“ 6WNµ§Îüów -L¶Ô5”ënå’y¡ŸW¸bM6´»¤RPmE–Ú ñ'ò‰'Ö¥˜S„׈FN•€ñö*2¸MÁgI̳‚Ýö›ºÄŽt ŸÉiPó'NšG >­ªñ¬#‚*–¥r Ž9šð ¶šÀåè«¡1µ[ðÓNDŠ®RU6JDOŽ– @JN¦Eg´Šž­cèܵ5‘ÝÇk= 6œêŠ$QYQ‚9¶¡Nèð¢è´¡žPz…ÅËÀrFƒ·¸*C™oòˆ4šl£{•~˜¹;C*ÿ1tªüMÔn‰ˆ»œS4,ý¼¦Œc•ù‰!õäýIø1 ¶×£vL[Ëa(ãM‘ŽyGYK5*¦>¿¹Ê5v7~隬Ò_ÐéBñ?5”2(ˆ½ùd;«â1ÃM£æÎñ@9KˆŠoyŸ2=›MaŸVËtµš)Àœ´á™ÐîPFúG ×ç­,º¶ÞaªA w+­UK‚S\ï¶ÐÌê -Š‹ŽªòØPŸï±¡ø{ó¢^êþ.•Á¡Nê9Ñq¿Cá,à“ÚQ(û_ óû߬u~3VÇïöƒo¸S¾ëÓÏbÃc§ô—Ý©sⵄ@OõP¥NØDÖOWÔª¿Á&yMéÚE—åWbšŸ¿CŒRÿ*dqVŸ1B°–— KS.¶Î[&!dÏcŽûp¤rr¤ª·Ï´éíE< ?s”Ÿy´]í¼P®FHV¡*Átç÷ÅüCTÁ‰«€N×´á…3¬êc`.H=$zÝKzG],Q ZB)¯#Jáhûd!¬½W Ê‹TÜþ@ÚÆûeZ 0t‰2 Wò:^2]tÚiæ5w¤WÃàï÷ë[Ù¢ÿôE %Àyª]ÙÝ;¢•i¿r?"?jÇ£;ÄúŒW$OºCÉ5«x³Ê@*¿|Ľsä“Tìeè*OéC*Då“Rͽ „@ë&Øàí€("T‘O+Bºsb²u>¹ŒÛ£È×t|37ÈÌB& -7ç¨'§e‘^Ã`@,ý‹È° 1úVàCµt/ãäÓÏhz} É“JI¥“ØS¯Cñã¶Ù²áQJ.f¶š)[æ¡#)¥¢'@Ëf`VW<¿¡¥l4ÞÝØ• :ø{&·yf>D jàÚ;.Ƚh9(=QzW*EËÐŽ)´+®^.…œFXù<‘¯oÑ…å ô3E¤ne@±_x–X#¾Uq>ynþ'°n4ÆÁ»÷«¥þWË œúçsÓÿû¶?X®@»Î/ž ûÆ´‘û DêœîÌ+iPNàóúgv¿ÿ¦¯÷Ëî÷5yF<€´2¨ÿ̈s ¦x§ó,±;Üý{+.X.õ2Ø®ýˆŒšˆ0ƒ=ø·Å'½Á¦tËðç,)ÝìÄ“®ˆÑ‰Î‚•AdÃÀܡӓS6¿×™óRNÔ}‡Ä«_Þ¬4“¿w4ù Jý„üs7—{¿þvŽìö{ §êcþÙ¤oñ£©áCBšüs*•]J´| -ÀsƒWj/ƒ;À³Ô¤ùƒÀÞ^.:¨Ï9ôLºÜ÷?åÞÂþuç%GuM~=g¾¥Ðˆ_&ÓF"ª™?ìô ]¿+ª~#cžf ƒœdøÏXJçãÙÐX.?'->®!j_¢N ¯ê¸R'f`­¼Bʲ>„‚Úšg8C4€9·‚²ô†ˆ-î ê„9**à‹Øù?fs¯i±>™²‹~r¤]Q]ö·êŸs†’7fý3ü³Ê‡¾ED †¶œeÌÓdÎ Ì´e‹_‡B‡½Y÷(—] -¾…I°î„±]±"—J(†ÿæÎËTà(8ŸufW?«Ü믚² ¨¹¶E¯bzïÚ·!´þn>‚Sä[JðÏšEðmÂÌ›¤‰hº·#ßiëÓZ }}¾WªÕˆ¢ð_Ú¥Øgñ4ù÷Jœ4£0ÄüEm¸Ô>ýÓÏÛÂÏVq×ì .+½ššêˆ +|ÁHã¡ã äG)§¹c¦ #œ2(Ú¤ïØ1Ûµ0ôGX°?ޛм-¢¼~u›ŒfôÔ0‡ã3èÌ“Gø›MØ=H‹C¨è(ÇMaæ¿ïï¿~ßpzþú£{´³àY@®Ø1šå3Ц4[nòîן³W‚^V8ýenrÍn»6>YCœgûú¡ž€@À·;LÞ#§ÿVöb‡É ±j—C!΀#«ÝÒ ~Fm^Pi^RdüÒÚ>ÓeÍ5J1Dáç'‡¼œ¡ëÊÛÄý,žŸáK¯7>BêoíEíÀYs×À•¿ïU×XyÅwÖû²U«/5°Œ›w‰dæ [ÊÞWÚIÞ™ìÙBÊršâÐγî¦íÜMa\„8ÌožÑ/¿}”ä¿¿ ~ùõÃþ¦2¿ü)6/¿jåvY¤Èó^ªæûÈ ½|ÅGæ?g ô4aÑÜ›Ë%€ eEôUŸóåæ—6&ågH‘ʹßjŸx$ª†žu¸Fª7 ¼Ò…G5ô¾B’?zÅlÉš§%Bu±lÚ/R¢Ça|árjÛéßêJÁ3YQegÈ2êJ_ý'‡”Äyrî¼ÍfDT†¼èÌEWt24¼H&_Žè5@øqÖ¡–‹Œ·Îà©?›ÖDZr¯Ö]ƒRra¨í<W‰ŠúGÞƒûá!êöàt¸ È2àòäfv¸5 D²WïË‘'ÝÙTiǹP—x‚:é‡î_†2úçúzB}ÓeÆ÷øAÇ"Ël¤êËï^GÝÞQO?;øiAN¥Ê®ÄÿG-³ilm½·g™QåÄ»álM¥lÄ'ò·ëŽ‚œ“‚Ñx\ñ{ ¨!_ í‹×D³Ï’߼˩0Ù*uèvö+ï8”Å·×€] Â^Î%†ÕË Q‰bj†)Êf` ÷ë€}u~„Ù™Võ#ÊLœñõn,Õõ”£FN€Y¬Ù`ª ^ú~¹Ã³üï¸7ã½Fv/wX2;w&"¨µ90âAª*Ï—z*õPf^q¶¯þþå¡}Ë©¬1ìv6ô­î ƒ³LLP;¼sóà-‹\­ÁÈsëç%¨üÏÍe02€}Ú™Ìçg‡Ä)¶ ZwŠ=mø ]¯ƒˆo½›¬ GÊzé•q¥‰ÜJ¦¤[¹¼"üuÅ0cE Ðý”ãâ÷ ƒò”ˆË¹Ë1¤(–³=/i¥zD=¸¯²s7¿œ¶5ÅÏNÆÀ‘ÆQõüÈ5¿(.©òÝ-¤NßD*K_5¯ˆ=C ±çQÏ]ˆ®êi·‹¿-àûåÎW”LC†hE3 ¼Ã_ÈzYÈUS-ѦÏlûue¹9Pw¥ûÏ0IΨ~~ùî7#Ì5ÿöí±ëï;ëóŠ_MŒoI@â³Öyæû_µ`”÷\‘t£zÌ#ð·«ô±åŠ£øSÖK/‰yšÊš©þ,îˆCâ`‹üÃÖÉs(ô»œÉió¾p•ûµIEûv†·—M `J3àÃÔ‰3×…½­"¨·søIô@Œ‘ ¢ú+5ÿ9 HË«BúüBqÍuY[¹lZeìÂ3´`zõNžöõ1Ü"­aôºEù>u4äÔ·R9÷5gø;¯ý¤cq>8r—tˆµƒhtÔ Iù¥t;ö¨1‚GÕY×éÁó»‡÷-O¿v\sW®Qm6”=m¹çÒ™@¿hãísAžÕÇ;ÿ¤yMoFMáùŠu†Òœ`Ü‚.Ðä Æ<•9ÒwÐ3òœéœqxê:  )F®;än#VA g.ê1™De€ßvù‘¶Xà¢n§È!à„ =«¿èÂVø,(ÿÀY'PÙ‘/a€¬÷LëçÇüjzîì¾X8<;뙹õ]ѹC£غh”çØ%óý«›ùM‰ç#ð¿Í;Ëxtõ3®Îiu ˜Ÿ¾Š¯¦·ôϪÑèÀs‡’%Î÷ç;ÃÁ ÊHh€èËe^G1°Ž¨ËŒ’•S®FL‹[`ã]NÂ]<±À»à „ƒV¸F> ÏÇqGèÔÞª¯¹BG_iç½ã˜÷¥ú¼7A/ˆ{®ÔYkïRŽ€‡sDÒ9ìÒûIM‡òd¨Úš7ú%£+ /FN}zÁÐ(™ìoæˆÆ-WrÅ ç]>„Dq÷<‚À ¼qw^4± ¤#ÇYd­ˆ·cMb¼cyowú¼{àßr%ø…F}¡óLgǧʀN§WnÜYO@;îÜ1$ÿSú¤ûÀûMúçŒùM0³>äV– ß9Ýù›+èÏRýsEÿú§ípFâ‹»Ü%‘s•ÎÄ]=£\ˆ;cX‘3µš»`GŒw8r»®Û²bkö?“N㘔×eÅ7§€æ€ÔÊ»X·0jw¡óûV} ÅOFÎyäK…»?íS„›g”¸WäR¤:wµgz¿Ž,»Ž^·õ–‹ä`öÿþÁ|Ën$_È]ãýøO¡>IæÊs âø×·I•,næþ“ÂßAŸQ*T)¥;vÐhÑAy¾NÞ‘–Î4ªÀnD¦óǼŸÛƒˆV20ó§´PÍø€°u´Þë2K”ž÷ólÒaÐ!óH†ÂG·=zíºRiûŒ±~~ÊàèÓRÀ ÜÍú" ÓZ•'ê˜S®¼ó¡7þ5Ôóf]2„ªð{,D8¨+CC™†ü© íIRq7a°"Ë3e¿’ãÒ>újs½—Xppâ‰í§Î༡HîÓ!µJóôîÏKcÅÇS‚ìëôµËƒ;ÏOH‡VÛŸ7}‰Ûӿȳ™³†ŽáHò“öJ°åÊ9ëÝjz¼O¹L HêÖÐ;«vò@ ׬ ùË;õ+Cí½nZ]f̪WaÐÿvh~^ØßŸuÿËÿ×Êø†s¾Â]7k¶Òìðîøõ7E„Õ¢D¹½kš=™ÕŸtÞÙ.5„¹9ýœ1ù=¶)@wØÌv# „5£41ÊóŒ±Xïb«N¤ì5®q—ü Ü©¶ÞëTÇ*JÔ÷í,Ÿmž>+KÜQW8â•ØØTÕ¼¥ƒë«šžÒt÷%Òq¬×{Iáü)còŒC‡ɼþ>p=¾F"”ÞRxʯƒ×ÈlŸåíêˆç&#é†3–“óõñ›»ÇðiêŒÍŒ_2yž÷mÔóÕm˜§i'ÂYð9SJšãÚN#’ccK¢ia•?K]±Fª1ÿjçÀáå6:ªîÅ= ÙWÍÎÑ“¢·Ï8B’ÿÈH(Š\Ä…!o×h¯¯û{ -3rºçž~^‹jÊ—º®ß5V—]õw›TqÀ¨Õ‹Ö‘÷…+n£½þÊW=hV#"ÇYÜKä à1’Çéåsn¤‹Ìw!©[n-õJfÓY%¥¸ -Õ)w¡… \zL¾T‚pqÑt­jáVÛ½çµayÚEwñ3D‹žb•š¢õØ»8pz2tuhú¶‘hv ¯ ÜÃzE)žÿyÒCsÂÈ•ø}iÇöþÂ'÷Ò5ÒÛð&ãU/Ïßbá¦Dëê/â·SönµÈÉñ=ðäÓÇœï -#zÆÀÚ¡£Ú§ëm>ëUR$zŒç hñ»'ý-)¯Œ°TŠâäÇÏåÅÆ1‰6b»Í/7÷{Uoþ”PÆå&øK„•"OdŽá„0 ´Vg¦çÏö´W¨Øª^‚ʇÝùEzxÑŒfMw^c YʨÏÙa$Èãq ÉõÒÛq§bâ»(ɉiHlH*t—ëžÞý¾ñÙC>MŽ=ºÆhžºËôKë4]Ö^œÉʦZž‘r3â.š<=wA«*#ÌÁQ=–»Ø]¿~Å®ÝYgZ¹u(sµ{V†åðõóù–óvèÆîGq¦8mÏ’ó”¦0¥)$Ôd~ô&Ñ#%KžÉü“b´y“ ž)üœ!÷Oi‰k+à«Ô*Úªå=÷À™#åœ"°®äΠÞ!Üv”6°¾ªåfûºùVKR"%½BBd¸¤–«7ý廌5†`F®«‘¹k$DnƦj¹S‰@¿ÓV;[ÿŸæˆÌ2oÀµ^¡ê Ø?YËWEIMèÕ½)vïU’Ž¢¦cjU0¶K ùô”àºF%Æ‘Y#—Þ³_=µ«327òMÉ5¿ù*`›Qs"Í}0QE‰N-Ks“ð°Éùsx6Wªh¨)tÅŸÊ)MÕ_£Êc¨ÖÌŽQ©+¢|á+¤âèhøw+-Î ƒô\3ÃÇjÕDR=Êšo 28¾"šZ*õ÷\´ê¢;o¥ûù»ÖT•3´b}8Œ~­g¾Ì¢šÍ@Túzó_¾Ë?hG¡‘:h9p3j¯ijÀé¹5hsÌúºF¢º‘õ¼F®3ì?º™e­‹zÙÛ¹G+¡µ}‰†¤¹™5ÐK ì*7¸‘ -UÓûª·Èd<ð׫›±ß§f1¾ÕRËl¸Ï(hó»éñM)5WÚ~ø9‹Rs¥ÖÂÜÓeûÅÁ+R‘ŠÊÛßãòö§5Kp–'õŒ‘'¿t>¨é ¾ëÎ߇ŸayþËw¿Ah׿G®¹õõŒú–Ð3È~ý*ö”ÙÖ3]A=0sz•ã)!îÑ>·þ„³éÚé_É‹Ø:ßÁ¤£øÌ’ -Ê—}Ó~,ÒÈf¾Ô©ÚÅØ"ßPÊuƒÙÅœ¾y&#êlž7ôö)ì‘ÐEncè±§{Œ2 Ì_q!ì;ÕïFÁe×Cm‰E:Å©#'mÿ2ˆ[…|1ªñÈ,éà®$[îŽ]²Þ>ʵtéš·ø+‰ åÁ„#†ª%£:š -±›Æv Fló 3„ÙÏYz̧FkÓêÃßE°ü,]]rÁ90:¿n’b®™D+tß’.òxg›ïL­î Ý./ê)¦‹^ÊŽÞ*ïzüc`MY g ¸ŽÍ4=Î`8’Á–ö‚¨e¿J/ûâ'³qP±˜³bD¯ßô{_”ôðw½Ø sQ…z£vÓ¹ ñA:€ÝbÓ37…%öøÕý¢}€ê xµB9ù„Ø!©è»Ë£ˆýªŸ0Êþ®f±‰kãT>bº©p»J¨¯ø6>YL©Îxõ¡l¯EßUXĦ‚è³J±^)mÿ¥Áºr¢›fç·§´z.l@ÆIˆƒl½n’ܰË*Ç‚IÆ·¼Áòë‹ÑSkºÍÂóåŽdÎ7Ük¶3¨/#E/£p5qAGyn´pFí -7)ã/å /\g༎”sR6öÜžUw8®¨C6<ÂÌ+ÄÎä2ºò,2?_‰n|xOKŽŽlr²g¤„Á[Š ´Ä²ðnÙlä7SÝà4'ˆ‘Eâ\L†àæ9¦àžAÀóîÛ*†ÓJBú6cÈ'zï·¦0œn7Z‰–Èýnÿ¾ˆ5U¸f„»Ö‰$•øy ¨Á{žRGøáaà2M¨ÒÔÞ¾ý‘­Ýiªï4éÈ/„@KŠNZ(8#).#P}JgDÎÃ-=:¥<ÁâÁå1ÚøÝ’‰<Á"ˆpEMˆÙR=Ô³¶Y…bF>Ψ6Þ%d²“KàÇæF÷‹çmæ0i·AÇü=Ó3Bù%c ð.¦«"0Åfƒ¥#%е¤à -¬VM["]Rw®MÉ Òa¸{ù¬%ÚbW°^P'€Ç{í°<ü.1³}^Eîä02Îèh˜o÷xÛƒ¯¿"|~Íû”"[“¾Éýj%r@$e¯Yg>¯’r ž(ÜôXµaÊßÙa‡ ¾šeåâΤö¼7nß彃Wù‘mz¶h!s "2¦Å‹˜ýN¯±ä°¿pEÔV̉Ö8Ù† Y¿ÞøÿJ(sþË8.Ç…~£P†ÂÒæÍ]º›È&@p§6šy$!„»L05H[ÒÒ‹ƒî Áì…ܾÎåiØ;„3÷,Ôƒ¥x¯»F ëš2¤²1ªÜZÀ°ÕÌpº[oý•è_Ý#ý%úÚZú£*Wx<Ç&õ$¤S•LĈ#žÂÅQïùЙ8HÀÀ©¿Á:gÚ|½9ØÒ5ÍœïଙâÞN­nFÑvIâÕeÃ`Ù0I_K+ƽ:œ'–´+Fèš’)9«Ç„¬xL}zdµù‹ˆr+ˆ¤þ]Öa•Zlô¡ªÑÉø;˜4¸Çwdæe¹ýå;½¬E ôÏšZ‘[)du•ÝÛ´ëv̉›˜.RˆÖÞ#†cW×qˆkh ϶W¼'Ž+/=Û㽊°üJ«0Q;T ¤ks°&æŒÝ‘V©¬Vš -ŒÔÒL^Ÿc²j0Š?d§ôÒ8ù8õ§¨÷êºf?ß–2ëG·üŸ<¤£ô>8Šýu½$n·û'ú]ðá'\©{1ÀJ³‹;¯¿+²X4ªê&Ë-žKççª3² 0­nÀbp‚c ª¼[C?|p#¬ˆå¿b_[Ù¢Ý.½hžI8<Ä.½žH"9°óÇëöH1\ë¥eËœî,æôT³êI“é½¢?}g‘,÷ø»Ønáôšü>!Xtêç¸B£Ó_Oîµögê¾ ÂGGtû) -Ù#G=jØûŠÛ¼ ÌÞ9æ~É®•8¢Bš¨Î= oDÛexœ‘a¸;cGË{¶d ê§B}µçzž',‚uGœs|Xwt Z(âØy\T˜ämX*ðk~PŸŠTóN«ù› -Ó«²“)—ïó„ ¤N¥õz³³þ…É>”!1Š!*PþåkÂáÓ_÷ð¶æ~ °°Î|½N ùÝõýDµåå -…à`½›O’äÐá -ÌÅk(C¯J罨Yÿ´ZrûU.'Šê·-_B×@ùtvÕ=×ÿËÞ»$MnciÚ+д—‚ã˜æøß@L¥aï¿ýyÞC)¢3«þ6Eš•e[ *r~N'q9—÷R9&SUJû£ZÁ@Jð¤"òØùÍVõxÒNx*b<¯èCû䨈äþY·>_ï»Ù“g©ÓÉÎ+ êá[Šï.ÀI¥˜vžUÔÏš.˾IkÅ`¨Ž_qùÖ~ÿô}°A^V«ÈaQiݨÈìøLáÞ=‚}n·ç<dVcñYP”ëŽæÙ1RB9å¸S‰ü– 1ð]3CÞÙan»"#ð2ä°“ ôöùШŸJW»GgåE{P åˆC;ÛuÎéwflÍÑæïãCœëã]ÉÛµü³B#Ê\üÈŽK´öÕð£kø×ðˆî2YeH¬;'íÒ-™Up”n™Sˆ«rƒ3¹ùaÏûÇ?Ùº1ƒ# -Gëô!‡Ï˜& Q´wö@÷l\ËÌ?:U»ôÖÙ¶)…(QN2¨A„^×¶Óóù–é5ÎÿΖ}ªO‹Á×{ïàDᔉ• zýøØ0EÚ÷Ò×è%8ÁȶÿpDøI»4ü¼²"Ŧ»k3{/Ußn8jpÈ[\eŸì1¨Ã,ô…K˜‰c?Ú v¿´o§ü¢ºè -`(ö„ä…;CðÒóŽ Ü¹ÔêüË9[ïô(OýüZÔ¥s³]-ZâܸïîîU¯yé{Šýáñ¥¨=÷{)ÀàjYôïlDêäÙc­ûj»Ð€ý«cs¸Bh"þÖT1ÜbC(òŸÓŸÐÎøÉq×18?u?i{Ÿ1í‰a*?~åKßAƒÁ‡d>sCf²Ï÷æÄ]Άòåh2Ïó=<%}ƒÆ·GÄyËQ"94±8â8bÅøje¢Â|Ñž½úËø)a'ñ¡8Ö™ãë&ÀØ;Ú-ý3Y¦Ù‘Ї1Þ-µÑ1rÉß]ÌVú_6 Õ‘ =\…¿¤Î`ß³;[Àå aì<4˜£¹2r5µ£¹âL(ÂëNå,3–ëÎè&¸Í!¿_Ÿÿ,4|V¾7A>ÅÃô¼ÏhØ5ïÌzTú¯,CwgÎ<~ ôæ÷ýš]Ú{lÀãŠÌ¾G01¾Xž¸z©ØÚíå¿ïxèìîQP^Eþ+FäH¹ßŽ=àÎ)›”º“.ð€u¹P+aMìÒíGÐ^fªákHë$}õŒæ©ÆX‹6¥Š€ûaVö)à‹Ï×NÅŸ×~ípùÌŒ\B^{¶êL‹¤ÊÔhšm¢ÝÛè8ò~¥#G'‘EÉähβØà÷®PçÔ<„Ïožõ>OEtºOn¬<„ûª§-­Ó'GõÇäöõØG¶õÇ@”·z§Éoޭ𠜋<Ú•NŠ“s~ýßáî{vŠ5޶³é ÑÄêB' yP¼µÉÁ̽P¼)!ºžÅÔÃ-ð+x°Â]p?:u¥žgPš«`s„–xÎj ω×P³W÷ö ,,‡ŸQ¹+±3kš|¶Üùt½"­Ê€Œ%¨ü°¢ÿÉVÆ…*…ãb×· ÊÄ= ‰ˆ tÒ™ë‘-:s/³­ù_î0€=ôA÷½Ó•ÁÎäö’¦䨍þt« œR‚ -qÅýÁ•JÊÅ:z•˜—ð·¡LújXd©s.xD]Féc¤ô³ìfG -4½RZëaFƒj4á ßPs‘—FF\HÀ6ñ鑾8]‘s2CìDC £¾SPþÊ]bÛ…~_„ÙJóÈãf@×N]q L2Ã9„ä¶z äï_ô÷HËñ?Wà’’›8¯Rgç3àg).1 Ør^ID¾x‘‡;¡»³¥O!Ž<ÚÄs3 %ÎW/Ræh–Å©_ ›jqGLp!ìÏúÕß`¶Íßa9@f–Ñâs¹—f~ãÐû½3‚^Eþ}‹rJÛÎ!fÇù"!4K’9 ƒh -*šL|ÿŠžKd®ö,1ÙÛ$w«ûtS%žH v›Rã(ãe.ág‘ã^¼—Ñãä î][ô \âÚzU¬jåÐ; úÝ!ù-½~&2Òïÿ¿›ø {ÂN²tòð­úÝŠü; @Mùaûù'ÛaþRwO²z‰¹38lZ!ÈawÍI–OH'Ï™àYxó•ÐNp580šßˆ6ðRËC飉Çfó7­Šx¯Ë -µ}–äxfÝ%ÐãÚ,êîBúôÛÖ{ù[þ†Ê]OæùŸúž^R(aè0dÇèE’LšÂ‰B*à‡.¢éo¥6¢Ù’5ú-Õ -4ã®jÓ(CÛì{雷.ý•–ÕQ6ÍE»z5Šœ: Ûx¤ÏD‘)YËV ¨"eˆHóHY‘ô>ÐK¢"býØ%úCOßûèßtcþ@ñ߃; moûfyD$¯w•šò÷Ëèæ¥HðQŸ3(}1á¢MçÇÆC©aæS–̹tZíâÑ$|uÍÖ(µPÑåÀ{!˜|«vW!Ÿ+±1aU"òêâÕSQ@â7Ü-Ì/>åCJ`Gpû)Ýr¾?ï œðV£Ec­Òðž"üÞcgl9é!J¾GØAàGI¸Þ#ïs\ä–•×åϲH#0p^&® õbgDzÆ”Y×ÝIû…™äûn¢„ýëS{ôo«tÙè÷<ÄÝžgÝç9rG$f˜ ^Åè=T2ž† ˆ¼'M˜&m|=øU@+,Qº´>Ì;Î6§Ô nîOß#‡g™š©¼¾{'¡áÀ˜—w•Ã,%Ô+Žþ† Ù‘†‘¼·š”²÷<¸+ÓAø½G®ØÛ›×§Bûþª²ßx?ËC¯å½ÒÂ={ |¹®±‚Å2~íßdtï]ý:ÙGc#æ-©ŒlϬ%},1x:{¡w ¤p”¢\—ÿvÜ¡KõLéï‚!…*¥Ü~ߥŠÇÑ|׳ŒÍùÉÌgBÓ§¿/ÅŠ¨·dq¹$ŠÀÞ™%HÒÛ!ì³Ù©Àš.&Gæb^89µô+]Sþ’º÷Ü·¢?àbÏÓs?ÞæNÀՒϳҹq™dÛ•Îhñ^чÜ)ÀæEæ=W=É•3™*–etžœ@‰-†á®þ)þ.{üW ­ j?päô}ˤ|é=k'c(A«ö´ëÓ¥>‹câˆ[ˆŒyî’‚Ç™ŽL~Šè3¤Å®TÉÙüg¦W×ì˜JûÈÀ]³~$QFš·}§aâUg)Üé½g„èåÕÏŒ˜éѹh³ŽÙû?«õ{ÆÜxýyG='OhÁUÏéÀ?çìñ`¬çdÞØ æx -c½¼Ì™™·òy›´4WK‚·êÛ× Q본«Ó\ì|®³}Ö[õùêg/ÂëÑ œ2úñ¬Ã£%–½X§áµSM{æªy¥ô}}ö´#x4ô–R‹©Q­Æ,qÕe+†8G Å)kHÁ~Sž‘@€û~¬gA¹9ÒÝt=+ÓqF×Ò›g2þxfªZ‚ Eô™ÝÃ'i‚xÖ;Y¨À #/±cG±ÿÓõdéî(uR§ø 9$?ÎmõtNñ-br+íoÑÞ T†4Ñx„0ßc³Zªýê€þ¢&.L$~Á8Žã~ö]MÊÈI`³R©LÙ®Œ£y>É+©…(ê`î0ó2°&A›Yšw‘óÎð%è•ÊPß9ņ²s œ™'¨äÏ´çÍ!Ü7ZÎ5gH—ûúÐÜä²ú¬èÖãh™^¥!½,ü€"ÒKsÖBéÙ±û‘‚ìG<çè-Ü>£`,`Žíë-J¾×ÙÍ’‰h¼ó±'ÙW²#o@JhÅÕÚ'Žò|Èð™1ä%žI 1ZìÐW®£ñ$½•bàªóÈ*f×­£&ë¦4MŸ»L‚|¡é²r Øv󼇈œJ«ygnÿbßu©°‹¦ùÀ´‚óêyÊ/)ÐŒ\ï§< p_9áQAÓá‚x“ÖôÆàð%8Ì! - Oˆ;jSâõöê¯Â½J¶§à2ü†ÒJ)ƒ¡õüRV$½ïaÃÛ9ŒÜïtÓ^ð°qßhY÷`EÝâ 3r÷õ3´Ÿ£UGVK$Í€Ú+}–G‹T4CrµÞÿ# xîìYüÔW¿ó@|[/È4;òhtºÍntæxÁr6Ë_ÿY6•Sš*„bzn4+òl˜h-K(þ-•„9j•hV¢…0ÏgÞ~ý1Yû‰~™/‘Ø3õÄw*xGØîEînÐÎÒhy‘Æ*ÍG#6¦ƒ¯^LHª’]Óx¼'U+Ü[ÇÍe‚D´YR|O¼‘:Z2)§Àuj£FEÏÂ}~ýîº%G»1 -Ï(zÀ6WVür²9ÈìbÁs]§¨½­æv³8a­ŸùÆ;Žl½÷]¿Ò0»*ów"˜è$nqvË ¤nÿ(»»\I¼‹Q1¼ú‚“~¹eà40ØXŸÃ¦N~cÙ5²]ôdöaTýÙ^;ÌhÏ•¤œ±T,Õï”w^…0°cÊÔÍž)RÌÎ&¢ À$ó¬€h¹}ïû§hß]íÄÒM"¢`Ä£ÿø¥àÓj9G‚yëP÷˜´©8Æ‹“3‡(Ö‰ Kuñ„ix4.Tr@Tœý3'Ò™¢•ïø -Ož÷µ²ï”hÇdùî2‰wDnÝ~èJq,>eçEPÐå¶ öÒ^z!SŸ±xĹ©¥òr`§Qæ<.…)Ÿ5æ.¯È§!ëð<2²Û'Îzÿ–ž1+Ç•ž™×y=#—î—f'¤çŒ­ÀjõŠüæ{<å§cÇÃÀ‘væ:¶Ü[Z¯5Gï<’^½2ް/ñ}¡ZdèrèØÄÉéeFó:êA±Ÿ¼Õ“ÉÕO†˜ö¿ç6uLۛ➎©õpãùÞUZk*¥Þ2aϳ&ÏØ§ï)QtùF/Fä×,pjïU¥è²™û;Sëi…Òfö¬ª=´bf°‚_'²yÄÁ×~JÈümjx´ßÏo³ÇóQ š][ß­LΆyqà+—ÒýëÙê/ŠJ«èÓYb¥qÿqÇÑ3jùnÛ€«8¿®g«/—UßÑ0¾6’¾²7x¨%›µ÷™ùÏÖoEŠ]æýRÝe¾m†-mÜ«Ùqm±_é²çžnŞ׳«mMJš]´¨p›À½õÜ}Açýìj›£}«Ž0jW»‹ôŒÁ°¿‘W7ž{ÑHѥ܄I7²r`+›qÑöŒL—×Õ)Ä´¸œœL#ŠwÖSOñv—Ñ›¯®ð˜ûSZ¢ü#M ð ¾^ÛA*L\ë[~õ~E»zN»¦×Wí¬žyy¨æ[ûŒÈœ#C‘ÂÌço¯Å“9ØCĪËôdóµäÈÂ@PýÓ{XäÄÞI¿¼}WLFÀðä!ïégpŸ¢c‚jÄô -]X­ ‘ÎÒF’mDôÖK¸fj³;‡k©!kÖeÏl×öL6È¢wÖ²ó=Ü]5óETK!ï&šh‰NlÁÕ›Â_ʆÅ5äÕ[ÓŒ-ÙCƒÇÈ‘U÷Ƙs—‰Ú•Bî ›ª*}…÷é&àÎaÇeï•àÔ-Ô_7Ä•º­ä‘¸Xý ·J¯kFÿ}T I>Д? ‘°=E3Ãö/Fòòb’C‹å)õTz2°,gKÍûêEòaÏkò¯ŠË3eWâ›8¯“ Ô5I~¢9DýN+Ë3éÉ{ñ2“k¦`p¤C¨;{"fpè Ó;'BO>Ía’›>¸—í²¨,°V{°}ù–瓼å¥>Qi½jÙPNGüoe²ßA¢_çs^{á¸|—öUWö‘½5ʇ!Ë™6¹ÙÛƒuEû(ÛeKÓGLîQ烙»1Ù'ùŽ#µ¶iەί{×Ý*£G÷ü)–Ì×÷â‰PK•„+Ffï\sŒ¬qD/Ò‰¼s®¡hëõܤ ”,÷=4SñbŸÜ{/Ä7¤ _ýJþ(è(E”¦HóM”×]UDG,÷iW{Ô…[¬‹’q@ˆG±:PëzœÉO`÷F3ñ=¶>]Å -°Ô/º.€õéÎK¦¢rµâëÓiXÞ\8íbÑ"K}Äž’±éz<í| -¼HA0'ÒßÞf}ܤÌu{'X´=ƒh¬Ö—,ü ÐPvc:SÚúÚÒ;Ôê÷ý¹.‚eG~€#Ò-ÉçŽ]º‹NÑz¤ ²* Þzä6ϨhÝ¡fØJ±òJ}¥JK»€F4Ó4#bUúpÓ—†‹È$‡ÎÊào•Í@£¸]3œTÃR]â !ò`E§ö4v †{ç,~ôDÓ÷®­ØMP´:ÒWeðq°%×;Ö‰ƒmmWwú5–ÍWmjÅ“ÒsØ±Š¸Çú•)Éùì¾£IéewL0xú§- uWøñ@å–èØtxúÓ‡ºhû€mèÂ5RÛ£ -vûœ·¦!×õ´¯f€‰¤&l >ßÞ+}éÙTÁB°W6 ={è;Uð.§2 ŒHd¶dÞ)ÜúÞC«Ú£ìLíÑ/~¨”¸?•AÖó“°ëò³”ÅWëoEʧR_¼ŽR?žáo=Ϩ ß7ÅìO_ÛéòY’ß%hÖó@(gÃüâM¦=ÆVŽÁ. pRñ¥¸?õ^…'ƒoDaò\”1z)ŠSÀ7ÐZ=ç­è]êÕ–¡g>ŸOPø)Ùâì8öò£Œ¥„uPoûÕ;Ñ™Z®€Uv$7í’5Ÿ‹ìs˜’›©Q%Æ ŽcËcAËèÓ5îú®j¹9æn¬CôåÔ^ yQ¶P…¯„£ë:a>’whú{Ó;±É- ·^è:4U¨~Î,¯3Ž|ìØ³eòë£ò -†½/ÊŠ ÕOý»)i¤ÊÛQ¥ïUPf%Ñ=јòçé@=[öÍöœ®]í’Óè˜ä®LCJÇ·MïZNE¯½ÂEèçnèav] ÊY+¼dø)ÙêDô˜¦?ù¶fpÝávÜ»Ý|qmieÖu¢îö®J#ÚA¦P3Þ+–µ‰Š%” s×j×K‘ §”{責nY­öP1ÚTWĉò fŠèu”^‡i®Àç匴Ïå\4š)²´¹Ã'Áûø®B\BtÒŸX§[“NÁQC…¼J‚´{|«ê³ÃÀõ¼^ÙWï׆v‡ðÞåçQÛîUù“BÈælèù94:p -h_aMüüÄ ÙyìpdÛµ ¨(Û¬MÍ ÇSE.L`ähB*&:zGvç§Bnê}ß{çSB/=IFªLïh÷2¸¿U‰ˆ"wåùœåªés¬ÍYÑĸ2 ?ë@[éõzÌŠyÀeU.`ä„ÅY£®»CÍØÅÜ«O­™SÄ2ó¾sÈzdõù|ß$¿¢6öÎ}ú`˜ê þ“½ âèsŸ-”(9û8ŸTÀNÕ‘Ó†È äÞÕBä­]Ûʺc¦ø€qV¼¨¬&´QƒÎÃ#¿Öˆ³}?™*¯h8Ñ£UÇßøÀ]U£'P#iùÔN/O_Dt³U†–ö[¾ï‘QCªôŒî4©+ígߤÜL-ç'œÀÍzÔy_ÏJ%@£4ðç‰XݾžôñÖP^¼Ð4”ñã ³ ¤ T’ÇJpû}?R¯W\í´OÃ÷ -‡x¥ µ -:¢fÃÌjuUÂÑ‚gòåu©ˆöZ¹Êén£òÒË ¾Ë¦d/ó*8£DýUö‹•íØýû·"pÈ32’µ -þ©i…‚ ,Èœ—[î.ŒO›V×N z¼©Â{L˜ÓŽœ 5ñî¥Ì<"%z—Ä Û`û3š/fx'&XÎÙIô‰:תf×¹Ó…ïeVô‚Ób*KQAäLï% -z~U€R×éÓg˜©äQù½« ª& 2ýMB|BZúÊê,´«ZZDàa vÕ#;(ÏÛ‚kÝ:刮hظBsæË£òÞ‘5DQìÖ‚+Õ¼˜È¦°¬"ן—¾(E7 -µ a®êÃÒ*Fi ›‰ð}§”I½ÖÛ$ð»£6(¬ø}-ˆÉf';xép ¤•Õúj9ÀA< -5áw.)(yб¶c…n“X(¢-ÈY’¬-n¥#°ÑÀjÄe(yç øJDCI\ B'M[Ob£ÚSt+4ý¯?VÛ~f™ÖǺÕÙïÈ¿DX.qÑJâ½X|§Lµß 5½R£xç@Låt a5F‡—µè‹ôR(eƒ#¸£=¢„T6ôS´å.l¥šCÄa&µoŽÔÎpr¥M‹àëÈ›n<4s¢%äyÃÍàÉr­Ä·³´ÇÜÓ®‚e˜ -#ý!çþ´g7HTX®o„-d4{Erz¿8¹¢¸SaO·ÑøódŸåݲrÜ´&V2Ö€—ømGÂÂþpÁ+5îÊŸ¿Z¼AgõU¦çÔ:ƵbÏíÈ<ßå앺j˜”'9¤Å1Ÿ×·_yª¾Òm2 |¹WJX13†lÁÞüŒú÷÷ºü½œ=›^g1Q¦O?þ:ºé2åhž¢*¨°x÷Q—€h Ï<#õ²ÙYgâý»èhº$xŠiÒܦ´êþÐÏ:(åÓÍO˜!'õH\ÕÔ#¢&ÐZº†‡š”äà4>=Bv#2a! Ì¹uÁ=ÕoÀGèHî¯=ÌèžÁïU‘݈ÐcÖïzÅ7ùeªÅÓ%‹!ë)pzªfù% ±{8œš-imvfP«ú•½”ÇO¯°_SNÙ"üM<°Òr±1Ÿ]äqΧ'£^ªä…aØ‘¦QÒó’ÂSFÝT†Rißå¢ Ãß8ù¬Ý~GÀ†ˆ€ÇJ݇,”õˆé‹hBk~*ä%©wVn{F…•:ñhŸ -Nš~ʼn¡’nUœ½¥b6aØù …µlLCú™²A}©ùô`Ø»)¬J^—ØÝÈHó¡®Ô²={Ñš ñ b@0i‰RÑ«®;ð9©;HU]²ÓÚ"ŸïµQœ4|› )­Q†æ!–ÞF¾ue¿aÆ”fRh0–€ï†ï9ôön³ºHõÞø¥C"aªý*h/Ïô£¬sÑ“=/“@}€VÍ|˜´H i(xc´;2¯ìù:9ÄA0»ñIÑ¢$ƒS”-’/B°AÝì+jÍã·h=uiè‘]NOB3M p#³1õad{Ô<QDyi³µêl*.$„žd¡£0(«XëR8P8í¯ó žÀ/š¹‰mæ§`VU”\žN§ŒÒîJ*üS2¥wH”Ô–ELYjýdÄô5Ð>ƒ -­ý=tE³ĵŽK¥eö`à­x?:ÞчÝv„ûÎè;¼Z)p©±¡à®}|ÝÕmЦsOá…Ù: ×gsš¡kظîá:0>ðÉ@2v‡Å Qêw[L ŒÒ8C—ú{œ7&†d%w¸#z_¹tC÷ç´fiZi±É(FA(aþ˜kµþ\*ƒÎuÒ¯0NàǹÁ±xD¸ûkn8;ÅÒ1d?Ö¡¬3ÔV]zx’û–¯M—˜ðu Û®˜f:)R¼g‡—•ÕK´…¹ãÓt¯ÞÛþ‡#³c<ùR,ûáßJöW›xôë³m¹"z©8׳÷ ~ìdcÿ Sp„5U J#D¾´Q|ÓC™DØ?ýxˆªMB!›Ÿú”VÖY©œç3±¯"mͶB’¢RÇBäA™ˆ[œ…+UÔ6çcÈÕ -Éóªê 0m£¥Æ·Ú5ÒöhšpTòîá¥éž:Y£{ lruYåÑM]ŒàB/³óC'”~ Ef÷¹ã2bèai÷yªýÓŸãë’®øÙÜèÓB‰?Á~?¹ûoQϬ®ºa \×ó"íª¯œE1KËšìõœ?%‘O)ï9ââÄ~Ù<®£K±–líêC¬¨;À Ð!ü´ÃÆí‘_ë»R¶ŸÁ| qÑ2ìKW¿Y‡^o"6fpv³!”"|yÌB9"¦x¤XûY¼²4B¢»=eÂÊùjãS—TP‰)zdÀ7ή@-_·òœ/ÛÇ…2§¨ÃNY§zvÆÌL»ÈÓH¨ª¯{7žñ±2×$‡$rŠ˜ƒUZ3ÄW*ÅW.SŒ’ Äa§“b*ÝpU¼Æ>¯ôžŸ:Žm Y‚®å±KWRLêp” _ÑZª°K_æZ%äÆÑ: åÜ£Ù¿b*@«™‚b,ÅzÖ,„3ò.‹«dï-æ€ZŽý®"Ò*Ȯ㮉ø½Vßx!µ ΄zÃ{µ$œB»³jÚˆËu -;œÅGšc°~•5[¯‘3BÀ­ÙWØ_»Ú ¯È܇³8Ò#|mE†{Äjw2«!µû(³m²ˆòǶJ¥3²q´*(´/š¬éQ;w˵Ì{1xÕ 1{x¦\|G5ÚԟƒéBĦÇûKÑïQ(› Gôåõfd‡K 0 87½ &¤ƒaicú Úd÷zºúÊ>±¯`þÑ -_ÓWé±ßn£tf_€ * вKÒøëÕ:¢wSdz`›Í‹JHÊ„¸˜àw¯¹?õ&_¤—èUõ¼Õv~ cð‰^ÒüxÔ5ë™ -5.±2tþkçM¦˜k=(¼«úRz\Õ&O„ Zt¦ ¶«T7ÓŠ¨ÈV¸”û„Vú)–)ìw*#÷¢ê7ç'¡J:Íù¾a¬˜Êo:'û(ˆ 8%ëHŠÑùˆ¾q -;ª÷+í_å.Õòïß”‘.±l£¼§Žj(¯ÇÐ.¤Æ*ŠÓ%EÀá5´™ºâ‚ãÁ¶‹BÖ-'&Ú‰ÌuCç^MعãÇÎÚžã ž¥c°ºÓ-f2èÉÃKxä%„< yyì½Â<ÀLmÚè\ŒÈ²ó$ï+^}¿ŸŽ­±"¬ kЦ9œ5Mù–²F‰M·˜]hΦ`>Xº+‡©öíLJÏ/S¤³X€jÇ&Õ\Ù¥œäËãô.mPdz Á%ÎûUó&Œw?·ã ï™n áÅY~?)äRö(J‰† ¬¯Ê@E-¿P±® 7ÅM¶| ÝàŠVÿv;Ú…€Š³o{ôœM_¢D%£î¥âYçª%O{†1ãàî¶îUAµ„ë¨ÐòßK™åÎXÚîþE¹øàÔMŽBŽÙ&cV¾æŽ- r»KE`GÔŽ,Úà‘1³ÇpÁŠ\iV¯O'ƒgböÞŽ2îK™÷|Æ\¤_sßù - Yw¡®¨óãqñ:zÞšòQ%±^`7‡´{~‚&aå­ë›Ìåëí› ›Õ^³ >‡ÃÝZÀŒ293Ju0OÚݸ+S™çB¦zˆØdœÙÁJ)Æ -¶þDŽ¥Ò5ŠÛ«Q”Á)l7hˆ*ûÉ™û±Ýko¨$§)A’nlg¸ðe\ -·À@ïÞÖ9× €*‚@œsÏg8Œžè_Ž€ÓªÎ2 å'P§?BË–xUpîYq¯:±†"~k=Jr¤Ûàüõ¡+4Žé¿››Ä]yÿc ¼ìRªÌHÞEz×AIÖ–EÜQñYØUciÙR€N*yh!¦0Uk× -ÿë.àqmí­J!ªx·øð¾ªY‚X¡\Õž–Ó9IBÒw”ûíügÍ œQ•êéA ÐP”õ -IÙfô-šöžiÂ&µ]E‚•ÆbK]Èo)—7TÈiã<é·ð7 ÌÒŸ§ÆÊï-Ù[ëjê™)ê[tþã'¾ŽÒ ™KçQï²~¹Ã)ªF-‚òýOám·µ£¢=Zòù ÄÔRòÞ—Ê3v“]çq¤Û)*À<}Tâ¿Mˆ‹B¡È¬‰¿–þ–ºÜý?ÃFúà,ûÞFúOŸîù4´ñáñ—w6©ñéó/Ÿž„8?Ç¢œiFµ‹÷|‰Gyì5O]_Û¯4åè¡ß20œâ[a•ÇK¶ê“…Ä9¥TðJWá6cõÜmtKÁ?ž¾`>‘HØìýÜv\»Ák5ØAØz±\¸ëICL)Hþd*øÿVíÿÐUû­$ü½¯Û² Çêy8twú†úhbøýk\pQÁ¼Å‹d`97Ä0w¹mž«¦ŽQ =xNÁ£´mHæô”o5Ù+Ýù4‘V2ðÔR,s¬·3LN -¿GÕ–z´RÛ§K¡Wì¡æ+/õ÷ýç -êC¹Þ6Æšñƒú…§À’´4Ê?»‘ -ñ‡97ý‘+´m>RħF Ù—ÄD±tßjçfõÅ “‘Ò.iž˜Ô—xÉ„ZJò H0À"ö²âöù’+¬ÐAê;¶#Ñû‚}sÌøÅÅÐtÞÒPð#1qg¼ÕçJêò‚Nqa -Â.r‹}¡mç–Êo¿“%×Vú±~o¤GACÖ£W«´]:X÷GÝ ¡ LDA*ÚR¯ì­é.…T‰–g\̃&µÉÿÕ+§“u`¸e¢›‘åfFŒÈ½Áñž|Xð?2Ø—ðck($yz/’EŠW`¨òfJk½hçø}W4`uh9?® -kü¬ -òw=uÉS&…о¨¶ø(Tïnu¥å&’8ç+I+®3ýÜZæÒ©‡‚³¸”Ä¿s)3ìÊ¯Š‘º¿áyè©Ïqïö*0éc(ê¯#Ï­Dy Ô7S#` í".ßhÿPÈyÍ#¤•84S!²¡n-ð#z±Rê|‚Ì,©úÌ,;çÉ«š¡õõ4ûHf®õo’f³tAVr>Ïœ™k>Dz Ÿ›égÖÆýTpχéËál¥BM}îÙR+yDÚ‹U|ƽ¾_Ú‹Tƒ,Y·’7¼Ê×ð“7|Qó#tÖV±8K&á,;v3k2êöFU1;ÔˆûK`©„&;ᑊw015¬½Ÿ4xS¡ã£ÊïdÃ$âV ®O˜¯¡îƒÊ‰®Hµöçþq+ËL/F|Ô¯£´ÿ¨ãÖ¯šš)g×sºúË2êÌb,î²Á«ª§çR™o`(çÕX…NŠ´Í‰²1+u¤^o37š¥Û;ãyÿfãr)\Êø ˜Oâ l2å†eÇør¸,¶‚’Zªÿ~ýe)~CãùÖË’Bôt%~šþ-ÉÎúŸ6íñ“r |@æ#­d÷ M8Ãíó×^ÒÍ0fe‚bLN~n¿õ®4azì¶ÈËD&'‡±ƒýdy8AñPüH3ŸÈZß·Ò\Äxó8§ÀâKCr3†‚÷%'¹ªZ €›ƒ²ö/Õ°mVôÊrð‹^“¤=2f TîÞŠ™˜‚@=î½Da·&çØé`ü-úÜLûo‰t•Q~,ü‚®›PñÞO¿#EÕ„ÅãMq¨oD°ôÁhQ€éwÉ¥µTòÑå¥3Z¦Ê{CÛ9½Fê{aO(K±¿é*-(K†‚¨Ä-ö;ˆ& -1"®^À@Ⱥ£áYn_%dEÀÎAÞ¥–+SNÇ/HQè=Ù‰ÛG{4õˆýzªzÀñô6p0k؆’™ã!½¶*ßQ˜?J,¾E«íIŒñ3ÖØ©«é„õÖ. éŽN› *Æ æF[>V"8º€ºÒÍôxÑ}™1¯J¢N‹·§JË íuëÑÇÑ[::ŠêïwnS»½›}l­‘Bx‡– …:©‘àÒGdKUD1Ï8Vö4ã(îÁÆJÒÛG’«©‘`€ºD1*o©Nhš§·ô:[³·âš>ø§‘>gîµñ*8ƒr(%Š"µ¸«¨Ð\ÚÕŽ¨U@ÌW¾]wz]×õéŒpª3Þù ’¢ÛU¯rgE…½EYÀ —qVõÅËzJ{•Š$FµÈízŽ;„—}U{ߦ*&ªxWjÕÞ—¹YŠ´-wÈzV´‘EL¡–h(/•NÎ]JY|9â$¯»”¤ïêðŽĈFÍ<’ ×Å †S)Þç]̰÷C¦ï~D„ÜŸrû¸ôgbÀ"în…¢M‰Î;„î9[® KàoŽjœXÉ¢Us‰ôY1êEx#ää]xÆí$•¹ü.?%û%wŒ‰ÏßIƒ*_§ -PJÏ -s“4Èyä"uȯ;Ô¬\$5Á˜̘ÛÒH8Ï]5¹ýÚZVdø ´¢f`á¹k¼|(µæáYHP¸}§¥«Áäã_Ë—š¿ WÇEwº-{œÇøÚ·”Ò¥£G=$øEf–ÒFïIx›h´«^…IÁŠêÎ.„x2‡ãjí2 Ñsæp› _ˆRÊÚXf³ ËØ>aÅ{Ä^¢ÀÁ#@¼(2^í³8Àr±fðw<;ióLSsò|°¡<¸¶k÷„”ær{"|o[J+^mV¬Ý‚ñ±òå¨+oýìÐÔAP­DÍZ˜ôºçÓ k%”ÝŸ’„–|[ñß2²×£Þ*T°®ãM®V)¥›FÀû±1AÁìzißd?BPá|øŠÎºf[šNñØÕt»¡ÖD¸UñS -óY:ynõÀVŒÍÐ"(¨Ù‰*…U]—–èÓ×Uâ—3ú÷<÷ó±:™ƒrè.ÿÔ¬èlÕ[ÃgB?ï;üt@ሠóeâ6s»íTÅs;°bz¦U…ó.9©íYÌ$“ÿàçãË›¦–y4R˜¾XÖPÈóÑ î36Nà:E{(vªm’«êÝ£™ža E(äšÄÚsõ`Ï]èöÃ{ê-Îݽ£ fììõŠAúý –0.-nq.VHUˆ8w³*tããwôXG%®1o”„V¢J.7"G“'ÎùFhÈÄÖkä0H´9@©ðÚèÏàiùZ"—£€ñ%‡ØYi¸×£¿ûõ—Q,.T(aDSEõßCÇ Nþ½Düè42FþtùÓù °:¾l"N[× 3صfÓT6%…«<œàGÅF||0òê h±ÊOÓPF:ž]Ùæ¦Z³ù³ <³üͨ©èxLÎróÝËúyééód_Wé<_òa‹[ì>³G0Z™Tð¨HOWF†‰â #…·&…ÁÄ’µ‡çLÙ »½ñ'~,‡2Oì(+š-6„®uÞß¿¾Ë~´ ¼ÒòVïþÜUíÕ¹é}Ú°ú£óÔ³0‡M¿T‡ÞQÅ•ØWÑÕ¤¡R§@z¯4¨ƒMë¦Ó]«=„p\5åj³¥bÞ•mñdy=~_~¡úÝ=¤ß“I‘&"Õ<ê”v¢Z³ ‚šuÖÈu?3zh?•1!Rwq"ct°8†@rIÄèÃÃJ ¨>=;™(!wTpÁ‘µŠBàjd),G¢t/ öx̤z¬à5'•¥$‡;9ÛÿŸÌd ¹5Ôf -üýn;Ûq Ò˜K}žr›–‰Åá𦙔qçû6[4c¨£_¸Hz§¯rß)F„ø- -*h•"Ž‰Ì¸Š÷|ïd³T¡D?„ºk„FÍ8Ô¹t±ýu¶:aɇñü4)aö'jÅšgDŠŠê×X¤š ð—DæRô½WþR‘à°/‹P'có\•%ÞuÝeÖûd#üí¹«Ú}¬çûÂj¼ø5RæÎb‚yv`E‰èµi.Û…TùÈk®r, ^ÕbS çwéÄLìØÂa1'yö* v¸ó Ar#h¶À$¯‡X ÃÖ|G•þ¬m[wa‰§PÛñø­â äË»G07”ºÔDe$MèpÙÉzd8¾H‘“ÏGÙKˆÑùŒLÿÎýpn‹IMŠp$dl^+—µ‡jáÉÛ‰öÃrd®X„’ñØü¾KSŸï—¨ÈÎüs—¥ÎeT¥¿z“IÎï3ÄÞß±’Ç^&¥Âê*¿uoJˆ*Ïi\¹M5ðøÁgzK›ü®<Þôüy-ª×ß#&ô¼Í»ðpä‘_ÅlÖóD–ßÌ,ð‰s]2s%-Wššž|ÚYV×ãX5ëî9ª(£9”LœÑ2~gÖ A­‰K¹‡ZÃIìrŒž -¦¤àß<´DÌ’*“ÐYÉ ¯>4 ˜„vîÚ1„Ãa\hU61ÏTÛy‚çzlEvÔÑÅw^8zšìk« Ýtã„ÓÊV€yÐ-û{!ßHªÝvðð]¯–žRÀ}u=nÕ t|W~î”Ghâ>äë¼à—Ä_}Œts ‰©)pˆ‚)’50Œ¨]´‡šî@ÖÚ–N/‡ÉµÒ!öý<#MTO¥c<(ƳůjoœjDvÀ‚žM_{󘔨ÊÌHâH} ˆfºl ËÞßԋ臲gèyÏ(ºú H5]l!É@1VWÑ–ç›g7’11°OtÎf•H£çƒŸYÊÏ›iÄ—FÄ0‰0Âi Pkr›¡¥šþ”§Ñ{ùŽ_a¡?¨kTk¢™W“}8øo¡Æ¤>3ŽC !yr' ‰Ðh®~{ õÌBŸâ±Åc@ž+´ü–Û;ZôuˆÇ;ñƒœG½âh’vý#ÕÀVCØ÷J¦ìåHþõ{Ú=¤8k&çG7’‹¶Fó,ˆÁªKþãýDíµŠÖÏ|@bãÓ/á7`7G\ýVÖwÛ‘zЄ™ØW¢á• ™R -­êj›Õ@JòÔ£<g‘…ò›ì{7ì㺌Ë_Ô’L +^+ÌTÞ'ˆ[Ó¹£1ø’)ÿ¨ûa?VÇi‹g®ðئ[ç°–ƒÅ‘³ÎœžŠüŒè~ÿà§øpÈŽ`þñ‹ÊRwàÃ{èJnáÆš½ÓJ×ÎXÉ’©T¸½d6&yXšÕG -ýÑRûÎz“CÂyùç¶î‰u=¼}òs'¤Ý3#ÙŽøí(ÌK1 º]1ÏÅËb«çĵ÷ÓdÃGèÉ ˆXŠ>­‹ÿŠôHUˆªíµY˜˜ ¹crž¥\{Íò–C0úAGG) ÊŸŽ*×Up¼…}E©×EžÐ½†£¾ßž% -xU˜´ËR•\ÊR:V¸·Hªü˜ªD éW©\}§Å÷Ù¡&øê4αSÕ9$ŒÂ²æLLŠE·´=yš×Qµ¹¢ƒðÐïëÃ2òßÙæ(Íéà cþÌËS1y~"øaͶÖYw3†FܓָiÄîa•Ï€!±o X¥F7†­g€J£\ Uyb—ÓäLq.ž2‘·!¸ÓèJ§ ï/n¤L‰£ÂÖë¾C'Ь£v­Ö»{‚¸ü¼´Q¹šzõ‚zÙBH ?ö#U*ÁÀ à~xY“ëwŨQWÄô‹¡rÁÆòö#6n£•Ì\Üêÿô”Þ–2+$çÌ©VëM3d³'«â¶à…ýGíi>MHGƒÈqöEÓJX¹ÂÉá)&äjÕ×´ë™áˆ(IÄà÷[ÉTƒ/KÇlí»ìn;wµ¹ªuœŸâÑVüŒ»¸ºU1‡Ëä»tò|E -bZÍŽ•×Q‘IÊàÕFíáð‰A+“j¢¦õ<Îø:6£K‚ãg°º” -¹¸]ŒU˰…àµ6Pkôño•¶•¨T]äq:âê÷5÷}ù-LJ[ÉÁ•ÉRÛ—M»¾Šs•¾Oö\ÂêTÞ5¤ÖG‰µ¦Á,¤“0¯«Õv¶5|•pê•iý\ÍÞ5M²Í6a¢ãƒˆ|HŸw)1 > ƒr¶Z÷ÓØcÞÏô.Uª£xq×TÚ9?g‚cyâÒØm?ó[åO–P$0´ “Ï…Îÿ\ÕÜ91·gA>½_æ¶?ÔnåQ½£€ÝïVᳬ~)‘w9jÖ닎ÖJl¸6ž-%Ç„Hèj»jCšìŽÑ‰Þô™Ý±Uv>t@ÿ,¦£6¹µ;úná°‡ÁÛjá²;š|êq…}õµr<ÏÚUïoRÏùq†þéöŠ4E-Ý.Rõ•þÎkñï í¸´ãðÔžÉH¸Š2áÊñÀ¢;ëa ²×ùüÔýpöFáúŠ`‹ØÖÈæ±Ã*C¼´×§ÑÆ8tÂñì›C» f…o,Û%®¯ôiØïbêv”ï‚å -¨6’‘¹VdI2“e_UóÐò»eò‚˜±ö <á[èükGOž¿HåZå,uÕ™¥ÅV{ìÛBéZ¤½†L;(Tí7^W€C”EU§ëx”FÎjHÃo5é&\Mëˆ!¹AíêÏu‘¤!ðЈX -‚c§‡gê)WScRf<±ðzšýT…µ¦®ÈZÇ9žë•ÜTï:˜Æêq©Äo›åÉ„ çµ€öa´ŒD…eEn™ogÃY’¬:³Óî¹KQtJ°ÏnÎÓ°$CÌž -Ÿ±j2•Hõc«sÆÖ’š%b÷µÑùDÄs¢ŸêÙ¢Ë1zT‹œŒâö”(« gFÔ³G°Êßá/ŽóIºz9ÏÍ–†±/PlûR 4¤öðº52zÔŠ4óý=‹Nz?þë*fºŠÝöÚ®íþj–íT\Ý™{X­„“ð?™£NÎ%s•†Ù5>¶ç‡èĺ‹VuÕŠÔœy§6=û]ß{?Ê©»§5ç.â*‡XÕžýǾèKÇçŽ[EÝnØS¹ ÕüQ/• ¦ -n/\‚–Í?#/¿Ï»krý Á8R€eIvf­cª}pÑ¡ÌûX¶®ü'(Û‰Çì9ÃjéâŒjX-‚öt”ÑÙåÌ»UУ[ òÖ…Êÿ#KŸ£%Ü#ØMP:‰Vä˜ÑG†ËN„’¤!Ÿ Æ…[&#­ùügé`f‚ëùBs  pwÐõü›Uí -Šíûžtœ?`Ý ŒàÛGÓ£G¸&¨RóÛžFÊ8‚z6äYFN‚Ôу¦lßïN®:Ù·Þ¿öçã_ئ¸dÄe¨d¡³G@1Iæí -†Rüéùg½å½Á£€0$½"r‘YŸ`žiÁrØ’7‘1—'àû¤°{^³¡®q·j…_~)©Ô_×] -0­”˜©>ÄÊnÑ÷#ºE­áºcOôû²² Ú«˜‹ª»îL=Þå/E܃ÀÚ…VëwÁâfHÏžNwÜæ,.Œ[ ‘S (€6°ÜÙÄTxz´•ýå¿m¢ã¬v–¬v¥ÈظµÂ–Úx¢ü^é/ -  ¾æssšƒI¯Ræ—Ÿt”ÝQ‰‰“·ê9žg£ccËüÒ&შÑQÕO£Ø‘Ÿ ´Œ/S|uÕÿ›Ú; -È%5Ó&aW Ì4*3)K$wRÄ{–n `-úêwI¾åƒ3Ì"RĻ󕧸SOÖG™§~–êU@ÞÔm "åÒ×…Àjñ _V-*vžÀ‡—ôèðüF»àñŠÌkz'f~Iäh]sÒÙœ›W[«¦Ò¦©xÄ¡N‰ý³:¦€»Ê#H? ®VÍø[&°gºÝ‚‡lR/ %A×¥•ˆ®»òn œÕì=KB‘SpÚ\Ŕӧ¥˜´„…bêÊqÿŒá=À¥‡6#FFu’ë±Ð½Óÿ®{Pv®•sc>…ѳ¡Õ_²Û­QêÐ׃11ÒÂõŒoÏr.öS½XÇáÜîºL3†z„_§Ý"Brˆ‘,ÁIÕm>—áQ„‚?ÆÀ†Z¦þàÓ‡Ž{ÌÈS)ñüóŠ¡—Oƒ´<)qO>ã«þTxÇ]ÅV =‚}[Å$SYÖu÷jÀ ª¸ÈòcN'4VPgÐ}pã˜HäTkD¶P0)¯¢xb%Þo“œ´¼®°¦µ…Ažš£<çÅ¡J°^¡ºJ6ŠTïÒ/¯E22õTîGD´Mt–HR®«ø`Jént¬èºÃ‹|jYŒ†@=w ˜½3ÐcYr§³?º³ÜwT?ËX5KXwš_BQ‹C¸StØÔÒ.t Úû¸ü·‡Á=>îp ­qz‰i.€•>ÌëZ¬*•̽–XžmUQ›a˜@é1 0 !"?)‚¬ÐöÊÖ¼µ «þBÅÃÁöÍiÈufË<òbšòG‹¤¡@L’U ÈêÔвßõxøwÌ<É©q¦dªÎÚ¬1È+ïÒüðš#ñG1æÝa×HÙÌYWg4ºÚ)ÞK t—^7ËÝðÿG¢õ߃޳¨M+¡H`ñ©õŸ -ùQÀØ%žÂc½õöb)ßý$š³O@}š¼dª¹°þ8ÑçŒä•îüT À#È(*ÇÀ÷l—a“B©WéàÀGJé­¼½qW[îNåÇ;FbäCT+À/R œ=ÿ<(¢ƒ !Aµ‹cŠ´f3¸KØ7Ëuk+¾Å2w>Npwa7®ô jp`åêc¶°“¤qúMØ’u‹mfG©¾wnr¥‹Ã›ºPˆLû’ùY2࿌Yd&<ðíu&@Šøë -‡ÏF°(¼³„m 3ë‘ü Ôü¿…þAòÝÇ÷@»‡ïc -#eò?ðŒ…šø°»•¥ Q’–ãGS*ëÍð‘è—Ч¯@ÍZÁ¨˜ÜÁ«¡õЂØìå¦ÔGÏ:  -sÍ‚hS† ØPò¹DmG¸g4A¶‘&õ€|ÊÍ­ÔýÏÐ÷D|‘8D,Þ„Z=XIÚg:t·HqÛ3²‚s}äón¯±ýdɰýàJª‚aq…únÖ¨$ºâáVzJ›Ã*Eèw€!ÑìÞ¬ øi„¹Ta¾Blj0ëJ¡N8f´ï.¬³ëÉ0]Åx6+$ÁdÀ~ I! 1ñÂ3Q±Hé*¢ˆ¸F|¡…¾¤ÙwæªÇY­ò_WžMÓ–ÌG,ƆGŽÐ.¨gÕVF±ý„!—Ní ‰ -ÉkÙ|®0çGÚ_€xfÕ…H®‡“¤UªF%æ*sæÙùŽË™gîá‰ÔíÑ€®ÖEîöž‚@Œbýì†ÒÕfs>X¹1Ï&‹‚0›EQ]Å•òìâUWwÅAˆ¡­<B X]¥R·Y*­*«8ÀDõœ¿a5úà–æqY³Zo±ˆk QønQÿD†“?š†úºBq´v¬˜-ä -µ%ʘw1õTÒGïˆÐKIÊEmò’áåœiº([c^ »…‡ß» ô¤š3ëzôo©F}[ÞåÿÍBíoRŠøncm? ÇoÖÄâƒ/Ô?–J5MpÝß@á;.×r䓯*ì&_ö°{4Eާ-oȨÌë€n©*î)'DlSNœŸœzdþƒgG[ ÜåDÀ.|^fžÑ¾$So’­Â!^IåIT”ô½® O鎈lóÇC KÚb㾣ݻ8"îŒ -Óߊ)å§aq'ÎìQÿXA:ˆ,mi>ùVž…’¤‘»Š®ò_À€n(Ì= ôJwö:Â[(p×££ÍÀgFõ#­2³oƒ5»ÉÔHý¦™‹Ž™o*ÚÀì'h>ɾf÷3Aä_ªÝÙxhuÍûðËö=ëÕ)ŠªúõHÜ=ågW#óåªgO;rðºÒ¡îA€†<ß;BÁ¥ˆN8ÝÉt• ?+šÿ;è=Q´4U¦¼(¨Ãw] -r´OQEf>ÔYГyíKäB7·«ÃED̪òÃææÄÂ’xÚ€ .WÉb†O],W @³þIº2\%&Ë_$Œx|÷¢N -O뎚‡x–ã -ò5žF~Ðñwyšj±ÏP¦÷¯Å&ñJ§úâF ÕÁ¡÷½ýîPd—î23µ3ÖüLõùGnn–ó×W/’Ý+­„²èjþà<‚(cÇØá–³ëªÀäjPâ¿­.ýuú™ÇÄ,3wž6P«XÃÇéÎmnëEô|Úoñ‘0Q+„Ø™ª yŸ*DÈà°&l ë·V=Ì'[¼ž.ä^ °œëñ·'΄iŒÆõc5êÅ-¬Üc62]Ëjž¸*i’±üù…?ãtéߟ.è{+#Îoy¾tû]0Jç#ÈJ~Æ”'Šì½Ÿ -í¡æT]“&¯rx‡Pä5ï Ma[Ok™Ó¤áèõ•Ú±=~^þ´Ã6Å·…ûÜÿŒ’² =®CnMºèY޶·¨;%Gâ,…ׇXi¶0£´ÐIFû• TÚٕݺ‰€.«;äèVõGEÛ/",$¦¹ -X;\O½¥ -¯r»‚ì#v)Û-awÓ ;ÛÖ­@Õ_bM˜† õXë‘¥ÑÙ)ÿ¶¡hªjêÙÚ†)“Ï{Ñžb¿(Fd|Yþ^‡±w@«?T™…n:¹-ýô8î‚ ÐÖ¤hø\we,ð }Á˜Ýí95I¿—V›Ò\#wq¬yþ(”O ¶=ëC›ø#ÆVg| vê»>J¶SkœL1%ú}Þ3ºÜuòV ™·oð2ßm(ØÁ’Ñ}¿ÀXÚ(„§³€©î³e‹åT9­|çœd:µÛ¦`hNºЙ^[YÔHÏÐÎsAõ˜pKÝ?=-È} -ng¢þ/1ׯšÕ9Ó!ž)Q3Åg=kª«yþˆ§¸ð›sý^N ž@\ÃX¾8gµ?™S*c¶?JÒnšH~xÉlºô¢pÉX$ G©žjCˆ•!›¸?ŠçüÜÃä”!»Üí1&驪+:>+îN =Ü’jþ°cýLt™¿×âØéài£KVBl~ÝdØ+q‘+´«ô¦…¦XÓ,¨VW6ËiÐËdÄ uŸ²†jrÎ]h?é?odOU„€Õ;’ôV™i7º®ûœ)ݪ:«ÁRwj!Î0·HŸv:šW ª$ÎÉüñÎ Î2†•Ÿá²¬—´©®­rÝÄ:0]÷j‚P}'*)øT˜53¸Fg81ŠÀÔl÷U §œÙW¨süíOðB:êE„…¡Rà¼]ë¼wBÁ™‰äì¡2e±a'‚ÀLwir9®iÅŸ$y;Õ%wÔL¿Sî¶B»kAŠSu ä¥Å€²)¢[H¬cpé,ãheÇ<ŠEqEóJÌjr¹§]~Ê:rží•0* ÞÀ |qî×~‰HžîU­}|/ìÀßå”Ù F.i<`Ò³„åBØ…C¸Ž(ï}ŠÓÅ †Í Ê}•bXR)~ûb{–Ոܜ(Äc푌ÁFoQ´¨“ßQÖqþž1÷3w.Ô ›Ic¤»0|á_s‘ùme~.Б߯§¿%"ÿ3jê$¦?§­äsu;ÚŽ}ûl¾jbKq=Œ,âìk€–§ÕAXífŠ•† -“[uí†ÁŸ„Íø¬zÀß²ÿ§½ÇoÏ÷.ªîÅɹŒbH?äÙ)s/R§ëPç#hèÖ”š'ô*DCÓF6…Ì’WÖ§b§]Uš®±½¤³«/Ù¢cµHŒ$µ¿+DyñÇ}±s¶(LëŒá¥“´82:¡à‰YÂ;‡g‡#j*½nFãɘŽC¶G!¡ðw¤ <ôB*¥G$A“ŸÂ‘dèíðúØô‚6±æ¶J.çKü:Ã¥âPpßuH?`’Ô{ÖÅÐ×)º,çQì?O¡us¡°œÊ‰š>J'»ÜïXKu¢B$“¹GJõe7$_¹ËŠ[øgÏ…M…{Šü<Üßóës>˜`ßáĦ¯_n䪗u•wJ=¸òUD:B-° -$ª»ðKCì‹à|Ü<£Òãù°çúã3…ƒñ*ƒôÇ¡œ©‰-ÂÁ#8Õ–+bÉeu -Ò´)û„)q•&>ñBØw¾qæñ¬µPŽ´ <”·jF‹ÝW…SYj;E®¬>É9|(²„al`,Ž*t¦ÏGèŠapˆ›Üÿ¼d= -³–ƒ´óI¿ey+óÊÿ™Å"¿L´5ϪWSç•t†ßÓÒeÜ¥/aïE/ZuÕd8§¸ô3"ñï=D^¾Ÿú+ÀOyoìˆÄEVúEZ:óÀñ©‘¡2Æc5£CÃ9R üóGw4Û¢î1àvž¸ó¡„ è€–Š —fž ßC+X‡b"lt>uÞ;JT’àŽ¬T§õzÆ,0”)%—UËê1æóÒ“‚è ·×#äè ö«M©òÏ„¬bá #Qÿ?2f…ÄGâŠìNVíΑ~©CS$:Õ;éƒTkôt˜V[ñ×eZ{Ã&²þA«ù9EÐÆ4€ôHž±a ü,ìµCÍ€Ȫ6lNígá¯ùÒ¤_Øcä5 om!–VrkÏϺ®|Aõ×ü†˜Ò|ŸŸŸ!ÕL3D’ïаæ“Sv_$ï"Ùu@@›%fóœ7j¬šêžE#x"þ³Êd¾†´[¼ˆsñYL}Õ2×Î|sÊV¯'g0JÌ®1€¤kxËÉä¡9:WToµ{9£Õ¯aHSse»{fïÝ¿Û+ú7NÌ`êY–(g‰GjŽÚ”IPC¦°œ÷Ná×ªÑ -k÷ &z­îß’LÉþVÙ5 SeùÝtW<Üß#rp=•èH¾"è}\«¼ÞóŠÇT>'Û–v¾IRÿ0‰Ïˆ=—Ž‚Œx²ö`1I)Ã-©­ªsù‡}ê§–aîÇÙãüÓ>Îæ6²¹ÍuÐ)·:‹ùð&æw(ؼ¨Ÿµ‘/zïk™soie½•šÚ³©±•í‰&§Â ­Œ‰ûVóƒóéXtE»vÉ÷U9à.0pÙ«XÁ)(M6ŞĶ5ÓO¹<åAÖ¸®ÐÐÝ•Ûw„iµåžeði™¿û¹«"˜ŽP*¨¸•Ýw%KïðuEâlFcJmHiñHg¹—ëOŸŠæ,µu&”¬6¢Y&lè+3åœ[¹£QB×ôvzÏicÛî ‰ -A³ñH•ÌTðf¨œµà#ŒÌ1¥` *Þn$ ùkñfèȈÍc¯<ƒªW¡ØÿaVš& `Mmº ó"¶åÓšË%nþ˹Éè,Ê¸à‰‡1«ï[±mSÀkáHVu­/šÊIwtúÃ@›SÿÌgмgWÛQe¬ŽÂ„Ki½=V~sDHµæeÌ»išó)Û}ì!/Z9Gtçñ\Z…”ˆ“›Q<»£d¹¤Ó°2ñIÔ :ä VKé!»r'êl^âP+EMÏsFúUNÃÚ4Í•9]NÀëk%ØŸ%«üXÑ?7tÝÅÎÂAƒ¦b×^q2¸M6ɉ=BÙb¶3Òw²Çcˆb£ý,Ñ~§V Rµô,„6¬3ßû3¶ËïUrßa7ݶ]·‘iˆ¥üß¾ÕÇËà•è˜~Áû¹ÊNyÕ —´ŒL÷!¿vfD ¯yp¤£Žs'•åäW0è*³–nPqo¼~×Ì÷–˜þ.*„:,ÖàÏ]õ;:(Ôªä.£4çÊï@éT!ÐvšÉ9D¨ ¡-jû‘Ä(zïÏ#=î3²§ùøsÕ†A–Î:#»4d«8œyâæy? éž¼Tàf¥þú9<ÃSc/ºÇ¬"ö{OŒ –`É÷ÐQZBLïþLuôßGT¶œ’ ¼Žïðj‘'pݨ㫴Å,é2ýÊiy¼\X‡¹ô½¾B™Ãì;)'ãU‚Yª~´Òx3Ãc™·¦ {À‹£¨d«Eà×KG9Нö̰õÈ)õ>WzGëˆeý×\YRhô],§¬óùR´?òǾ߃þ³ÊöûGtûßU·wo6à"k:tçú”XKvn›õÆWêpºMVâ‡@!‡ØçƒÚ¹b¼`' ”ZŸyìêp˾w¡ÿ¬þßõN¿Ý?H`î:›{¡AþпQcPÔ©hà sκ¬yvÒ9íg@Ùèl}½ŸÝÞ8Ç录8­ªõ>ZKÓèNÌ®jÁŽI/e×ñ`Te´îI›væ—(EhK Ž’`’X8*ÏÊœ)K=Vªò_"5q%¸gôŒÂ~¬Þ8Z(ž¹ÊÃÛG'åKY ñ®?w¢Éœ¯bÓZþ Aÿ²z -§;užqe—ýƒÎdÇhtä€|„÷ȃG© ¨–•·;…é¯ùÖ<<¿¬Ù61*†ÖÇGPì§WJ€÷ÊÒ¼ðæQkm'ùneM5ÝÑûS¯à‰lEÊ¡}/Ciû†wiz¿W`×õÐoÅÒÑ"VêÉ™o< -BŽÜ# -iJæºhÞ¯’´2cqrÔž+ðu·;2”_¼ËDSd<£œ„u7ôô)õ6å@Å=ßHå/î>ò70_ ¿ÇÙ›Rí Ž”.£!²R°»´î$l܉5³ÁÇÙÙ!‘Æ(ô»c¥,¸;j}©]jbG§°4Ϙ\| ³AÑÚë,=åµJ­C_© ÍÈÚ˜çº4 t·î%ïv"ƒØÒ«o((}ÆDŠ9vçºuVN™ª‚¡t]'¦ñk~_ÐÂoa*œ=ïÿ²ä›ôÍöX€çÝD½m>ÕLàÌš‘¦¼ŠÜîû¯þ‹ªwwþãzÞw´h|Ý= J[;ÝÀL,ê¿Ì™Ÿ f¹*Já»HGÿ¨8\¬,ó¤Iæl!ò©¶G%TG^Ö˜ÿ¦µtÙQ1`kõ‡Cô®Çxß“µýÈŽÐ?õ¢Æ¿Å¤¬{#²r=ˆ1¬Qøs×£Ž= ü‘µ/¦ ÓÍðÑÁR¼D³¿£D*` nvú™Šðd¼”~QiÕ£”®€Ý¹h 0¦3wŸþ&±•À€;"]>j©„_ò‚ñ¶Ø ò¥lî·•÷{æûä”)Ƀ‘Dy—ý‚5,©äF -]1޼”˜/5G›ŸÙ¶Nxkj—Ìtæ"[} #•I§Óõ'Xù¦xF:rå†5ùtàÎlÿýôø™ßEEÐ.ß3y¼# úðŽªU~çe_éùþÖŒ&dIHSÓý‘±è?œ tââƒFÂz*ÐÖÀ[®›wÙ5µb¢£=ßÂ1èñ]gäikdù·ÇЂ1:Å4>]•þu -ëôGÛS`×Üv}óÚdLɹìG}_ƒn¹†~¹©®ßG”ÜÄW]~ufÌ^µö,¹ÌJ‰¬[m¥“ÊXS&Q Çßk ð’ÝÞY>1V[¸õ¾êÔxÕŽªZèŒíýÜÁ;ãð·ÜqÏqäΓú<á*‚/qhu£ÚCÛ޽5³áu€”cw^Ö9ëë$oóBûcÁdqŠ—>öǀ鞥[rµjV¼×óŠ’‰-w&¬m?SP…ï'ÕÏ<¡¼%äÓ×f{òæp&ìØÏÖŠ·ÐÎý<ƒ#RÇñS°C?.8¨—(³ê0µ´€Ðé”É£QŠ×;Øn=ñ#DFMšiè!ŒÁó¢-N’—âKW¯³Djø÷úX¸gÏEž -ó2ðxåµéí,)_ãàŠE‚CWøˆªŠìŒ%VAR£ÍüíC×I>£Ô=žIåvÇX ]çŽÙ‰Z]þÚs.f=A³³?7éc¦"ëMut¢ylI:n9¸>Ûë~\dMç›3R‡%#æDß¿“Ÿš›Ty)7yÖÙpÞ±bñ¬ºKP<ËhÏ¢!û¢ÚÏa[ÿ8_A˜ÒŠAñšáéG`-0 -c“†Á¥ÎìݯXuB>q&§¤þ¶ ‘-AÖ{ç2ËX% ´¢µÙJΈçû©ØTÝÕ UÑ¢L‹]]îÓêeä¬FÏMó™SqÍ–óEFü_mAtzH4j%йèþ¼®ª!ÃJË­‡ŠÀzLÜ𪶌ËÍVÞ*x.m~cu½Dì^ùÙq¹YÙ¸ˆÏJ¢ÃÀÝžN޶ ¼„£À4Áü÷Þ¿þð–~æκ>7›+½75óNo9ü-uæòÔEÀý[f/ÝOelÊ ÁBbß膗’ΞÕRdDŤ]Ú­_‡ÅÌKuÄÈ[xÝYÉÝíu_ŸëÞç=4¢4£„=â‡'"Ýw´2r­Tœõ›»sÝuÕuŠy;²?#üFîósÝÎM=mvÆúÌME>Àb¹w0…ãñLÔztõobp;öY VFŒv¶zEºW0á¸ÛsÁ"*m·tuWnÖ#n1²B—9òÞÏœõçU( FŒuøÓ÷@핦,1€ƒh q×U;Yõ¤¯¿ÔXnÉ«FÖÎȱž‘üCœç:´lfó{ÆìQñX2=Ïh6FÜéÊÉ&5ãGö†6È}ŠÃû.ôïYW‹NxXt…ÝB4V*ly¼ Ÿ©%›a„¤q®ÜY]§èϹ>É4B½ƒ­éé³3²>êî9Ôk(n>¦´ñ‡È”ûúý|úõõÃüúÿ~êÚ+kBØþ°F*Ï<ï먧tÕD}v¨Sù8fÎõ_59þ¶µ·Ê„$áÈÙdò=óï;¿HÞ\ô½y”ídD!»¶+zÜ0ÝrÊŒ`³ÑK–µªÁ(¨ òD`,¢°ª¸¶ü逼x"«ÅZéGɱYcîfŽŒ••#½gäèŸëÞɪcÞ¿gL–®¦ îÖ늽 ¥¹&iÕ²qYîSkz{?$5F 0q¯€$ïò®ú’ÇëÓ`,<~Þå>F"èöøúVZ6¿Œõ_¿{uãûŸ|ĺÍ*XÏ€çJ+_Ûz$×å·¬â!ñËÕt;뙸ÑÉ߬c÷š!<´âSðšÄ¨sóÞTF;¿é.9–ŸÖRvHǨ‡ùç¥ûéÈŽÚa¯_[·hÊ=ÙÏ@)þyÞüÔ5ÅwÁFVóm>kJIØ•W"¿©ëb|ûC$Af’d"ibôïYRH@œ1LÍÛG<££èŸÏ ®yÒƒëzVŒ ¼ä"þȰ6à -—Kå\‡:{—Éì.ÌÃ'E`¶nMý."'8”Њ¥4\²î ޼Û~>GéP}tÄþ<¤ÒçÒ.B¦Å_úÛÝÝ‚áz†L@=ðé¨W=¥D—@pTJë#)sÞùÒâ;tÕ+¼úsé_ÞêOò½ - ~4Í?žáÈâ[bË—±€ë;Fâ‘Ô‚þè#¬;¬Jµú¿<ïjf0N{=ÃaÛ¨¡]/fûÖ€}£.¥>çòL~˜ ?u©ðm±l/ìÇÏp”Óuz›5_,aò|v¨F_™.*¡ÿ{Ë;-' Á^Ui…ÀbaP-Czb¾ÐkW#ûC6–¾äRaû.®XÃÿ³…Í+’¯Wµ)ïoVÎŒE˜Õ?uô ćœüµ0¿<žK‘¶ù=ƒÑºØC'°<¿Ö&·Ó½™!tëNšF¥w5ØJ täRå"|nzó»îkÕ¯¯L.?¶çcÓþ£Q‘!év<ÊsžÏ•Y0ûþ(ã²e]w½±Èß¿¬Ÿê²W-"oéAGø,$Öì[cá<뀗ÿüŠUù`÷¿+܇Œ÷05'F$Ô íZG•ývLpŒcñ契9ÙŒEð‰6Ÿä¹Œ‰W·õw>Ÿ«‘øbä{¿2úž—=£Ê4:´îºtY®cì•´G‘×kWM8F#;”Ä­ŸqÌåFTˆÒÝ) 2:$Hd›ÏÅQya¬øŒ­D¶#vŽuõnž1`zÆ®Œ)gZ±»Æ£ Çw/G§v¡u—¶øìQºšg9惭G–ØQy²>³+z3ÙtÆâþÅßòµxoÏÕ8g¬Õíœû~^‚§„c×jŸ‹^7Iaá÷Ïëùá´:Mdcø,|RN§«×óñWׯI“çuPÇêŽÏ䉚‡oë˜×çÚñŒ²˜üæ¾jšm°¯ù\EœQ×gêEÃÍQˆÂõkdyfTŒjÆŠÛ¹£ÑÏÜeòmûùíѤq*D ‚ÛpÃq¬zÊiAøÚn1Lü¡ûßiôÐﻈrÍ §ÉPé\Ç~š¡.³Ž¡ÈHKm–Ux?^ÂŒ×8†ÚË?2¸?ßaŸ¦‡4‰ 9Á -|Îý]H­Ó w~wi‚?†ºà"‹†÷^¡1){¶rÃJˆ1’sžî»êM åJ ¬p—±¼0›qþr¨"ˆn{Àwº$_s©¨ -Ýõ ìfdHÁÁu‡+ü\Z„EŸàïõÔ A¹å¯ºÛºñÜF‘e÷Õs“ÀìË·`±&DÖŒèk'{\½Áóù©)³øî¥hx¿Ê´0CΌ̵?W -ÌòaŠšdr}Þ|¿äèò|ó½(ƒ«&\‹Ž­ƒ˜f~¹ÜZõjœÏü}fÜ*ë6ÿ7{oÞÅqu¿_€ï0~lˆAtUWõâ]p°! ^b;xÍ(#ÉKþø}ö·Ï¹·ª«zF Ä`9™8q¤«êîZoÝõÜÒbÚUºm¶µ´õ_«"]×qª|ò •º%»e×-I·§‘§ì!Ù/¨FöfÀQ WšŠM~UÊ%n¤Ž8I ¹’¯ÃYIª?1ѰÆkæõ}‚á÷3e¸ÈÄXAúÑ'e5Alj½kEò.‘zÕR!°£Èƒ—@¢+•Øè•*Œ¶ ¨ÄBâhÛ\•댥öWøLiÕÐC.1©!E{xŸ€UG¯ÓRÇ ª6'X)¥Õs š¡´`$AHV€h³S–¦p:•a¨ šR‰¾ íª¸„ámÙª¾ÖM¯1-_èe×NïTôAx¢o$1ªrí˜ñˆY4ÍkA*X‘ ÃXɵfõƒ=Ò¨ 2ýZÒÍQ.™É„}¬K)}ÌŒr _G|R»–T+y„lRˆõÁ,—ºÕçh·fR»×ÇhŽÆ_ -M_i¦=”\µ%è€Õ‰$ç¾ÑýFñj‚}4Hl⤹@3N[écVá$›ɾb¼0.1(\.tœõÈŽaneÝcyV+äÕD)!Zëó!9‡µ•zò˜³…BX£ŸëA#´ß¬Ç#ùçmxÎS -` ?Íb@)VŠ˜îË—Ÿ¥MðZS^¬}•ª 7ç@ÓŽÄfŠŒyV&C”ÓZñíÞÌ )5ÃŽ*1µl±JL­d‘!̈…ƒûÕ–äW¢uo±!ô¾§äxÀÕ­æ_ÐeRW -Õä7¸ß·X#‘±nÌõ0’ïjMÛhY™Ps²·å1Cp£`ƒÈ‹ Á,BÂUûFŠä‚»“Þ+°_.““'#¼‚išì£ó55ÎJºä«C!Ç«T›$ ;aù• WdkR4½„µaHi•B›Ï¶<×Ö:•ÌLàÛ£ÙB?—­ÚkN(%)³-¤Ì˜ìh'åé$Y¥’™“b®7eoÌ[õ¦d’H\$Å2$kOÐÖ ‰¾Ü }6"l@ü1šÞéUm°,D!% -0ߌ¦fÍy¢/Bh.kLxR :@Ttx'¸Ý“vDPÂû}#@ˆ¥W¢×GvÅ…*$Y€$«â¸ÂûLQk»¢Õ!ˆ1ÈëBÁº%y4Á"s•š;A¢þ*Ƶ¢—‹•Êùë ´²Õé%–¥eÂ2èÊM ‰‰ƒD[÷D%UÕң颾Võ_#¼‡Âì9” èL±Ó)¦± €ï¢aþtêäbyaw:hU–eÛŒš ªyAœEAмüÅì±J”4Ð -Iwa´c@$tŰ07_Moš>F÷&ÓÚM%Â!ËKZ}¬Bõ†€–Uxް€P,Cƒ,y+ £ný‹ŠF &Õ^ÖRç$WiJUMs¤ H@j( -š¥8FL&sMÀ•£ªbß`¤}B JÈ8‚ù¦Qr ”ƇžÂX©ï(Áƒ­ä9/•%™§ÁX ¥äsŠ JØhËç¤| -0,´KMaâcÌYc¦Ó—3(ÓIœ¶Aá ®gáâs0€¦õ{‡^”²3jý^¶^«.!Ÿo¤C® -iW°vVœ)—8¿ÎJEËùëXO_¶o(9Ø¡À«–BÉÁ^‹h‚D,`/!”§'1ìm†Í™¹šÌ.¥· ?‘Ê=@}¦ß•¼_ Xñña £HRBñ0¨aG$¯!VQ« -:nxp§¦`ÚBÝl™!|ò‹kÉcÛ’Ò8T§aË”™ìî6:Ó/ ‰vÔjŒœò?ÌÛ6x÷š,Ø&&’¬¼ 8,°¢ ÄP˜† AzôhÒó×õhŸäA@6Îh¡¢!c¶À¬Ê]*J„Ôq¥` sG¸JÌ ÝB1žK"²¬n8ËvVdd™¦F°xxº¸ö5R@3Rµ Ù.(€NÔdPyxg[Tn—ÈeÐHÞ6Z&Ë”š „²^Sªµ5mœühS«±#¬€Ð S(†ck}¡z[ dÅQ–’â‡n¢$š¬Y[G.EôµÅ¤ÜN!ÅpÅ×d¡…¢0lFcÜâÁôÔ84˜1­1&aÄI±²·%ê'‘Vûºœ(=-!mZ©†ö^pI\!°¾…è‚•"…t Ò’ŠÜà6D´²@ºð#´— -$\²µl>–d7ŠP¨¦o -FÕ1ŠB -BvSYz鉯Ž;€–ò Ö)Ož#V÷¥µŸ;½&83>UÙ¢þˆã'ضu#u¹pB  -%”ºïŠHâ43jE^^JPcÇhäé#ªõ…rS•±óFïŸNý”)Á!¡}ÎfŠ`«%V± -,+瀵- °…I‚춦yûî™nO”¡Zõ’*( H C”,¢¨LÌ1,ÁPµ1J 6ˆ—GëH'ו^mûâ™ãÈÕRº¸Ñ'|ZR¥Z‰1gaŽ%0G,M5x8"Ú7V^LS,nÆH|pz£œ *¹?Æ=L¤‹×$*a²,àYj©‚­&ÌB²kK'ñ à,­ìÚñB¥@Hþ@[õµ¬ÃõÂ2ÃÞk„‹’°`{ÉE‰Á³km¨äÁÚG,ú!5¼X æ „f~2u=OÎH±îëni{Ë D,Dþ8 -·Ì†Œe褪FžnÁÔ(FQ"ä[b FzãEZrŒ!­Ñh;„›å*K@UÂyçGÙL« ±D­SÈF³RÇFJ`ÀÞdÕ]Ö”zm°´/4ìƒiÐp4C?®å^GÀœd…pp¼QE -|è÷*ÑõZªuèУ®I)AjÀh—Z¼çwGàê­BHõørhÜAü ƒShÓBjzÍá’z†€l&¶z#Ê!R€J¦^X̹&Ùýœ2$D6¬Ú°’ -¿2Ò‘Z¢ØfZ%Bé#¡"õó,þ)NT뮵`¬¬‡…-+ÄÒ!i²Êw²)ý®&ÉSxŒA¢N; IÜ4q ™ü¯FA‘@][jTÓzàÒMÉ»EéjK”txðXT Ók«E¼W€õPZÅ•j”…H0¼ÐËd­X„¼­ÔZ¥å^:ºð) zrÈLM‹È;â›ÙE†I8•8¦¤u”Öˆñ(´)%‹EžcR4«kFš -s°×)!4¶50qL•åÄ1wÓ À—L®Šaq=.šnJN‚IÒ¥ë(‹WÄÂÚß8A…L¹Ô7‡ñ¢&x@A@“«D¾ÖZWÖܨ¬2•\ ¨9ÊèÌh€®‘×°tiÔ¨qÄ2:‰x2Fë $ÇP3}òcèõ6´š­•Z€Z…ü ým¤£¹J…B•›É0X“ç×ËŽoX…Z½€_mðQ Û³Rµ@-K=¨RªQ±L@Cd(T˜¢“ª{н’ð@+Y)ÌÅQhÍGl¦—ªNR› -%RT‚1رÂT+A䀆%UÀœ-Az?e´(+»]I(xæÙ*Êg”qN–oû‚ †u°5Ýÿ…½Bžé;kô²`ň¥ûC"Ï–d FP–ÒIˆY‚0@Lì ¥8½ô… ËìP–Õq"@nñ!¨©šâ(kʲ¥8aÀ"NsÁ³TJÞ‹ý58¬ðÍÄäV‘±á«Et¢WXiä²tñU+-Ê*å@úlÄ׎†ÑºãõÉ^Y—Û@ç%‰!ü4VäwÔøv–Ög§`²¨ÀPÁ®‰ ÌkÉÆ¥ó¸ñ’̈3`éÐlXÚž>rèâÊa5¿•2K ^.סK%µ–ÚEçÈÔ ¦p#Àn çP\v¹¬áq‡u¾–ÚàØ²Üàu#>0T0 :ž1‹u£žnÈÝÑ…Áº1“ðR1êÕ¬S®hì¹s((ª®¥¢.À(pññ÷n«zÆæ[šõk@§#K®f¡zƒüc:ÕáT£K0{m LÈ#Í+˜²šG•:Ôh´/ P†º«¸ʃUt½Ñ˜U\˜tîT­Ä£ä lCSÛ£9uŒä;µI º‰­¬ƒ£ŒÅÊ]²§©÷ y€_r5$†àઠ%-˜¢ ´LôRj€ ›qVˆ^uµdBk$Ö)ŠÌĬôîjÁs×Tb§1Hx‡4’šÕÑ´à5-€³¤TW‹…´"¨Ç‘Ь)˯cÛN Ý%" - 캂 V³*¢‡\ ¯¨ŒæÀ˜Lœ:Œ4»þyr}1[”šN -˜ÄºQÿ‡…±ÞIIžÇ¹sðW*Ö`pœ@4à!  p‹TòV8†æ£L§Ó9‰©Ÿ‹=ãjQ˜ -ÑϬ2Ȥ.„;úÈ•Š´ µ¤oÊ`*4†)0¡’º€c XCß±5k¾‹Q&ÛbÚ¦6R¤R› ÔÏi‚jé*±uãê–Òw ®…\T˜‚{ŠXºÿh7Æ8'`þtýU|®¦u› E1·•`((PãcÀöœ¨6©~7LT-­RÌÖœààx±ZïâÎ$o5ì·m4ý×Á~V©KTý5ÄÞ`Q¤"äĶ+± ìVTð}-ˆ²«mUJ±lîBFÏ8‡ðð&äVŠÛÁ=^‹^éJCÇ’„ÞwlÖ ©àÐ -È¡7R‡™B¡“ðäîL1,¥žyeñŒSó‚ƒ7VÕ{Mº44MÒŠy+àebU‹ZO=¾—0¦-)ñìÑJ€OÉ;½¹¢þ“¿†­+x)(Ùã93Âï\vGä%´-»Ò]utí•ñšpÈjØ,…€ìÒ·µÄÈŽÈ´CÄ„ª…áA´ª%ßUšÜIÎÔ”½H¦*®á‚3¤X ð„9QHâ—&¸vɈl -…:_mO~ã¶Àk$7)TH|Šè ¦Â@Z9¤áº`ü7%^ ¯uÍø5(¨¤IlF—`yÁ»éÔc¯a²D·ÅœŽÍiîD:‚üÁY’F¥L‰•,K¯©»ºÌígÏuÈ@pTô<'I‚¹¼8þ¬mp ×òœ¥a—1•^Õ)>WJÐ…×»;N,e¸/Y8º;×^ ¹Á½TAbhePå‡6_«T…@«&\+Ý€Ãîá4'ØÅÈÁó…ÁÌ&ç¤!"U[\R‚€ÄK·h‚qã5¹!޲@<àœ¤†lËsLƒÂHO[y‰½•ú%fpCd'huRòJ ‹•W»kÃ¥Ä_¦ú*âA9y«o*:‡Bƒ\Qò·w0åuƒ·M¨P"‹¼; ‰%ê8¢NMw颼n¡`ð¸Xa‚€ Ã¡u”VËe™ŒJtmŠÑš4D•>º` -ñkD°6„¡2</¬TÝYÉœÀ;iEk?Â*}‘TRF„0é -Á¿Œ0Áˆ¨&œ^–‡D+FG×ÈÝÁçà¾7#Ö·±•4j\7toÔA…¸9ú4|)£ÄcpO Ó'«cvw.hx]·< y¦ 4TvÆ-¥ð‘ÞÈ%Dh§ ¿Á)Ü•¯/º¿"B‹vviíá€3úX‹ÞøF|ØðÞxU`“¦Æ3Ü•Ññ@©aç‹4É4ߨ˜OŠÁÓâ:)ÀñrkêStƒ‘DtQOèD%q> @9ÔÇWšL€L’År’,YyŒŸÇxáâ¤Ó´ŠÓÛ x\L“’J?!ÈÄ0Ìåv€Æ‚ºC`‚€‰A1Âjô3§‡E¦ T┡`“,j0™ -¨38Ƥò5®°)ÌH]pÊv`þF¸ôIM€/ëM†QQ•.Ãáa_o½BĬ9ªß&›¨Õ9:æmFÀp@`[—£)Å‚5„#`²ÍqwÜ}í—î¡®£·My|x8YÌÚ‡ÓÝíÉÃíñ´ëÉg‹Ý/&¿Ê÷½†«$Lží.ØÉG¿îkP‰Ynxó—ýùâ°ocWÒ6³ñ“éä³£ÝÉÁr+÷—Éôþdñt²}øð×½'ó©<ð"ô/›Ÿ‹Ý#Å㛳iÊßÝãë]gJ¹Ô nor¹Û|›Ýÿ¾þ¹»aÿ|Ô­ÚxÑ#ÃÙêþúk÷ËçÝ?v¤ŸGntwôí÷Åh§kþõ¦—It……S3ž¦)?ÔI:Q̰¦3løÜÖŠwáˆtš”&§{Å’%ªP%ÑGdÌ…j¤„F<…„°=Q€ž¥‹B‘{±Â¨ ¼ˆözÜ O:üÄ €ùDàdžà¦W!KW"J -wBº TL¯µš®6V`MpºZ1P35à‚øÙH {¯ðKßÙÆÇ!_Q„ÄXXB;ÆKæ1âÝy ¬B»¢8C™¾Õ-ÃV­ÈðîBJöšôÁ´}vbé{œ‚2¸  -(‡ŒSä kñ…5n‹Q]rP “…Jlµzs©‘ŒxLBXykкÚHB3úF”,g¤$ÍÆ’» -¸‘]ˆ€uœ™r¸‡ -áÄ$Nä–bÞ”ÏÀóÏÊ€KÃàa:,KiF ¸½ †h°bÛupͧÊmÄ‹†¬Z¦“[-¤»uÁhî)^N5ÁðÜn$ØÑÒ‡øõRT¯Zùcç#vŠö4ÁüÍ$í–Ž0"s"/‘¹{ž½ƒLCG• %AŠ‹ò½Íd.nþ!|ÝÓ¢Éòètî]€ÁQ®aÀîÏG‚b‹B€äÞÔ ‚°4\@Hƒe q÷+=†_à˜‹Fdi®Ÿçq'êƒöL`5–œ¡ÔXZúã`}ª(ÐÁñPˆ¶$Á¡ŽdCwª$äÒ¶O«"óKvÓ0S8ÎN*žÁ®ë&Å™Vða|frJ€ -ؾàBú3’¬`¨Ã梤Î|yÕÁ3ëÖö)cÌà£NTðºà¬GB Y›—²Û¹N”˜Ó!OV´ßw9|Î]Œ”a¿Š= P¬0½‘6ð…@5U™i¤úk±•iTJ†UKT%‰ä¢¹™XÜœ†E¤XKª( >ÅÒkRðRvPô!XŒz@¨™.FÇÒÐK26.ÐöHC¥lˆgâиØñ€ÇC›«å9c‰`*ÕCe‡’ÈúõHòòœ¸´<ÃÁÄ“ó.’#h£[°ÊŒi24'°ß ª4:\¯ í¨.±ªê㉢ ´TðeqpKÒ¬‹ðЬÃX£« ‰v -®Ñ\u+S)E¬L-g;¢-d²„ 0éO]Hc0V²”¦^„B«Y«Œ‘YU¢“šŠhUñä·R¢@ Ia|QYŠAm›qÌÑçL²‹¡,„+C‘H±VÂä¨PBæ®TBRgË*, N±Êm„í…¿ÔkèžgÍ$ËŠàµçXXe fEA¤>¦[ݲƂÄé Ϋ5¡ ²_J‰ªsZ\üq¬¯Ü\ÂòƒBQÕ_‚h¹7ºï!”&t4ïNI+·›×kN!ô`Ó-äRcù¸->'¶%+†«Ë³½m:P–"n›Þ[ƒëJÊ+A²u8p\7“@a-‰¸ÍR³…m$$SJyB4àž„o€üâ’\MN·)|   =¢5yphíƒÁr”d­žšl“rÜJ`zJD³Ò9|zNpp×Rû ׯÊVõEßjt Ì*Lð„q™r$î}'5`ç‚;'VÄCVܺmè;Ft;=À”÷¤P ]u˜o¯¥1AáT‚ûý®ªõºö•°Äm–‹eb ÆÂàG^ü0A0Œ¹6Z †~ÐÁ(–"8“†Ö«L;ñÅLË DIÓ*p[£õÄ %HµÑãH4¸†9N6‘WÓJ1åî)'¨^S²q 22¯›YÔp¢™F™Z*¨±ŒZu#(M´¢¥FvFÞ"ý;F¡¶‰bÖñýJæ²Tl>é€W% ½c* kAC¼”ÉôÙÊ5œZj>!^—°¿^à=%äÓÑaØÍ Ws§à@WŠN)€÷ - SnÉ`Þb'©œÛ™q > —*!`üth\-‰`JþJœ>BœbGªõÝ( #êÙGÂÔºçê §× öòÉý!¹‡‚'ÈŠdW¹Gƒèˆ†¼DúcUÉŽpßW4î¶»/ÅœºE©cÊ"E |4"Ž´R(Œq¤"è!nŒÓÑD8#‡Tã+¡TÈÑ&ocž©$×qWu]·#~VSHŒ=ЙçG ñD¸ÿŒdsâÈA(„X`‡=aqEÃÇÄ…ußJH¯jUžvÁÛù7ðèòδ˜hSBÛ ­W4êRE,Jhòzö -ª‹œp<ä4ƒÎdpw_ Þ9rf™ŒQˆe¬006bD0Sc8¸`˜c(,òùÁÙjEW†û‘ljÍ/ û¹ë4Hb{bHàݾ‘Xz×x1[ÊÁ„ÙHyÓBSEx¹°ì»À²"|Í÷Öü¼¢èad òêµB´±ÂUûJjt#ó_k¨Ðc#¨¤ ²³¹Üâ%æ`úEh ÷X!kîK•t@€ëºu#;€V†é](+QabãÍRIxýÉHÁ-¨°ƒD¿w­¢0™ ƒñ‚ -|¨6·áÍ»¿!¥Ž±yˆrÂ] g˜ í+—Eü+Œ*9¬L¢ÌB‚á¬RJ ´b„%æ½mjì⺬ƒR«Õ¢#&å ªz+6<¤&˜)DîmÅâ´Î'-6ȇ¡ËYó; ÇT+®l§RóO8°[Zñĵ—h ©¥ô%-=z ÓEBã˜+õ8u:‹‰2¶“ÈäàØX„,gÀ®æ¸kʲ>pÇ4é&G*KµŒ¸`ž"ÿ;‹I$<“ð<ß2Ê®àÄ9zHħ`ú–Nl†WA®FÕ/€.€™ -ï€ë!<*´ºp½DA ±E\ Œ¥-èdÇÔpƒ`CÑÓÀ~Xî·ÐÔv ½u8|k\rò{idÕÐÅçñŠs4º E!&,‚Ý]‘ø]R…Á\±éhy±At îd¶`*óÖ1ªSj-ÑÆÂe˜žÈˆ=¼FìÍ´Áb†dÜNu{ìÄ8p(V)öç ùoOÇÔ—Ôª0É XQ<x}Ä3-vh£j'2ÝÖìÈFü`´ Œ€gb-«D`b[t£`ÕuA!‚Ô–80îmhóVçDj?n_€ÑÐAµ½óëÜf®–hM0uC×(`@ˆvE’D þn$YAØ/ƒ¥¿`82’bð.«ˆqs €+|nŠŠ¡¼%»1à\Âûf¬èbüŽ=ƒ4% ÅÁW1Sw9è·Upy@oÁ:ˆƒM0^ÁX)ƒ,ØG¬k˜RA¨º‚! øŽ€S¸m) žLméV› Ðj‘ñN_¯"š/JØ?›‰ÜA-2±‹Ñêr@¾ÆÇ±Ï³ RúX®q VŸ`N2>ˆ0ç$- ¨8¸O·Ž¸$ØÝZê¾4½€t±PY#› -OL1…, ‚C»½Ïl~©j dDæRö©‘MdøR‡‚AˆÂï& -¼ ™—%¡äbtjÚCLžT+ÕרaÈðÖ-G´s?Ö²Žo*+c‚÷´–¦„ ‹K“©‹}ƒ’WÓäŠu/œèç¥e+=-L¸ä*)8!6ºîèµkE<À–¡ó†ˆX2P‚Q¾Ø-RuŽfi&¶—©[ nV,µx†:4v¬Äez Ùa–˜^R3«”+¾p–îø_ŽâI§ÂêÒIîi#RQU‡¢—…m_¨Φ1ÞšDÖrbÇåkRP¼ Ó§ÛRsEÁŠÀ8Z-˜¡_ \™ÐÕk¿M£§T±+UtDaÀ®Òó7Z6•ÒNèt#Ÿ|ej™´lÙbº JcCÐøJ¬M%é2[Ô%Ý ž4I tj1©["ú.ލ‚g"”S`H;„}¢–òg&p)º4±®W¥¤wnQËs5u*¢ÍÕŠ®Ñk;Zþ{ 6­U1ejm@¶ÒÊX•"¢ÛPd“Ýôôè¨JÍ]¶RÇT~l0 Ãú¸ô%~ÞÌXäëãà-ר”DÓ-+Ãpû>‡ -f£–Ï-yŽ–À(ÐÌÄ·cF˜Ñò÷Ä ®%çX!•ÖôF”]įy³h„ûè$Hú´ò€‘ñãØ¨¸€ÈOÛp’J"ý†O[I ÑÀJÞ1¬èž0ö“iÐ5ôöÏÇ8w‹Þ¯ZŒ¶ç{ûó£ÙÎèàùx2Ú›ïL7è1oðÄ…ì%Ôx`µ`|-%TÔÕûù†_ÖÛû`¾7ž-v÷FwÇ‹£ï.µW9žï.gN`3ÚÜï~¾q¼û·›ýàÿ-àÿíÄFá´­¸eãowä7þëÎ…Á±ÍŒçn'ðŸKߦ÷ýe|þ– lmò1F’(?²Aò£iè9>þµ†/®êøâð\þ66H~<ýÅ–/fþ‚¼8<—¿ ’OqÉ‹‡/Ïåo“`ðþÇÓ_ìøâÒćçò·•&ÿñô{¾Øô‹Wö‹—¼Í´ù§¿¸’]ßkúµK^Vg?þÖoí×­_¶ø"Ÿþpú ¼ÐÄnÆåН1uöÓéolñF'ÔÄ>öo²mö“¾³À¹'úŶÀâ}ÔðÇ©þXÈ“ ’[£ïypâa}ˆ¬¼ÑϯúD¡œÎ ¿°Ú0‘âKÞÅ- "ýû$A É1æ$€“!f[eúˆUxÒ*F|P|KP#%éƒR4 oǤúFÑMÄú½O21‚½µúMi¤:ýïêäè),„bX:¡%¡ ƒ£u‹áßU좈­ 2^CH•¨ëHm?-=QrMØm'ߤ=›£S@ûLè(=ŒUw¡Äy g†³nr …µ X•µcÖ4?Ao,w}Q•qñQIT½! ×0b§¨©‘y$öwx”&˜K–ƒ!»$ÑnˆÐ¡û‰aú(S›X[”0åúaRö>è5%kÆ•äûéá³N3ЋR*Z€(5«Íò ‰A©œ6©«Ö“œ¨úh$Jú£¼ >V„ú·Ú ©™Ó¸è’¾YyÒVV¿ "8H;Þú~’ôQñ¦&íÈnåaLŠ÷gœ²=ˆ‚¤ë˜6¡B —ŠêHb"í“DN"ƒrH²­ö£ò•’|—´¢UD0)ÕPI*âY :Hqç;;lW¨º¥…Hy²tâÔ­NZ­ã—À’¨º6ÄRŠ:NR$Z:käeaz½mÃ7®Œmz6ÄX®Œ3á :yÔDž&õ`A*}쯤ˆE-’S^éÛ -ήĂ4нŽ k¸4ŒÖÕý+?·Ïè¾,貤° ·RÚÎëÊHµnrZ賡j¾,j£ûW|óMˆÈHtë£Jôâ”—•ÂÅ#"uáð¶±»ê}òº¥8‚B!3ð3î@J<3¥Ëš}ÁÊG«È·¥B=/ "î|‰ñâW>X‡·Ña½|‰n‹äÿ +H9éôþÆZ  ’ëöd…—þÀËÉ -Nï +œ ²‚Ìò`éœ*‘ Æ›G@yêpLŠɶ=¯!+Ñ…#N¸’^âHâá-Ûž¨] Eö0ÍYò‰Ry’\Lµâ|êû7”]Ì@‚0®ºP|¹H¤Ùp ´H…òÉ«K1ë‡H¹ÀtHòÒWÕá®çHû„›ÃØÉk˸6 -N•‘ˆÒa©ƒ†¤ #It-Hñ>B(a!Nm#QU Z˜Ð„Ë¢§$ ‰l@«:€T‡µ"»®­„Ž„'™ÒÉ¢ « ïÒDôR!­)l®¶'…­ -,”üQ ËÞ/µKÙÓôâŠÄf(\¹±“Ç$'¥c7¶Îƒä“VWª°qº%v1[=kéâ1À)’ÂØÉ¤+¯µ'GˆNðšXŒ1#׃šiè£ •UArU¸z¼nJ­ýr1k5Ü -')´ü¦i‘ät¸$é£ì’¶³fð.Êg2±; -„í kvÂT::Äã@ƒ@è•ÿ¤3‹fösç4mH,Ë*[ '†`Y3…S=\Erû¸äJrý‰Ó ÏŒdy2ösjÞO6'>jÌ`‡<Ùë.ÔíOD°g‡ÉIs~œ\¿kû‡ã1‰_ ôìT#¼µzøƒð!uµ@FIGi•N¸gIx’Ó:×ïB€[Uf‡)ˆ'ÙòéÅ•&§9’ƒµÈ¥Œûª^"2¹+åê!k#aý!‹5»"\p=Ä›$Nyݤ¤x+¥D½¼œÄ{¤7œˆªü&tЧ˜Ü˜ -š›\«Nòó;9žœôNvZÿ<{X´áäÚÇù²áNîÛimÞ E8Q SYÃi°Ü`ø¹ôr'i"¦!­ª£ êÔÁUá£à”üØ5xy!í¥?ð² á+Äǰ—ÊT($ƃd‚+±° %Ž$¢ ô2^L•„2ýåÕSé)%Ê9{™DÜe¥ž­Ú9 Ÿ½´Rco9Rem<\•¶ ûͦ-…Äí䌧ôDƒwÓ;U?’q%$“œ™žX…'ÃËÂ\:qÿeSî,=]âëVÙbÛ×eOJ¯½@›æ4"ä¦T„è?8°‘C¢` *QÎ9E>+àõB]õ܆d¥Áp kAvˆÄif¤äNꉪ‡»¦ - =쟴ú$ã³”+içÄ2),™Ÿ&†!gSB¢Ž^*¦ ‰%10Ó7–…`öüÑ2å…Ýë÷„lsñ h ¸rOJ&¥'ÆÉ#4a>Å¢C–‚>ôtÁDÔÈU*£Rd¶Õò61ê4æ1² ["6 šáÈžäÓÁ„»W -HP>hì¡UÌW‚LIJJåôHŒœˆ…md!‚^/ØÎUŸŒ }kò…MF–"¸)*h·ÙÞ i•"-<Ù ‰ç“V=í&S–_)£(3µ'ÙN))™“žç.¼¬ŸàøÍt!bßÂrI K¶¤’$“í¦dìénà\°'OŒÈœØó±ÁŦ׷ØX¼·«Þ£†™2ñJM~ôþ®ï—þÀËÚX*ÆÜ¸6ñÇÀ¸šºcœBV䂲À @’¢;Æ ì!‰Á.®Ñ.mô³8MI¤p È8oW„¾I[ ¡¡k0÷€ÝµA £L‹HÄ6¨m=)ze¢óñI#”2ŒSp¶Ò]3ÁÌ¢ºfœàc3E7êS=©í§¨'ªkÆÄô6¸f0P£ù¾Ñ5dVd©kÆi’HQŸ@ ÅuiÛA»J'7xfœ¹0¨è™qÓy5‰èr -ËÔŠ %3Y‰¤è™q!!¹ž™ €¤ž§°¦X×›P%—p(AÇhÔŸKp˜õм“µÌQ~B<'°ôê†î3^3j\=^kê1óHtÂHjçLB ξ5¨{­³àšÄ7ã%…Äà›QÇIêsñ¢BGRÐ'Û¼™Ó¼.×$Þ%Fr’da©”Ž×LȘuMtÍùÌ5‰k&ˆ-C¥Þ ÙhP'– -H¾—x(žÊ»7Ñãâ$]!’âÆ¯šA;QÖð¶6êo•N[ô̸°ÌMôÌ@Ys2!e¹D‰ž™„<3Ns2 e|"þsv›„!µÚ®âŽÔ¡B$yÏÝ(lº&ñÌ8ÍesMô̸EÝDÏŒÓj´®I<382¥t¸÷Ì´„Å'§ÑM)zE …#CiÖÎÕámjñêÌÆ -4­ZçN]3¡8‘k¢k&!õ®™HlƒkF¡¹xþÔ5ãU^âÑmcÅŠޝb­&›¤>—„;¤¾‚:¤íœ‘7cä2*z£ ȼðêpj\x[T§üBUßLw'‹èbu‹¦ aÀ`X‰GC*’=’¹ÈQéK/AJ„4&lÉ%<-5ìºLX¨_H|Ô*œa N•h«jðp¸ƒ¸h5É™= +l7ˆË.=6úd¸u‚¸˜P¢í$¡©…É2ç\¹º _¬K½"š~F9x°jYÉ -e0±˜¾¬ªh¤O*s›ØÍl¨¶Ò$f7­.?$ŠK5…ÄÔ\2c¯_*cã°+ -æC« ³L<©†”¸Ò 1¸4¬d -ȹ ‹El -’¢õSüHT+©U©¯‰–T.ú&ói‘÷ôN É¥Mž-58ù@©ÝìH¯0¥šÄèÕÐ|ó&ñjDRêÕè‰Á0Ä\4¢ñÈ4:ÎÔÈd—í½¦gnA’ìá@ÒykéÎ?¢˜´‰ ¿”[SòÈzï TˆoT·F©©c ©ï ìe‚Þ­ì‡JÛ™f$wI!DFPd)¨P™÷í€z>x›`ð«eüjbúÎIà‡Pé˜û‡ªO6AèJ'EròÓ'9lCb”>₨|Ú¤6ÄpýFÇF))B² bèXØŸÁ±‘’’‰ýÎ&šíOâø÷±à´eû½Žû8ž -qæg'Ê©"©X¨IýQ‰ž &#ågÛ©¤žÜÝj”㸂g#PRÏF¤ÑN¢„Ö„/êK%Q…'jÏS™Òõs“¨ú‘-¾SÄŒ»þ^K‰‚Ù™°w' Ì逮†w…¦”%WJœó6ñlô¤Ô´‰Ñ†fÂýÔ{6 ã³+nG-0Õ[îhàJ®W§«”ÝÍñè´Y´1ÛÓ‡K-K™]ÿu]…Ë9 | ›‰œÊ…IõÎÆ?cîôñ'©k£ÛÓÁt¡F -¦Á˜h¯H~tå+ØF^ú/gñê5–U”Ö‚XÖ‰´†˜¢ºÕÂAF׊ -uâ9Xs’3W3E™A‘©©ÏwD%å7êÌ×%¼>ƒC¸hìàaÁ1â'¢¸Vh­toJy.v9ìaæ´ÉÀÂNïIɉè‰ñäHDHáx1h‘É!,‰åb<¬®,¤»ý‘p E­Ðö´0zꀀ*É|ä8a"rf#M(¥¢œDVݯӊe Kl¥ç›‘’r×@ ÒšJ!¥îCÌ*ékÊз¦J)[ªäzÔ¸@ -Û‹V…@Œî`ðƒìá†1.Ù'·ªÊü÷bN(«ÄÏß -¼N•DDRæ7Äà °nP¢ƒJ|МòÞC%)ùÙÒè‰K×Osøªìzul°öbÄ@Þ^sÑRH*«, ¥mt›y­S+‰y’ŠD^þ@Šòš—‚Ó±6Š(1‘T¨²lŒOHú(S¼íÝÃýÛĺÀ¯–½*O{dÖ;1òp •ŽA a¬Á„E¯e6+‘6Ùüiy‰%"£’ñšùWVQ`óŠUV‰ÄæzB%6ß½ ±¥$›˜Ï1l>/™¬éÅWËf°•½&&[Þ+2Lr0¼lç§ -¦ Ÿ5€¸uû‡ûÓ?!(GÙ÷ö+Œ X[Zá‚UÙR:=±ÒYÿV¡¼bÂdŒ àZ>6œª`’ÉÖPï±ôT¿}iùk -§;ö‚þ²D¤Rðw/‰Šé-€®è•ÚßH®ÕÛBï”8éýÅ“’â•õ"óê1N®;í˯E¯ kÉõé5ô$¹d½Fèd7t<=é í5¨<{X‰àÿ.¼š$B…Wø±^òðj$Œ?—eîäf)c“õÜK­HÉV( )Ù0P‡4XbÐ`~hº­zSû]šRƒF$7Eò2½Ë>Êð¿`–ÐZ‚ò®Ä*aŠäæ‹’p´‹’uQnä.ýD£²ì5ß–ÇMoOðZ~$hËp´‹_:jÁ¡ø\ Å/QI»À÷£¶ Ÿ`&8ú“emT–½° ©\¢De9!eïÒÙU]Ùk… În¯?*V³º²ßF¦g€^¸R¢({-NärU”áJÖWÑâRË'z2Y¢žì‰Á˜¨ÿ†Š8¤ûOpÚNJa™¢RSP6N”°ÐýôäªÓâ­ 5èÉ ©×“#± zrof±V7½ŠN¥pG}Ò9=ÝAO®Âr”QÿMC¯'{HOÛ!A ÝU‘e * ¯¡"îz­¢WF=ÙkH«)£ž<¼J3=Ùi@…±Yø¢M™…Ð2Ùeè¥QQ¥FÈ›Þt’c4 µI!œÇϤ(L­d‹æNõˆAñ È–=\Q5)„“ÀQšÂISjS§xãô²=) ªì‰ÅI -h™ÅILt&Gqª½¬ÄÖÃqYåô=Œ“ÖìËaœjÝ)Š“Ôñ0à˜gB#—I¡€*劽R].aìAËR3ÑÑCtR¯È÷”Tµ ÄÆI/Ÿʼn%IJ`œ¤>´Ia–TâKÁ˜‚×"‹¢“BQ&‡ža­”ôa`ðSìæ(¢“¬“¢åHr„I1u")¢ë‰!ŠNô$c“(:‰w66‹¢3ÓöQtEÏá¢Ï(0c3ƒG«»([ýVbD‹m¥Atá.I°œl«ëï£-4Ê)–k‘ñ—XßCHÁi!sMŽåzÒ®Öeìß&(&GsŠBLß;ñj™Î)DØepNMºÒYè3@ïñv™…¸*¦hNÊ)r8§ _'^«*lP‰ÅÏIÉ–ŠÄ~ë=7Ù ãgYŠÂe¾Š9 ±®fg*ºÈ²:ãu!ûºþ¨Ä:Ó†Ÿ„Ð…Mعƒ’’„ÐERB׃pæ: ¡3z}fÂ(˧¤G -^¸2?RûL—Z•’WÙú{­ Ÿñcß_o±Ö ®¢3råô×€|˜üºðZBº¿Uâ´—I]OJƒÈ"1Ñ_äž×BÚÙňž½-c]!JjÍ©•ëäHÉéÉ!$‡/y8MħÅ5si¡fÊL&UˆJ‘Š‚[œ (Ìd1t¶“£GÀª»2Ù V‘[’cµæa¶³ ¸*ÛV+…÷Û4¥$Hì=ñe½G ~´7Ã' æz«‘³É‰´Š$™æà9ÈŽsðd׫QÌ‘Ä# 5Ͳ®”¡Š®M<¦ Ì!z")•zb$âËziÃq&JŒ ’J^$ç6“pŠp¤iœæ„˜úÄEGù>1ÍݺQ.º21áª" GIb.O}oÂÕ»¯Ll¸Ñ\ÔÛp{RbÃí‰MÈøè§L¨uÄ©¿‚sš ÉR_6¥ h ‡ÇYI͵m0üdD«f˜Þˆ+¨rÉÁ·A7LO>Š´ùÉÖSŽ6Xq{RbÅí‰ñðI¡ŸdŽ­¸™²¥°R¶9]2«~Êdaá sÉ®gÊ ö„T“56ñ ØÁ6Q"«t-kµföß,û¬wzä£ð.\Lq°6MIOJ¦®'F~$p=\ˆ&¼-êpÑ)1Í6Ž¡_~Ê6Œ!Åò†’¼Š1*Ãñ…áF‹-L°ÈF—€‘òÞƒˆJF½¬ÉfJIqFR¢Î\ÿ²~zã7Óe;".V/Ä%5b.3‡@yº¢ÈØ;bÊɲË-2D]Ä›îÑ ƒ½^z€;©©~ðëÕWÁ3|µ¯¼l0p²‚ã-Ë`·ƒj¤F•bL¤ -±‚¨žx"¼g°a¢"îP$B»Ô¸-(³‡×Ešm³üU¡vR{|¸ñ|]QÅ41“¢ô6Ûë³rPàFIC•9Ðlk[Õ˜¤øW¤І¡HYeÆŸoJ?üŒdšÞMŸâE.V°ö]pZ7µô±1ˆJÉç!“ oë§U²Ñ‡Óß°PZ¾NRÝ,_OÉÜŒ´0 Ô†û¡’á­ ‰ "Î4}|H¤‡/¥“b`Æ©è÷„æ'NšïcÁäÎ#žZ™Ì—`® (ÞÖùÁÍ…x.™?n¡,›!šDÿd)4në7!7 *fD%Yš+ÃQâÌV¹‹Ì8ÛѦk‹Š—÷™ˆÄ(çƒ(|aÞ")~$öѲmÈC¢½Ø¤Éã ¤¼p¶2¶)‡ë§’¶S3XüÀvš–sˆ«ý'mZjòÒ¨uk3b[–UÖ#˜sª&ëw©‰Žƒ`öéˆ{&˜‡Ä4²áuÖRR:©‘(ñ>ò6]]M²­îŒ~%Ë õõ+Ó­®xØZº%ÛSñ:M÷TâNÓgÕŸÞoÛ2xDÓÝ !W¦©êÑ ä"ìJOéßÓzI[Äeä‡ÂÎ;ØP‹9]¯³–,Ÿ„]gª Ø¥ù†’ìÒ^IJ‡§eBP­Ú„9ŵT8®cìVS’»Õmµ~F¤Yƒ¼–eQ– Ž7ÀkÐØž3̰@9o,b9å·¥‰'_ö!jÄz¥iÝ )™@„Û¸[wœ8¸EÐ’"-¹9zj¼aðB¹¦û{HMyƒ«Œ‡µ¿ÛúJz–&ž_¡†Ûjx…–n?.|ÆÅÚXĉÁoúF œp_cD~‚< Á‰´ä²4EÃqéÓõp"jíK:³’~Y°o•MÉh-x¬ÊžíTÒ_ L¹Ø–ån¬\°">¶0²¡(ñF[µ\×ÀZ*W¦Ù¯ Ði´qT°¨‹ó9sÆ'mÞîª-б§‹Ÿåèç‘ݽ åµ*(fµ¶‡H$)F­°r¢þ%)·Øý-–R|(o1¾L«1vJü{òÁì{É粯e|+ÿÔÒ—Ž›ƒ9@]-%I³¯pßÁ•ÒxŸÿ¤®j{Ãêº.þ)œum.¿Êù(ˆØ<ù© ï,ïWò¼-}Ç¢°,ªŠM¢W q]±òF5ÛÕéª2ø"Œ4&“‡¸Ž˜Gªç©éC‹G¢…$ÍåW)î›ÇŸB#þ¤6Ôìy7ZúŽŽô´ÝlE›ú_Yij«}´¥¾u2ó2Ùr‡¯ì%CM'®è'®û39‚<×kïB¾Üù%»$ß?ùæŠË‰féBç» ÙùÎÉ·Uì÷}ØèhÕ0ÙÙÆH¦$_·|²“åÎ7B¾K⤢Yºbùr& o|ľù@ÿ×.ÓÙÎïd1_âÈ& )¾ ™§"ÎS~\u1«(Qà×d Ä]aÕèˆÒàúKÊàû\ÎlqõíÊåÓ-wI‘¼]IY}ÒÀŒ†QÞù}:ÓÄ_#ó -RŒò/Súôï.Ÿ —mˆ¸ÜmÚݶïn\öЀ«ž-u:…nQãÃeúö²{ü|lಸl2Î~¯¯—¿}Ù«ÿ¿c“¼¬D°4êáÜÚ2›[›hýµÓ¿%Û,K{)ßkK{1ÛªéV;(Ù<ƒ]”ï²¥]˜mÒ´¿á6Jû_e“ï¯áöÊg0[õ¥M‘oš¥M•í¹t)äŠë½´òí²´²Ý–ö÷¥äŠÿÕ>»èñÇÝ/)‘ :˜ÊÈ/†ìâï.>¾¶¹8¼±»7¿ŽÞïH¦­ÅªdËîó¦Gs]Ó·xíÁd<½;>\ìþÒ5]Ú¼mÊûãÃÃÉbÖ>œînOn§»³gŸ-vw¾˜üzyt¥kæG×nϳÆéýº?‘†f¹áÍ_öç‹Ã¬Éõù|š endstream endobj 42 0 obj <>stream -·™ŸL'ŸíîL¤ÕÝÿ.´£K—G_uáb×´x|s¶óð×½'ó)~÷øõñ_ç³û‹ÝÙa×ß«W…|½ë×,ýÃ…¿îã/üåþô¨û÷½'?N¶/\ÚÜ™?™Œ®/ŽžîŽgãg“ÅèÞbg²¸|òßFòÇ­ñtºûl1Þ¾»­-uC»6²£ýÃѽŸÆÝÏËM/®v¯HÛûÛ¯hÊWÜšŽOm_²ýƒùÑl'oËW¬x >îå¶2ã—Ÿè6å½Y·¸Ï³Wo=O_Œ®Žn/vŸt Þµæ+NxäÖt¾OG'{áÝ;ò÷ØŠ¯@ÓÝé“É"Έn­ï¼1ÙžcãþÔ-ï+Ú dCöH·©²òï´a×§Ý^Ònw?#7‚[^þWŽ`©™î—r´yûñõEwÞ¦>{g÷IwØo=ì6±oqŽ?MÞÔ`ŽÙÝ@Ò_±Eð_XGi/Å%Ô¡8©·¯ôñþìñÓÿT£ÚÅÏš×òÙr0ä²û' 8ùç7ÿ®L5¶?ëG2³-ÿûz?ç5üàÕªÛ­2Éæ5|·LrÝýó’“ü[ž€U,«ë×ÍqS^ý}6ïMvFåµQø'œ‚k£QH6£dâ€x´Í+Îæ1Lë Ò¬{b䊾«èÑqÝÔ{`d\b! ¿©%8ŽËwcZbó:οN~̵Á& ³­5¾?Ê:t¿Ÿ²^¿ílå/Ý:><üu:9¸pí‹Ùüçéä½K›³ùìòèÚ_»ÅëD§k›Ûrøëµ­ùÞ>ÎKwÍtDûn{îÎFÒ@¨—ej“+àzíË݃Ýnëâ…Ëoxx8Þ~ñ½áeûp}|°»v`119ûó–˜Þ[èƒÝS™¹›¼ùál—nÿw”f:‡É"ýk'ØÞýðcGÏÝèÛï‹ÑNGþú£÷¾ÞéÞ=y:ú`tat)yÄÚFüìèƒ £k÷Ç‹Ã#»>Ìv^bbN_–ž>оûýóÇôþ„lÍg;G»‡gDú–ß|«`Ý«ùÄ»úé„[l4ä„ñâÂKì“ï“øbÌs¤¿Lnþ4™ÝÛÙùoß<¿ã˜Î°\/5¦ ×nþ2Ù>Bø>»Še\]®ÙökeÛ.„ù‹c'îukW¬¹Y³ò5+_³ò5+?#+w盕ÿÏÈϯȫÞ§yÕ™xE¶rÂE¾±˜ï>ï̾ÌÎ3ÆKh(í6Æ»ûÝn½¶k=Ñ}){×µG»‡§ÏªKV¥8%F—žÏÿ¾,_¦áÉtÆå­žL: lµ}°Ø¾¼Š§Ã~ -¨†H#USò‡Âv÷}±Ñ(×ôŸ0úƒí)»Û“ölg¼x±Üߟ`ÝE7| Í÷ÇÛ:PYÈýñîâœÜ ¿'õ盋®âµ@ü¿!¿ákê¿f§üòíjÍ·×|ûüÆ5ß^óí5ß>–o×µ9ß|ûpÝÏr~kt• ÇK -ã˰ۓ{°¹XŒÃ4J(¶j]òkÛVý¯&þôA·7~9^ì"î«ÝÃç¾O&|åe™ ªË×òÏçG‹méRü* - ·+^Žü׺M~-Ûfðé­É¬[ÉßÝÔÄnîü$SÑOÀ—“ÅÁ.|×|Ç‹ÍÛ×'ãnß>=šJ˃nO÷ð€F"® @¿¡è¿™±Ù5c{UÆÖ±µÚ¼ Æ€é„Í1esÞ¾>6×Mò-oÚc¸^•uÑÛ²]s½5×;·\ïœÇœg®çK`´¿®W¢ÊyÊçêU¼ç7âs­[ÅDS’3ÀjÍØÖŒí3¶µwý•›­}ùFøZk›œ¯½>5•µRVò5_”ɯ•7~Í×Ö|íÜòµsîï>¿| ¨p¶¨ßcó~ ˜–¯±Y”yX-°•)wuÀZ[3¶5c;¯Œíœ;„Ï/c35 Zß_smUe|íõÉk¬V±5S4>ÓC“vk¶¶fkç­Õk¶öªlÍûÚ¿y­(ªÔh¹r+úmø\åRI±hq¦š¢J=ºéSk¦·fzçé5k¦÷ªJjQÕ{#L¯4©W¡,ÊÔÖÆþµ wu™GŠØcms™Ï¡Y3½5Ó;·L¯]3½WV`YxäMDÆUn™3¯O´óuñ–LjT)[«K¿Va׌íÜ2¶¦X3¶Wu¥–MùfB~ “‰o¦v¹4׬’°~#תi«Œ»Wºô×ì©5×[s½óÆõÖ‰¯*ν ~×ú•¸¿‘äæÊÔhÝñþÛ5ÓZ3­sÄ´ÖI çši¡lõëaZ™³À•«Ï5¿Zó«óƯÖé¯l3+œ¯ßˆwÔ4MêmÓ §ßZòj}uLúAÝd¡'i»5c[3¶óÆØÖé¯î @­š7Íf2ƒT[Ú×¥ë½OÙWåšL`óksÍ×þ|m~ðêálµ¯ßH^•+šÌø_6õkclUéRÆV76ãkîØ/¯ùÚš¯#¾¶Î>xe¾æ\U¾óY½2'à7bdi -[ô»f]kÖuŽX×:ÃàÕUMo߈ ÍE†àQº×Žáë"Ë^hòÀޱ­ùÚš¯ýøÚ:‰à•ÃÎ -Ó¼MÓ\¯3(õ>åk rÃÒÀÞòØdÔ5_[óµsÄ×Öy¯,¯µ-¢K߄ϳ*swcóó -›JhuY¥|Θ•)¡kƶfl猱µëÀës¬N<_‡Ì®yÕùåUë\€W ò®~3ȶÖg ²mãVqšßÙ¶1Ç€¡™¼A½fkk¶vnÙÚ:àÕÍýî ¡g˜¢Éâ3šUIH¿‘µß¸ã€mMj÷¯«Ä¡¹ækk¾vÞøÚ:à<«–CŽV¾.]3ó&˜,ÌÍä8ºUY­Öíür´uÀZR;ƒ¤–êži“5K[³´óÆÒÖ¹k–v&åÓš4Í­‹ª¬ÙÚydk·§Ó£½ÝÙøp2úf2Îý¡Ü+îÞÿ|$ƒ~lîÌŸLF[ãét÷Ùb¼ÿ|w{t}qtð|ô¨Ûv—¹¼Ç¼é’<ÄÖh¼1ÞÝï–àþôèÙî¬{d¢‹qiùå]³G»‡¯Â¸ƒyéÆn÷vv57ß(\ë«®ûEë‹¢!v£)] JaÛjôÅ…•»ž là—äõŸÝ{òãƒîï.ùÑþáÆèÖt|xytÚüêtuOp -ºã!ósåB.†î&ÉÞ|íï³ÝíùN×…Åîì&ä`:þU~½œH§òÿºúáðêìÙ3\¯“í±ÅoÏùŽËï mÞv2¼omeðCÕZS9 ÚÕõo{‚V-èkâv÷ž>=˜ŽîŸŸÌá¤9nšƒÃÅøp¾Øï¯æqÙ+#s;q΃MÿÒÞtw3pÉEÅ.ÍŸ¦ÒܳÃý3îü?S·ggêÝZ;’J³â‡5W_sõWäêØW®}­\ý7gh¯O}¹·ú ­Va ŽÑõ'ÝI¾ô÷Ù¬[ØQw.vv;]qd+éÓÕíä•áßןu_¿þŸk?ov—ÔëMòl’B¸þ|å~)‹ËÚ ß-’üƒîÿ:´äßÁ¤ñ›ñ„ÒHŸÞ$Kx­ÓÒ©›uk eS4‹Þ4¦©<%“Ò™fôâ¥'omzI›ÐN€uòÚb××({ЛVð^Õ:y­mm§}Å3)ZÅ£Åxvðt¾Øû­tÓä…gÓLã`Ó^ÛõaF…ÂQ†?ØO'y|²Øî¸z6 9…ߤ¾ÙÑÞÖ|w²ò5_¦¯1ƒ?Ýow#Ô¿døÄR?°¿;»?ßô„¯¸³;›dúÇÞü'týð ïöx¶3ßÛý÷d©í—I[¡.æ‡ãÃÉãÝ6Ö©LLßíáC7&ÏéÑïlÉúݘ5õÃÑÖ|1›,ð½ýÃ×ɱWžµ¬¿Õy¼ôlgÎô;¤ÛOGÇîŒõų¾xÖÂàYøËÃçãùÏk!ð|ÿ`¦ùçwPôVßn7óýQ8'Ýmh(펵÷gï:Û•ÂH.=Ÿ/þÊO¦3$D–t„£EÚ`û`±}ùø)6ÿÑlOù1oÜñâÅå´Ã?M"CnDd’Kóýñv&€îw§³Úÿ5®ûh÷ààh2º?ÞÇpÖ¼wÍ{ß®°¿µž?œ&|ÿáæÎx¿ûåñ>Ùéãû³ÃS¼¯gW2¨çvýìTÕ³²äk×wÃ6nÝ¾Ýø¸»?]|«ûÏÿUßÌ¿»ýï­ÝÊKøõfýå‹Ãò{{õ­ÿÛø#‚og|8&Ã,‚³ïþbòÓÅä ãÏèK|ÛੇªZÿ®Jo½vá .UÜsæ|ˆ.ÿ§æÒå©i×§æ?÷„}¶˜LfW4bõÊèÞb<{6Yíï|®°ÿ‚3öä¿öŒ¹×yÆ~·pÉî‚ù4’o;êøhzø}¢Ž<ÜÝÛŸFud¨¼?ˆzæ1Ñd«:nô>¼ßq¬Ã¤77o·oÎv6‡üÞ(”ÇÏî/pXfÏ®^òõIwšî§“Ãà {qÿ ߌ.}û û+_7àï±:_\R/ÜßNsé«ç»‡“„ݹ›ÚNUR¢Ñ‹ÿÒÖÝo¾=˜ìÄÆ)]ÿd"K‹)’¿lý:0Ù®O&ñóéMfÝ æß6 …JöìöÖGåîGð}óQ[Œ¾è~ ½e«Æë»>ê-¾ù¨#}ñQߪQ ¡oÕðey+È&MðÕ´Mé—>Ø‘ònÜõè›´B÷cçC#›LèG¶Ð&Ù÷6|ÚÆ¯nSgj¿²z¼ÁõsÚȜƖaN—5ª®qËqÆ)+³oëÿ埯ÓùhøÙŽÇÅvM¾îÒ†Ñ{㣚Í0¿KƒIZq(\­¾• #(â71λÀ7ù¤‘(Qþ+i'ÛÍ¢¥õýpMÒ¹~Wæk–´ñÅÊ6¥lñÒ÷ëQöo+}ºÒÃd±Ï·&‡k³£€±ÚlÓù0Z·>lÑÒ¥-®×†N"Û:¼ÕùðmßoSþ¯JfÑÇ#Téúа’^Hcô¡bcŒÝeÝuÚ8鮋MãŒjêtJcêlFËÐ…&éo»Ðdý _—s‘ÎCåÑ.ÓÆµ®Wß°îO™ON6w{Ãÿïg¬Ž=¨¥©·ÆLt8 ?Ep´Ö·„ÛAâïíw—ûÃùÓCi5úr÷Ù ×Ï©/?þɾ»¯$ïóÔÊîß?Zt7ùˆNøé©/Î[ㅗÍÆ—³çäŸo“—|?ÚM…[tÿJŸñýgéÝ*.œón±Óy£i»ÿ¸åvmÞ …›f¹Y“7«ð¶z¹Y7óh¶¢sÕ Ùr Ÿ·(é½Ë›Ùcz_æÍÌ1½·y³â˜Þ›A3·ú£>¬×õÅî³ç‡ýŠõ‚ÏG='Þòõ@¨ýŠFáŠI_6*’{wõ­›\Vý¥¡ÕðÒ¨” søLµâ‘û×£ÐdË^ÚLÅÐ Ý• "ëN¼Låèè[]ı‡·2Ysà*X’“ùÑSEå7…üéá¯{OæS¼æÿëæåèIÇ@º3»»‡ÿ¿5/v¡ì0û¸#\çh®=é”ko<=êZw"éâÅÁè»NÉ|8ÝÝž|wù²~¤ëVú‰ã»ª³yc¾}´×I·7:eóÂÅÇ×Âï‚ßm -¿}÷Î_¡þâ—KoïhÛN… ™!¿ìMg]ƒ«ø¢k¡$]|¼úï?!?\´ªò¦>üu_ÿć=Ø›ŽE·þO;`^­?iÂûà Žðï߉߽or>èÛl?ßî,&3m3ضIÃña÷ñ'G‡byÁŸ®,åýƒ1q‰7¼°gRÞc,gœ¤|ìOþÝnÀÈaçò®xÙIÄ@ç:äès9¦¼ËÏ'P¢Ní§)ËXbà¼löÐõåuøPF§ŽÉ·}¨Ý9–v~yT¿ž:¢«¶°z‹ ž—!ýºR89}8Æ6µóÕ9Î/«†ód~ØI‡w&Oï-v;íðô#|—çdTË8'wmw5ýÁ¯Úßî¾ù:ŸØ²­¸yÛ˜ÑýÅä`²øi2z4ùåptsg÷püdwº{¨§¥þf£Ñ£“ÐŽöGwƳgGãg“ÑýùþѾ>P•MŸ lg?‚“>—c¹´¹8|2/vFfP㯠÷«ÊÊ´ÕÈx¿ÑµBï1~áÁdúhþ@v6îÝK÷ç»øÿjôF¨› [~tÕ•íFY¸¾3¼ÁÆ™VuóÁ`no„a¨Ý­ïyÎAp»¾|ñq]×ÅFÙµ…ËŽµ‡òü&{ñàh:Yä'V9|.BúõÚZÌ÷7“±Dñæ’Ææív4¾=ŸJ¾[Àðwæî»Ý·ýëè,oq!‹~3µ£}Ø»?îM#&ÈÅÇeŸ*ˆF÷Ž÷GƇ“Åî¿Ùpô`r0ŸõÏŸ=swrðü”'ŠlW‡?ÚíZÁnùá -Ârlã³N·šw{¶˜‡ý|éÛ»“Ý£½äƒß/ïËU§0‡“Y7#ÐSë5_ÚïìD&¶y{´yt8»à¬3ÙãÔ!ljŒ{ïút2ÙÁ%ùeõDqë³éüÉxú`²4=H·aòè£ùþ—™t/ƒ}±y›™Ð9@Ø\·?úW¬nvÿÆ-´ì6ÆžuI(κøðèIwvnÍg‡pò—4|÷£(ÔýÆ0Åèi<Ž n¡«?MÞ;êÖp<Û.Ñà‘Ž)Lwg“Ѻ–¸ÈªÆ‡ÝØÙq3±5™N·:íqÅÝœ¾r{º»ßíSX[éöé³n³h'|Ó6昩Àν¾¼ýïÞÀü§Éb¾}uik”dZùê[Óù|ñ œÂúð`é`­| °)Û5n£Ø(I°Û·Ž¦ÓÀd”¶û«n‡ÊFYeU>Kx߱ݠ<¼?ÞŽçˉÊxü$¯-}-ùaze&çë:ñ”¿œ2Ii÷N]¡tõÍP˜>ñiïaW8G79Œâä MÉ;Ò¶Å1}ínÓSZà«÷»ƒ:a#9àC¤âŸüçàåK¤¬Û³É/·v§ôoøÈÃÉö|¦»¾1­5ξ‡ã(b‘°“+øü)Ê%5Û=xÞ–äîX΃'n·'Éî¤Æ\ÉAë3+®ìõ´wÇï/w'?wCé„äÞ=;ûèÞ+¬3ëT·/1 î~O uÒ›ÒTg~Œ!='gv6a7ÅèªwæñŸ -À€< «6HSÕgÛa*,¿!ÁV>ÙãʳMP¿žÃ:ž;ò™Œ;žyƒöŸ[Ú¡gÞý;òM±dÉl'Ó¿Œ»;íμc¾P·á~ûFÚ2ýó#ø—ñÕð§kIh÷ CÃÈïWPøOy_8{×zÃĵÝÙ‹éÁáÆv/÷| ß»={1B@ƨï*eù¾C÷w™L»Ex:Ù>…5‹ÚË¥Y¡v½yÿá3£N‡g;b -YRIÓyÁ#÷â’&Yt‚º*|?ΟÌ÷EV»–¨ŠvôùüÉèžü)Q O[šS¹ÿlïÅÆó§O7Ž&ÝQ®8¸|ìôoïm™o~ø|²¸|ìÛ“–Û{¿¾ÈïAÃn"&q×_ÚšwÜwgtëÞg6ËvôÝ¥Û\}Õ¾o‹Â}wyY‹^ú4“»·×=~üH;‹ƒÉ bçeÔùô¿ìoÌ–l7ƒóý£Sœö†íùñí@8¹Á|†ÄwÑPNü–¶LÕ“cZ>[ A¯éZ.†-£ò5h8T·äf[ú`ÊI@À]=Œ®QwÂòvɹ6}>Þ™,&'¿oj¶©`ŸØ¨›»Ãùì„ùe›FL»ÙäÙ¸·ÈÓè «Ž?yèx¯s¯zÉÓÙáÆA§EŸ004Ù™î/žÎ#7[µË¤Yäª•Ó Ž´‡°;p$ñ3œ¹ù¢7ôséDKlŽè“O/Ûg³ùá ;Ž‚B|Ò®“†G³í3èp¾æ¶ÓN,¨ˆÇ³útbÏÐ<™Ø3´Ž½>C۬׫ï'ØžŒ'?´[$©P§4Ýï¤ÝÙÓùi_^$#9áòÔ6±Îár»ÉSXu‡íªWvW~wd÷ÆûC†xÊFå7º§‘nyö'ä(ô×ä±Ã<©x'´ÉnÊ•K¿½ØÙ€™h:ÞßøéŒíN˜-4;ì´µ`—_yÉwm¶·g'ñ/i³?ÝfÑÕ74O'¡ï§¶{~ÚÏNŸ‡®Íóh;^Ù÷}u.<Ânÿí0žî‚µÇµ›/€Ò{Ê)Úž.6¢Á£œÔ6ˆûóÓDË®e?œ²qVN­Ð‚È¡:CÓDf\u_ÆvO4fç¤îLvŸÍN›ÉýýÅ}d'm4z>O,œÇ7ûùlÍÒˆ˜^Öó«K7&O;¶3zòëèÆ¢<§0˜îñ~}šbµ´Ö5ÚÝë¸qv#ùàŽ=®qr/çqMãetZÃþ&„ó1MÓYóíjvŸOÛJµ«k²#ŽÝxj×è%D•ƒÃ©6ßßß9þÓh¦ŸŽíÎðæ®-æ4 ·8ÃC8(“EÆrb‡¤}Pµ’Ôí“4½ù™&ÝË‚>5:¡{áh34N̵¯B2Ä’FÝÛÚ3?²êíŸs¨úâY>Nw‰Ïˆ×ñÚ—â2ºžê0¹/™†õÿ<ê¬cÛºqûÛE}áâ ÑgŸ~¼ýaýÁ?îÜÚ,>¾´wWïÝüòë¿H“­+7`—n|ñÅÃo¶žûÑ[Õ?ªÜ|lÞûòºûÇÕ+ùûŽnÝpúêÂÅë?NßýÛÍñØ?¹±±ñî³¥ÎßÙù¦A}ãÖÅý?Þxâ&—_ö§woÝøñ«Ç[»?V×½uÓÞm.\¼hœ{»(~xçÆxöÙW·¶ž~þ8ûèƒ[·_~ë9Gpë‡Ç÷Žn<ýþ«úúôÓé×ï?½þüpëyý×%¯Ú6wþõ釟¼ûÕ§ïlì]¸(“ùÏgÿœw?½ó¯·wn¿uýjóãŸ6^}{&úz¼sÔþxé½í›Ûþo—¶ž»Ç|¸y±|ç½ë½òÃ{Ÿn½û÷[[“£?üåço?ÿ`{{ü?í¾wóéçüò…‹…)®ëÅîŸ~x÷ŸŸï\Ÿ^üäÝ«ÿºxô¯÷>}úãÃ×ï|÷ìÊ¿ŠÖ`,—?ýàóçeõÁ—ߺ9Û~wï½î~p­Þûî£Ýº¾vð´Ü\lß6ï½xßȨº7oßøüàËí olÔïNê¯ÊbçýÝ­kãO¾0w?ºtõÊäú´¾¿'cùæÎÅO·nøöW7¯´þ Û6ÕÛ×[ó¾÷á—;ß¾oŸ¼ý=_ûñìb7´«?¿Ým´¿­¾ªþ6ÃŒ}Ü­þ‹ËÕUÝÎ_îÜ)Ì÷oß½qmüá;·Þzï ÙcÝä=ÒäÉÿÝvüé½o}¨?}øÕõÏ¥±lC´³ßØÛÝfÿºÀº||ó{ùOÿüPßóÕG~°óã_ÿÉU]^ýÆÕŸŸ8Òü[×o<æDw369øÄuÛïÇíÍG7~|ïÆÓk_ü«Ûcïþézõäïkï_üúï›÷¶®ß¿ñôáî¿>ý×÷ï?»î¾yôX¦2Ùµ_oÞë·óO÷.>ë¶ëŸM7ÿpáb]?Ø™ó‹ü^óð_{_lÞ»óç/nݸ¼ó…t/Lõ6wwu¸¿ñ·÷>ùrü/ÐÇU3þôƒG‡ÿ·ùèóãU3‹;y&¾^¼^öðûOß¿·uxó;ŸOßÿú£âÖwŸ–Üþðá­ÝÞøó•÷®ÏÛÞ×}åô¥•½óÉóƒ£åyúüÎg›?7Ü/¾·ÿÁ·.=ýbc³øðÑ?¸ØìOe6GÚÉ­Kïì_Þz^=xqó½;·ªÛÝÿôË/>ü~ù+÷ÿÂ…èÞråÌwõÉŸo<´ëv²ûzrïÆ­Í“_\¯ÞíF}ùÁæ#;¾¬cÉìþ_¦_~pû`þtk÷`·ºñôê·ŠÏoþkëävÝ{ÚÝ sÝê^[Ýý!œÚoæÝ¦º¹ƒCugóÑÝ÷:öqÛ±‚ÇÏe5Âìm>úêO7>ïío…oÌ®<ÒÎ=}÷/oÝðöèÂÅ­çß½õ~{ÿëßlýðâ³'æŸ;5_ÑMõ_¯Üº|xegËü}ïàÖ[¾ÒtýêÇ0Ͼ»ù¤øÐ‡{(áªÝŠŠ›Ûãé®r˯|óÉg›õ?ß:è.”£ºoyóɵoÿ´yÿÊ'ßÖ_þíÛ»ïݸrñèÖ¥Ïvž¦½:½ûá•·w¿Þ|ôè+Ÿ~pç­E˜Œ'G7ßÝÿ–3rá"?6|ŶŸMŽ¿sþñàÏãvzkþTÖàÑçGï¥h~x¯[’§ß~Û_7GÓo»»òáŸoéßÛÍ>ýð/[]£¾ìXÁw®›=Þ|xôÈõ÷>]l¿ÿN7óÿ°Ù!½ö͇ŸÜÛšÕõã·‹Ïÿ¹Ùqþ/ÌÎfñÅ¥ëÅç€ùÂ<»ÞýºwShK ¿J;i¼«øâò'ºŸ~¼!M’W]¾þ°ûŠ.ýóÓŽ:þT_qéëÃ+Â>qçÃî×ñ&ì~úö“ô¯‡÷>ìšüý“î_߆¯„Æ‹|àÇ­Žðáßô+ì™ô6öìÒ³-ýz£½ýç'ú,ûu¹½¯Cã³ö­ÏØŽ3öíÇaÿ?kï¹¥6³E‹>A?‘ƒYB9瀄‘sþ³Üg¿EÛn·¿°Ï¾wá6´ŠªU+Ì5gIÃ|mý<ñcÚÏUÞ¿ûfíoÏæêo?ý˜ùÿ¼Êç§ß¾mèÓäÏ•ç_îÿšà‡ Ÿþ|oü׿¢/Œ3ÊOÿˆÑÿ÷0ý^à¿Fãw,Þ«ü¢ñùµƒŸ¿û#ð¿½óm–?¬ùÜË•þ²wOª¯äû–Y¿¶ö¯^ü3!?sì/)i÷¿ûé[|e–ƒŸ>ŽGíÿ°)çõ?÷òG¤Á§Ã?ÉAž°ñÌ 3+ pÂuõp&ŒõFbf²ß€Zf€ÐK‡»Ìôiå‡DK€.ö„TÊ ~tñŸœâüŸ¾Qò~£”úÝ!?ã¢áôâpKÿ\êWC|#£7¦„{~ö÷Éhèd²øhû2Æ€äßÈïÛ §+Â8SêÐŒî1 Í'ô>ËÒå0™ƒŒ^ç@$ÐnDà &mJè%RÁäfgaD3,r¿ùŸý¥è§u#ÔÍBbŸÔ~òîÈ¿øÿ/ã èÝ´:‘ðOÄL±04²îV;¿xT#?ìÊ…NÜÞ6ߌƒÞï[»Þ–ÝÁ'Ãù€[ØîÞøWŽó?3œ÷dBï•e¨# -ÒËvEv¿˜R¿_þ;iû)›U¿•wį­1“wœ;oÅs;§4=kf¡·ª9³×d\o/O‡ÙüN ¿ñÍo“õÿ;§ÿ€ÿï°úÿÎé?àÿ+¬¾ô›$üIÇoš“sþû½ˆ¬´Þïhýé'.ü¢ƒñgÚ#h†˜2Ñ:}ý™•6|øLØ¥š?<ö}Ú7+üÞ$K—Š1Žf{ög4XòØØLN;v~PŒ¾?Éÿ}å• -öÂààžz÷-{àUñùN/têÊŸÓþËìf>%Ìã°>»¹¡ c V‡vÿÒ/V~œu¨ÿE þ5mªãôIDÉaø‹MÊ`»e‘<È…T¡¯­ë_Ño¿ mDª½7Z~]úŠ%2ùˆ€•QáåÔ ™.–¿æi2Ñ$1ôùS2ÅT%h5’@êÚP«æü“òC¿C_>LèŠé¼+âÇ%0ϾöÓË—4Ò<¼b ‘®å#ûPW÷ÿO+¿yòß×þÿ±òÏHÿ Â:È 3{×þÙ¡,Ùd¾Í­^Iެ¿H-ÚÄ1"¯ _¸B‡÷¤À1ù\±jÑt·¸<â9è/_ˆŽïï8ê€Î0„΃ÀêÙvWDúçA'Ÿ‰]‘x»¨ö»ÿ.k ôsÜ;._Nøc²ÿE/‚y¸ÒWìë³zív!D×=LÅ¯ï³ ØÿŨ·` ‹8s [eèn¶Ýþì]ãªû—Áøa%Žþ($û¥Œ{ŸÃüÃHÖo®t©²uù‡? =‘ ¿ê¸†¬žLû§3ú7 åMbx,V¿ò.ÑŠ¯›2Ê9Åwô­·’ó©u9ýðmO¸?>`»_m„n£¡ÐÑ|kÆñ²þ9™¿à~¶Ó¯qÂ<.âëgSCVë…õáo -ÒVìÖ„žFpðÓ<2Ê~øö+W¥÷Y tÎÖ(Ú)8ÞýÉ~ž*Ÿ"î'Û‘6 å«ù'ïùìA³[£Wt¤ÆSÀ:oZyX@B¢žÅu`«ÛúÁQ~._{·F; âŸv‰9^™ÐóÚ†0¬ÃÂì¤IÐåý;[ûÑxz@P¤<à0-ãsÀó®ùã?MjYé”ÉL¡´ÿCïÉc+U¤„l.û[†þl­ø0Ú=ðdg2·ÿ°h¤®ñ  ùÆú;=?J×) Ôóù­#K…ž¸I4žc˜U.ýq!“ø™ŸS|}žõÉûõº½Ëè2 E‚¡|.ÿÏqùc$VèL.Aø—ýÿ1ÄÉ7•EGýËÎ*ÊF›-ýh+›ÎŽ.^Æ(ÀR }+qŒ™°§g>ó¹ÿJË"è@Ôžy -, St¼žö¾±ÑßC@ -¨€Y#:½p‡­ßÎÿ…üÈ£8þqZ°¿g¼Še#ÉÈé7Œ(!±FþqlüRÿi‚_™Ü·ë4 Ö“Yü¯?ÂßÉÂ=࿬ßkÔ~‡û«¡H¿ÉÔ¯sËŸ3ãèȃ0J,?Æ,%_Ã’‘%Šç½ÔgjR«n”$4}¬d$å爫§wù¤>iÇüuV=&ÌøºþAs«½ùÍYß( Ö7úmûMÿiHDŸ)æ+›37„eÁîü-ÈŸ©ôã’½³Âü‹Zë? ÷PVÐú7+úm÷Oºðkjm…3ðYîú©x>ã÷þ÷¾E»¿¡àÁŒVä €Ë±”ŒÿÖ©ÔFÿºôÀ®xè¿Ö¾ù©’ú­Ñ6X¦ÿëH XÆ¡ï3Ø3 øhù_1WÝš÷/™ü5èW˜†ð7ýös§ìó]*GÐÜò7Pm¼òÛšù›~]WŸtè—B÷ýò9è”m]Èc™>’û½6RXXË2]ŽšòCÇâç qœ¦Mž~¦ùį•…U6–¥a[Ùþ, TØyDHfúös/?Ñí-R׋§ f´8¡ßÁLx=ÿÌ–Ë…ÕV¢øÅ̢ꉭµßÙûF˜ÉßÏSÏž€‘C*ÂËR€èC§øì1Óc¹òmðÔ¤ùf;*˜ä±©~'ͯ¸Ôrç€úœ¨g2uÅ -ÑbÏw• k~î²Ç3púa¹üóßÃü˼ÕŒ¸w"%ßô,9ìêµ$M7ùÖÈ?ï@ü0êˆ>µJ\‹ïÓJ$>²ùKWù] ¿¹úç]³Ñ.™jü¤¶mZvÈëwß~A&µ§6ÅÊvè¬mòÝÀ¯ÿPÝÐGsã&yò·A`ã¹Ç[_LðíJbFÄd~”¦ ¥ÉÕ¿¯ò(/%‹µÖÙ‚€¡Ëß;}³¾ÿ©ýš' Âw+W€£ûÛosdD ô…§FUf×<¹žv¿gþ±—ÿ…íüAif/\Æ“oÛ}CJ ^œ¿ýu@#ýd°„ù v °¹c »wÜÏ›¤€‘hÿDÀïæÏÓ±•O3#Ï¿oõ(í݈¸ÓóYýÎÅðEm Ðß³P³ zÑÈu ¤/œþF~ìê‚dÛ{# ätÀþY>íÏ{$?îN-Ò;Øÿ`¦–Ä„ÿ:7“{v.ï2ÃH9—Û5ϼ^øë¡qU!À»IÐÅÞíû*_#BçZ_ÇÆ÷Y`†^¼’QKåj^+ -fZtÚÿ4¡Zôå·ñ_Öø“™ÌÌÂLi ˜ ùbrwÿƒßS\,q¹=*„–Z¨ÔdçCßö‹XéíÈÔ:3Hÿ&¯Ÿë}âØÏÁzÜ Ÿ£@f5òëþ0öÝøã3E/ââò}6>øù»S2ý$S ÆÝ?ö2i;Qé}ª`šS€ãËô;ÓçÄ…X1 (ZŽè³~cux•3mÏŽ?¯þ6þÇ*µ|6÷N{N¦–ýóˆßaæiùþý “f¥k€Û*Å×ä*• -ÚÈ*¡õ°Ä(‚þÂ=~Ky…Ïc©7ËìÕÛµª “ -ùý0F)÷Ð8² hQ -zpÿ4  Ùݯ.Wm!«ç°…ŒS®JmˆÜ¢ÿk©Sp+;fßg¬©ÚÿÓÿzäëÏÇA©Ÿß*õßøJ$Îù öLÂå„ßï×èâ5=l8öÄÁU‡Ô!‰fío‡ xx΃î1|b¯6©þçó#ÿ‰ÜŒJ$ò™d"‘ñûºòóÏû‹° -ø7Gðz¼¿k8¼ë< -^Á½÷U‰%‡+öó+³àÝûvÏÆ!x‘΂¿ÓçûmôçÛ xµúñ¶Ñzæ&Š*x‹•Vï·àê08~½Ÿµ¶ÁçÛ71Å|0×ö%€OFŸ–}ùOK•î^uÔžŸMßÚæÏ=}Àß6ø¡“Þ\· #I@2z§wðZŸ‡_2¯OƒQ]´‚ð -<<ð³I4×2X7CGdÆ40Á©rpdþ“ÄÍ;úéÒO÷}ûø¨Ûͧ€±6ÙIˆ`øGªuÇY8ŸÞºð1éÂi·Ù®fHdÖÌa)»7Ëœa«õ|›¹^ rJ6J—-êdºYj“õsÊ byæe3¾öpß‚Ë*riާ¨óJä«ýIWª¥•ɹ¶(=Zu!®yXUPØYgê ›AÙfWΆ-h?ZÓ„á,šVÚex ÔY[tÔMpè6Zåg÷6qs=¯œ½ß½Î %†Ø :pù!Õ¨=ˆœt^‹…å‘Ü­ÎJÇIc´‘JD¬!]ž•·ôõZŒ@¿|¶¤· v%Œ׿<>oTb~žèÄÙ%¦"¤@T‰¾‚éR»s§¥ËËjÈšŽÀ -r±VÊʵ •ºöNZ*q!4/+îôÆí†ëû5üxS2³½‚b†Yáfµ§Ë¶urMÉ{ÀÉž'X{i· CÝc'ÅxB.výÌIÜúãK[Vª9>1ðYÚ Ó‹ì*ô¹ÙsÒ,SåÉn—éG¬»R¦p‹¦^½Ì°õ>7ÛÚf{Ît| ½ÎvìÀ%wQ=bμXnFÎ’ÐhsùÀYu¥®zܺX+ ÖéXÛà–möfb½e8ÎÜ%ë»ÞÎ8Uà}¹ßó÷“‘ š°wXeíØ_Ãã‘å¢% ½Óý\…Î29Mï’Á¶ó‹­™·k¹ŸH^ƒš¼ÕF«øÉLù^ÅÅ×ÝkMë ħße9KVÊ%°;´¯*Ï”¶/íáÚà]Ïìåš´Ý6SÀD73kEì@gpöAÅœóbŽùÖízËŸo|Uyh+ñtP\+ÅNïå´À®Ë¡Ð™Uz+bRy˜9 ¤ÂšB±¦ÕAC¥Q•–ãV­¬BÃÚ*ƒ ê ÙécYÈ4Š^YµA4—³&4w_Í@Ÿ4[=¥J´žf¬âÎS·qƒ‡Ú7Réw,j:é6µ$Ô=ÛF¥§-k½~õ¾“ûû¾ ¤éè6¬Ðòpm§‚$™ãº]€)Óq·9.(•8"Ê=¦ º¯ÏôÙ·Œr~°l™ÚBl| -ŽìŠ%>aׯ¼}º¡–5CaWA‘+®”Ö·Éç½’Ü$ؼdžÂežËX…ŸË¤ê½ü¦ÖL(²¶/ðKë‹9#=↷9ÒfƒâMsW«®,¶žMÛ°V!m?GÆNsWcŠŠ\‘©á^¡\»z³ìÐò‡³ë¿(7všÓç4›æ98Ók¨ª•Ôµ¦ÕÉ.g°‘xvãiùº+M7dÏš Îø1/©ëÎ|µ,G1yÈ¢¸ªú‹•~F–<;ŽV¥‹¯Ö -}Z [AÝTvu³õ§‡x+#Wm‡VÐn—=÷÷âòdÊøl{XétäûåÉ©,.¤Ó+ÔÏ’‡í/ȬÌ\vÑëu•ax« -,H'¥Üîýy˜¶Þ¸˜Mb‰ÅBRý†îÕÝ"åã³ܽø±Zai»Ÿ¬eI?‘ÙO}#ËOd.;àÃܼˆòäÓ* 6w(x-D,6pºxLÇfIˆ…r^-9åhs(WúIÙ«Üsù@K½5wQ„£JèRy¹ÕÑ&BjP]Šj®Œ5ëÍbe_?ì =´Q¸Ø—Æœ,LšÄLZ@q´[^nWÁñÜ$‹_:“t[›î2ªP-vvIªÒ¼°þ÷Š@õœÍöi1™p=¸É^†ÃÉ%[&üÙ­Ev& AÞµ=?²:5•jÀºEƒC+‰‹ÉTò;™Yå'KŸè—629.,¹'¾+e¼˜¿ÍBЗ~ZD%,® VK¢n'ÃPs$û4|WZ뤜j^Z•"ÉÕŠu¢®Í¶>Ôºœþø²aO7g³ÑMÍcu-Ynºv¥iOí5Ó¸|À_j`n‘0ÇîªÜ®y¼Ný²tšøÛ×Ô«ù×$ÀbëÜîúúƘ“òhöÙAgªg•ĬÝÞFsÈ©óóê±H¤ï‹ºÐqw‚j~ÀËñ®¾\‘‹°ºÎãq½†5o#µŽã-úØ›Û#DõvZ¡{Ü×ãú~¯tºQ©”…FerŒŒÆýÔ‘©ëéZ,Ïz±f^\t»Ä¥ûáÚ#^ôõˆå€¤¾~ѹR™ê½g%›÷iw¤àÆMÜ Mï¹­ -³Ë·2mâä0åÄÂ7Ûö£›í`©“þÎËD—T/MgV½q†@ú-ÛRûGx~Â¥6Ìnò0Øæ>?‰£B!¤Ð+ð(›\©£±E†T+s^S{vp¡YË¿3)us´iwak‰Ã‘]NO[Žh,æÜ=öǼŠðºP0qVOPGl´œ¸›'ö-“Žœ w#Ù¨`e¥œ -®°]J‘Ú>eõðjõ5.s@ôa§u{IÝ*5<Ëy4 Ô¹š.™²ôÒ¸nWʪhÇ‚³uˆF¢é¼ºÆÂµ®òÈ«?foÏ`_Ìãbm=ý€ÇñZ¼Ÿ½…™[ §LdOF ¼erè4šE?¥Éö¬À½š³µ€tærqÉÇõZ0¯7Ð8=ô™ax€W¹ÖÜ_mÄÌh­c³Ö¦#ÇÍmº_»æ§ÒN¿*‹=‘œgµìmy„CÑ>žn¼qš_oÓ³Ûí£¹˜ô¯d»y½asbqO{«Ìý®£÷ûP—„âyÒ24OÆîÆ2øYš—…Dt7’°>Þ죲.™ÔÐÀä?^æKB›†µBÖzÀBå,>x×Ïûö`2”qÛçM?nø¼CHíDâý<>âS8t½§aóÛr5ÛP(&]èêpêE«p±®æL‚¯ž…Â|¢<:ê¼WwøæHŽòˆè*½†ðÌžìæZk(W{*ÁO£ù!Ÿ-:Ò§ÜÐÑ]yÐ;YÐ%åsg4€üÄiJêSk z%ŸÞI®Îjêqýjñ]{&`Ëca¨ ™?+W–Wr¶ýLÚ¶8[ÝgãL µïøpÊEí^[kºù‚Ù!îÑyɃtídjϹhð­1¶Ìåé>ÐÊõÝ8º EÛ”¾o•Æ1™EÝ˯S—äFÎa(˽̪Õ3ñwÍ? íF"Ó™ò…Ç̼Û²<[;ŠÃöv8]×~ˆ'Ç4×óº úª“º35`h<êsnocVqŸ»œîÕ,=\—Ôò"s¥gf²âÜ-NíÊ>WhO—ôƒç¤žZ̸.ïB̵‹‹~ÅËœ× ‹õiz·°›œòs.tnŸ7á•ÚnñpÂ%5Ûžýxïeå2\«¨<Ç›îär’«Ñ:5úð½{—oü :~'s³f'e[4s2÷6«ëŒ²xr£T%ÒÍ—£S]ŒÇ'X<«†Q±¨Ç÷·F´ʨ/üêšCé”T[ •スÆrÊqkR Q´Ée8s¤|wDŠS»‘Q'l#ò|Gû•ÖsR噘W儨ãÀ.²„;.‹´$âRu6T—ôA«³UGíUnhOÞTú0VÝœNÏ@:ÈmvRaíT¬'õ„$é;–¨üeÁ¶²×¨'šÅ˜ ¯—aW¿la(‹B¯R’éT-Ô -‹-aõÞ½mÑX·smaK'…Œ8šÁ²¥A°èvRÛÅ9j,œF¯‹YL]Z›B©búNf4Z£7쨋2. V€:5:+Î@ô¯»DOxÜüÉ>rïZÐn²u±x«¼ÂT´^¸× -ÁÍC%osW¬ðhÀ§ ;àbÕÐôk 0ëÒ:½É…%3–=±h‰En!¯Â[“0„uÅØÊòÆÑ¯ö^Oa4’`Šy'á—BÓ§ -Õ;’,˜Ç…™8iÛß'Öv!ž§([صM$¼d'*­ø‹@²CZÊÌHz\É´†Bµ‡/;¯wvsñNý€…Ò=í¤‘–”ç¯,¦f•(–aYÜ¢ÇRÌ”ã[ -OÍ—P^³ýѳž:ùŽR`joUmZ9p]ƒFà.·¹ë¾Â§¶÷J¿Ú„ÿŸö “€’*ý%âˆøMÎs7¢ÀÈÒ ;b°Õx¬ åÌi¦§å5~r­«¤ú¹^uæN©xÖÇ¿ÎiƒÜÆ¢ˆ£aGÖe)5ÂVÉŠpOà(¨¨ý*òÌÈÂiR<É*žRÖA;étñ¼¼"yhd;>Oýøä¼¤MéjóÈs6s‚Ò!/µåŸ§S«K¾âÜcñÔ)[ØJq@JƒÄ&>›&£UVÊ¥e\ÒjMç4¾a^MêêÛ,Á¢³º»¸•Ž *çIb,e†³ÖŠ1g'ô¼Œ¯ÙÜÒê•Æ²~hžÝ¼†m§÷FŒI¦¬N¸)tîg -‰—nÓÅ™ñxãÃ"û•gµŸrÃ@²*òÕ,´/hØ¥{o´Ô׈§3rKP.©d™3zI]K<¦äБ®-n»ÁMåli¤nCI¾×Q4µÛz®ãÎ+¯Š³ -qÔOÍ&g“%Os#7§õKÌÚç3%Lë#_Í— ònÙ‹ñY°/lB¡Ûá!UNz)1?±ƒõàb d@Á¨¶¬ÍùaFiMnY¹:`’ê(1#‡IO¬JR^é)C1°+㺶”š‚[VFæd9bqk@¡Þø€u{Ë9'VZG¨!êŒÓæšXÊ]áqÅ WóVÊLÆò«˜‰äk¹…–8Z‘ üúÒÏõçr¼hµfV®˜-hÍüa"Ëå\ßHû­«r'»ÙÎB ǘ'…É—ÑæQ©qGÝʹ™<9­Ú³y? {‚‚•µÃèé™ç‡Ñ³W á¡hžÁËdŠ[FVPn7Îm«o“•=»…d¿ˆˆ;héŽ-6.Þ±p\©Âeõ* Ié¢+K…'ÅâþyVQ¬˜<&e£õÊ–w 2Óñö$×ñjg†”Øì¡k;Íâ9Ì$çC 3þRÍm…½×q— P•7¿ðl6ÆVá“XÆÆÔG6¡éqGï{—£"93®aEˆ˜" ôÉ -ƒRÇRˆU…êë“3åŸEÕ–‰>-{AveÍÛP:ÍnýÉ&ýfJܰ]íy¯vi®Ýå ³ä1J=¡ÇëYQ§{¡ÑyGÜÞiž4u_…’"?n ¾›Û/:¸CÆb„g¨­¥ß„SœfeyÏë—Éô—Þ(p1Üpu±PÉ‚“WÁròõríæÂ۩ă“KC°¹v¥Î-¹ÂC¶*YÛ=%¦Q µý|ÐÃV -.H)ä@©Àr(Ùï(.÷J²©‹xpƉR¤‘wöd6ÚϦÐLF}Êš´>ò’Ïîîfød$ÔW%ë–ªtÀË7¡^UKX -»³ýÖªr+Ó‰SÝ£¡£°‘n ¥i]-cNñ¹ÕFÅÃú'礼ªÊƒ‹Ÿ¬+øKx(§¡õšW›N®·£øRfmg€ÞzK˜ëžÑ^óá«;XÚG#½·ªQ鼊ÇGÎÒë‰ÕKÑQòfñ³‰´bÎJÑIn×Õ3DEŸ¶«iòª%%»Â<-‹#.5[Žy¼Újyzœ'K‚> f­YÕ®ÛneïטzpŸðu;’¼ÝSÀ^fÔ<Õ…ÚÉ üµ§¶ÏD±v¡½'«¥2£Ag{Í“µ—`J7ÿ¢.Ý䤶¾Gº$÷xÒœžºãz”'ÕcÆê+X]y›™+7bb%³Kº:òÁín|í¾-›(*߸BÚ$œÛ+Ê±Ô ­sHÅ+Yv†ÛáFOs"ßD•[znÌq/«¸ÔËÙFÞSΉ§pµ‚{1g ­ömŠÁ¦Ò•õL@:´szo¶éà±áY¨ßÒ¾1Ø@'7)¡xôK~n†ÎSruÙÏ9ÝGÆež—ˆ‘ZIqµ›gH/fydÔU8.$w·‰T®–GÀcÄ1¥¹OY•4Å6\bý(KîÑÕÂÔÓ¬ƒÈ&†2ÖD”\·‡‹‹¿­evWNÖ:HS_•+00-C¬–§ð™QXIƒR#Ù>•«C«­4XeE°©K2¾ñÛ5ÕSŒ>ßOùe~5SG­ÂC]°¤+öªug;óªµ(©,ä[VZÝ6,a†•†éõU|¨ÝrFx²å+ìÙu°‹\hâ È\G|X…eËJ5C“õe&TS­ÌÅi¹®á £Ý¸‡ä\@‡Ñîùzà–cº×Ìmt5BÉ Î_Ù‚Ú£Ò¨¹«:=`ü†äSÁDO¾iÄÔÂ{Êó7·)y!®Š!àûн~ñf2;=èý•¬™Þë]/YF‹—jEIùÊ|ˆáfzݽ²OìüG,-¹º\ØÓ!{séå³—˜»ó!ïò}ËHÿ1’Mä’_›ý©Ô†@K`H¦0Ó%-GÐÈòå:‘û€=S²d”c.¼ ¶³ëÆ®köi f'²”Ë:j¹T=E FÜÏg¢ÔPà‰1*qu>!çª&‚üÝ%»{ßx«VºgUf{Áåø~$ÌÔ™Ô€)ÉžP<Ì23†APa½mvý,ŒÂ«cCb„Ñì¾U`üø3õ[’øʕV]ñ¶gàÛôÀp*¹6”Q¥vWÇh½-Ð}ˆ†úûÑZ[·þÌ”ˆñ+‹«íèá$ùYa¤z™¨ß)·ÓÇ9تW›7ÌD~¹ÀОîÖ[SP—t 1ͬöLÓ*NÓ Ì/H„\Ÿ´Ú3‘]Ô@GvâªÓI§äkIŒ„Í@ÁÚ­21.¨9B'Ë´FÓ¸·§ÄX.ÇD¡5os¬3“Éçhá²MÜ(W@;—䌧[òlCÆÑ´;lFB7˜«çÆ%XNÄô`¿o¨ê²2É{êŒêJ²ªÙ tqʘUH—ÌÝF¶§ ä§PS­^²˜¥*LwÒK¢:—X —ÚEþ‰[Šm\3zÿ’Òœ«5?àrN^²zÁð}ûe !`Ê£LñÈè ‘‚Pä.eû¡¦ïq¹Úƒ©·YéZ'é•ðì¸Î¨…9(”DÛ¿™~¡¼<~”' -4,±jbê÷ýCWÚ}ÀáS¯m©gd„n£¥•|%'ÔÚ5wÆØ³Ü°á˜éq¦œ¹¼ñt¨Í·“c`ÈýgæÊR¥a3H½’©Nâê Œ7‘gjaÏ]`IªÎìÛ@½:OŠ?ÛÝôæ‘ÜÎ=9NlùÁÏ¢Gæ_ì¡‘Ebèþ$È’Ù³A¨–¯ºÍHô¶ìVKâÀAÁTò1—/SÝ1ÿ0ÎÀ£ - iÖSh–ÿ€™åU‚ù›BÔ§;$ÍuO…ðç3!žGDô¹®ysŠ„þÏSPhŠB¾´ªÉÄ("E9£R’[™s&=-û9³§«YgâWžDÓ( -±í]ç“øb_°c±'Rج鷋é40åöÐ8.R¾¾f"Í+ýqO™V@³ï¾½dbuGuðʠׇÉ ‰~²'VS‰}–*«š!3tê&ŠÍrŒÈp³Ù2éÄ: -êé8Z†A’›Ê¥bª ΀üqc:,Dèk¯æ§U×ÜÆÍ·!þÛQ,Ñ…"^Óº±ÃŽ«H/„£îŠ¿/õZƒÇg>ì|Àèë°`´']›zY¥Z«UZ´x\Êò@¤0ãàsÜ™µ3tHìxN°TŽiKšv ï˜0J.ýüsö|z¤ ո⎠9'€!˜ûÇù ‡0Á\N>²Qþ²wÕ!bëåõcÌ*¤šrmkròJR♜K -Mä÷½ ±_†µl¹ÓKëÀç–ÕóŒ = “ëG«9˱`·Et›P˜^„ƒ¸¬Û¹„äÖ¨´ü6¥./å+gÏÚÝÍðrŸs¹ÖÈá¶ò¼î.­æƒÃîé%ŸµÖ,oÅœfžŠè&=Ûeî1´ Bf7<ùÜ5!d¾ÅÕ&¹å´â-U1ß*o8%-Ôn:„…‰’ ¸#< ûü¥¶øºUâÛç\C¤QK p©ÈÚ„|Y×'4ªÌƈ½KÁØÙû’¬AWè—~¥1ãº7–¼µ±çâÂýQŸçjñ’rû|9N)ùy0ÂÀYªÊ`Qö{zéÈ!슬K -ÃUòG®Y˜nz³KšQ²GZáB~PÉ6E«ðK s¯˜k}ÕdlÛ²aAl{Íæíƒš¢M¸Î³ìl5œÒ‡ñ&­Ý)™Ã¨Y®žï|µÞ{Ú\·÷pVÆÎë3‡¼Û›”áKßÖ•Û -j *e³Ž­-.$G좜fz¹nù©?‰ÃÕ¢™Q,ñ$þÜd¤u¹¸ìô’ÛYªÌ~Ø9ÙÙéäîE§v‚íU:]Û/s¶ÐÛkì–pŒš™æ×&Š¡§˜]¤8þÐʼn¸ñ -HFqäeȨ9™õfs¾ó[é¡6Ÿ=–9lÏ]kewâfiìN“˜LRÁNJÕ?¥|T¤ó0K.Ôa—fÌî±$&Øxävj(›’u•šrËXÔÚ«÷ޝZÕ¾Ñ[$–ÚRÂ΂LŠUx8'vjc=àÍ{škyXL­Àb⤽æ²ÄžÚ8¸u™er -W5…Çæ?Cœ…-‹Eái©oĶëZI˜ÔЦ9½$(tg'Aí=ªq^’B-ÈX‚ܱnÕÇ«! ¨ÜóÄ6!¶Ò•Õ¥]¹ Ô‘E³,SY±ô &—b+%IO¥³º0K`…@"dM¡siiåàÊ¢ÎÞk:¸éóJ²žÕËÝrŸ”¡¶±,GU5/(¼Ç周g\¥5d»ü­ÎM«}g“ÑïLwx³T”.¶î:$·ÛžïHã­Ùª«ï°Õ›°•‰ŽõðïÂ¥“.h–jz:¿P!5Ç–Y7‰ó>s/Κkê…¸É3>Zhy“uëâÍû<9Á ru(†e¦¼ Ö:èÆw–z‡Þ;áÏu´W§áÎü ,¼ƒ€®üNŽ(ðUáY‹Ÿ9)eó ÒÛ*ofL"@\øaÃhÃl#ÏÉ%3 ù~ÑMÝ—}ŒFüçs÷­¤w¨Çc,¦˜4–O¶=æì"R©Æ—ìe·³¥P>Ìב«±®ãíղиŸß$^^J½I³àý‰»T ®»¹r@»={’ªØ®’Ç\Ï·\rsCéÖIÈ -å¢ÉÃ]ÎÙ†‰²ž]‹ÚˆЬÑZêÒI‰eþYáRnÕ‘˜!?È_|Â-Îdïœ1øËâ²l8YìʲJ¼|禿:BF­í¥µp3¸ã¹8á/Ô“ð”ÁílK篽ÉBÊiz%µ™ùE¡!5=YÏIú¤Tƒºr}ÄÑaâ$>PýŽ@I#Á„$A("7«âæÝÅ¥,ôɶ»¢4@·RÞ¡6‹ÔAFõTÆ#¥UmÉ9™èA©¶H…P®sy%ÓWzHÇ—Á³õlë@‰ËDs‰’½N¥ú~o"µû„!]kJ>l$9ؤTª*ee•;ítÖ8%E{øb†TdÔ½ã"8’ ÏŽrK°Y‘Øêa\œƒû«Ô~ð‰Øní®=«¯Hѽe`vtpãÞ™ÌÄZ]â[ì -ÊꕹJ|¡—Þµ¤òeK -¾Úýzó}éôã,¦(%dyw&™™#Ö£ƒ68˜‘…v Tåñ¬.*¶’­ÆiÅ û'ÿœ{»ªMЉ/¾$·Â} Š¥iSìÓæÏzîá^°…óèd·Òת`‡º@ !Ì¡Ù^닯õ’pb2 ò½P¯³¦=ÅEé~ÎÊ´Y…ÍM’îA¥3àçnÍ/mâ£àž -q¥‡^.憙¶yWV¦ƒñhÀÄÜ=¥MÎÑ,£^ƒÍ¯†**Ë};„«^r´"®ÂêÜ­†O.×Í^”\2ô R˜ðp+ë©s ó¡‘ò³sõªNJ‘'‘5îÈ[ÙæY±;ȾoŸw¶âK^×3õ²j—~µo&åÕé6£ÒSØ€½¨¯ò=67 ôa«^U–¥V“îÇ·È`Ø“ÆYxª—D-G”%-úDSç†bM:ׄ.2øÍfÃvN€¬Œ«eËÅìtñDŸ™‚Œ¸é‰«.×ÊÃ2|!{í6|DïçÑáØÒÈ8¤ãoÕÆ}ü2™i½dó*%Ê>·s'£°Ü­ä¢RÖ”žQÓÀ‹\µ€Yª~\³rJЪV[Þhn›KDv¦Ix÷FíLŸZÌ8=’Ã~ZŒÒfC1=¯ïIzÛ6LcëH Ù¦!U×ÉoUF¹œàª”ŒanІ9S¶ó±¶¢yñB£šXN-w}O±»ëe…iíÜ·ÉÅ0‰­Üµ èû±oÝ{˜Þ+ ]XR”+Ín¸¨ù¦\Pru÷öÁ˜»4r¬ôØ?œ†¶vXKr冃Æîn*Yä‘:=Q7—™Ûñí“.0dÅø¯ÁI•bÚÑÒàù­¼°'RÎåª\ò³¢µ©[{L鮂ncÝÌîΠ‹¥×Q‘Ý‚KTl“Z›^³&;\‡Þhy¾0õaIЗ3a/Û¸ÐÁ»„¦ô¡¡©;ÅsùdlkžõYèH  à8†Äœo—JI±žQ»<Ñò;‡kÒÎX”1û[4*ž*•Œîfx5 7¨¤—q -Wà'IÈšD5H02~âôˆ¼\a2‰å -wÜj µéAKï¤_8k]¨æÄ¬uU§G]à1“í.Ó†=)³Ãû ¶â“§ñELe‹èX½5ÊŽ]•ð¡º*¬¼ü2NXù©ØÅ -aóìX;c´å”^2‹¥ÇX 4S{*íSÚ•I1b’•J€)ú©Ô”éÙúh\‚ÑzëNÎI&žÞ®O:lqÜÖl¹¼]/1¿ ‰€!$‘£ÍFâ‘ãõI=hTí~}NrÌ€!<ÊjµBÚ}ß“O·€Ç¬:ÜLQõ¦Ä=[|¥šä¯g-13Ôv!;M(#äܼr¶xôéBhÅVÕðÚ|ë}*.š”º6õr¤ÃC¢ 0dx˜L|¤ÏcŠ9–¶‘ãXQ³÷ä¶%©7INܨ:%ZQIîǪX¼ð$'cÈ™R-±6oCÙZe’öø±6†XÈã#€cÇÓ´à¥ã¸Ÿ/­˜ÖôKNw¶’Þ¦XÛ_”ÚËÞ¨<ž¼Rãø ‡Úqz}s¨‘pðQQ¸"½U~eIä¾èÂ{Q„®‹ŠvÎêŠÅõˆø€÷÷0^ЯnsÄöÚá…c” _a÷8cÉäIÐÙÂî¼à†R¹Ñ+æÒO6n¦î¼¾Ô³ÍõþÁ!L7ɰl×_ôËú˜WŠ‹›9Ï’Öxªîyš'á˜õ»BÃcêÈsuã0ô˜­yíD’—óD—s1Ù…}q$·§ÍàÍV%°ëå¹D¥ê"s=`IU]Œ[õ «ß7ë.)±1á4®¨uõÌàÿeê<×T–5|s¢˜P1`@É9çŒ9çÌýf­½ö>?gž|í®®ú¾¢iŠ<'+Û¹fðê&–×müÛ¶`€~ÝÞÕqÓÂxe,Ä|é\ïÄ vd¬»Kœ̓;èfÓJSóûT\¶ÆÌÑC‰çŸ;)¯&11ˆ¶âHZõ,tWÒëTÙ[ý¡…§Ðâ&óò0^ßTå÷±_M_¯«„;§þh…ÄÜÛ­>o¯ g­_%o°i„qtQXÕšíû+¸‰…€©v…‰'Zyj$ò«®7Òa§¯í4£ –/ôd#^T4Š¥AÄOMÁUñóñ… âfºš4¨á¼Ÿ¹Ðþ„V¹Üxηº?®¿9e‹"KoR<ÎÖÎRäáñrêmÿã'rpøë…^2ïâQ ¯ >¾=vøÉ{…YÂD BÙñv¸póÓr÷ü’¾zOp_¡öÇorô²ÍCn<°Z¸(–BÁ?¹wæe¤3yj”%¥±ÀJ­ˆ¿~U_>…~`ÛÚl(˜D0“ó¹ÂÌ}‘ûr§TBRèÜ˯¯xÃÏse TÕÝÄÝÛ2¿'2ÖÞΣžÝLåÂɵ¥Ì±ý‹ü`·;¹ŠÝqÙ®%꘾¼ÜçW´|”Û:èˆ*ãL•¶i*ΙYc¯5wHz”dÞZ¨ZÙ“«V‰§¿çéAmž‰’ŒiH»3H[¹¶TìWáØN„ÑÚ|w®œ$ñ0ì쬞1•bHòëfU./̧ƒt¾œ-tewÏÚò®yr °vSuYiï¥â^$fl§$8Y¤öó•×s·ï­•´oKœ½áéAÄÖÌÚq¥ÙGRÚÓc‰4Zݾ%ÆôdÃ×ëไâámt\¹IžÝ¬ $à}íI¹ü8ä8K¥É¡±@ÛN*Z†`1? dé³WÉyæ’Üúð©h.²ý49`­¸ì"qZ¿ÍZ»b£åº¿÷.›q,5 T£àY£xn'¢Èù4Eü³˜}ŸK‘™Ï#ÃNL']l‡ùuÛ¦Lõ˜C! -–_Êžý€ð« t½¾U®KõÉYnÃù”‡uÐDãµò¼Ôù{"]´Â*k‹­ü|Ý«SÙQH™ÉÒ` .ÚÚ®xªÂ©p÷¦"xi¢n[¾¤¾Üï®ÎåDÖSèI¡EžT޹\ɨ ÛÒÙÀr“ÿæÞT®•J Q‰]ò:¿ìâfªÆ§½voN#PûÙT[zXÎó¦œ(W©ì(÷Ç.ýºšÇX É'å5]ß‹‚Ö¬*2C|T²R*Ç» µ"ëÊs©ÚI$]C•ž’@Ü’xSU[`pC±ªÓnë" ÞyàR´²ŽnÒ¢a—gÜÊöò¹ã{Æ-k]pbEZô|‚Z}_ÍžÊøýšÛàÙ4y¸Î<ï‹ rÑU-<)+µ/E‡ÓSöüZIQÈFßÔªÖ×qôªÔ{0H³[lí‡ ÿ.ÀDZb¥â˜/·´Wq}5—{!e¼TWðÓi쌵ƒ™¿‘³ù°ÉWFSÜÀÏN>AIc°lÓª¡¼› ÞÀý&tT¡8qæ’ÆÑØ»ÃyŒÝwűü¾ªË…,<"=Cžo:~Q:4W¹Ù•ª­†§¤Îw|°äi`óê¦Î,î'¶®Tk Ú>0]yJcû™œ¼j/Î6Î10Y¯óÔ÷~À³9W‚cð²$ëÀ:*>5>æÌV €‹ˆ—¯}#Árí*Ü÷ŒcK?»Þo*FCX‡m]Ÿ„¶Ý¤^ *²³“ø[b§©§øš˜iVã÷‰¡;½µÐÆ‘ÚêÑÕŸec€Ö%îóôVPKûŽ{Ò\‚Öz±¦œ ²±’'ÍÚÔØÃzî«Cd¥ÜƒÖOï˜Ì‹¡ä+.}eõš—U0 *'ù¼ðå³¥9°Ù¸ö&´ò»•PãD~˜Ü[Ý®Ñö›;ý ScK3SéL¯"øùÎ0^ó¥¨Is¹ß/÷ï¤Vmp5mÜÝg…UgÐQ¢^ Õ™ÜÊurV›u¬Y§åñÆ¥–¹ß?RuŽøBðbÚŠÍ”úzøjs$œÀ, ¡¶( NyÆ.l¼±êeБE»-A iUýW¦Ï»:ü@j3CúñruÍsrr ”®^4ýÔÀ¾ uN×™œ¾"ÃØOŠÕ“ê6Þ'd(߯wÚ*Ìv«°¤ÍÙ‹:}îí<@½&Ó¼Z³šÄ]RÁnÞb³Ÿ!ê -+?àjVŒm¶/*¢íûÐcÛÆm}¾ ”Xa§=$¹t²8ÛÙA4w>)ð"êVf5hDè ¦iæ²74‹p'—º)_½ßí ÉοÝÊut{ô–âük­{¹ül:xŠß.›‰snŽ »OYšHn×° “ÅŸ=IÚ×PA¤ÞÅP;‚Ý;ÕVZ ßûÊ0ÜÍë¢Þ§Úú2wâ -4èòyÃî®uÌ”*®ŒOXØ>•ÆUÄm÷3ßÚÑÖÐ+ w¶àµç_±Ohlºéßt8@Uº–©OÜé­¦ Ô'—©^Æ“µÖ¬Ü7¹G.\Ú¢³b™ÐPI•«šýuÛ6û•Iúðbô6mÈèNÄi—]…œn;¯T:ý뇷Z2 ]仈儇¶ãéo—Cž|ºK<ù¹1øÀHÐR¯ª™ûƒë^5ýÖKsªg6+£êN=óÍ]² Ø{¥cA -¿U.\w¯“Æ®¾ÈÈ&\f7;ŒõÕ¹’u+wèeÏÞÒXgW¯qók\‰5•‰¾ÕÚûѸâ¥&º˜;Ü#½Pþ£¥êFåC}F È\<„ÙnôÞ缫T»eAúÜUkZðûbÛ˜FJj™×^h*ê³Ç—Úæfè=ñðc1 ¹d"é2FØ=¯|Ü'X½%í¡ê`¹ªmz…š`KãÅÆäüɤbn¡eÉö¤(VG»ÛM+ƒ¢æÔé¹`¼z‰õQ¾°»×±& ±88x]êÞ6×m…í]5skxF™bùw,h7ssèÞÕw.S®dü<ŸH§@,Ë¿‡ u½Y˜'ˆ†5æe²ÿÚ‘ý vè`&TðÙ -Q5C¡Ô2™>#£CU[Ùhì¢út]â¶Ì˜ãÁµÃù¯Ã—÷/·˜_ËNɃý㨹¥‘@ÀŽ“ßçÅ¢Yeþ°ÅÅH¢|¤/²zÝgbí äIjdI»1«‰´V¬yn-È 6Å%R¬¸8.À['Ń­^”Y¯Iè÷Aý͆<F¿yŒ±§çw Ž¾%Óû¾í2¥ò·é¾úwY´¸¯«_ž¿Í -û·¬Î¢%Xr}º´Ô[‹5ý‡×fÉö®«8îÙmÎzïË:Ta«:t¬ý\Ë]ÒàIœTÈ­.ÌRó¤íG´±›ÌÑ`m4S®R”úÍ^ûžiìÚ¨ûÀzGmg±:HMæó°õ¢óPïts#—Z çÝ÷QZž=ë•r ã3öQ&v»d&dv'…’Ù›Þ‰ñ“²ýD(8Ș_µüu@ï§œà5aE ˆ¦3cWÖuç8¾i{5±xG'­sÏÆ©ã «uÒ½ºŸÐ:&sÒ¾°õ‹ò{¡ÒšaHÆaXñ_šÓð‹‹Ç°Ì]ÁkÊ…’suJ@Oùt¯. -[²¦cpTûÈzqØ·ÿ‚”*s2^€ÝAPkôÛvªžZܲï®é×­]¨BP,Ô$ûzªPÍf—Ñœßåi³ŸJëH•˜nú‚÷ œâÌß$P\ya½zk÷X’Wêê07á·p<É닊EàxØgBm| ÿ ÈW …é­š¥µ›¤Ñ¨°nÝË"Rž´5,&*"CÔØ’Þ®_Í,òFË*zùHù1… ‹À‹ïŽŸÇˆ¯Ñ˜:õXy줷¡.ƒGáð錖KÖqåû„Ê+Mù#Óƒ ódÆœ.ª7Çℸ%j)ÚµÚÝÎÃYßc›kï¢ì¼ïÔw %Ï–õSà ”ÝÎ)x·WäþC)]S®§|]%¹+)ªQrôÄÉÆÛùÚã\ÚÏæÙiôõæ-w+kçU’Ê]<=Ê¿ ǃ‘ë1iu‹vóîÎ>âxÇÝJúSئ=¿zëí«%VÊ%G!º—öYåªå؈.xÊÒA¼v=·hÿ%QÊ:ÄÛÓQÉ™’QÆ)Œ_ÞÆFP?`‹Ú*ëèZD;j¼cC…^ðû¿(ÛâPÒ)¤Pžñ«ÑïNÆÍW7Ÿ„[½Þ²W.i0Ã^"°uG«`¹TÓµÜî¹OðÄßý€ó© Ôi«mfotR—;ÍG.5 Òç´h±|—[Lep2Z§â3ä‰j·Ôšb%¸ôdj kT ºÓÛ![âmú=µn~íFú­7GçÍ<ÆàQ„ô³*U;xåf ûé}<¦õ_”ʇ— ÚØzŠ»ïLG)ÔI#·RŽ)ç)ðyZI†U§ÀÈ¥<•#ýÂ~yI”ô{˜ýùù°—¾Â2Ýs§–×øàõ±á £™3vê÷æÏjæé)¼=«¢@ýb–Iðì±i¸ýÈíðòñ -qòEr<°“ÜÌ%.~æ©ë–ûÅr­qgÉg­$Lîõ™¸fºÝ®†Æ´Có«Dž§èo¤€¨Ïû?êO[1€Å€¸=—ûBãëUÏrÏyú >¼&…›Kòê½½#÷-³ 3Ï–/.C®s3æ‹~5;(w½}ã;5ôaK¥)æ—î •;g¼›Å)¸Ê„ë®)vN-RQ Âc)bÏyycå].R«c®ûqÆá3Ú²z”\¯|ûÊWól9/œÊÚ:49¡ŽaôK;­hÞqaiÀ'pMljë»ØAܬÍÏæÛ–¼É£jIʱc^€KÄ®/–>èJÕP¥÷T€‘Å­3òª ¶ùz̤Ìm{Kgíßí+Ád‡ÝÌM†§'½Ã¦tuªÛI,‡;n imÐÜ}.¡$.»àøP:8¼ÊLôÅß3zˆ £¶#Ùéê›Ó -—Ï‹O’’›k-ºöÎå¶ðyï´ŽÚû3ÛÈg=å3ïElü :Ï ×óÈzÏM@ x¼Ël˜ý±+ ?°xuHæ¾Ï2‡öïWÌ+.ý ¯õàà,}Ï>¤î ÉàYÿ:}Ze~Á=K4‡°Ù§Ég -¿¹I ƩԌBŸ—k|¬ Ûôx{l½þæq4Z—*5“µ‹‰.áþ%Ç -PËÉ ®ÉçÙ¬ÕLþ!GÚT0bÂMA¹éh_˜nÊDÏ~ŸI\BG¾.O¡={=W'êѲ™Oé^ÿ€$Ê•¢³ì(£¶ªÌ AúÔÂKP:&²™Œ \z7®Žü »ža·D†p[Û×§î ?Ó<[¶ÂQØì•™Ž^Â+oÙLàtt´®Ì×R„Á¨¹ï}û:Ÿ¤¤alo _,lr.K[jÿêã~±aÖk«¹téŽ0ã•r#ÒºÄKÏr&™ÇØŠë¢;™IÕ ‚‚-É. aƒý±b¯­ÅFòn‹aã<»4-ϯráé5’ÍmQIªÎá.!ƒçËAÅbhø³­Ôc«3Mïx'[‰¼Öï? òVRÚå«oA"F(t‚²×!Pª)ã¯_Ø!«i!DB©¼ùHË×þc M¥H–¿sÕÙÜ8ïÞù-hjœëÒk$?÷&/ ÀÞÀçM´Öÿò”¾Ú€·¢»šØ5~è„y -¦ß«@È»ŽqJ© ¡dzî‘…ûÝiUðÉv·½¹tˆÐö~;IœlXõ¯ù -;#Ç3)õ‰­q•Ž+î­¯´›©T[æ0;©-é3"]Jïê¥Åœ¥¬¼Y(YÎ?Å…eþ«ÊËbj|ž$·N–ªÂ²dëø*5”ëEÛëã!¸è” ¨Ì‹EN§¾êýߘ”7&´T9=›ž?ù¿Q]£¾à –-.ú¨Þɳ¥>,ÃÂРú™›þN[\ŽlÕ‡#MŒàÏä8î£ÛîX%¯yéY°^»ét·Mãló00ÁšíåwÚi±_4¸Noªy’€ýùÓƒú†o¨—¼Ä7®Þ§Gº÷ò¾ÌÍ -û ¶–»‹Ë’ˈ•<ŽŠÖ®xÝ*ƒ6žf#jAºÇX袿„ëY‚!Tð1‡ihï+šý€Øw>æ]y -9Z?0nmÆÉ@ÇT‹[­¨ÔJÿlèW7°ëá¬w»AŒ ¶˜>é7¯ŒnR³à½iYÙø`×¼?|ê9xª{q*T»Šÿ4 çìwOï/Ši•à%“dÕÔEni(ô nÏnŠ1i¦»ÎD÷-‘[÷:©µ0[óÖ=)y³Ù»ÙŠíÿ€ÈT¡Ü°”K—bøeéF:5,ƒ¨sª§<ÓÚÆ3¶O=K-áÖakV¥úz”{§,Ü:ò¤y¦Rk»º8lšK‘í -½H“Ö9°2¾ð˜ÔY(’Ôm86¯ãÈjy¯«#%¨W²Æ¯ü§ûgLßY>)IÁ¤ñDYXí¯=L“>E’³¢!=ÒÌäg€|'IQ¡õÕ쯧Çruûâíþmi!‘§w++ñv8kû|ï{5´wøèä«rÜ€q\%‰‘ÙZôvØ´&ÙÔÍ f~þ8°S‹ñT>ÜË<½¦¼ÆùÈœ—eܸÀcܵŠDRcj@ŸíÆ0…­ f‰œ+X½×Ú`QúÇ>èBG¦h7ãë;â¶½ÝÓsnJëñp«-Óßô ¯Ù[õÛ¥fÁÎ[4´õ›ùb¶ì¹¯dãz~ót*Xƒ\hþ€¤ŽªuWP‰§q?Ôl€KGD{QN;,x?^º„æYðOw-ÛZÑ©׭ˤÐå ¿n×ÞÓ1¸M ŸýsíÃ8åÀæ²yƒhÃ9½Çóü»|ßýã?$ñ÷»*jÁ“-ò1áóH÷…Ûý,ãŠöwénQÜuF§w{>>nm*]Ši!.ñ¥FcúD…Öu›‡V¸îÛ†Ï`Ôõœ{äiç¼æ=}ºMg(ŒvÕŽ³ø ÎÐW­[B¨pç}Ú6ÿtv‘ɤ֢P\»‡§ç­Â9>œÌ[âJAç!¤P—D[‘Ôf¶SGÏ—°¸¦è·ÄÍ—ìe£‡ƒ\’šß¸íkômÑ®+¥7︓Å[žÒÏP~Í7™}»éWh·òãš%ö¬D†ÿ@Z]5>ì„!qQ5ãÆæ™ß=×mì(^Ø -3îæÙ©|¼ÜÔeÈ—lô‰Å]"¦*UI -AØgp2Ùz…ñtÖÜÐzÛ˜vLNœ”ŸCkúñŒtí^ˆM >ä9Yßn¾¨•ªÈø -4öÕÕ—mº¹?çå•XL]L{Ø=‹È倜îhçLôdsöþr–'¾ôâiQŠ´ò‹ðþ‚ÀÀüê¥Î»à5"&Wãô˜-­Ý7ª8ˆ¹ÝGÞdä”»ÓÀZr 2iƒ•ZÅ|øõ¤&y_™jç À<Që(ºÅn6Æ&z¶ò^ßvb#¨Šwb9;¹Ï Éë¾ÕI*ÍâéÂÿC›wïÊ …è‡M¢L,ÊÏ Û_ßk¡U(©ýÚ øäšnìî1¨¾$µÍ)ÙñG'xð¯}³z'rAß³…zÐÌc¬xê¬#½ôÆåÎbÇ»áþ4§]6¼(ª™mùî©].„ÊÒæñPÿ"<¿QEìñ‘]†Ç1aû—H!-,ˆÊ‡|ëÝ›TÕØË®ûÖ¨>di¸ëÔý/Êâ{£vÆÌk¾$ëv«Ž-ñŽîáYWæ¦ÈÁ–D fÇåš,À÷§g$› po{yvZüU&Br5­(Íž?`TþVZÄÀªk ;þñ»ö‘e:h¨(è\‡ÕïE÷T¢¥zŽ-Õ¾ÿ@fVô²w#«·ŠÑ8Aõ ì¹cÔlR—x`áY펑ï&±<™»nj2M °êÒÊ~õ¹ýE‘NwÜú”·Lh™;Š(kݹUXÜSm›Ä<®æ°l×àXõûÕc¹‘Öv¬Cø?ô™çBîÅlÇïëj/‹£tñÃÚ ;mz•`cÀõÓüh“ºeß.g”;túÛ]ðOÌåúÖ ‘G‘Ç¿I¨z逤½3>ê!«î së‚kÚ¯~=kC³n—5ì_†=ãÄœÕ<²9/ËŒ^ Ó»”ü¦ÙÉ»šÆ§ÇõÙ@óÿbï váä\(”ƒ‡ÐCþ[:ù§pœ¨sÓ“L'Àb¸ä.•ö2!µ\à¨ÊÓ£iMë^œŸ” -›Ewê)… Xk6‹x…¬*Z"¬ žÖ[mà¨åšðƃhã̰ÁÏJ ±Ù%^‘Ñ·{9Š)J7Ø<ÿAaÞJ´’²ðN_c_2z!ê×}ÌX¤ÞY‹[V:œF\è¯ù.ø:>Ai7šdÎË™ÿ@&ípzt;âÍ”G*¸Á¤~V»‰Ê¢6¿…ç­¥4„bsªÄÖ éòÊAäªÒØÛë0s¼öJº[hÒîÆÛ£uоüù~åÛ™!ýá¨ÙáÍ–ª¯–ëÌö¬©ÞÒ!»¯ÿŽÉ™ŽÑ˜$îç5ñ`»KÁ0wël¬và]½!ìØGZ%”}ž‰Ê÷?<ÆV/›uPlßKCÌQÔ“=yކ¸;­;I,»Ö684‚æŒn—{·ÂCo¢k¿ºË5çŒ&i–öâ)%G¯£Ÿƒ¸y>(¹Œ(·=Ëü0H•¬×¡2lßê5Æ¿àÎï3OÔå(ëï¹Óçs÷Æqý•ŒÛôˆ Öˆƒ&™ëšÃp§!ÐÒ¥ŠZ»³Ì/îêMlÌìÈó3%×cöðÖ_r:¨[m~ÍuÖÜÑÛfË'+‚HÔJ<6·-ꆼ -bcx"…[×ÛQé‰ ÑŠð}Z­sU'}άÀå³{Wú,nSil󗛇 —çáŸ|qftkvl¹=AX˜ÄtéÆ,§2à)&½¤:­5•¥ãö‚­—£Y¶êšë‡¦ó9Õ(ú±ÚÚï^<æ¿vÅ¡9eÕ›îòïÒÎNËxÙ÷ͬ­>èç~JxÃùall7׋•P¸ŒEYŸîW' #o4ж¶Å¢!’ÓMIµû–ª?ÖMj–Ù.}'„NÓýN EÍ<'[· ì°òÊ¢I.Pç&Á°1gv=hmô“æ„WÚ5gq‹+nM÷6ïÐÕܾé-VJÀàåÝrt8SØõ‹ ‡K6¬¤Ý›s¨ ~ÀROì„ÚdþySöRŽ«vaéÓ!6[j±$½‚I ì3÷°s"®®¸›— sYÙÃ}­>²?è§Äw07^=å÷1Öl5{V6o ød®Ö~À¦ˆžù7"¿i£”çÇt\_ -«Œ×ó4i‚Xô—zÿÌØžé’R]>ù†Þ©õÅ“9òbõáÑåZ\òÑS{„·`.€Ÿù~\™¦• f~@v66™/AaTê 9Nƒ»†¯Š;_Ëv-¾G\¹f³ò!"èE#òèxÖ(œhr"dr7{SÆWÚÌi?Udʪn˜%–—Å–e¥7È+>òãÉS)n½q!X´"…N’aŃ;švýƒ”¢© %«é†?ñsVzlHx²¡«²HßLFÝPQß.¤‡½~[†+Ù? ¡x}V<¥6K„‰¦Íµ´œÎñÐZIÑüR«r¦ÚplßÅ*±ý€=å ˆæç(êbù½>F§ÁTÁ HÈ_5jg[< -¦1ˆ •- -Þ@ªxjSºÁ3^Œ!Mi:áóp¾Lé "³.Èç-Ï2 ëÕMÞ§t•ÏþÃĦڠĦK¢ž€¤¤Nasà_jñŒe¯Õ¸”¼ öå+= -È)+èöŠ«™ì–XðúÝ…ª¿YÜøy‘Y¼Éfª0 p”M“z®Çlq%«’Õ¯Õ¡Õù£¸äÛüzO_a¸ÎÐÔN{ºV^°<æŒk)_üâ‘îðŸFÃRnõaÔ û«Œ‰™ÚoüA ³YCYÈç4ívQäŽÅH1”zƒÒyUÜmƒ¢ÂY¶9:”-ÁÏÃVvÕEmiciè•Þ¿[3 ½‹Ò>ŒûJ­›÷•f{^‘?’U$Ž«®”‘mó÷­B霽 Æ—‡R³®ñ¿îZ»œpšµSÖ>šåËîúpX³ùÖø¶¯Ë†ÐGG˜Bêsµ*ËÎhñ­ñüPSWé믔vMê¶Ïc _O-HÖÆêÊÛí|M»å#ÜÆŽ1i<žr×ù(~¼?èŸe˜ÞõÿÔ÷ry9Ó ^‹–N@uúÒûšîÙ•ilmø -Õ¥»æylxí{¶ù ¿”tïàP­]-rÆ]`ÿv’ÜÚx¸»ÒnM¿åâÒ6=KÁwRÚm—ñ ¨ -ÎÒ-¥Òt8¯têãsj-‰—'÷¤’« )=õ/FyÓD\È.${OR,a¢ß ^I¦ÖÐ(™OÜ[IªéJõ…_æ¦&vO@¬ƒeéB×›Ú*6DÓ·ÇŒä¿Sý˜µßXB¾Zy¶\̰¢Î*?1¨Ä­¾ÊÜè Kàoœ ÛdP»ÏMƒènÀ÷ÅQ•õ‰) ʾߗ½’¶õãT÷B‰ ´óŠÚVû$ í"Iç E(Ÿ—I++ é«Y×ÅѰ*¼•†;_Z;Vç*Öj›Ñ+߃ž,lufkñq›‚Úù“`ÿŒùr©óo£ -˜OËf„´`Êhþþ„e[SóÓ ô)ô_éå,{Ã7£ÞÀ_ŒÕIV÷ëo›/æÜµ†Y£©¦¼_°0ò7-5òwõ 6;“˜˜¿UçÊÆ¿Dàð`»ÑUYuv/³ÿíÁ^RD“X§«\eùcœ±ù]=Ïn+ê.uZo@M x=•ýì|ø7&¡­~Mè®È­Üû¢+u¦Ò'Фøñrâ/pSNL¥ØoÙÝÖ®É[1˜Ú¶œJ@ÿ¦]ó?=Xãâ÷Ðð€Õ|Õ–¼…ö¡rûŠßñþ=yèkÏ*ò0K[é÷“PuûU>Kü¡,.~Ï3›"´½£kã%»Bnb{‘!”á„íUɶºŠÈ¢âåo›ï˜ëéò|×ÍGÛ+²…IÂKÙI9äùî^åÑ%†Ër \T¶²0ëöþ%7«»‰¬ì7R.CÊ*KN+³Då¯Ý‘õtÏëßI…‹ï·Q +“^AD&Êëâwõî‘%ÍXRù¼¬ íoVÅÓÎÈ”Ú v uF}i{=®–ŒqáHúÖêz‘AË: T sܵã–2ôÖÂúo›¯§š<¹ò…{US,Ó@¾ƒM~ß—$r+ÄÔ?kHÑ–Ð.ê#ã —Ú“/OKue4ù(‹A -,³wç5ºdÌ™Á´kÌ‘ñþT1v:ÊeÜÓî £])ؘ¬»¤å -Vî,4v߯ý·õY3«ù  Áì;÷ äE¸Û›#ÚöZËÙ^¶Öå“MàŽfSR“:³[‰m’#MÀšå? ƒç†!t3O†zCªü8o@ú–x·ÉÌS“/ 7Æý^o­Œ»…K‰NÉÎ_ÊRŽ&rSy6å¾|L0ôd6KœY0ŽÐnTÚÁêÛàsƒBØ[4 -~@[hÞ»¿(Å -(ñË-,ºf‹¡ÈE=Ž„Ágú¡À˜¿ ^úØuQíÖË«ÚVxÔ&„¥¼–ä?c~BÜ]³{`×Gõ*áãV^_,•ñyç÷$…ÝZ6ˆ£å6e·)ˆ}Ïšöñ/À϶´ôŠtòo¿Ñëµ÷Ê*e:Nˆ}Oä /_ouêØÈýPêOmWÄñ¶e!õWP»}/¹¶TØKamŽ"ø‡²S\—,m’„^›îj¶‹÷±±$¬dÍ3öE µÖ»vcv_hh.¢Ì8duHVÕæÁÎþ‚ À¼ Dæ€ ý€Äý4MèÉS–\¯ûôÆV nñ¦a˜níø7F·é} ŠQé ›‚ãq‡m™( `×›Â{ÜëMë¶¿d梲jÂwôKXژͳ¥Ùðº+À*‹¿›º×ÿ“r]ÿøúù}¡]¼&–ÈùéÞúÅŽÕf„U¿Ó·ºÎ©~„L0~tj)Ç)Pôú¦Ñþwå,]¢=8”†hc…öÑn‡þiøºïfL¯UÞù<2Mo8±µt;òÏ·zðÛÀq²áþ]9¹P{”˜Ãœ„ÄÞëc“Sºd“\;ûnMrzå­‰sâë!{Êø÷t <žÃ/§ï¸‡íF·(B9Ü/jPh¼DÔ%ÿiÁ¥á¦r¦ù÷®¿ópµä6m:ú -ø½{ôÄûÛ¾‡bÀÔ¿Û|÷tÖlúä¤BO2CfÄÕk€yNõbvO4?Æ®ïr#*A¼pàØ -(åù„§„åÉGôÙÈJåéyõJãÂNÞ¿¦k—É–a“}†B®úv@cèNjš£tÅÿu’Ø€ô‹•çß gÇÉ\©‡ZqîÖ¨Ý×ufZÇäÏDùCÏ·j´(ÌÍw7ÿP¯`ØUú? 9;Öº†‘ 'õñá`ðê÷ñ tˆùm¢r@ëk$ˆzýEsÆèÿÅÅÎF®ì×7ïÄ·Ù€ò=e°o­(¥ÑœÙ>1”ò í0Eøü>º‡Ë¯euÅr ó5تîB>_Ï6;Tâ¼Ó{ȱŅD¶ÙnÂRQ^é„©ð9*÷é6¯ÈšÓ½ Bú>žTžú¢EVVn-bqˆTñuøå-‹§£õò`×SjB2¾¾²fpa¼å“¥WõHÐp‚òQ«—ó v2ݪ1 sû>ò+*‰Qµ¯AT’ <"ºQVJÉ\”nU·ìÝÕ¤i.t!¼V£¶\¡+LܦÛÍoi^3{­™'Tn+M ¿ÕO›HÏ~lAýKädgÌk¬~‹¯ó)}D©nÿˆº'ÛÁÛ2SžU"nD¬:od6n®V©Þ˜~üA‡¯yQ~^ÃRã R§)cÉú6õµUdª]fÐLz¿û.ôÙA”ꑉ›m¤ÿ<Å|­ýüšþ†i~gü+jžØq¹*³¥Ýs-¬!ªÒWºí§nyX$Ý× b¬ÃEóήۓ’wÓâ2kC„Ç­¿c9_ûl?â\8ðÉ¿cxT}òÓÙÉ”úŒk ðÛ‡$we-˜ 7u5}falE#²Ý•³ÝÅ8H`ŠBy}´‘|–9ñ¸r~÷À?^áháRQ‹•"3\;®{Pड़ |„å§ÍDbNØN6Ýû‘SpË™d!muŠð„YÙ64ß:ÉÚÕÛH“ýˆüGÕv…ŒfÆŒè»[ÎclD –w‰½ó€–ºN•1§DŽÐmßWÉb÷Ì¥Õã ÊÄ[ *o‡†5%r¾ÚÍtÏY®·œä*:;©œ×ÂÛ‡5;Ïhì–´nÏ ˆã±°ó6 ÷6ÖËÙþ%éFóÞUÜ¥ =C u]‡rÕ>M:ãCç1ݾ|_ÖĆŸì ÙÞ{¶ÍÆEÁØ´ùî\(«ö ¥äö5vx¯CúÉÍý}›É‚_zbñ4T\Ç®™Ë¹&ŒÍ[ïioås]k)tÎ-…Å÷Ïž»Eh0î7~ì™ -õ𜡖œÆ÷k$S® &µÄŠž÷ºH)ÛZ×þ«ëÌVi–+èEH à !¼÷^xï½7ûbnwÏ›ïç=ç©Ršˆ¨¬,¬Þa¬ø;MËPåÖ$kÀ¾¿/:Òž˜MÒ‚bñ²Hê¾æ›#l7œ£òQ1Yå’#2Å8O©akb Ê¥*ziÞ/Z'MVß3ïα àJ»ÙûÞa7EUÝ^®IÞ¶y©\ ¦Ú£cærB߉›r‹õs›ô¬rÆÔþɰ&.@}pX\¢Ø¼:bõNh´¾t“ƒ1k€¶q¦ ¹å-ê¦ÉÎäó/ÈÔjÍ2êCk&`ÝýNói´?ÌE±ö½ñ,ó"àãœ/PÓÎÐLÿÜf‚h ¢]2Zk£=B»õKv£‡0Æ¢@µ~š£Æjj þÒíæ,{–íbÒKÔAÕ…Õе ×wDkž'OÄÅ)õbÈ´´GñVä:7¦»ÿ§æ´‰–T - {a©ñœ›Ü 5h²îœ°‰pÓ·Pª¤Ï|ÎùN›в·{¨=^Tˆ¿2__ ÷Ûu˜‡h0î¬AãëÃDã5+Œ˜b» C£füÙ“„8Þ}è1úì#Ç\Nt±òƒLÒI®UéðÒ褵âÞnwŒcÚK––¦4jÝ©ëø -O&:â„Z¿S=E·ÝÓæè°øÇ’ BÍÖ)èWµyÏB¢ºµ›zif‡@ŸÍsJaÊÀwæc–:tµÏ ‘6åÏÃ'Iÿ˜? r=²o?ÝÊŠöTOÆ‹[ÄUúÁG¼­ŒnÑÉ©-;Vu²>ê6#ðúÙ”5óžLÚÄj÷½Ã#®‰òÌC›IpW¹{JÃA °-¼¡ÓßôÊgܰgÊB…ñÝÐÕÃd^r-ÇÍWKÕ^±â‘P5‹ÖÄN}ÓZ@í€{–kž1ô¿õÅú„T¿Ê”aɬ"צÓðÇamû0-™[òrô˜ýzbe…ÿiA»P¨Mög[À8‹mîljh3¥ÇV\„â©»iX³ûê§#)òŒéø{ç#×áL5j´Lx‰ .ÐP4v|ß÷Zóö,±(¼tq¬»•Òê¶ô²»q¦ÎÚûWƒ_‹èѤ#P×—ƒk%cùhÅi¦ÐÕ~ 'lý`‡]§ÖËög’?BÍǤí ÔéG©÷jç‚ìãµm/[ª*ò+õ¥’dwèMV+[-PzÔñØá½>*±[’õÀøªÛ}jô·Ã‘-gáÓ¡Ì)9›„í[t´…ûh 賃T´I¦´®›è¨@"ãØø®bA¥õô.rNú­Áºkø¾JçžaWT^ìç÷¹»l}ÌÖ°y240².Õ—u+ƒ§4õ°´*˜uYÌÿêMÒË·§©È¤Ø~ΞëÔÃëüuTog>&†Œ5Nõ’Û_$æDæ¬0n¬ \[å½£ýœ µ…¾Š`š\K¿íå¬(éÒ¨0Á†¹8´Æ¯¥òX«2‹0R¬ú*BÃ^°ëwŸ¿5X¥ËR+‰.%@¬0Ï¥x¼Wã„Ýâ¤ÚLàúC¹VëôJ{Í0¥ ò¹B μrÔþ]x»X±Çƒ²ó 1™Œ¤UÏé±Î’ß¡¤"‹ïu}¿ÔU¿±Œ,7òFÝ8>9>ÿUמœÿ Cm1qß%_`Å;»Ÿd3"o'ÐQ­©ô>æá^†½õ¹ßÍÁ±Ï²%öâ‡?2Ÿ»c‡¶»èR~ ¯ß%¢fìÎY{1µûP½B{bwwûW[¾†è^†t»3O%÷“¦{’.‚ŸC7Ž,Î+4è…g¤`ãÄJ2iº³äb¸—qt­Axv-ŸOÜÙ´5Ë¡F’w¡]ý1ê\õ`KïZžv®¼Åm|ÚT”ÝMj̨ÆÒ<ú øÒ]Ùw$tô™3¾ß ¿sá`cPÿ«8¾³ßîšê£Á€1jŒ!÷ðêmYpâÊQq/\zËHÕÑ{ê…AÓŠœ7´rHr»¸‘ýœ”5—íG§]{•@÷½Àó¢Ô“ÌÇ -i«ë²&PkÄLìH—^üZHÕÁl—¥G\ÞîÚåU<ËpòätŠÇ -|ùúX8+ïc/#ŧâIgñ !³'„Œðç=^Ù)^Žp£;“ãÈ#qj„ab$ê,ŒÀA½·8ôÝ?=ŸÇpŸB¦ шǴü ж—×®y”}Jû8|h#±ÜîŽæz܆ê ôw)è—×Ì—¥|ÙhÜWýhAé×,ˆíïøä2®ÂÁ§¾;Ô‰g¿T΂sfÍP_ ^òŽNð?¦¸|´Xç&ÒæYk+Ãf¸,¿òMµn"Ö}ò(Y•¹ÎµÕÞ¥ð4Žã›-ÇŸŸn>M³µ‘[׿õð„—Ý_—öF¸‹4‘\p>¿ìºÃ)–sîsi@¾.…âEHlçU¿xÙÿsåòŸyF¨ç¤‘y/„ÀÛº™ßzæÚA‰ÉåÊè%[ì-x­fQùl·Ÿ7ÆiGt?c4ô(«õ^CÖ(²œ=UØ–^뀮.uò]9—­úðÃÛ©^\iW/¨MkÏí!z¦&Ó–ΪXâ´ç é z¯v¡XŸk?~š·ÒíR.F÷£ù­ÚªHAv”òä§­PNf'Xû–Ö™÷®}"ÏøÖ:p;Xž—YÔ=D¹ `n[®¯¿ üAìWe纻aÿž¨Æ×Ê=ùmùžÈFOé·£1k4²ÌÃ+xj¥ÀqXJà‰ÚôGAk_qý™êÌ›ÿR 5N­sƒíwBÈxm·V­p£ÕÅ2åIž©^CD›T—F2›ßKHö§Ú(_“}¯ÿÈ¿½ÀSŽ×B«¯Uí’*=ÝPz†bÐ{LhßbV÷“qdÃßvâïê߸;¬í ñƒM¤=Ž»æñº¹¦ÑkÛjÉÃ2h¡ÅÖ1¡¢Ï¼ÚOj¦E÷Õ8|ÊçÇ‹–/w¯¯vþRÓ=®fQÉ#ǹéÐrM–-6àÂ4cÈLŽYâ‰a|µìe²1G_¬·ÿ+iI¡Ô{{]ëZNù;Ö*m¯7O®?Ʊ#‘xU›>öÜà¾ûûãsY…ŠqÓ]lÀ©˜„ûÇ”|9¥ö4Î]h v‡Á!>ûHžbè4óà.ê{v‚á+¡dËoE­sþKM&Ïÿgˆx²¨Œ­¹m$ŠvÈ·r£R#:NWY6ÙÞUŽa*‘tú(Qž‡Y¡'ïÊl·³Žþ(õ‡ékˆ<‘A¦%̵â¸ß=YAv& rf¯^»l’¿ âÝãRW8’½Pd¿qwàô^îÄþc -¯ÃnOâ™Ó¤æöUŠ:„¦,õ»'í×Z©=®ò -‰8Bš 4ŠÅß,ª^ZÙýqeØr€¢v½µ÷»r;õK='’_òp¯jcô.z —î#õ8”ÌÎè)|½d±¶'þ|Uî´y'õz#v8£€â{¦“ön×Åg~TrÞmâ¤wHÓél÷hêv¢?WÖÝ©Õ?åï0p{O·Ú¹uÎDäx×Ns¸v–‹¨Éªi¯] †óSÉd¸ ° Jüí¡µþ¿Ë='•¼]ïêÓîtn{b”·›Díà‰»,)˼f7Ø^¿Ý©°cÛ÷¯s·~ü;båЛôjãÍ£e÷¡kZûy'ðÉjml·Áž»YTVØêF«9ÀmV;·ä(ÕïŸø_S¸ÍÈý…W »ðÄ«NLø¸Ì·µãøZâQbí·¨´‘n¨¬®ŽOÚÊm­\ÖÙNÌ_{軳c¼je‘®†¯zeß~SÒº¶ŒT#t ²•ú%øcxbm†îÇ{üÝÒMsÿ2ªo¹1¿3ö\¼Æ‹ð#ÞPñ$%¦ƒNÆ‘‡íG>å¾z‡ìOãA-'jŸTnZW~Ísåñ!ÐÚ•G], ßû±H«òçjÒYÎÍ씤۪z¢ÎŠ‘nqn8$mÆ3¤:)Tº¥8? õ…ü̘¸1Øà½qKÚ0ݸJs´î昧tR²r¦½¹Áh¤¥è@^jpóÍÕÌm%ó÷øÔôƒÙ~ ž"§ƒvh¯3t«rbý}ç¬Þb‰dxL-0Cé]ÙcìW5k0ØŠ -»«>JÅ'Z;æ¸2^¾ ÷÷öf䪃¤À‡H$*.¼p-Ñ‚|Y)Œ¨¾³š¹¼PžäÖ†N;™Íyæ/Ü(»½Óôå»ÃjÛ¾@H¢ÿ8_kæú‘ÛùŽ8sÄí:³œ¯•¼©ÕÈÕ àâ£wõhÇ]™5‘ú DÜŒ9=Š~ë&m¢I~A‚Á$]ÙÐíP¾Á‰Àæ’^ÀÓż„‚l£ãÐmqÕs´¡$t»í‘‘ʵrbÒô`•Ä&Ä ŸQEbŽ¢ÄF·y®' SÚÃuÏ2xÑS@û­^äËïþ| -ÝÙ9ìXÞ‘x1]Z]Þù¹ŠTå¼R×Ó×\í‹þ´R#AÌ]'nÿ6µ’»¹è6ÊB^ÇÃIü.^jºð¨¨û‹&foÿÕÍ9ÒúræµÛg`1áZ:)=àÒÔEºŒ%À©¥¾ˆ[bëŸ]¡ëÍ9ZzñôSa]"ç1ö 8Fo¢ « õŸìT=n‹K­¿ïÜêV›„u¾dØRû¼s1 7wA¼Ñ†4Øœ'1„ -omô°G -"†g£&Üæz2Öºª$Ö­{»‹Z}ºÒ„MÁÊ#:o\ÏPdG¹M§cÆbÕÔ|»!¥{¶è¶v…,'›ÛðPWÀ:6Æ|nU$R›Ÿô@œwó®eÃŒ2g¹à2Êj½]8!ZO¢ü~Øt4Ë¡ý`–èìQí;Úë("nµ˜¢ ;­àÞT‹•Ïð„®r&áT®ý¶Ý›–3è[ç%ŒÈÃfÆg7¿Înei—¼4±Ú˜Ì½ðY•Jdð„¡•GtwåÏ®/Mb7w¸8óž( ¡ÀÀI䎿˜_l¿ºž6*ö…Û ßë¸üMs3XŸ&½ËJ|UQ7\ný=Ù“ò:MߕϣM¢HÐÒfßóð&i‡ø!jº0|w¬¸PoY]9Ù¢Ñ÷V&#ãÍ%é>þp˜…ÔàÉ£[ î­­Þ•èûI¸[ˆ¬QkÿÑ& -]VÒ®#®i–ÜjâžF› tÛ1Ju÷ùH!¬=aÛäÜhœøÔ^#z¼fÏb3—öQœLPÆ`¯cG í³œ\9Û„T“¶ý‚Zƒn+Ó=Þ~ ´>ôR\#ˆt䟖µ¾l+ª^ãšF—²òOúeÜn¯ùÓkå®þОöš¡•J¦tŸÞöÚ›¦<%ãߎˆû˜.YÙ©Ó·•ì±çjý­^Nümµ„Håh®Eë¹ÌÕå)ÞÙ«o‹Ûx¡Ò眫kë1±Åëq]0ò¨‡Vùl¿÷/ò_ ZYˆÐ†õþEEôÇ«Y¤ÂÜXžÖŒT¦¿öˆ(¨»‰”Ù¹µ¾4V¹uzz›4¦ü(õ6‹y6b3qqVG}« Õwæ>ñÉïÝ‚´€X[j+XȰZÃzD²ÎÏÉÝš™ûø÷{÷KA:”-[` ©IŒ¡6‡¾¼saf¨i‰J©6Æ—ggöh+f@6‘Ü,1 '~VÛ¿5Xku÷3ª*F·hn$/Q[¾4ŸÎŒ+Ì2Ô­UÜ{»¬,›Ø¢§ariË{Óתnî¥éŸ)Ç{`ò¤uP!îò`è\rcÇa«˜ùXKz˜ÆÐt±×ß®ƒÑ™ÄèYÙåÆvŸƒZòcˆIêzÊäê¸m©œZ•¤4G JT»ðêÅ3! £H,1lìÉQM¹Lº þöÁ§?‰G’r%Ь@ -›‚_}vVLSÏ’T•Y?ç]Š•%¿=É^êYþ sh]Ÿ[(¸Úùn@¾aC1NÜ~°Š—h_vû»Ì¯‚ã/Èãïµ~?2¬Ò¿MÛžp~Jn•Y/ËC?Ã$íÌ©zîíž'ŽÚÊ« †îh.Zk_j¹<µwã‡bTìÒ« íænѽ¯N0Ï2ZÆ+åõúG]ÓWbÿ¡=Õœ‹-š¿Ž¬­>çKçi,ir-«7çÙ>à«H2†û~%.zÞü˜²NH¦¼<$Òœ¯pÞÑ÷ü jÕ/pÏ*î¦S‡‹ZÝ -êû3«õ´Ç*Þ+ÀJ{ÖrÒù9i n9˜Tæí7ò¾òò±ç:5àõ²E> [w±æ`ØûžIƒW¥>é Ô3‹JšJe¾`bÇq–éÖÝñzu]¶½náÑÜ7ң•hKæ (ŸðËÝó†M*¯3°3gÜ¥V?Úڦߵ2h'lmò>€x¨°¿ `É<ævñúxÖ£³ ×a=û%geÊ_çé¶Í’ÂßÈqWÖÓÒPéqW E. ëÊDïfÐeü¨}¨´­ OBåN™:Eÿ°D“Éê °ƒ¶g\{Í~ÎÊÎm{¨Q†eŽKYÅ©°UÐr¸®¶fDõÄÕ$e¾ÃM,¬ [Óp¥ãŒêzSi½øñ’Å!†/ž­ ÷\M•*½G%‹ÎA¯ÔºÖÞÉQ„ÁÁûx¿Ü¬(Á˜„³ëò»“:kö|fÿB„³|Ð_£ìŸ+4³E+´ò|Í•@8÷ŸµóÈꪑF‚†bxÐýÔʪ˜0wÐ +³¿§Ì‰Z`vŽ ¡Í‘6ä¼ïÞ·-ÅŸ ]± -šïM¦\)ظ¾Gg ·´ä«ÌÍŸó³µ$9Ñ›ÄO14ãb mc5³"FŒ÷·æVÓtY€ð®ì í1æjês¸äD^çÏìÓc)õÙÃ=â/{`ç„äZè3¨íœ—OªÍÿ1…¹1tžCrûS¯ºëQdéLùáUƒ»ìûpÇÌxn -§ñìÞ#€ …Á[ö4{9Éü ,A½ªêµhíÛÂ3Ü »…è<î0#•ßk§ã÷­#4_ê¡ä~Î4í&S×åƒù1ÄZ&‰d[=¦sü >u}LvÖùzû²ëËÉ/H©çüCG˜Ì2ÞYpt5zå¿zƒâ²3õN](9C5W;™í¾Óe*ƒtSV'f|iƒ[†æƒ -¦.&Íy|w þñ·Ø²#X¦ü`÷ÚÖ©<Ú‘ãÉuyÔ4uþ¸ŠÓˆ„ ?ÏÖz·ué§³&¨…iA1:ͼóŸ—ÃeŸ•û}jõ=“¤Ý(õäñ´bTÖ/S×ßYì'Kd™@ˤé›â»¹ü?ýŸ—Ó:Ÿï¢Ý|öi—õz%¼ÛÊ UOâÜ`€:±…ÍpžXÍSÔûÎÅ^”oÿÏ]ÿÑ`™}ºZô]ªËn\ù’irF*¼v­»r²èÇ'³@B“ÁUt©K m¦gþ¯!3pXÈVžLR ¹_Õ±Ô‚ýQí´‹OrÆ,Ð÷ë]÷ãQ Æ±ú €foâ:Ü®ÚüÿkrNnB}­¥µjòXnÿÀÇY5­Õs c>1\û¸xSs¨õÆ3¨ªâàð; -v^L÷úq«"ev<þÌM6^ä– ra>Ê‹ùÝ”ÞÍüXY’ðIvO7¯~…¦°Öz9œÃß5QX¦“jOV3fámqd+>ÎæŸ½ÐÓzÑzSqŽģ,’ñkÄ5NðU~å«^”Ø@(·Ú=j"0Vê#æ}¥? € -Z÷¶ñcÙ5Îí»gÌÊ¼ï´Æñî“’ùç;Ìú]N‚7pÜy…Á%Ûªh[dtú°9‘¿1•¤)¥7âîX;<æÙõÆwXÒ·…n“øUv!¤Š¾S–oµi}ởbDŋԪVM©ÔŠ˜_I;ø½W>±<+óyË<ª?'þŒCaj 1LöhÜÄ`^õò~h¸Ø˜Ë­št×_"­Õ›×WQYóÒdÍOÚ -¢64õmµLœ›i×~A²æ«]u’V–JÐÆaesØZßoí›ýКj¹¡-è¦øLÍ»ÚEYL%þL+´ùø4P2R1´÷£ ÍY¶¢2M¤ÍÛÀx«wÎM¢¤ü "v·tkði'Özødœïž–žáÁϼæ@J•0a¾È·C“gÓçÌlßë/5‡åýfqùJÍO<~«G†JqyOõ¬Fis×½nóOÿÍ úžJ ”g^ŸÐþÞ^÷¢¼µ(ˆ[i“Þ[ÞÂÆ©}«r9¨ëžÙËQòúåzt|R³œìå½NSi_ËŽË'Û½•c­SÿüiÜ>ˆ¨x"¡×/(¤/Å›´ðÊ6Çëz!0Õw[Þ®®¥Ú›*þéÑñåû@óß¡=ËŒ˜ßÖÎiH5_ç-––~3K¼*=˜I“ÚºÕ4›–Ç!gaG° ‡ã³×ì_ûÛBw±j`»ªå•·vßV@“ -Ë쪀ð( ÙÛ·ÞÃkµ:£4ÙMkvw - ©qÙ¨Š¯ì+ެi#vNàçqÁd¨U †{6Ê¥,¯4Kã*Ï—£Åø½Zq†2=“w;lºl¤xKô7Ò3ÕVwý”§§ÜÜè°Óˆ™àrNͧù»Yö…ªX(Í5DgtÓíSÐ~íûZ²½Ù£N5Rσ­+ôs)lÈgFÄëºj@0”±¤F@­XÝsB؈ö8‚œ¥ÙN¿ÙäÍB}ëO69Ž­ï8ůS¶1 yí‰÷ûLÑì‚&!˜¦žAÜKå_àfZ{¾õŽSx€î꺶БüÖ`õ7ñ.ØñÞ«çù§/ÀA°[®˜Óz|mðÜÄ–üëAG|,{l¡s´µ,™½©ùW[ëÊæmrò{þN܉ÅFê4:ô[ ¬å~A®5OŽš´·øJ·\·uñ4àyÐ^Ä÷l¡â!nÿe*Û)»ÑaÁ/ȱíßÉ[Gm?'2Ù_–j¾8èmÔƒŒ30Ñyù- =š=Ïzý«Á&°|ë#¹ËÏšÑ\>‹Ê9-rv<†lìªn úôÁ!ë±ØÍKÜ€S÷sŠ ð:{–Ê¥Öéù¬#ZTrãû­[„w÷£$î¶· -¤?Ò§u¸V|ûÝrR>wN…{½:tJm«LïU"Ú8•daYJÃã­-×­¹‹´Êß‹oâ¬Vª5ºòmð/öTòþnHa®ñ±Qª€³;4Ñåûº˜³Å&>":6TÑ·tµ[Rx%—‚jí7@¶þßQ’lνõJd…§¦|¿hÉ%bû ¢›Ð;¯…œð²’¨–ØæZÒk—k"‡ÆÏ}3F¡öý”pÕhéIJ\L]ëmùÙ^«îàºØY½3–q±ÜgXî¸r<¨üÇ”%m1Çjƒ™"ÕW %ºo•‡T'fˆín)g‰f‰A6jú³A›´’ÅRþC8¼µ˜5dTAq^Ç m9Ès3¡|ÎKxïº PhJ'1~ÒY.r¨˜ ãÝâ; -!÷Ç@ˆ¢kl7Ç*ÕæÏ¬;‹p-:Ö>2Õ GêÉEè`Õ3 “Œ°#Ä?ðz×õ©LaÛ§!¶f@+ÔO ‹¼_dVê^ŽQˆC4†ám/w;×m¸YÅ.Ðç%…Üú·=$ùcˆmS‰ÍV¬Yï -ÜÌ‘<[ÕÍØó՜ǴÝH…Ó¸æ¨t¤9~`þ(sV»Û*ÿ‚¼âŽº ÞW“äo¤Ö>¸Z¢UE?`Ç6^ÇbN/’;2‹Êk?c—ŒõùÓü';ÿIíí7Þ‡MPÓPv·ŽŸÓ,´ª‚Rd¬’ÙØÛ{YažööÆr‡l½ÊÁþ_¥žJ•ï í%)^g6š‘EºÉZÃéçìñ^ŸÅî\aÍjâ‰õ•¬z;Ýn•T÷?^"Ç^MØÄ´žÎb9‹=hŠxUŒ‹gðŽÝeó’åä‚ïìÛÎø¾]¬7ìý¯)²€>RNaww¿.ê§Ö£é="3v]la—ãöæ¥óÚÅoÓ pí5gÁIÌ ùª -bþh6ˆôdÑ š´#¨þ*½8âÄÍ¡ß>÷Y?·Ë¯îm2\?K/ÍÜÊE_[uŠco…N»Ø µw«üA[zépÕ¬!˜Z:f\l¯ð£62ß!®ZfxoðÝŠý3ƒüP,§¶³¹‘ÃÂp,¢¹í'y²ƒ[ɉ€¥T_MJ)Ô£¹Å ¤t !AqÏþ´ÌXHg¥ýF°´‡Ëº[=$a‡íc z¶«%õ{‚7­÷XAÞ9~tËßE8“šÇêßÇ?†äÜx±›]Ȇը«Xí¬Å}Û–5ÚS§>e˘ù ÛÜQUõŽîx¯Gêûÿy94²4ÚÚu̢ѥ떫“±]иŠZÁÝܸª'ä' _oý«ßΗ˜ ½ÿj°û9]¯Q¿ô«ÉAR%*UÁ{æßAÑtÀ9>7ûfnæà+s±5Ü~)ïþ!Ôêäöl‘òðð’Ǫ3^®mG@ÐÐtþÛ‰Ø:1:®úÍE}ñßOÿþ²ýs¶§b¯>Ê2¢;+=¶¡Àº|qœ<Ýðô<¯qÎÍ6¤èÖ+hÆóbVú×ß¶6Àgr¯îXwJ-äæÀTJe/"ÌÝÙ¾P•¦íAp¹&+wÞת tÖ `»”¡j‡Ái ëN00eM¯o ñgVö_rV+%{=”…~PěØ(*VoHùÓ$óìmsÖf’–’,U‘–âÛœl}`Ö·ËÅGA)ùSaÁZ6d©]]mÍLjWh,ia]Þmdš¡ ocT¸2iÊT^¾cn™-Å€$ʪº¥/tÞ‚NçK7úÅMƒfœ_v>m´ìWLãNt»1”+qHå³èì×´gˆ€…/O3ÂGó_CU •ü®ôÞ5ΤF‡š¶@`Ë“v;ÒÐÇŒJžÜ¯,›ºæ4¤?þµ  ÷ã‡Éô¤›K;Ê”qê$eOé@EΗ|–Çaîl"¯QÒ²YßÅdµ‘¦%Ó:&’w#¯›f÷Ùˆø|±$zG͘{ÎjØYŸx(í¹\<- ¸H ê@Ô©»§º>‹ŠD^§<åÿ‚”F§‡¹ÖÛmñw‚¦ñZ´ HØ0Ú6_Ef'îáß>eÅ®dd=üä<ñÅI¦Y˜léÜ6º´'È ·|Ë—h´Æú­#:ª·f—ˆr(œÕ}öÞ n@Bå–NP9šB˜î®]JY¥SÚˆ\É÷kÉäßó .›nX¦Ú·ÂGëEVñòXnðI>;‹Úw¾.ªÏ;_Æ&'Þ‡V-cPÚc¿ ‘¹®¢RPލŸºrkjœ¤Ç­ïVAÓû(oðË—­ò³©k+ó9C]g5¾îd8Zøšäþ+½“Ø–Ã)o5Èa- Ò¾'슯n טÿŸ*qø¨â]™ÃÎ -ôn½U×,¦‘ð…àSbÕhéÖq>Ö‚¨8šú2ØhIjGÿÖð$l›bRØÛóØ4œådæ^‰©C«ŒΆ|±ý6ô73w×Ö‚ß7/hq\J ÉøCÂ…ƒßÚ’ “]sô"5mg–žvOÞ—Ùª­ÙÌæ ö¤¹Ïy)T°ósá±µÓE~nÿ6ÑM)nÛ`lŒžê”¶­é‡â¬–åo€Óú¬×ç6|mC¹ã+ëqðã=/Ž™¥žåð§’zפXÛÕQƒF™äò ÒØ+.³zØZöGF {5¶;ª¢åSØßûí’0«kñïÕ - -B>ú ÄŠÊ$>C! Ì6"Ê,֊™#3*G'äR Á°'.17:^óâ—lA\&7ë-êr²yxR’“V£|ÔbÉCdývÛ–ŒËÐ×Hød«Q¼úÙ»³äµ²á g>g<±ªÚÄŠX{Ï?NÔSKž&"Î4©V„+¤gùg+¡€Aê{écúí¿É«ŽïX}sݳYT¾£2¡T·mÇð–A‘ËI3Áý‹‰©íµÈæÜi Üù-3!÷ee³8ü¹Yè„,[kì410 5­s^‰9XãS¾‘À#»xöÎüuDóÄ%|Õ:Ç$BÕ×B¨y¿nÿ¥¡öª~¹º}a5bJ<‘Êñ3ŸºæúPæXõ›…J½àÖ<àûÎÒÇWäskù -¡<¦å?§–NùzÛŸ¸_žØþ6h¸Æt†µ ·.ÛŒŽª .`®+ÂJ¼·5î\ ŸW=s‹“¿»ùw"æOD¹yö>†\%£AÄM’-µ«zFDxÈ*Ûêw÷-Ƹÿqß!ÜõÕÍþvóM•ó:7ÓÞ°ÂâJ«@¿pXuhñKŸjOpô¨Åq€‰§~¿®g©¢šc§))í›]ñ{_…ePy›ýÊöÑ:( |ÿHü“º—cÚPɪƒP[ÿ†JëÆì¥•“Öƒª/YA¯§•‘þª·PD$ŨÇZ$]»º²V‹÷¬^ e £“Ò ¢û/¨«ŠÊ­/ue?WÙ=XÔ„Õ¿’åÃÔcb®Hj):-¼$ºB0Ë(àe$=WúŽj„uLëWs7ù*Ü}t€5+1©Á†µµûÖèEµzÂfzÔ¬Äú{(0“e6ZØp(% œ©vVnÃ8•SUÚÂ|=Wï eSË=žÿ¼#yz„y/w¼Æóº¬Ï,–¦±8Ùz¡öíS²âSÉ_ЪŒjS´–U¯aèÄêöǔហ†¡”ežÃÚ™0÷˜÷#e*fÙwÚFÁîKìS¥¼„w¢2p%Ûšä0ßrRYüF°F¾.l|çcöt#F» $@iùI·ÅØž¿ì‡<ÅmŸnü;öHWøöB» ¯Ç­âQ9¢'’„ ÓÐúf»7hªKÕ}Ü*ãu×¶ËÉ1)…€ì¼ò޲Áï?†0ój78›‚Êß Ï‚W2È‹jV_ĦdÍ e(žüM? E^^ºEî â>ß™ìè<ø»&%Ñ"ë~ÈŸ®BƒÍ«£5^öãÆñ ¾!ô» l-™XVvȵÃßk—ß>&÷ŸÛo3ϳ(]ÞakþYï±®<Ð$ ^Á{áå ä½— ¼÷°ù¯~tæëž™îÙpUUfFF”ª2ƒ}(Á8± s•Ç—`¥õ®`Ð¥rteÞ*vGϬ4B>qVÙå¯OŒÿŸM©ÞM[í1ªÏ/8co¸ºH½+Â? OCà‰m˜bW¼ÞˆE[*Ï3TXú8õù^üoœÓŒ3©‹úíâhSØ4Úu²4%«œL¯Žo"ÛxÎkᙞ5W®Þ矟æñÿ½;ÿïSõ§7Â#‰3€ÀÇàÒ½ÒKP ¾ã¨#¬/ŒÚžÆmªÚ@Äîÿª`Þr…ÿ´8bÿcŠH¶Ò¹M·r Xâ“ÏóŒxŸCª;&À)pæ èVžÔ‘ÈÁnýwaâ¿ÏŸ®; 'kz­/÷òÙ(œšT>½~4äoy¾â«ä»ÖÚ†æD©Ý– ð›ÿßìø\q 2–pÞkü[þÞ´–¥¶¸né‘ÃidžÕ7ê\ÝúEÕ9qì¨OWùþÍŠcÎü¨Ýžæûǰ£=WXŸ˜áŸ‚!£¡Ó6»¤Ë–‰™o£ßH¡5{ÿk #€ ÒÒ÷Fî‡ÇA¦·^ôªÕQ´À~PクF¾Ï3º=ïŽËS®W ÿX¢›ß¿–5ÿÑ´Šñ£zÊýô<‘÷¬"÷Hï¨âM³Üå?¼6öû¦:+8{c²­ô™@üÚôû­ÿ×ñÅoÿg›o ¹“1x¸òõ¥ÿ€Aõ^®2¹ôUª*â`4–_l °8e_JಠäE²Ûn1øÿÙ öÊ’DX#FgÛñ’»ôË$ -=T€/GQH]žhá¹QÆõ·Ó‚‚Á*ëÿyõï§ßí†RñÏ#îØØLÅyóW.Àö2ã7½F"!—&Ÿ(ÈJ$HBýÌ”¹'´ÞÁþoE¾ÿÝïþ¤?§“M!,Ť -¦d—…½Øà†”€·±‘nc‘±ÝúŠ˜ãb%ªÒg“cÿË8´t’Ëü–§ÉN{'aÔ—¬ˆNõÆuÓYÏ{ŠÜ°wât&× %^–g…Ÿ¦-Pÿ¿~" ü*^xDŽTŠ!Qã òö+Šò³kaÓm°u]^—Õ*Yˤµ¿ ÿ÷@êû«OÜ“ó×5çüžoÇ:f‹}Ü^:otkèå-åvø{—ÙÏÔÒê&ÿ?w×(Ì4j÷× Nnª“4¦òmr0‹×øaØ‚V}ý<eK}|ì•êw£+Yÿ©&Ë?n³ÿ\w'ɤÐXÌwŒ!¾yuow#Õ¨éÖн-ßÒã3f­dŽû³¿ÍŸ•6éð¯¿Í`Oÿït_z­}_8öX‡ösß±>°¿mh͉2ãûøRç‹uuë“ÆoÆjÝÍô?D\j‚íÿ0&"vTe‚óÕ*ÇÉ»;è2Þä‚T w]µUåîG6•»ëÓâÄú##¶ÎÕçùï€Èü¿‡"ÊöUéõ)êÍr̉1ÕN M lT&x¤9«¬\zUýÓCáÙÝw>ÛÿÙoT–ÒvñŸVýïdWûÖÙ+õ‰¢sa— ;Ý)£A-i¦í÷lÚéß^Øÿçü‚TÉ î§1^gAîmù‘(FS‚—å@†ÁCe•ŠwR—5_ZÿªÊYšK5üÛ­|nÿ—ŸàÞóäd}D%'ŽQz‰ò1û<¤³IšãÌïʧ2´“Ç»îz)(êõ?OWÿú%Ù‚0ëI^SÕj¬P“HAG@?•1õ¢”A©vBGU»7Ôm³ëÿýßé¯vU˜`z™ïÖ>UNvÙ—éÒÎ%ä_{.JµB‹É¥ôå -Íøïe]3óò!ÿ»qñß§MŽ»›Óö¯¯®Üÿ4]Ò,§~ñ³6K”7Ã5ýš»ÀÌÃKŸoÍš×­šæ¦ö_/ …û/Û/­ÇÊP~² ë33èô9Ó£ÇcÛ±QóÙ­ûdkìÇÕ—eýÿ0n‹ùq1Ë ócnšÛBÑZ› úÈ׿ÐÉÖº […þn>Z·û¹t:öV“ëÊ;ý½!ûÏþ‚B”ÿ¬IO n2›7ŸÆÎ™kÎ5߬ï½3³J_:µJÂýØ»ö : -åÿ™)¸ÂÿÚƒyúì­Qb~Yò=wûÔ'aß¾˜?;>ËF¿÷–NüÒ„.ðçìçðûµ¤ßhY½æÔfàñ-2Sùmå: åÄ?ØÂž>UGV¦¥0ðPT&þ?ö`£X+˽röÅXâÑÍ\³,Ú®®$]˲¦?9³´™†WW?¯ÿå%ÿÅ`±øŽ‹9õÔµh@+±@Åö€V%Ò)þU±Y¢†ÙÖÿ_Óø¿~³^ú?hvÿOǤ÷FK¥V½üþÏÿùKÃa«Z*•êRy8™•+¬¨|ªÄ›”€Ví×rÔ"œêÖ9$Ç&+Xjãì–¶M7°z-J|>ÛunɶæPê8\ )®w‡*uTƒò¸Q‡ì!èlÛExºDÃ_©Õχ:V3çwl~ˆbÆXÜïªV©wÄÞ Qé€FFTæÀ‡XÞ*}RBk&ûuâ° mf—က¾Ã×ÈF—]jØ×ñ¦“]¯’¼¸ ê$¾½ïÝÊÝà–ò£Þ?®í½žÐ¸T~Îvhù…ñ»õkaˆÐ»Kj¿wL,{ŸzE©¾XË …7´[i–úïʹk~«ö<Øÿ]qFz»]¿ŒËX}PNJõó2qÊ)7Œ’5W¤ýhq_¿Ý®HÍK;&­ƒo€ZçZ[{ Æhm¨e!´8\ x\6bøÝlˆC¬"‘Ýš¿¨ËVéé ,2úxïÀçÞå„è}m3BíGð(4ÊŠkÏq6·U¢<ìPDÜ<Á$Ùœ7È=¯QöòT§»j·CŸÊ@çd -¼MbÒÍ×,}¡Gÿ»Óc{v~¤ÎÄÊÎOU 7XîÞ€æ—Þµ‹F¾aàQãÁ2Ø‹ƒG¨€ƒ~Ôa½4ÚA+ŽpO ý6OŽ¿e”Z­ß|Ö_û×Ùëí"YâÜ-Ù…ØX´ÐA²86zé4ÌRNQÇŪö”Õ˜õÖ[{oz*mÞ¬€oÓSo»ã6Ywß"/Ú~ØñA'®éýœ½ãµ"ŒNv{x=cLÚ;ŸMzø¸h& ]Ûx~]¸ƒ÷6„çíÙa¶w§ƒy¤¹x=Öàûò±¿éóÚo&/þ¸ _·j½ûùeü½òæ§©6GÈè”Á’|®T.Ǩ²²·çªÕ‚§T Ð"ᵩõ±ôY;à÷g/Â¥þMnÃ:ANÅæN¯ŸZ<ºü¶~ Pn»(XêÀ•Þ¯“Ÿ‡?p´ÓJàã´¬AF»Á­,£á9IÊu¾„ÿäf›;Tcˆr·‰×ÝŒ-åWؽŠÂ=]ëývÙ¹öë5™ôbðÒn+£ˆ€ÅS‹ØSäæ&UÚ$|† º_½9^YJëðÕ¦õÝ(­³È˜|k|VWFUؤq(“Ùá²bÉeúe¯¦€pêdùFygñ©ÏeQ¿ï…³Ä<¦’Ÿ¼Åªux‹!qzHXér•¶ËÛQf›BoÉïu’*¢8jÛ#%5»Uh¸ØMí´*õ©Á…F‰;ñ† ÷!ªÎžæâ.,ê–ÖåGŒm¹~Aœ*Ôœ PÝnW˜ÜÝ-È]= ŸÞG.\ØwÀ´À¥k5<†ì „‡¿¡»‰ü§ÁǽOÞŽÏbÿ‘h­ë&í`ûeº^U63aï½²:-aÙR\s¾D¶õ&º\ä YYN7e*oË?<ß«ÈZ­²îa³Õúù*gœyf’]€]“X%»ƒVç÷N?'´±Æµ18>î•¥~ržææÌ–W Öxm¯@¦×Bš¹·Õóµ¼ÿ©¦‡Ñ)'OŽÂ íøê¯ØÍ»ïêïK­‡¾ß“?öy_‚jÝ´¤M}UŸ)«˺ëAOEUÂ2ÔRkéëÂ÷„R–Ùá¥âbýŠ {¯B†ûQb¥†žõº$0Å+¦Pî¤ gœéhzï(3éªûÚ礽Q.ޏ£žÚR¿ 4TIÙh¿ÜcwÆßç\éW/êét?–ÕÀî -Ý^¨.cÖ+&Àª¢}s3¬Jé /§^E9kXwe¼#mØ»@òÝè'nyR¼Ì$kòëPý%»Ë–u†ˆÄ)¾ÓVëNZ¸[Z+b¯<§&Ó85Ú[®¼,…yJ3 Ž}W‰äé²…]àXc¾ÝíÉ›åùîãcìªô€RêÄaÊ>†••5ë.‹•z´­î£S}2Ú«²læß#î¾ÒÞ—–d†ØøÝuéP¯Eyؙ -Ò}|þê *Í—g‘weXm¾)é\¼?;H—WÁHdJ²kùðpzÏs¥+ ÛhŸ[&žO¼š]–ÿ‘´+ñEêê»Èª[â8Ëèûo_îVÂÍ!~î±@¾ä¡Œª)ãÏgu&Ú©,íº|+m˜pºK—ò…¾ ¤Óýuúõ#E%¢áý1ï¯Ö{Ëõ͵¿œ«ëü/Œeøê9ö°×¬¦ßÕ“­Ù_]a¶6ô)ÿÉN’b‰.7t ’Uãvlý»e©=þh”!Ú^uÚ“H½Úw…øI¶}éwÙªôeZÙoñ*é[SÙÖ–Ž5ÆõqèØú§Í¬Oï.ý…ÁZ^™·Y-i>\+Òàga­oC#Ã3™½kõmN·Zeˆèw&°jv¿:4‘F;²Ê,$Íþ]¯áL_f« C|?ô¥ý%ú»}òr-õ>ÿqÅÞ¸v´†©º<åË‘ßí¼ ^i…œæn;;6C~´:ÌÌͳÅ'V s¢º[ÑØ¤­ÝucW™~Ô˜FÛ¨çK‰–?]Aó~»ù·tOnYPs4Á¡&"^‰¸/ D[$Èè¶rÁ Õ$¶Âß{5cמTM ïç_k¤!¯´1ÜgóÇbµ*{CM¼ïÕí·?!ŸB¡ŠÄaÌ„ýóÒ6ˆø¨2Ö÷RË—U©¦ Ú«jí :{Y͈¾´—õitBOªÊîð‰"Pè]µÖSí¯K‚À|Dz›ÜG%«*îÇ[/ªoÔ ]²šù«dö}¨,ÁnŒ`p]DlrPÑm08Ï“d IC²Œ6%ùiŒ¯&¡•çQoý¦zÍBç º**3ßó:d¦ÖO¢ -Í+•ÿz?°•6¥Ö²fBT^àÖþÍ£§ÚwüãdýE•:?ŸYø$ÓUÅåÇö”™í­ëW…Ö”Êi¸3 +ŽïÆå¤°ûd©ã-ÝCá(ߨmš.²˜¥#kxÒaE;õ{c“Zn6žqåA ³ÚcX'>SWKKê¹ÄuÉ2ø²= ÕD¼qZ(Vü³8Zòí ™Æë2J+É&1ëÕlÈã™§cž¥>öuV=îæx´ÒnzmÄ Úá™ï‘S´4XÀUópÕ9Ts'ɯ]cÖQn®ik·Í`}£”ðòoºÙõŸ=½lNßÎgÝAFÝî&ïùr7xc›¥†ˆÓžÞhüõ_ÛœiÚè¦Úß½Ó»Ý0: ‹»™á½¦%Ýâ~¢^ÿyŒ)TÅéRÎË|¤/àÔ é¸ç–ùÎ՗̾WÌ…û°?½÷÷Žú«~µY'¯â¥r4é‹WÑ€õOÝ%;ÖÉÆàU4Rò·Õ†ªÆ6R)˜evŽÕ×¾¢ûÖ¼«Ý´1R)>rÀå£ðÏVó„Loµý߽èʙj¿³¸¸9=fZn”¡ à(¨ŠÇ/yØÕAZñ¿öóÝ'ÙÐ$ík6e¾fmïM[½¡Ãl,„õfAC¢«‹èwƒnó0x°ºø¸×­ã{ÎiÒ#ë-¦Ü”§"yö—ù÷áÆ‹k‘b>“ªkÊHÌÍ}SŽÄ`iKfLPkÀé*àtïs¡š |¥®6š­Vট îÏ7œú\•0¬ãGÝ(Äœªkî uÜšè,5R!kZûáÐË© -t¦è73}ÍÝß;6kÊüâ·xäì‚ ú]øØG?àE膈' ¥Ã4Z§€S“³QÒ̃'§ÜatãeZE" üý–#ê7íêˆj»ùótОlÉ5íynسǼ˜Ü­CÜÍìj±ßÓ‘ «7¤›®Ûèð–}Í‘—½„³‘áhV6s¼{tTÈ1¦ó½½¶f¥aie< *aãmfó·¡ÞéÜY÷‡Úþ÷K}üE•£èã1ú¸ãíRÔØð»uãù¨oSƧƄNضÑSäšc\ô†ûxn¸v§ªSô½‰•–0Ê óhk¤9•a -áeGt ×›Òkàµ,¡guò›ÜìñKáÉÏ;1z¤2†›ÝUýÝÍgÊ’zÞÆKßÛÞmøFMýµóyøw(Õ®Us‹'+Î=ä%Îluª‡µPÓÜ÷u\o'>+k“Ñâ©}k^#™À…OEx›Z¡KÚ¯9…MKa]BÿXe0ÿAp\üõq;µ¥kÛ>®_k“NüÙù¼OháMÆÁ5D[KDyå…<Æñƒ§E#o<„\^çý"#ÛëáÆ$+Ú+ZcÍóI0YTï|؈ðMîšÑî©|mÎûFs|è°OQѹ´ç{"b_¡Úõ…®$å$W›%Õ$¯Q, ço“ ´4Ê›['p·ÈN~!{C‡˜òÍ}òËÑèF¬æp‹º·*FZG­­¹õˆösçl¯{éAO|n}©‘åªÂý=²J `Òo†Ýƒ¶ ¢{©R×?‹û ž õo Ýÿ?Ï*+cæô—^içN¼›Ü–¬ÆörÚcì–(Ø{€¦Äë‘IÑéÇ®¥—[ð3 -äë£Ðx­zh?¸ž¿ëþ¹Ákm/Ý,û.T•¸±ã¤Â.§dZÐŒ§¯au“ŠŒc¾~ë¹KѧoŸÒ¤¿ÃÚïx7ؼ‰D0x£Týr&:YzK¥oØ+(̉WÕml aÏxÈlWºÖ Ú‘PâE¼øò°3±‰ë;®p‘޵aÕí&à¼J»pÔò?5¯º^7¬Ñïâc•‰˜;8tO‹×Á8÷S¹smé©ìúG ¤ÜÆŸÃ s–-}ZŒ<¥j…y—6– ^˜[UÊgÝ´‰Gef@ R«¿:6»`ðٮЧÊÄqëŸÚuúµG¢Z‘Ï`нˆÞ<¼·Zæª_çxÅ–ÝF|OÔþ퇘• ²õo‘+Ö–I“›Ü=©—·\Ö¡ßœ ½È'óA©®™/qâÑÍYiW;%­ýDkMà+ð8Dqõöå3úÅ^¬™3ý+;4lG_ §iÝ ïý‘ýù*ô ­ˆ£ûÌsƯ˜éVµæSË&‡¯æ?ÖW¶ÊÙ-?·½d*!„ª^TÀFWÇ4lo¾ò„g®éŸG÷¤0ÁÀõà©Ñ27kÒÞäàKë74¿ÜÿéSŸbÖøÌH9g‘»UFS -±úN;0ï>Çš -Û+«–Yœý¶T·ôœŒô×WsqsÁúÐL™‡·æ ŸÔ3¤öÒåÀoÕkµ`S.NçOvt<\M;åÅæI|}- SA•’aêí=¦ÑgfYeFÎþ]p@Ó¦­ýpê3Á&ó~ÀݦáwùæôRùm§ØZë–êëñò=Íû^úgýÔüÍÂFœÇûÝu­Ö{[,ÊXŽ ´L€á~í—ÂIÉÅÍå` ÒŸƒ“ìÀ<¯3¨NŒ4]]1õåØK`]’õžôƒWêLíWB¾&¡¸Ün~ÿ€((_r¡Zª¦§‹§Õ¹_îEä´•¹A¦²ýþ]•„ygÜ¥x^ö”'3?'¸ÜÚ‰QRöͦzíÃ%¥¬|‚¶ï÷ë„GÚßc™.ë`êÝÞ$Ôûɦ÷êñÊ×¹§éµþ^³i­Ö„fe÷Bö5äMµËêE5“ì/,;Ï‘UvàjæüõÂåoÐǘW±Wœ†RôçýLŸƒ·?ןd¿A¼Aíº¯¥¶Pö÷ ‹4­ ÅßÅ`tvµ_mlî8RÖZÃ\`¿ ÝQŠn¡Ìn¤ ÁOþm|—ëb´4^õú§+GØ&)ž[}½³vå0‘z'°äð‚SŽ{¥îstÏæeKµÃª7D¿ú™ïŒÀ’veFâ€xVµõ|ÍïÃv íÓ,ôÅ´·J~P¥Æio«ˆç¥ûxØÈ 9¾IeËM Ê-ë¬V/@l@*°1’nÆÛcxq‰¾ò™m¬É]êîÑ?œærtI£Ñé¦JÎ{à¼÷£¿ëWnŸ½U½ä%+gˆ‚õQgSÑn3™‡ôl¤-~Î=FßG#IŽÊ)Üžñã]OÖµÒ‹d°r† ¾wÖ5ê‚64Jgi¨;äm`)…ʬçlø; j5xK¯È½þf?;Ò§{’ÉQÚ†·Gó -Ö*¿Rðã¶A§Ý‘|ª–»ÌnMUW^ÏtHùtæËùCq®“úí)ÉâÙIØÝ[Ã=Ä´”•ŽÂ¾ Sø|ùýÝnÛCx÷Ä«ÑÄÈ›>]6±åκý„Ié^x¡àP3&es:oÄ­ -†l½×ÌO/ººã²­£,¢ug;¤vêÎü>VÑ€õåìØÆƒ9 \ÿª}ÿJõ¦¾í]³xQ #ÜÖ3H/³Lê;Î)+õb'‡›©ÕبëMÈN6‹d‰â)% Y°ÊÖAIÎÙ§çáÛ3Ò«Û /ıdò8ëê<+V¬È†x¬ŽQQ›‚'bÕöy6u('½ Élþœè“¼ùX[øô‹áÆv„{›o6äZî1ÊÖ@@F<êSåžšÐõÝ0JÄø;~M.L¨ ãc©ð1—غ^Úg/½¶ ‹Ýè¼GAÀ@(–7G+ã1³§v4²E«=Tí²yŸ{rÊì§F yF”<© -u>ee!Úô žÆÃ¦u˜ý àP)ÐL§-£wÅ-ã¹™úsô’âÄð‹ Û¾¹=`÷ÑK.«ã…¾Ô7 ÜÀC@çãvª †VE]nla\ƒÃÍ袣HZ’ã#Ó:$Jï(CõùWh5W¢ãž(òðw èõánòå¬sü)†ÒP⬔þôÒR[½3îØÐfĤ!:ñY.æ¿‘V'‰¹ì;Ü·«šŽZ®RBë +¼^7œ¦õICÑ_ÐeXê eö)¥‡6²·ûéÍôèÞD®Jø+Z‘¿£1YHYæ¿9~©s »šüYz Ò GwM¸å€2[?Ý‚¯Ïî%×>’Æ€ãÀ"›;ûH¿nÒƒ^ÖV«L/²øÝÉ®} v~•ƸRCŸMŒÐ„KÛÊÄÙ©§ìÔkCºÁ4<”XËUªDºÿ²­ô,¶][—'ÅŠ™ýM¶õj¨—áG¿ßÖkîi…m·ÅÏyRŸoŒ ¯¬!M Y°ÊIþò¦ãÚP.Ÿ"a2¨ÅsK–м""õ˜¦ > -‰þClngk³ˆÊ™î¿tñç„dX×óH˧mÉ›‰4ŽBÃóYô$cþ|J,ÞÍ™5fg;¥IeÎÅdÞѪé]1¶Þ×y›ëy© RÃùê¦8øQÈ'miIçEÞgŽ°Æ¹ûJžËfPöj²Fwˆ"®ü-zøj;¿â2[^c=¨dgõ$@_â{Àáq—=ås­’ëQöå«òƒ¹ôOIÆ$¿wßý4¢l?3xÑéJ§1ª¿š¥¤9‘{½7¬î÷Ã+“•yS›újN7Ô²6‘­|P}'Òb>rRÍWSO‘÷«¿MÔºÊ* ¨ê|+ªm¤PIj÷ùŠTé[§RS~*:MÕÙº¯ãz÷:›ÜwYW…Êé7c6ã§sèGí7޹1XÆ+}ÂšŠº[¦öÑïšw¢º*nQ8ŠÀ/FÝRYEÁMý°Š›Rd_èa®|¶‚äÝkÖBùúA¹Û›Ñt·ucé©’òmQ8~\pë mµ+Üg‡­%˜žÚʆyK)?îÊÍ1%¾­#]©rÛÐûÄ?@™³Ú}l¹@Édø9­\£I ¹PÛ…ƒHIƒDÌ7N²ŠJu–P”á¥ç5³—äWdQQ¤…ºš¢¡¾sÀ|ùR|Á=âj³&™µ÷ª¯<Ðwv¨–.ÛÂÇ.ëçHaÅÎ'Iþ2îf'¯_|í¬¡]ß·_aôÚiܪ}ÿ]%2³©©¾¶ž2¶tŽØ‡Þ¼¦=x¼“¡«íàUœ‰jWq¶ÐûBnö¾þd6X+ãâ˜ÛÚXñ©£+ó»ëì­½ÝX[E*PùYI`{ÌÕ´%Ê?™-¸âìÇÉÒJ{\­ŒöÛj߃î£Lñ€@¬‡ãƒ~ïù0ÉuÊ,˨I uJ þ\ƒœxìªÒÂnsz]tj·ž2¡®Y½Á^:‘äý¯VÀ…¿¢,¦²ê5„ùCŽŽ Ï©õ³³ÑãCáÔéùvy å™^™%¯ór†Ê'v:šr W/*¨Š²÷›!WTa¾N%Èh -ÄZìÑÃ~·¨>P H/%V›ŸÝš®æûc§øíÚº®¯•OŸŸtØò?`#/‰¾½¾–KzæV.Qy´€­þ†Ý륎²P> 14nî३ž - OÁ®¬‡WY×€ki‰ í0Ò°MîèÇHdwùÌ»W ¼ÒkÛ">A#ÙG^/좩lˆóÍ®T0|×@´ZWl ü.ÐÏTÄ«‹9u]§û µO;RÇö±.¯Ø««…fÒPÔÙýþêo²Út(6º«.û—R:´ÿê¤Ö(¥KN¿‘ V·Ysϵ6õ—eN!8듆֡ZU.\ ª.àŠX‘´ gPª…±©LuTU­òaݶtd.†«=0zÌ™zœèÞ‚ÖúWp~ù«¯À\°©º‹×Ýa§ú9‚|ñU»ðõf¨zÞx£ÔÙl¿É ÔeM¼„«5Õ$hÇ-õ -«oŽx©±QxD5n§¿WÉžµ^‡r0ÃõÛZÞ»,^ë¼dˉí÷ý³+áóÅ×Û $—d’6ãÏ*Ðþù¹”µ[Þ×»\=ÿµV½ÊØ»ã©X{£¬©h ¾Œ·]\p%âf;ýndÖxL~J¶†0®ÿúC1:+Ïžæk×ãÑ]‚Íï[?ÿš•ƒeÆ ÓªÞ˜sŠ)/ê/I,ÃM瘅1ŽJ9 }×¾L±š>pÿªJÃR;TÔøã‰v‹®fI}3R,T?yæºFß¡3ã”{†xíØRÓÿè‡s÷«Ûz¸ïW˜/ -žY¿Ð\/x!vàØ=ÛxjWêè^¯³OAĘ×_ˆ‹Þ÷Z=÷<Ó/ …£V/hkh!lh—η–“‰f… oEh‹Ž§Z\ò£‘¡š4üêèÚn_k Ħ…‰ÑSRë’¯ÀNo{øìÂïk–Ö Õù±4…ì[VÍ-döÜa{„‡ï¶suÄýºGÊu“]ç”<ïÖr‹dšWxpÛsú€ÃXkÜë~¢×kyL~Ûj–§À·ðä¶Gò!³D~úMœÓøòû¹éÓ´'~Xô"®Ö2³îÅÒªxžKév¿ÈF+Í,èlîÜÅa/UÑfÉ¢{ú>6–aGßltúqèPýžüs¢Ò)¢’_Ãs¸+8}å{ŒàC;8<¼xfЙ۟ӄºz´4bÀ–†“œ¡t௖J˜ 蓽JTRåç^ÞêyXž¦g ±u!Üc@ÍlEþjŒ1µ‹ì”ˆô!nQánkáJ^!Ef+¡þ¼ -õ`¡¬Ë´ÕÖ'Ê=Õ­Þ9ßµ—åŽ|¼"CÞ\mÒö8ëXmÞÇ—Qé¡~¥lm¶o—Í?Û^Ï+5kñ7fÅ0Å»ÔÍj´÷KÛž8˘Ѻq­­Í³\] ù!®ßÅÈ\~œlÆ3GûjrÕ¨ÂZJ¦ -ªU.ݳ{×Úg?m‘/§ònŽ -ÅW…dž’jiÃâ§5Þ¢[sk,_Ƹ|€²‘f»ÎP'sÊÓ>©N$sgŒûðΪT¹š¿•ÚwUƒ‰¬7é¦üá-ö8ñ¯&íªjo&X‘_lê³ v‚"¾9Ð6@¥åÚ wíwùW/lž#ÓNG¼ª`—Šì1›>!ú·Ä`ÄobŒsÂÔVul$^Nj‹Ï¹žÜ(M»…tp³OqcdÞ]›³ÒÚëÄ+guî9µ mZ#¦²¿ ‹Ù€Â2„é#lÐtGáëæU`Vîa¤-É\¾v5ªønÃ\^%htnæ©û j¿"^ fb,ƒ%æ²±èhNs~ Óé -˜¾ßCó£«jI|¾½•çû‚ƒ|K2÷Ôé[ï=žÙ¿Ì,'msááSð~BÅ}Ž.áûýp+”…Òš¹íBhN±ùÝ”Vµ¹­Ãð4\NCf=ÆZ Uºët[ÉÍ'j4å^ˆöÙHÛ—qÀT¿½Üˆјû™Žìná_ÕÃgKÙñVþ¸8Á5@ú%³û2êàW/ª©Í”\ƒ-§,ç{ãÒAï\«`â^¿ýô«¤Ž•m™!WmŸô†¼½[ØÉÅ»Wƒµ¡œéúì ˜Á|k´òõ¯º¤h/ maókKuë W®–£ê°Ùpë3~ŽA"¡/ß•Ÿ±ìT9—«Ú°±ŽŠèýVºhnÀ咽 ùe‘eû[;”Ó$gµa#§ kÎI)4wÃÝtºw=ÃêÃH0Üï›j¾dÑ_m" Wo6TVn½ûª³µÕ&vÐÉubíð-WOÂŒ¨OËPíªß?íÑ´JíT ëÛøJ|GývõⵇŒ"Ö¸æC#!±w-úMŸó¬ïÔî<+êžãÃÙ[üô«Eµ©¯u=4JnòXõÞ‘¾º7ŸµyÔš±û°Èéó*ïÖÎ7*íw…–:¶{2à,tçã ìØ—¹gɼÜ4ÏìÅŸèoÛ#í‡t”ØÛÓ]=¦¿¸ôF›¡(ß}œ‡EbœùÅ ^Ÿ yÆŽ#§÷sL+9! J¦ñÂØ×”îžá»9:ªâ°²kÎ6´Z›&ëg_Ì~³YL3ÀhxJ¿À‚qDÓC_]Æy°ý¯uœèZðdÅf–¯ù@yÊç¾j˳IŽý.,VÍÊrÚ|!Œ$ÁAäM)¯’¥;.h(ÒÝþÅWv=³=ÚmfZI»$38™§&>UE®9Q¤,Æÿ€áÌ -mu°^ Þ·1jõ ¦PQû;tb—FjÐ9øPæå$óÜ¥¬¾»¶¾©#TÒÄ¿ûÉokœ´/E‘…D_c=ïÚ´œÕÒÇb}ÃÕo»ÈoHÔøÜ‘‰e Wꆥڔ'°‰®R2°µ;Ô>ý±QÿÞuoZᮽ–üZèunØ²Ìæ§ –£uêÃnÖî쟊½cföùobäÅ“Ý@ç -Ã|;¨46ÐÈHûkÇ¡äuÉÏÖ‡Kcµn=¼7Û:¦èyÙ™¶üÕYË/! oÙaÈRmÆšK¥©WîhÇ^#Î2#a·¢»'ë‚Gæ£ÅL¬ÍvŒå(Â[ù«_«ãôÒÌÎÀ…Dúè§ï¼A4>âÜ -b.²ßß*°]^0€% kšÁ¼‰ÅhþÚ&ùö2­•…iP޾E iÀ¢†Cö;Ϙû¸ÙøÕé5Ñ -lÞRb>'û4襵”å«3¼¥íù,òs Ï67l5f)Qï›Çþ8 &¿:'-ŽÑÑ/E;®Ð¿®kGÖµÌóŠ[ªòdÃHÍ…Ê!.7MΆÌïÆnʑʞý¢š“ÕO‘¥ÇÃn¤3…ª¸ì),«Íz<œ”ïÛÝæ­\%Žª¬tÛg-/ôþãѦժ°ÞFç™0°KÕR+FÍ#ƒñÑb§bí_`’É»§*ûx«oÈés¾Éë®`Kv=˜j’,JOФģ6*}KÕ_ºÙëÖUßÝ¿ü¶eÁFGêòWö—ÒFý‰Ü†Ín£zš‰ z1Æ'sÒhê+ꎙ¬ˆé¨IË›DÌ?Ë#‰%ãì·U˜üZöãҚ̱çÎä†5æÏÇÈG§’4hO§ˆ²Igçøò—Ÿ¤Ô]kFE7mĵ›v‹ídQÓÒèB,Ëg™›¨9·.¤ïaï>&ÚàÊzQÜ>\Üö›±Ù]wð;œ6NÖY:Žá*©ŽÎQÛ¿Ú";îï$7ô’Äå7)'WÞ£¨êae7ÆŸ–?™wÊÝñL‡Á-´6 wŽ»N -bI^,?€,2Õ£;]ø¤ϵÇÔõµpùJÞ Ù>K ”.‰Xé:¾:ßfë?1ª6“‹í^À‡9§OUšÎ:ìÿ:º˜<žô¶ 󨯧Ұ~#eë2éMŸ>Ö\iKe <[è“f>‘„h}¿ËÍ -Ú8Šuü1R×Ý‹ÁlHÎÙñaé8KGútãçßdk­}ˆõ,+³î™v9=ì~`t5W…Áß)µŒzÔ}žp©ånf&@¶-Ú[}é$²%&)+ˆVó™D^ ˆšøšT§ý”ž†¿Z¶-zm=UC'Æhïçõ²cJ¢ØÀj@‰jëlH©Tľô{àB¥UQÛÁH©x<½[‚?Ø%Ñ4߃¾v;n`ïâô\§îo÷µÞ5ô+Qjœ1Ÿ74^±-S-í…öÊûfaFuZÞ¬Õ< -мRQ=˜n 7õ.«¿.g«ÛQôwMeºÛö¢à–’óÈøÚRÆÆðL9ÏË‹c£%d³®ÄS´tj¥:½3¢tvúMßo˜Ò­Xˆ|txï‹×Ç_ Ëq`å²À{FúZM -µ7=]É£4"oÕj -“Ì&'ì”[d”7šEîrÖ±Ÿ‚égj~cÐò$ÂÉ<@±†%/UOÖ¯ïæÁÿn«Oï9û?-§¥cΛwå:SZ¯Ns nWÊ/1ïì˜h¢w÷ _Òêó¦Ó¨P§”(ÇÍ[žÓÚM¬DO9ï…›ÀT\ʬ¼°åqÚî —…]Ìiu­6ü*‹›n£À±z™U;³wÉÄn*Õ×验sM€hpRj¥öX‘+ñY KÑ@'Ç·žux\áVo…»R®òTô’§KEÙø¥º|ÄÊ…F^|±w‘“z(Ž1⼉QWSe¥ å/)ȯÖ*hŠ*Jxoãɼ¯ú­aKùT)m;»lÎVž1mY—p\?˜)­ë¾<Ç®¬z¦–A1÷W‡KZ#µQÚëhtxA²ˆ:¹£êÝTr¹éöìEùö+¹-!Bd²ÐàNãä½ì¾vÁe‡í–ùC—L™ÅcùwA±Á|³8 endstream endobj 43 0 obj <>stream -éÖ±ÓS|ºúÜÃc¡^ÃÉÓæ¥Gö#¼ò¼³š>d(sŽ*Ø@g‚YEÜý&è¨ìmmóû–Ò|¡Bk^;Ÿ¨–þ¥ù°â¸õ¼¥½“1ö´þý'Zõd^úÿž–zº³Üæ†É´<˜Ž5ÂÄ÷ç2šâ†8´æÙ]ÍÉ·Íp™t¶Þi[®JàêÏ ú¶åÞo¿¦UØq0Ò´åã{k#P³UÕƒJ,¬ÏŽëµì†ÈÙü ¤ÉÜheë)ÓÕ„‘'kéÔ7$îuFþìÖÉ‚€ù:â×?üC –-v†þµf־۬ݧ0è]¬áÒò”5ie¯Aˆ-땾Å^¶ -èiña,#}Öeʹ¦ÜŠ…\¸YéîÐþðïžE²´Úâa‰mn9K-ÃNr8¼F@8n K‡Cf1Y0à)<h]C£¬ÈâgÊâ iT}nìÚ#v¸Knž7Isü˶ª¾ÖʈKÝ=ý91Kè㣠ð\_‡â?ý’hßœ·uÌM£–Žïƒu¦½¯3¬6ŸÀ<~Û–ß*^ìÍ]ð[ŠÇ,^CÃè0Šc™ÕEb0öa=I[þó›Í1giŒ7þ¾"ò‹ú£ÿAöIDó‚?Ç/›miw»îošÌõ«À Ë1Prc†^ßlf-Ð^Ýy¢:|YëâS—†gÃlíÔsÑÝGHËΡ2噬j:ºñìç–T\ç½üÖëõØ•ñ§"|¢ =ï:„0{ûØ[‡‚G<·íú¥•í”E¼Çô€aÂܳÙ-:WqhôfÑÃ|‡W9`tû&Jt˜%¾^ í:¯òzéâ4üÌà1Øž„êà6Æi¿/’€aÖ’¼Çå·cÁ6m„­6vªèp”ç#e_Þ0âÍ:õ0 ÄÕ.´Û{Eìl°ˆëT›`À¶ yC¿ÞsÕ×ò£lND·u!7¥CuÚ}‰J*Õ°>^4o»g× -æùYÛ§'Ç¢’A´ø”ÂKh‰Ö‹ÁëäúÓ[»™98[¹›fÕÖ«©Üg„[háÖKiöÊ{/Aú5a -dþùZŠnÛîГ«]Ðp^ú{ÕT~†(¾öqÙ^>a±*áÛ[<õÓ· ûúrŠA¥”Ç|¼âè@õ'bI7ÒöýáD«åõDÖ¢óÈl?„½ÝÔ4kè¯+'˾uþ‘uKNX‘?Vûæ- +À‹¸ìZu·;ï|s¥-Ea±ÕÜ*ÓÈÌ_ó¿Rº ÙÇ~åqq¯Í¡ ß‹•Ö˜j,h)¸]‹Kp$ôûÁª˜pÃÄ%sð‰é2ß»Y†üp(¾ÜÜf@îù[˜¶áúpîÊc?_I8)Ì»T‹’òéðÊe6=G]wy"ï“)|éÈ(޼½!˜pì¤q½zÅ‚(9‹êöÝJœ÷Ï^ÁŒvw™Û HWn„&_›P°Pb‚‘¾žíw<ߣšhþî&øós@æ²éÎ/÷¢-Z${­÷àЃ6‘¸ˆVΘi°ÃÙht Cw¥TGë‹Jä úŒd-$‰ƒæ¸Qv¼Ä[ZÎìll@JA©Jd+'‹ñw[¢•©¯½oF_ÔÌ‹ð¸÷/3¹ªiÁÛ Þ;ýB+¿•Wº¯¦©æ‚= &üY± -³[@ †¤÷ç•øZ••x~[Ôç „owîüíÜ1,£7¾|ª±âðœÈjf#'W§:²×»š™!>’´ÙÑf:æ™Ú³¢ûûdô‡«:Ÿ·,0˜ý·J-‘AfÎ[£k¹U¾ù¨·V­jšÿ ¡ÖÒmëZa°x\žñÙ5îRÉn@YõYÈ&¿7S(²Cë2•NÞP*Uñ¸v¬Y1ñ£í ¾¯ºßn‹rUy ’sçÆh½ç­â¨ëå:!õ¿´ø—cÍçÑ_P3–J A -&‹í[Ö^Ý:—(}ÀkÜò6`è×£ó*w§Z”¹c¢jÛš1îî CÄa¬ð0a†à1ûlË»ÊÙjÊyô§(EÍÎò•èX}¨•yÒ :†ë†—¾èÃåÁÕã›ß£Ï.Ö)å7Ó£oÓÎVíxÐ>ä+°¥dñƒ`3T«k 'Zó=?'ùÈ#/èzY÷äZ3ttáñØ·ØNíÖàž¸`NO†#üøIˆ[cª^¦M+>4êtLð7÷Æ`!Ø}ÅÈ/\@n6‰ÂÁÝ—\ÛQ˜o!gM=w§å{0 }¬£O&\yÍ -FæÒʃ»”ª):$»bvp€kÚÃÓ ÁyG’˜Þ¬¯ F5 ·|.•Aøô€ö.qÏÑ -ŒÇr¬9ŽÞ>zÅ4>ÿ [/Éúö‰w¢.©VI²«“FçÍ1ӽܨðÙŒk÷ûø5ú´tj_L”ým†‡iÊ‹4í›H_»Æ±ÆÆK­šÂR}ؤ=Àü…´4×A=ïÈŽ¡‹$P-dq£2‡²¨2ëíÕÉ?@=xkÒV&ž‹aÜÿµÉ–×-ócù^UÀM»^c-Õ vãΫô>t‡ŠLˆ“ -€õ÷†Ý<žEïÝøØ)ô<˜î¦ŽRæ3Pn'§4¬T90!ïfvó6†ªªŒ- À5£”ôðýÐjàˆ) +þ­Ib곉h#‰ŸnZ·>]²]øXôZ‡¶Úƹk#—‹ùt»FzJÕÜ‚ï¡âþƒFµÞ¹Ãy{¿ÁÃO£hê«!nUBŸR¼lÐÇköµiåí_a>Ñçf·ZZà†"ôi2 •Þ‘H¿_èløì}ãr5‚`Ðm=On~g$9÷YïÄM+÷) ¬ç…œLöÏåñ¶”¸r$4˽ #àÖx5~w6z®?Õ‰Q^k‰ŒV@=;o.ééÏAÝ0C©»Ï‹@‹æßîiû$?—=»Pô>ýi¢ÙÒìvp|]ltÎf¸8ò¢±|<CÞ; ÕxõG¦¨ EœðÅæUyXWl>PUȾ†S'-ç„,4å:›©ÅIµz¡÷7%×f_µÈõÌ‚}ókœ*½åÎqU7lꤢpÏ6Ûôè˜C(RAPCVŸíl7tH-QÏrÿ5Ø ó¸@¿lî.~x -+¡wÙ\­ùù¢eòþÒŒ¤õè ˜5o5OÇzˆ:›1k¥_WÎ¥ðìhï«uê+Y¶q}z«m,·“DIyÑ@gÃk\)D•Ãí©?›†5V7_‰ë®V¹ËïP_[{”+RY79¦ˆ§Ý£M…’îœÌ~ß<ƒûƒ“¨”ËN×ÿn°nÌÚŽ¡ñ)¯™)t ï«ù½³ÚÞ‚OÇ-ÔŽ}YÇmñ*Õ¦dKýí²Îª×!3íÒ™…]Eîõ9«!ß·ñj½«ÓBJô+g>ðoÒò¥ãcø‰›r°­Ÿ÷“â¤FçÂgÜf h¯å©OF¾y™¶³“þp•óD5äûásµÀZ¬uV¿íù¥gQjSõ×ÎÚšÎTyÅYø‚šFh¿®\ÅÜã¬&»yÆ»mõó;íõîÞ›uüñ¹_E8kÊEcî¼—zÚ§ßóWÒ‰Î)‹šQA¦˜÷Y¸¾®²¤ªäT®‚òÊ`¾™d–H¢Ñî)ï—ñ|=ssEäƒ÷K1Ç/½¿í3ÉF×­?.ë‡Ú¤b¢Ç¼Biî” qË8œ4°¶æïú…ÌZöžá•—L˜ïÓHÐ’U›tSºétöáRGŒ­èÕcîo×îÚCŸz™¤,Dqg(?_§Ÿ‡Od})wf -¶Lµ8g1ÿ¹|"8¼ ñQrž-cg½*~ž†2ºÓn®•ôõôz÷‰Õµ§Z•6R@&Ø­á\Aù/Ôû1Ç•#<[EšEׯ.§I‡ ñý2zÓ¶ýxZ£W¯¼9.ô7ëÚô0£+öÞ¨¸f¡Ö ¯ýÀŠìª¥¾—åoŒµ3^»§æ7š5ýL°°CˆÎ¥m­çò…·‡·ò‹Ï„´Š­5åî÷›Ÿµ(6FðRÍ>ûëPƒ–V3<5LÂßFÒª#ªbÁ -áµæë©/³´*7$W0¼†±²¼ú"0¯è,MO?5›)x4û‘­Ñê«q¡]<ÆçPG­wºÓ£1fôæEÂ€Ž½:6aF¼bh=S†È|Ë´•†—kÄb„y²^3j»AÕ­!Îm% Ù>¨zQ[L@œ'Aî.¬ûzeÌ_í·CöÞZLW[¤ÜOWÛÇ sRŠÆz‹ëà·³>—Uzú-l„À®>R2’ ÄX·–'ŸÆN¶¹çéŽí·˜­4kSwÑ+³íÖÄ?~¦„|êÿ.¦@±g ‰Ò=ê㥀@Å릅jŸ:0Oߣjw¥”Šl‰¬‚óìz34ÎÌò>Dd–@_]rP;Úsëç ëzKÚ¤x•fyŽE|Xc®e5äL»oÝ/‚$¼­]hvDÝÂùwM|°’®Þ¶¿WÔ²6‡-Úµ+;|‚X9lþPùRЬ½½kƒänÃkÐdƒf¥'Ú¨ßþÛ9}ì=ßË:Ö¤F÷úYE™:Ï ]µÔÅ=$ ¥Nçˆà*V¿öy=ÁPãqΤ Yý@Ž¿ôpt•GVÖ› Ò>YD¾Þk›Ö`jݶmJ.š5п·9NÂ>ÁYeÈP'û£´Ó“HkVlõõ/gÝ"Pïw-öLû¬4§üTîVC-²zªHŽ×Ü,­Úc|Ò^ï6¦»Ïuz掮ÿª9ï×§u`õµ­d(“vú“‚™F·”<Þ¯‰æ×š¨0H\ÆŽ™ Ä÷´#oÉ´áõ׃¶+Z]å“V —fœ§o³R¡Ÿjù#kèëß™´ËÜ;Œ÷•"¤¾fÞiœÞ“È ‘ËÙ®DÐ%Q}Ô)›U§ÇTØ)ô£…ºÝYD$ß¹É¨ÔØËFëå¹dlØ4÷ü®#_Šƒö‹Æó<sã\•þíLB}Ûäƒ)AÜ:u3ˆ *ßâ4noÂÈýçÝÌ“õ¸¾ z›6œÄ¬_„«Þ¬õ[’ìŠ+íçuÆäqqfô,ì’‰,~ÒÐb ¶áž½•éIÛµêÌ;ÂÙ:ý—:§b_bN·wïóº7k &6$Lnàñ‚VD-S,€¿JgPoUѼ€YþnÈd.”°R~¥y<§™íÒ2x àèint%¾ƒN{š‘=)/ž€Ã7#dÝ¿'yrŽ—®Xþ›Bíoa|ag³†Ú5ä<¬Ó*oóßò»'~;Cwr3ÇyU:ÿlý -mûùeíÈGÓM+#‡s¦õèl{­‘kÁÎo# ïé4hM‹ØÖÇý¶Íðsד'(5X Ù.6˜Œõ[ŽW7KöQžc̾0rÊà()/<âÖ)SlÈ>½æýoŸSAn£[­ÛéÊ8ÉÏ»¡á•ü“Õ›È÷(†Iu~2:kPWºø•ó?3k6x69Fµ«BÑcÇ@þÑÞyÑÓ«ÁÝñÄù¨ùÒÖ7c.}>Q½ÒlÞ?­LíÙ@v¡«lÉeX[²ñÌ[wŽS‚›¸‘mέaõ¤UÚA©‘{€65ß4 ¤Ñ}ŽÔܺŸ\6ç§²aCÈ'j²>ˆ…²±v)È°éŠæ—ß®ÙBÏ‘ý¬·WH•<Ì•ÖÃvéÉ€ŸÄN¬ X~ -ék¹œÜ–Q9Þ<È{sÌò)´þÞ³à* ÇTÊÿüvO‹z1EpRù÷Û}fN/«>‚:#^ôÁ*MíaÆÍsg!¤ý“Áõ çû†ª4>}€Íöw[Ö]£s+iHFÜ:Z}ñÞÃL¿ÜØCR¢]4·NN÷®¹ùV› -Wö–VöÂCü ¤Òö¬ˆR¼Ôk%!+ß$?5R/(ðÓŸ%áÈ+ÔvÿÆÞ~î»7ÌÏÅqnjJCû á`~³jW‚á¾7t›OVø=è6ãb´¸G”7:9‘Ä•ßÜA·®ŸI¦[ÁÛ¦cg{Xjªø-²:öêïåš'YOº#Ÿ<ǪƒûÞç¿•¶à÷ËØ©s"˜û£[=îdÕχÈqj c¶\±–SJ¶ÑöâofWµßÁÜ[﹋2ºßU*»á<°ç äÃëMuÖ°¯ëÚÑë}J™F€׊¾tês”iÎDÙ¶N»É46¯úÅ(·†o«FÈg~e@©W„+9ñù­õžé€ûé똻íüî;{aiî¯"Ž&ÄSQq"yYîEL‹òÙã ˆ³aPÝn>=›+,ð‹û$¼€Ì»(ìèô<²×^tjnOÜ#¯áûºÄl¯õ󢯯Nµž…ÐñáKÚŸ÷;õŽaeÖ˜Fê‰ÓëÌ&·0_Hܲ·§q¤+^2k×½#Úq+£ø)VU)Ð%;ñn²ðbŽìu¬ö†…8Œ:›Aoæ¶vœ©ž'P#wÛa˘qÊiÔÆVU€²žjÆQê-ÖJ~o\5¶µ‡¢iµô¢_ uæÑùÉ«¦¡‡—<1ìÞÊ«’Çš¦qó¸n 6õ¡ÝùuD®\}îfT¼øT§Ìòp¢–Ön»ñÃ%ºº¼5Ž{®=¹cX +Žû†­è&j˜ØHöI™L;é©)VÖäÁ½äÊòŠ€º¿1ê|ùt|U;úY¢Tò¥d–ÝåÕ¶33-«9p;PªoCE|8ÊÚxL1n1ñì¿úØq®{‰w§ƒ›0Ñã6w¡<õ -qòÅj³1ø«—îÞ°‘¹A<Ïbdg¹É{ßÜ ¿[ëKæÁ½Ã”l–·nž ->CïÖD§Òh¡£^_zSËc "Ey:ROwñwâ$¹­ìùQ*}ˆûi8T KŠžï2ùíhõsžÞB†J dq=õ9k·yfJª,ô¿ÊKL ñEà {vÌÔdsÝÝZ©Ó1·ØX h]b0Ã÷ƒ~E_ÅïÝ?ÈÛŸK–<«b¶X\§ÐN|u\Çßbv(eö3¸´õˆóÈ„ÙÃ>U!ú£‹"«Àd›Uäó®•M¤7¯;¦ó7µìô˜±ü—¥Õ@.ôrwa–o8§ýUG3¯žI¶<#*›&ù7Œ¤aú$lx¨¼šÂ“WÛ¶JBWš´îmDvžòˆª(’l©ÑbáïÅö%§$¤ÌÖÀO?ö¯1¾«”Ú…¦ù*Œ̇ô›Ó–oü-ù P©#ä+˜/ªÕPŽþ}_zíö¶nÖ¢eøÞbGVî7ÃñJâv½)ºÁàoºéP;Ö^ìY4õ3mE¿s¥ªVÙ°ùD×ý4=£¸—#lëõµÿFî«( oü·Ê®²ÛÑùæ aËž£M^‰—ŽXF»Îë ÝƒÏ wXeêïýJÛ$Øe7¤ ¦š^K‡%î¡ð<‹#kõ‰{V]ïŽäÈÿ†5ª«óÌ{‚÷ÙÔL´ob¨]' »­ÖÞÌF~Ô®RA«JÖ«•òs!Ÿ±!»tRSWjXn‘ÊSÅ‹Ú%9û}Î9V4-˜ LÝßññn¦Lìd¶R(NïãÊW}¬X ¢ÂÚ·ˆÂCYœ n --Û©ôÓÍšûd½Ïìöx Ùˆ¿Æ¤ú4×tiÌb%¦ä~LSNY¥f)c_Êp<ˆÍ!Þý˜¨SÏÔÞ>s›“y0ùåéNH¸V6Ìt ï%ÈÙ™“ç'Uy^°²©ž¯^X^}€µÃ›Ê¥nT¥ãiBƒâ…›k’Ú¬QûZîhü2Z¿ãi…q@óVH¤^²´Þ~]ûJŒäåÔ1Œ£Ò}:œ•æý·ú»3Ò}}EKäH/ØóÞ&¼µW欈½ê§ñ}ÓCGÿö2¤îÂcK>ûê -ÞºÐå¿'®ÞSz–C“þnø³mž‘‘~PJöR Ž…Tx’·P.›Á)e ñ~/ÃëOÑ Ò ÄÜx+«ÙZí]¬Zß]}U^K™ …7®{l"¸( ó|óÒ¶ÁüHÈY¸è ÝÆwsjÊÓLÕÄõûóÉÙðÚU­Y.¸« Ú¸M›oêA¡ó^–¸ÚhºBÜ›Ø!6RÜT:ô¨tðÛÒì'úr³&U½c(²SƒzAѶƒö5þrÑð1íÚ`4-¦ßø?h¤6N )}ñs¾ö瀛¦R{’Wý¯Ûb4ŽU5ì$„12à  OIïÖ³ÌG0×બ"÷U_ë>;x¤—¬úûûß"nu]kÚÑé§yŽí"Ñë1y1ê›s·hlZôú…ñ)Ö6j§Z¨eÁùöÖ7´zÚ9µ‚eó¸iO²ýKÅöÝ,¿Ÿ~›~*4:®MÁ¹îÌæciÎÌδ“»‹Äº**:\Š¡1]˜€<öZ™n^´é­u±Nù’ô’û­³ûº¤ZnÏ BEJäÍÂô+®P›Sw˜Ê’ërúU±8+Ùô @º†(Êö$¨ËóÅ\‹°ÎF¢A]à•zsþdÌ>¥ït˜l¼à-}he0›ƒqy·öjË©=}rÕ ¶«¸ëS³®© úÉʯéÚih¨¦¯«Q “\:'Úñ[ùÉþtzK?Ð—Ž¡Çp©¸sÐ ‚ÝfÚT#ÄvøwNÈHUY;ͼ•ùòÁ)6Ê/Cž-œ)¼Ê޲߉òŒ¡ ‰ö߉Z|2ʾ^ûª¾ÌPhtv[­Hê]¶ÑŸäZCp]OÈã |Ðoîá)›NÃZ­1òи¦ Yý0”¯$­Œ«„„Û|¼±­¬þî^<Ÿ”Yƒ®À«u£µy,ÿAcÓ q´©+f]}®X‘dÚtç¦Vß§jsÞ)³e˜“¯Õgæ77ָᶪCn€’ kp}0<ï°IˆRù¨¶Ï“­±±Ñ¿Gã”­†¹™lV!—ä‘N8Õ‘¢H›Æµ’R€œÎ|ˆYµî“­öß~ t£~¥/'y -Ÿ«¡‚ x¥€5|iy4ŽïÄô÷ÎÃø-˜©n[ut¹­ë~ŒÀwº\<·cþµZÛŽÐÒ×ïÃ\q,bù†3Èî¼8H¼sh wçdl'Ú\…fµÞfbÆ­ƒ—=Þ÷…s –Uuƒ‹»&ì ¾U×#úoGß -»°ÝvÇÀ2Óì¾Rc‘ƒD³o< ¥øáÍŠRa©EN㥩ºó;j¸ÒKçEѬ -û–›»}U÷Íæp€¸ÝÄÑ·mtÃ5é3s<;¯—–øRƽËÕh¿ˆæ¨“‹½a<øAª^ñJß*ã½^Ô@KÛ´;ª!—Üqy]Ãïµ¾èð‰Ä@½´V;ŸY1‹$ ·æ—…ÅK2ϱï(žè©ùêo:¶îw‘‘¥?z™F*ÔäáðM‚7¾bAþ®R0ò¢†¤á½°ä¬#ö–ú]¹©¥-î$^X7"åêG;¯®"J—ܼEe£WíäžML ÙåQö·‰~&«).ñôíl)€õí²3“Ïññ:»ê£ßúÐ’ªF.¨5ÎSb¿r:5i}Ôlxɳ¤6ýwÞx _ì |<¨†¡æšlÝ8þ$uš­é^±–i…ìl ûç\R¦ã°‰ü(ùk<©>žÿhþ‚âh±×CŽ,ðP=ò›Q2{;KlËKnU+'†,]‹/|GD:Ü4ó¸4¯Ó‹e¹ÓnÒi OXÛª6pâ@ÍBð·m~ß-.œáĪ¶Î”ù=»œÞؾU>¤wK§|!üÀ*ÁÚ\<Ÿ¸ß -g{­©+sh3Ì?Hs÷ÄÀFÇÅÀLîh“ëÃÍpíñ%ÏäÌ&Lùu±.]· §çÅI­Dz™LL^Jt@_¹¹»y،٣ˆH–ª;'xæÄ+ëå CÈý…݇î»ÌŒúßRzWëÎFˆ ¯Þ[o‡|zÁyEfEmZ¹ÿS}ÄræfýTzß‘¯»\]ßš½þÌ"ø -D؃sò1—fí•3ŸµO¸ŠQ6núàmí†QNâùê_ŒÞ9 ûW£æïƒ­j;}»ýƒr¤è}OShžø|!Œ B†‹Œõf|ö íöN§ÏÚ˜õó2ã¬íS¡d,Œs§7ýìWþRD³TØä>™ ×ç£žÉÆtWJ;„˜öUT÷%Ï%Û{ÜÔÁþ)æ›±£þ*M>grEqÃoOGOªMy3¢Nü üîn !+#`ô9¼æ6 dìdc×TfT¹›seßOŠ=±.ê|5˜Ù):û°‡Š—jÇê¾Þyl™Û -b4ÓÆßp›&ø#o«]± -pHº÷ƒ^Еy§c;"òúÑ¡õo-ÀͪÐôÛ“Gÿ cØ;K` ¼Î³…v¦n´S5½SÌQÇ wKÒ–Åa_þf^d»“&Ø Ç.k!¬jÎã`ءՇµy64źäÆÍS¹½œS× e+N÷lô×6ï—µóât0xÖAÐwgKjìsâ£Ø:ºëŽ,¼êåÊÞÁ¿×Þ–òß} éu~~‹"~â²’ÐòXtŠîSWÝ=*ûSvNü¤2œœ мƒÍö¬¾sòª¦üný̓õÅQWW¯…ë\ùimÇkB ¿ˆAM«Mk3çlJÁN°‰1…Y3*yŒqƒå<­fd.Q‰ÓÖߦã1ñÕyv×Ó…_K¥Ý¿6òO×+‡<"¬Ë\Hë,§.ð€Ö4[ǧã‘éjt„!nWû±Ûö]Øóˆ…V.äÀÒ{íœ8M'Æ»ý üFË“38Ò”0ö—«å)¢®Á5™jﹿÁp˧àëŸ/ðð×ìîšü¶Õnáï㵹ÈÞƒ¶Ž±K÷–E‚Îý›…#³lÑû¯®T3L+³fõãa"’Ÿ s»Ï¡ÊeÇ›s"®õqG4OpÛÍîMc3J6¡b62ÆN·â¹š“;q~ \d•/í5©%?¨xýtôá`¡uöÜÖ[Ûßüoj!Úê¹Y½…äùmû|„¾?ŠuR‚(Ra²ËWJ¸:WžDP6ÍckU:ºïãnߦØ‚Å8ÝÍF•ŸKéå Wõ[7±¸_YO{7åxÎÒ¬u ‚[{›RóLƒõT~ô½ÞR9¡ì³5ælcÉ%¡áu²¤*HŠEÞç9n~5`þåH׺­º0Ü÷éÙaÜd6kÉÝtÆÐwð‚AÀ™«¥PM¦Lk-æ,b¾ÜjYÆÌíeàz3¯óÆ>hüp”J3È4ŸÞýãfÿ†ææ†µ m½mc§UU}¨ë“]ÿ–ô˜=Ü®|½ñh é_r…)hƒg·,b@Ê -åÅøSj ø/µöWÇvУY‚ïÏYÕ2’bšäþú*Yô—tö'Ž4¼ìY=åc?“ï'Ћòû¹]ŒÚ¡‹0×&ªyåkNâh·òûoQW—ÜaiˆN§æxð†.¾W0“,pUdй½}ÞI`eåa÷:ßâ5=¶;ÏêÆÞ"AÔ,K±k}ïœËJRî²äþrzTúõ$¢Ðò¢cÒò›ý¶EÙ2ê9‹¹p¶ -™SÎÅ›‚/&Ç @99‰¯ðzH`D2ú›‹ñÏAÒ´ŸJû‡Þ†ôl¬ Ø2ŒÔ~ƒiGÒêzîÇ<>ÀãT}ý–[½á“ßõ4Tî—ØPG(˜é^5å@…\Ìq­*qH­“–8ÿŽAC¤£½:úó¼º9 -®8Ò¹³ª¹#Ó3&œëÂÇz_µãDŸ ~»* íø,MB¢ú¼âEÞè?úÁ»«›ÊÂ-³64ÙêèF×äÓí,Õå÷½’‚¨0´Ü–ôSmߊ”Ýš'žæ¬g$kûpç,†/#2™ð Ú;•øHsbþ0éo ­ ~æ„oùmvO§wÝn»hä–[ÙÕN:ÚŸñzrú™³¢_Î#=Å`][Vµn´3“š¹¼+”]P¯È‹P²ˆÊ ø\±67N›è—xQðÇò;óŽVŒ$êøi©b¡j xŒI±„2¢qizÁfw®?Úd¶#ÏèÝí_éÏ«:»‘Çù'wqìþIókAuÄ™äì§³±a Ïëu–‹ÀÛÞfê:N$$¡´X0:Ž&íÏ!>M‹ù5 M›{E¯R3È|¶@®8[»r½^%÷¨9þZц©©/Ä7H…™§ÙŒú¢?-×tŽÜWöuî3Á Tqí'ޱµôáå’탲‚$mª££¯¸†ÅJ1£å³4ô\Z²JÒqy=«tâ'Š’Í£Ü'kKÜè3©wµñ¢·0¦, -£MEÎ $+ñÔsm+ϱ2!ˆ›|AÐwÂ+R‡PRµ.ôÒvõ§W¯~¬S‡Æ/õN#'a=85³ªçÑ×QªÐ”ôõw4…±õauÓTjO:·o‡Z× lßW¯û£Á¡‹ð›EE©Ì.öudµÅÏAøÆ¬na [UnY—fZŒê%ŽÍî7ðÓ¼*›D³n>KL‹ìÁ -»$âÚ¼O@‰·ZäsøØ®Hi··bߒʶ;á$[Ø'¢Œ1&,xÿFµª£"‘Ñ­o¶\g¢ ”ž¾`äîçÚçÿ¥…mð ‘§€‘/±[UëËÌ-2ÂT>bò† ØÔN$n¯5Ô¾„­)¸ -Š÷3?H _’¸uÊ•º/øZ —K[õʪ"Ùè®.ÐÇoî“ÝLJØ(Ó¾QnÚs?Ó©g$Où¸›¢WáûRvÄîÄ>^‰tŨ‡çììkEæÙ‘ÇJßÍÑ+ª®£®YÂÇ&³õ\á~2hvµÃ?(ÝÓ¯Q–ÈêE¨¶ÕСí9¡W­Š G"«óàÐQú -?øµ¨Ígµt±=˜ë…ù ™ôIèÃfÓº*4[ ÿ~Wñœ"g­Í‹jJ™¶Ú®ùg 6–ä:nßIGÙê~*ßÊ9€ÖÛÑmJ=›\œ¤Óýh®_ÿ L.é÷lüæ+¢Ýc[˜R²¯{é؋p|xJ,–ùîòoCqc±¥µ¤q8£Ï›7?æ¬ê -¡_9ö…“Ý/éç=m Jí¤‰üÍPUFVÒ_Vœ©â}”©Y[y]’œœmôMÞV âûé.l¥{z}k·œÉr -5†HÝÙªÒ9¨¸*øŒ(ƒf#¤ÛnÕ4í–¢W³ùp—(ÇÜþ&EŽ4Toê;ر§qex«ÅÁÔÔi®—ÜZMpÒgȱ¬o“ÊÃhwt–,¢“1)ÿSãÜYòw¬q¯é-uDNcÛþÆO³¼¹¿WœµÔÙõ*o]*ŸÎíOÖ@O6OPVºæú°™+süCøŸj³ÎBPƒFŒëu¶;þÏÑy ­ªtwT”"E@zï]ÅÞ{¯ìÿñ¿ð%™Ìœ£ÉÄã•ch:@´ç·C9vŸ#ñnõ¬å?|¥“0á Ëoß È»†¡ÇèKêxâÏö 1²î(³•¢ùÏ2T.‚}Ò=ù}ñîã#oWöq†0¿¾ã®V“¶öüH,3bçCÏÓO#elF?/b̺¯fÆY,µx-(¯‚SKØY`FóøÔœl|ð½ìÑiÚë¯ïcßn°Qó‹'XG$õâjÔø[¨0y–ÅÝ .›TMÒRvx‡%ÿXÓ|Pd„)¿žûô˚݋·qi˜¿³Òp·ÓÕ -½3ÑæDèQuòaèrÇ7#ÃŒ•’ê™[¬áñØ/bo·Í§ôšé.íFÈùƽµ«ÀÁVYTP·ð—JÚJåì•EZÔÇH·=¯¬¨E($Fø–q×>R8¢˜õ ð£C;¿m˜†ÜU“¥#_¥Ø`g§O¾©í$8äŒz½jÖÆ~ä\+}»²`±÷­1ÛºJþÚ¦Ž†ëùž=U÷WgÉVSh|jöãyʭñà+D9Ô|PæÒ¡ìÚe7tÖÃä 'é“ÎùEÙùÕíŒák^>:äBvÛi¶ýx§ÎôV+ʇ9-K“]Täð;—ZÚÉ]#J´‚°Ù½ëÜ@Êš1‰ò¡†ÕáΗˆXÀ 2´~ÃMž·ïåÖž¸útÕ ß->Ò`~Yè\ª(RÑ %^yDÓŠ/¦PÙUÕŠ?+ÖLyÐ=­È\ŸMíapÆã®¦ã=ÐIu%¨Sï/Çí:¹9 ¿oŸoÅk¢;©?¬Mµ€ÓÁ”Í &':Nãב‚1G~Š{…ŸIùÞÏZ´„­ž¦õÚÚGŸª48**­?Ì›^¿ÑõЄæJ–ëÐá».ŸÚN,>ÿP#õ`–3\ÄGC¯Î•Î_ÜY™—ÚßiÛ¤X…9Øq½*³ð!}py¸¥ëAó·~ûjºëãÌ;%˜àþ ŠûNæy¦¿©{>Æ=¨÷ -µ¼Ò.=¸ù%–Ñ”ÿnŸ/‰n6;‹¸Ëöźð‘>÷Û‡ƒ¥&O” -z†6ó‚Ô<³óÎsìgYuÓç“húÀ›=äÛ§xò—xèÎWÖÓï,%£Øu)ƒ_qìeëíZôgì-œ]bÀ+ Ôß?€šSá7 “©ï¦d‹G“{vóV¡¹wEzquF»F’žú¯¾·Ÿ¦Ê^N„Üé†×D¸ëN’E„“Øß—G&Q"ç§ÿvö.§¼oPZ›¿=üŽÄÿ€”ïû:ƒëwÙ‡­IüªÏkB%=Už/žQp}\:ö\éJ ÂRdDˆÏNæäxuüû¶-­–•_øj' ÚîȽ!äi -¶=FËÇû×E¹¸°$ýªÒƒÕ^"vmS3ÞÇkÿ”5öņKv4Fï šôí~ôúžŸÑ]ÊüzYÃ9ר¹,u¤ðÜ0¿Oì þ3C’³;¶¹MÞðNp¿X«Rf ³×_q)¢úrÏ6?jáÊWæQš ¬3¶/V®`GhX³*³ô‘[~nBÝpêZ!c -kN¹DÛß—þ ßÊ«vÛtÐdÌþ‚™ß“;íÙ‹sôÝôd9@2Aýë.='<~þl›™zßy ª<#ìEÞ¤#‘bîkàvƒ_¶¢˜ÑØ²Ü ûDüÉO° É]]‡ÿÞp~‘[y”cq»I Œ,&£5¤\Ńê}J‡Ü,–ù¦Øzø’]Ðo4;Àa´ò†Ôeâ½ÍÒVk÷Ýi ÜocV6%kHDº;æÂé¢ÂIƒº§„¶Éå!ãã°ø5 X ç³ÜÇÆæ³ëWÙFäѽé -µSÉ=#C)¬ñQÍ¡}%¾k‹Sð€Ê"÷þ»F&ïÈ#=Œ|—Àkà§ÖÀëY]™æ5Ä;·[·b,‹y§©7/?F2v¯uŸë<,aH¶B»ê7-PxI½ÏOYëÇoÌ_êf[Ç4×°¨­6fì´ùfw+ï &ýåG¥åsVÿªj©¦ÏÒÙ`Y)ý´«Qñý”òD¯ëBF<*Êðº:øÂÉžÒ,·ƒöýl¯ŠÚfkÚ-xÜzìï\<çuÓŸ=HôÇuFC¶n‚#d)Z9h»½èmÙç°tîâÐbذâT¸…Óˆ)P}nJMKO®‚C³5Ÿ©Xu‰ŸEÆA0ÖK,׃Om›À 5€ì—{Ëü2–±š×6dÖg)'cŠÐ¦w7Ûž7µ@ìõ8Ÿš–SÉ>ZØc¶YÛ³ª²Ãaˆæw®×òØâ\ÐQnJ¥…Lyyävúv˜Ôáñ¸{4oŽ‹üd4“Ø",A­¸–/þ0ùÁ‡öàY¬ :æ^Zo ÷´ºÏ0/7~u>§[Uïv¬œ£óªëRÁdR÷dî„¥ò,hû¯¯æ>ñãã¸Üo‚]Óuõ§“’B“ë“f9x¢};Ž"[ÿt'Þðs,Ã\¹¢cüBù#ÿê[ -|/ƒ¹‡(MÍ£ktYsÃ=÷‘Pµäïð¬U^€i$P£ìÖ -"ÉéI0´ƒ{üõ±åã^dþ7t[kÔ]»O ¯ŒˆÖj/¬w³Üê‘Ô ¿9½ž>Jju5ë÷DõËcÛÁEÓÉßÄjV–¡ønÉæt*t»|­ÊAü%Ðlþ\좷 -ãÛ+ç>‘´r•¼s݉­v¢V>#Ç©ö?õÐjFxsfk;Ð^‹!ÃqŒ­Û'í&Ëþï_è qr ˆ””gvf¼«-ŸßÈÓtˆçYÓš?Ͼ¦Ì}ZKà°ó kdG6°ÖD—C¸×‘%99T «IÎJ±Óæ(%ßE›¨ðb‚ÕVIRGfbFP€#HͪlõâÐû-Ü1èTÕ®k6Ÿèî#ÃΨX·gQã.îʽÈp c4÷‘]i-Ct¶v$g&ö×7ÿÁªw¥í8m÷Š·1DîÓ¶Ó-1Zz] åsüï‘Si; ÷Ôo‘•q×mÁyà?P ¶æÃk¯º@¯ÛµpÚúÆ?÷ú – îìÒzðn¶yoBl¸Õ;·\{×A¼mEZÊ%¦« ¼?éãS0?+É?ÝoŒl[YH¦=cèí-æ‘}€FøñöÚÚ@ô—ŽÔ’ýûÜ,ëØdž;ä7FèjÝczPx6ÓžH•w­•€]bXöÉe­`Ë`óŸ0^¶™Û–8-|ú¼c¾Hd¯±’233¡íL‘µeÕ:·e|¼'«ÆØ*5Êð€c¹FëG!¨ZUµZ-öHC·B -Œ¤ñ‰2 ¼µRFÌF‰Š-éñè -ÖÎßõKÓ~ÅÚݾ o[ìkØ· ¦[xôQ¬G“‚ÏôëM¥MÆÊ¯¾0Ö$þUÖ‚‘4Hæd]žk¿k3V.ýÖì4MºÜPï)ìÏÔ&…;KFP¦8›Žáš œ„k ÙŽÊîJ%¼±"1Ûäfß9 ÒûtC#ÍFN8ºZr¶Æ#PºzâÚôÿzA›m²Š{ðƒÊ1ÏçzøXfî<ÃrõSå.Ýt°ºÖ{ì#ß¡†–Ï]|=ºóéäõ Ëž„ µt_{êLí4€G¿›ÂΣ²¸©¸ÃÂň[o«½Ç!Fâ7÷¼˜Ÿœ}.í»’ŒÁãKºò \µÛDŒ@×Hš­—˜–×ú|˜ªv™ê¸ËÐT¡¬£·nà3ßEåÃãç±ÕãÀñ[ì—Ñj}êM[”.¢÷2ä`Ý@H\ŠäåñY‹Ê4ÿFô8hîüW&oŸ÷ïÏÏËœŒ‹J` ]|8ŠM†Ÿß;x(Vœsÿ6S¾pmØ^5Óê²QÔ}å8\íºU챋ZÏ%¤ß7.ˆ -‹“å}N|[k´X§Áï’ÄL§]j-û™KŒb^zgcx¬ÅbëÔ´áYPȉtˆEfý1ö[¿†Føª_á‡.¯ê¶kG}wW'!\ÑØ‡çé>ä,ŒM-3CzW3ñ#ù´ReH#3½vµ‘•ö­õL©µÐ¿!sV×Ï:h“e«ˆøþÅÒ¥ Q¬¾‰³‹UzÜGf»»úRh¡6aíf¶M+™~z/¬ˆ] -e¢¡³«´õ3T}ÒD]íK–­=¸Š¾ÈCʉ`?y'¹2¢¾e üfÍÂÁ¹%õq£;UÜP@éíøuÕ ÷Õø·qš(?véÆ}b˜-4!Îsè"&4°n<œ â•tòL—ë›…êCM¦â¿åsEyu+Gµü©Žì·¨å~£N?›óŒo\6Z!‘a¿°àYë‹c ¹Ä£'²Šò‡œË=pÌ›—hv ÇRS{`»ÃÏÔ3èö¾~S¬Qú®PïtÁ(+ª_;ÿ€Aù|©ôœw=”X;{Ù°ªÐ韕ÎltTzãwM$ÓÓl'úм}F%dh–ñoɈãÈ3Ò|,Vé93ˆš¿iG.Íô–é6áF{©È0õͱÎQ¹½›Óƒæ§ŒßØ!´1v`qæ+êåUó)‚ÅɸÓOÒ®ÊZç°a®5U0|xR½¹Ð´–‹†O£œë‡t…ØoEÃàÒ-²¥ÕoB =^\¾Q·?î+ï -oẾ2ngå¨%à¦j¦feÃ=òÑèü䇵߫Gœ† >eGêýˆšà]½V¾lyWVmÎ@ÝñÄP‚¿ûÈòè&u;Êæðú©«àY–]²} 'ø¡ÎLû¾Qöêsg¬C£}r-fóudß8¸ÇÖ±»r kQªq‡ -¶–J=óû^ 8ø¼(Nãÿÿ’ìý2R¶½;çH[¨¯‹­9a©ôqžÇÄÔéÖå,×[ŠäïLæ#ß>)ß Þª†×žmµÂµu&^‘Ö»©e{ÏÍš6«7®a Óз3{uÚŽ‰A@|ƒ¬†ŽåIkhøJZÎ ¨™²2r [–ÿNÛ~¾GÏêåMB2jØÕ-RüÁnt­„Vƒ†e“²½mhmôF}WüÞgÂ\§nΜÀRÅ* ~Ö†ú±ljÌr††Æ‘Á½åÃÈýj·?ó@86êyê¦>uJ À£ŸëöónßòÚÜË(R'–m¾ëÎWª{á:lßí‡ågA—†BúëßÛÍoñz]"/ ¡V@ºî2*×M‘ -Z̘é |O½äÄ!ž`/Áü™œýy)<)û®5¯YcBx¹ç TU9{–Ò1´­œ‚#÷€á•¶ÑÏœ'Ÿ“¿ù„'á¾, -güj¾8YZ¨r…¿hý6þö^T#G#›˜aƒÞh‚¶æM_rè¹²ÏɆ¥8jªÔ}~öÞF³Ó½Öö½bÛ^¿~‘qA‚:ƯŸ»žì'­Ó*¸Ø´¼u–5Š[?õLËH,òÜûl!øü²ª?Ç â+oÒyÔ”Àä?‡Wžˆs䦿 ëÙ+3îgÓ¥­³Àh†’%œ›_JSùu¹¿M:4ºîž‡+Ý@«l-D½ñÅêwý'U0±Á@Ü¡…jP¥9¼:Y"\ï µuü‹jv™USW¿‘ ãºm9XÌ|êw&¡ÚTú¹Ý‰+#NmïX7tMúŽY‚ÓˆþÞOÓŽ“ZUÙÝD·-ÿ`ç±±*ÝNUÝVœ/œgc´ÂQ$~¸­1sA(‹!¿]¸ý»;™àÍ´ãiGùÝŸê9ýwñ«2{»:rvRtX˜E',Z5\gí(ä%cw…·¶kúæ8OÃñÖ{9âºþ–ñÈ‚ë¨ÓuX¥Þfrê3ò“ÃÏÑ6m¯ÐÌÛ·Vsg3¨³}=ݵUŠ]Ù¨t—Z¬ -ƒ@i:lïMt kÖzþ¡|Š:Э†—·|;åb ?/D8‚Wæ ²K½l†Ò®SKÍÝRpµ„._ÇM@³²ûä&žÔëï&ׄz*R×îš¶í‘Hþ¾æ¸FöúÖBX—;øÛœºè#þ9}jè´ý;8Þ²f{7¶˜×1r>ór‰K·Ø+»6á¡UÚ—þ ð½ç®‡ -è9ÎJè2É7ô¾ çÀìyßp™*´qó¨ ËÑÎÚø²"µÂ6ݲ¦QŒ³Ú…ƒ¨Ûð:µë{Fóù‚ÿщibî°ÿzwШµt˜X¹~yº sâ -«.û ~ÇV|ú3· ¢Œò¿@^ó'&à݆|ðòñîäÅ~+r=óZýκØ9Ó§hl.¡7¾Ýú/tÏvëàX)|¾›Xé{"Oÿf=U° "ñàÕ!,ÓŽõM ÍCòÂÍ›xÏË­™ÆÒƒ.°¹ÁIÂåÐ…Ï=«êŸ)"\ Õ"ªÂ„ê~;í#6+Ô+Ãû¹’ý vÞæÌü8‚ýeyö¾©LcCím›ÄŠ9¶'¼íƒS²¯ä{¤ß3ýø_‘±êñÒœñ<qAl&¥äg/´Ýûšm•ŽìLÇo -§Z¶¦*+¨nèû}£\wÚ>6È<†ƒwX2{¤uÚ[Yª>Ðî„0&Œþ‘Ù¤ý2ÓcRy­›F{|‹»s…ÓUh‰/vŽ©A …nõ¯—šb6ü‘Ÿ<šG].;Ñ#»¦O3kp­;g[ûdõN˜ Êunð´bw ¾ØdÚo\·±oeú°»NŠ^áÒ‰? R¬ôz -;fãTOvl )bÌz,Nשּׁ¢/Ô}Ì|ÛtæàäÀ³Œ èè¼Ö¡åÃZ«Ñ*åÇ‹âô+%/¸À•HöÿQ‡dwÙ9¸Ï}P¯c8KZ£ö~˜î¸,æEæß\ Û½ë>3Ú#Uk"XŸx”£þ)¡é,gT§nøîËñ" 5 ‚^7oUŠAØdöÈðh÷<¨2V¼=r$[$nO'ñÜ;Ò½ÀLÀ0ò …|ÒóB¾Ê$£æ–To÷‡'º?YS³Þ ˜sæHü’0âÒ²®ùÀ%[Í-Dœ“IXkòBNiH~ÞENKTsÞ‹‡Æýr1cŸtBè_Ì -Sþ P‹[_“x ®|R÷ÈÄZtâfâ­7>ÛóúIßûŠ4( “û®LœµAœd6±ç#kݽ™µrùl´Ë\³YZüOÞT©zPßꬹCÚ€$Fû­zV<ÞòÕ Q¡m«É„þ˜´ùs|7ëóBAËBÑ\ìØðM¦‰’‡&¿jÏ­ì<é:úáP—!o -ò½Q¬“ùºF]”ðÉAàì;¨hnÄö|0–f¾ìG{ŽéÕB¿k9ïQæÚ"<\ßUš„îžÞ[yJ'ý¹ þ4÷[ƒúü’íÑ:÷+`öx:tñ«ÿâå™è„n ç1åòµêS6 çQ&QäWáó8íúK¥Ôá´|&¤³´ôzAãmê”TéçÝð”ªÙýÆRñÎÛ~•S¼lÕ8=fžr9<ù¨âÿ—³b·Ð¿MÝ}{¨óR_@WôÔy}Ê0?®s[bÙoìž8gÞÚ\±z3ÙqIÎÚm~uÞc‘3»·UR:ÍÊÇA¥6"Mô›_û…’cá•Ãç¤<6êùÏ«$¾.R ¿ôMés}A1yûfÛㄎ• ¡ƒ#­aw¬„tOHբ럻ôؼ¼h?ѾO-e…@US2)_5ôe¤|{)¾"#V×…ÞÏØ´)ã iŽö§Àf?a¿—§?5…¦’t}xwMRí³è­•‹/ü”2‚= äªzÍÑršHF-ó\­!fÏ?g¤=®Í‹΢ „[ß‘¬'ruó›q.Í×òT¶ys¹ÝˆZ3Õ'ôã-¿àöXسðw(ìõœ¤Yg£Ø½×öñ÷3FÙ9åfþQ÷¥ìPÖúú_­”Ï€V+5ÇÔíˆJŽ%-«Æ{ôm"¼·[:ßÇyf(ëÁw4Üxæ*0ÅIÕõëqn{°)ôÑpÊÍÜCõºÓ[}n€ …2¸ÁìXµ¸´ÿ$÷xE†Öw7䇀¨Á‹ †hÄHÊæÒ‹©÷üuÕTÞΧÔ^dÞ>Ö@ÃYÍ\Dx×=Rƒ+‡7¸K`ÑÍ~Ü¿ú’püFn Æ*j VcqÉ;sµQí ¢¡eb¤á±ñ«GM´Ê÷"™¹–÷>³UÈãrf_½ø•ä;èQ°—HÙ¦IE÷Îö¬›8A¹\6["3Õû·÷×{ðÑ5ëм"½šóþõ«þE¯’'YÆùöÂøV~Ûg\L]SC<Ä3>À†1(BÓQ°W7í ‘D‘µ²½ëÛÄž("ÿ u‹œ;EEnT&¬Æw©¡ øTÜ9O+UÃrä²õ±8È~®•wW…î¶Þ~§C5ë_õr=,5}°st õÜYB‹ßÆk‹{¦×ç˜ @´5œŠÅºØ…T£A…gV˸)ÄyYÆvI˜õ©ßU©$ð0s¾\×_Ÿ—ÙIuïh—kñËÂﯜ~­±C{KÉ»-ì|»=nmR:¨scí_Iñ[GSˆÆ¥»¥Os;«š1’ãö ì¸b;-ÆŸgá!µ6î*ÐgÒ ÝÕÌ5 î$ ®ÊiñÅj')×n7ÐZíˆ7·éÊ©…˜£å¥à18{Vê.µG)±sÑî€ÿâ£e±Û¯¨:|øÁoª,­»öž¬x»C§bTàcxµ²².õÅ$䨌¡/¾oõäm²ËS•Ç­UAãÕ7« ãË,æ×ÕSó(ï´éåÕ×qkÒä¾§ÇÔ« 7%Á”ÒšKI­8)˜ª0Žó˜S®v4‹wp«|QÒè’Ùž‰ñÔG<ùÆ|&™EÀ¸°H{ ãCx3;ZM4úKû!æPyd‡| ´¡*NãÚàoþ#M\úêÔôœ¥Ð’µ_–ºe}s™jN'ο2™÷çÆæÉ̯´Ö¯T%…'l^ÈɘñkÖQg·Ÿ ³x³-®À•9ü4¥ÂÓmÀŒß[yÍîO]È’þŒ“fß8ʈ%6ã{zˆè¹A~ÌûšHò± ÌÑ¡V0¿íC×3t©‡{ÐäuõòË%ÛXbÝox物ŸË/ûšFïúP™öGî[´½¨[¤zê´¹ð®Ûn{íóÑ™Æçb<ÆCk{nÝJõ ªyGA¥ŸÐ ÝjX¾èY7Téó$¹à‘fØoÛîäHß߿؞tÃC0öèì/Wæ‡ÍG[Á™­â¡&a·ütŸ^/jɺq»im¯N^+–\L¸ü–ܰ†1²¹ÐO–˜=a¶û{UV…›å¾ÔžVÐû€ö†x†NOÂÄ÷?}À¬ÆÖ½M¬²ÂÕ²•)'¸{NÇͨ§r.+MVíèµTu±¬Pœ}ÿ4Bí66ôá‰åeù÷–ÍV\«‚0\›6š¤Ö·¶Œ.7f×~]…õú—Øëê.W£‚ˆ‰ÇòîÐ@2¤V¢µQwyÝvn`ù„R²l³ÇOuW§ÎÚQ2b&5ìj½wLýû…„YÀJSF›¼[È¥®!Úpm¿ôØÅO“g¨ÑÔ9I‚ÔMÍ.UìÄT¶3Ü<èšjœ_ R˹H=m@/‘£•õÄ–r^N×k*PÄØUs‡(=j­ð(Ð.Pß ì ‘Wtdw’Œïö¤(3ûìN¨›g”n²(ô®›&¥Ÿ­k峯g¾ZSîM8Ës u—áÆi…S_ßÄ,®¿ÎƒõßY¸a¹(ùóCKX~Ú‰5ZWóÐMsŠ|˜U[~ÀŠ]' Êô§}á½ãï¨]™ÎÒømôÙýÄ(íI½íh«ècl'ÓJ÷P½mØ„/Vµ+U Oÿèl¶ñʆ­¤™Ïž[q_m0û z!=²ç{j®ï€½-=Nñ°ýC1Ëà"ëäøq/\'µì|/£tøü¸1ÍM¨Ÿ•¬~C€ƒûÔ°ÿÚ\öå½9øtÕ¸•ËZèúÆiâ«ê7m¼>+pmy±Qsìæ‡‘ ·Æhîl›´Õ‹tjè_ ´©Â:dKG¸GÕPÒî[î‘Î>hÿ½Èö+ ”UGcí4è±7(íûF‡“ð:/ÞOG÷½[Òø*Èâ§|ÝÏZð`úŠ=në^ùVN1}Ü'u¢ØèNû–v¬¥–J·Ôj ‹[|Jz·ÜÖ_¸÷dzTr´ÖpÙ÷#w¬ém$‘Ýè{®#IVð2îhînŸd8Õžu8‡âà‘Ô–FœúfÚÜ> ƒ=åDøSª ¥ zo`r·Ç˜¶üv]á U[C+¿ÙñqÚôê]Q¡¤ëlðXÙÓþIἩÜ2üçôæ4©Þ—L´KfR–uFûDXy4µ»Mb技üп¸ñצ2Ýü¬#&.ÅÁµ²2OÇëÌæÇ鎾º–5”ï±ý´EcÑ×Å´ëL”Ù:Ô3Á î5Þ|3Nô6¤l’Kö‘«¿bÃ~Ð›Š––áßYkVú&®í73""MœÙž”+Õu·\bÜA†–̽Ʌ"ÅäªÛzË\³<݆ºkæÃ.|TÁ:,|r%__9íÖ{ÜzñòÞ¥?‡þßXj±®0Û÷‡á23··Îîoxůf hy›5F ó #§mS±D¶Ò²{6?ê[¦Ž~Çax_Z¦ÙzNÂ÷aýëÔúHž¨µ\2†À2åwVÏ>׬ T®î[îNªw‚¡¼U¥ËûÉ Cͬ6ztƒÅJnß_–€hÌîeuûŸÚÎ,˜„ô–±%Ê?ïùê4®º¸祓ã÷É…Ï*ÌQ^§—³OsÛÕ±ØL}ŽQÀ»±‡y»ÖMƒ[¶F^ïrß·JŠÙ¥ò|ç]4-Ééx£lo@É›SS©ËìHóŒ#T°¥UC}üL1C²ï¯À“ ,¿ö”e\ÐC‘m¦|9­¡3CñjÇUÀÃĪ©;R“pß3ôÐùª·qÚôJîñzÄÛU÷1µ$›¹¸¯Qku–“ÎßûÈ ÜsUp4¯M4N¶ìºú&3·W¼5×’€¹ -LÂøáÜ­ —]CGØÆÂ±}#ÌíÍ7ÅíF[+:”:Î ÷€¢Ú=K6Xú®ç»‚þ:‹4Ûýæmî–D¡Ö.SâÖÁ[̓K’ÏZK·Á9>‹„˦û¾èVÈYgpÙñ„ç‹é}ž…°¯«ÓÛ.¾¸áúúΞpøx ÜFÞ.hÜÅlxª@Úðf¯’çÔ÷)V„Fsî9ýxèà÷Û®“r;Þ«¹Ã»£³ -ß.½êwÝ”œÝò\o…n.º˶ítýŠô¬–ÓºØwÁÙ1V:ŠëË—SõÌ>ºA ·ÙrVQêÆ 8­WÊo;çy·’GSMžþñzô>K¶Q—âŒÒͨÔÃT²;ݾfMƒç¼ ëæl£y¬ú—¯]lS–¢¹eHõV4ÍgAò”Ìp”„ÛÇ•¢ëÙb ôÛ«88v˦©¥†öó)Ü?5ܤÈÁþÏôÙ§_j­©Ô„Êr܉Мô¸îBU -œé«ŒböD¶ð¯©#–qõ7ü˜@NŽ}4)a/{Ÿ.Œ7§“„µý`9‚…<¦ìŽkÀ@¥ej»gúª=…ý¡é¢¿ñTSxÖ×ÞÝy= ltÏ‹ŠÜÈMÉãÖmTL·±®OV)¨ïðnWð–±X‰Gß©–#â•#óÔÞÁd{È¥¿ãìÆÄ¸zT0µ›ÏÓµä¨öîë/zÅG¬gÕoïÄg³Þæÿ÷+‡}k {oÀkŒþ’%;@/º‡ÎÚTØ»èH¨êÔöR$S£nÏÙOÇo6v—ný-¤NuG—½ÁT9Ûnïàð©]ñÀ"Û:óòßwÙ±éVã…Œ]àÙV½ãkHrñ¡nºƒ—ÙóâΫKñO”sÝF¹âÕ?CÃd -¹Ãë°î·¦+  …ë8õc§šïlø§çp¡Ä¼’Úî¯WçK½jR‰MiÊyf‹¹ÉnVA¾ùtóúúêJ—õµ…7‰–`¥Q«ç‡îÓ²uœ?øäöÌXò ×îÒfŸØqg–Ãèý»ÉÕQ핆Ö0œþZé† ŒCã¸ö/—„åKÌ(kÕõ!ùÈì‘îB£ºŒUÐGìŒÉ7Î;”w+P¸¦žMÝñ× ?hÚ­¬ ìÔÊåô”l’×½h+_~álúˆã'˜‘“Þ¸¦ž©ã9*¡b ½ $ çØó¨ÒãÑ:w+g‹§Œ_ýJ67UF;å÷©È±Ý›ì¨OíV0ÝŽô#4yùx»5M 4õ`¼Ôªðþb.f•Ã÷¹€…Χi~Ïû ”/1Ë -Fg³î¹ ÑÕï–´ÐQ`,–çYpw‚.zõ#«õRÆ~¸/£PÅÈö`^}y&9¹·>§-½‹X‹[kÕœ‘›Kغ;$ÞøžvV¿‹þ¿7á›?34@­œz#¡xo÷I¾Ítm§leè-fèæï°üg­—SBˆúәѫµW®vú\k{m8Áu­‡^MëPÉ’.;3å -úüòÄ+øûãì;TìtNÃÏíýêVF ÏÃåú¯yØœ:.þr„ÐIz³¯®â¿`p¬nÓ;Iq<í>î¤âG£{‚Þd‘q'ðõ슛A;$»^R1Ü%dÕ˜Ël_ŠÂ¯©¿½£×˜ -Ž@Ç2¿­m•÷÷ƒË¢ü¨«¯Ï°ðJú -M©ñB±œ5²šl1óÕ²w\·°í¸¼ÿbâûø< ÷~ ›)Á‡ -Yeò(8ñü E(Z,R7mq-p¼2Ù &ê”EŒøcïš½Krn[ä?€ÍCì>[1©NÊ‘HwÈB}†]îÃÂîàÚÂ$(k©ç›ÆÙ('Þ6ÎùšÉØë4|‡¹>ùMUnû(ÆJBcgRuF]òd rª‡“AëȶÛQñŒ;8ÞŦÆ4•„n]ù@z) §ThxŒS.“¤|%T6Ž^ú•tÔe”XNÙÆ› mѺ¸š!Ÿ^Øä<‘ÂB e†Éß-N™:ɰÓ/$< Ì|h§Îtô¥„õõÔ…O§ílGì]ÁŸµIgª´ÎëGÆFÙ ÷›.É#…{w×zO|캰¹0ðªçq’·dÂ"ó‹¿n‚ww#iè1gH)Ôlbýë¸äž[ß¡¹ol¨Öm6‚ÜdÑíŽÇ §A/„ûu•ß8¼*–*?0hFGÌ~Þ ^°íƒ`Ñ{oØD-¬Ø•^Ùλtiƒo]Å<#ñ*ÝØÍá*6?‘Â| ±Û/ͺ½ägû>Tùê.Æ¢¨Áø¨ªtÌç&!ù9!ì ª¼Ôj¹27±¤¾Oà®ÍÓcá/B¼û¸²9+}þ©‘7Íæ4û•­õØÀðT¬j}t”N»ÍÄ9å½xX|\e9è”lô]*­’…ik~<`Þof yçiÖì׸½ÚzMkj›¿ß` @Ó…/ûÓõJbþÁptî­²ŸÝOÙ+Û«P#bToúÙQÇn—9«¼YûÕç…ìy]˜B$ÀÖºISPV§±ýè-í›Ö(isL›ò7ÔgP™qU©žf3qiT3{3¨e•ç«=Þ?ÂŽ»5 S¿˜O³ ®N˜\—õᆘ·ämÉÚ7ø°›-­1²È¡KR/K;88o“.ËŽ„ý:q~os6ð‘l$i–ìüKÕ öª?dY‹ÞN‡Ü}•ÆMz2ä=­8Þò”èÌà8nªñÍ9÷¯KkrˆÚž{U¼@Zœytëzû`^×[&.¸ç[ž=uã 2:ü"iøŠ»‹„Bu«ÉÀê0,»ÎÝž°õdœYº· Ö§p ´$•òz¹ë?Iœ“émg@k2ê7lË+vj×n§pöW+7eÉ62òÁêGßFȵcª­‡tqùmç¹®¨Ò­í#ð ˜sÎJŽ’EP*ˆþÙGÿáܽ¾µÞõv÷žþ§^Ü‘ýêFÏLy¬< ÛÉý~Mõ ^a‡'²÷=rŠôgнâ“ó BÐpŒÞ2Hûè´­†ÛÔ3jÐAYfܪÔë^Gø -¾p6Dz²_ÄÃR–S¿/([œjè ”d0[l¶§½iå‚]êÓ <šfÔ®–/ñƒ=ÐÑã-ÏújÊMnÆih³OðJ6KˆHœf–¤ÌsÖÜæ=pÃG]‚Û·ic®ãaÜmÄ=Ú?‘b«ÝiF<<%!¾Ãc˳ežêdXtÂÉP¾AÈãÒºR+Ãb_l¬L\ü^§˜HBS(tpТ`ßË u-òk -ðQ󌵈¡‰|õxXÄó´Ü4)^·`ÿÌŽå¹úÉÁC1ï´_oÏalâèÍÓñöÎ÷ÕMc2Ì¥|ûµV›“q0×È2 WìcL¦·æÉ˜,ÀVmáȽ5¸fÖh·uì5E»¯s ó5ñá»4½Û”ñ{³rÆÁŤ0ÊÀ‡ˆk˜³®71ò¢‰Ï€P4’×ëœë=æÃF0Þß;ôÒ¬´Ì’Ñ­ŒðR’)"úK>Áj÷¹˜j” ¡Ç®ú¯gU`þt8Ê®3§UïÉï7~âÜ"¬*›ÁZSsFåÝêÃ1•›§ØŠŠÄ³:dvoºyÐ7nOT©[ÿPØ=]·x> -î 7 ÍЀ￈caOЇ1ÏíÊIè²h¼ŸÐÞû)$ Üäq5» íšQ%råN.„=¬5ƒLœJéªú²ü!&!Ï%3èZ?ƒÈC[Ô hRŽSI,‰¢mµƒO³lï%ì™^°4ÅÙkc¼ºD´7D™ÏÕÊ…<&’0ÖœÎ7¡jC½ýv‹oT?‚Dk茴K"Z3C¦þhŸ€pT#éór¯º>ÜG$©K'":b=´°¬nȽrŠg}`ªŸv¦î’8~£Ü‡ÓUê¨Ú˜¼ mçdC©nê°™¦ð¥”Ÿ•›sîJ57ƒgl <gúNOs ×LGªgДP†ac4ÚjjoÇm,W -¬¢L7U}‘„©-øjj³¡"fœ…°M¬Ï³n55e®“ô‚t5,îPÁWï"‰9†÷ÈgÅÐÊÑJΔð>~Í”û8_¤lHè„;â6v[sc@/DÜq8Cl¹ÕgÅ™æ“|:%·ô5Ї€¾Ô§Úûå¥"pù9]ì‚9xÑ»w'PÍ}á«mËŸ©Úx¦Jebî®öÝ›y»AEOŽsd²>?ê‹î¡‡ì_u/žH‡R©Wn¸%…c¬ÆBãyÛ^"ó \¯â:L^Â@«Ò0 Ò¢°h1Ú°÷Ë΋,Ú÷ieÛMÊÎ^Ct{+=ÆŸÁbo_À-Rìòmv‰ÖfÒݳ`3ÁvORkrú`ˆ)kâ¬rm¤3V3É^¥;ïŽãªû0ûÂËœ'Ìéå㺴†Çùï£Ñ9–ÉŠ5Ñ7Äò>V=Bõº¯a*ŒH ‹;íù£²Â§aE…d±Ñí›§‘ מ=Þ§uø î‰sãIÆô,­Ò’Hîêý]Ò~zÎÊ Ñ!3ε‹Œ‰18h ‡ìù>‡žwÓT«úBE’/Žz¸p¯ìµÉ[Ø^‘3ºÇG¤tªS#áL Icà=N\‡aYwÚj73ï½÷'«]ÇتOö‹Zd§01X?V%³ `ˆ»£xp¼/¦žXÛ0Pe`Z˜Cý0ЙÚ|¢í!k´Ò3­MÓðÉÁËϪ›tŽºÊ›5™ÇüuÜÆ4hæ _¼ÈÐùæ±7ʦÔâDõÃ3Ýp™l'Ãj«9£Õ#z8£óµÎÐæ`­~if`‹Rú¸Åk·´F†§ì}"ïëõé”ÊÙqƒ¬a=¿>çf[2E¦/Æs^ËÃÓ¥HPöö¼(Ûk2ƒuæÑû%žÕö,pG»öždYZ‡ü¤ª‚s­~&ùò0Õ¶j©*LBœ|QƒC­»k¨ý0\Š_âÏâåÚ~¾GdÕ\â·ìO’È£ @!X)uê‘ÚÚBÒ•aŠ, -ëAÕnêoé’ƒò­¢ø¡Œ(îÙæfÓg΄M[p¬Õã‡iî!Ï| jE´Ú”Õ°AªÒ%ÂÛ ®µKö7€j@ç)¿T˜Á(Ù#¥)Ö@kÌôN(6~ÿX§®ÅsV÷ñZwX&€ÜËiè/ìDkúLÍVÝòkÛ›“Ä”ßO¡ÉùÂô‚fT<ºÄsËòSª[¹è=ìâå{tmwYЏèIT7l9ÎB— -‰¢ç-9ˆr©[=^Égà ¯'KkS!Ee­NˆµÓôªëÃe/þ”Ì¢¦àýW mÈiÔ¤ - "p¾„WrEÜ¡ZZÛ%uƒINù”¦ƒKä j1=Àð+E/ŸÂ -CO¸Ý®‘ž²ržî·ë"åûN¾DZÙ{¯ç• -,m˜tæ^,æú'ðir¡×a´G¦-œ"ºè‚ЪfHî9Ý,Ão¯¥jÈ"ü*}± ¬D×£ ®Ñõ- ·ØiV¡Ù)D£4Bã6VÖ.|y;¦¿M=êa QΑ ô¶WlV*aGÇçÈvK÷jÙVqšŒ{ÑÊžö™ÌãšìÎ9ŽìàÕ¼30ŒåvDÓíú½NZŽ¼Ï”òíʬ¤*nŠ83@]¼}4HmÝV’6Mðßö½'õýÝWéúÑiúø¥8÷¨{Ù³+ü-OøÉGƒÒ×Û|qqèIg{ÍR6“cº^.·d¢•L:Y§¯MÛb‡Äj•”¨}UWoå4I¶g™Z,Ö§’¥Á{ò`¥ÇãÔ¾]ÊØ¤î"/Š"®½syÒ6E„’¶n³î,F:yYA<¡ócœ‰÷0¤Aå\¬JMŒA£¨V mªc3tÔgsÉ¿©n|ô’d‘Éd1—úŸÿùŸÿ8&€øP‰Óåë—™#ž¹´6«lõÎz9\RysÒi3™C–ÔZQ./'kãíð°y®¶…S²¶¨IÕw¨Ç ܋ȦɺZ;ÓÑNqÈgß¡ôùæšRû0ž¢ÃRœ[»$Øs™RSÑž“È=¥Ô¶Ýʼn½î„Üæ^•´Â’Mq/Ð÷ã8é5D¤Ô »8DùÐèOJUSHGÓýÉpY»2á7ï¹W˜t¾XñR|«ÇÉ…$Èž¤ÇMƒÅØ—V>òýzIú^;YLWÚ7¬óšÜóM -{ÇŽ ô6©†ÓÜu¾2Ðù¡øîãW¢˜¾ÇßÅx¤úJwÀ×âøÙ(F5àЊªÊS~ÄÿãzÓÀsòRnÜJ^Ÿâ›Ï‹-NòÇ®L5ÃZÎ’`€U ûæ6 -¯îz_:^T×é+6¿ —ýÕþ,¥ó›·V÷-Ûð3ÚOJøæ¥ôÐüº*]î#­ùð‹-.¼Z—†›Â©m@Öêm³[æM­‚vÜ˸¾Ò¹}éEÂ1;•Œ8b‡«¹^ðx°üÝÕz—8ž'`gIG³z¹¯†x䣧}îÄ…“¦7Z÷æVMxQº^hÙmV¬gZJ·ƒå‡xRZ)í…‚Œè\\¯"#SÀ÷¯SþMÅgýáùþð¤Ç5w/fÏ 6h‡©.lô©^—&ýØý×])Ë ÓÝ.Pý ­Eѳ—§Á—÷Me•ÂÜg9ñW$|­4ªÍo1èˆA©ù)Vp5òlöâjvÛ)î®2Ïñêübñe Õ%9‚2ºœl-qkL ¡˜66=)¯¾"Hï}îÝ¢ð»%Ƽ4¿`êóC‚U#¸éN 8†9´dŠº¢εÞ2ˆDždw¼´ií4)3_îÓÏoøJZ±§CZ^ˆ 221¾/ïºüŸô/‰jä­Õ²€ÕKŒö$ƿКvsÊÝFRßÛåöš5åTŸÅÙ¦ã>9§´eaßì­y§j½Ç–Õ³Ò¢;’yf0s¿,q]þ³ ª‰a JUù¨¾ßlo “Ú¹ºh›†²VÎê¹<ä)·«ˆðµ=0$Eð~áy2}[rÞ@_²ç "/®ý´ÀÍuüOúãSâÃo”¨vH5ãP© ”&ü1ˆÒD8™Ý2I¤Ï‚=näy -{Ü"m€\ÒI®Q·8¶]BØ…ÖX&Pî fà¯ZíÒŸ‘ðÙ¿ü¨žÊ7£FN„/î³ÓµNòÒ‹¢(£±µilsìò’d;[©¶¸ç”áBÂøy1ìC´Ö<Íè‰ï‚”^æg¤‘€¿GÀßçQ}Rªßô_pp+Æw×<¼¼’ŒX¦ï<{›EÉØ•sx–^KÓµEÓ Òì®d!„Ý€šD¯7q7káÔ6Ùþð÷ù/K(%ƒ§ x®n$O@V•ÎMPÂÀ/ݬÀ2S|£RF{âRùZ/$È®þÀ™<øÄÎÓæ kÉÉ$zíGiT8ÓhÝg ¸>+‰Oÿ¥—\v1spȘš_žæd$¨¹Ûp1›*JS…H• ·x³{´Ð›EP.áéá%XkNðd³>Á©yÜd6!}†æ -áÿFI‡xx—òQudáQµðtBÎÓ·ÔjŸ=S£æõŽîȰߓâP2™ÅÐ08¡w܉pê¶•oˆ¯? ÿ¤Öôy£-‹çµÞÞ\ÖsI}¨{'O‡_–ïcÆ9ò¯‚~B>U—_ŽgøeÐOÕåÿ»ÜÿføÃƪ.¿'ž±ûÖøÌ·ûÔøßàþgãûÖøÿ«.¿ñFJØøÌ·ûÔø_sÿ‡â™þ°ñß©ËïîAÀ6þ3üaãÚý‚¶RvÈZpK™{à|°n5¯_½ù³ˆíEL4™ãüýM"g½¸kÛåD¹ë«SúºYÏÐËZ?ŸÏkà Þµ<‹$•cž÷RÀ¿ -¾€"G"ª6ŸVÈgôà6­]cÐç³êIzvàèË1j•NÁ\TÉ­Á`o'e†ÁvˆY¡>~”ï›ÁÞÖYõ|Ó -52z -£‡òM®üsœ²Â q Àm -FÀ¥Œ”ªÞ}28Û X8uåÍ££êÆå¸9î$‚ŒAŸù—>¡h½›É¡f>ë/µR;ædÉíâ«hŸ¾)qöúÝëÿ’”ÏÅ ‡ØxˆA…KpÓÜbŒ©4ªÇN´¦/±ìN…Û×V‘n6õä1ìîÈù±U‚EZŸ¶ýÌÚ·2*ÉÓK¨¾ÄµZŽ¿" àïó¨zTò¦Ìð²¾Å ÷búBTç•£ ƒÃÚÌ"6ʬ„½{'væ¹¾ök{“ÚFE ·Ùâ…¬Vž'SJûäDÒ7B!w«=ãüÅ)çß#ÿÄdÓ¾Ýn/|œI{ÃUŽ×NØw“ˆ89§,ZŽÑ±¬ªrx½IÑ30ŸÒÊ×ÑSáï­»¤)À™·&Ù#{™w¶ÑEýïË{Eñÿü£ùº¸Ä-t•7̓jPÚ/Žê©õj÷Ýg#…ý©8ˆ9›ü ¶©d&HšS;+öy•ä<Áp‘={\Œ·ŒT÷LFvÏ¿,q&þ>Ä RÜBÇ8¢fÉx4ÑÒí>ÎÓ)’•Ó¥ºx¡¿!Ý5YßZ5ñælµZë°Æ:)ou®ÎË»;»HéEÞâq5½Yðx¤Ð£¼¢ž1îÇ>¿,Q5ÔÞñ|ßñ§Òî±8õn÷ÕÎLû{¹[IçŤ>:ÞosÈÁfôi{³¶òy]­_›{¥Ë y<>Ĭu38êzR™{9¨Wj.QSp¾ú„Äuù¿ÿ蟠êöq±Û=š=RWX[U.Ôý49õ† °?'uNUT³¯(ݧ Ë«(c&)`¦P¸köDÚKFNGõšp•îÅß#ï™R¨ÄÆ+q ­ÕÚzT»‰~°÷“7°×¯^êÂt|ȼ T£&¥ô`š—U(§J3g·s͆þ^eTÙ`JLìO½8•£Ò)|ÈûÈÿ¤D5@ã©Äí³öÖQ}²;‡=ÉJ^ !XôžªÝŠçÉí1Þ[ÛBi¬Ül•'TÀˆ;’”ÄÜb¨¾W×\}Ò_.üЙA·ËQ¯ó™¥ÒñÓò®Ë÷®Gµªƒ·8žúdÕÏW÷žnÁþI”o‘][r¥mæt-­„~¶-O;™™o0BÀFUŽw¦Ï%_‘qcÙ[GZt¶À ”£“ù䂚açß$Q­gå?¾I|¢Q½ÍÈa/n‡Ú:lÜÞžw¯î3RÏ2Ÿ’×p«*ŠØ€?Œò0_íÜhŽ,ž=s aq¥j< .GG3š¥Ç»ñ‚Z?‹ÿ8bÿYðCÕæV.ªI%(ªG;öjv”›lΈ@Ï[×zw™´Ák<ŠÙ&“çNù[“½lõ1Ëç4dqkSÖjý,¸8=™É8µYÎ0*s©`äŽ/?$ à›—¢šxÌÅ¡lƒQ­:ñžè©eí3»öµÍRsB] )ÖãZÌ3¹¸oõÊ¢¯ò]fDFôtôQº`Ž)Àc'ï7cäcšæÆv‹&DIºþ&‰jÎ-÷8Gd#î-[‡ãsïŸ7õÍI-”±ë u~,ÙÞ.Ø3*ÍÝèMjš¡¶f¡Dš‚]!l†®ekPÅÝi½†×¤¨†jç:¶89 ¬‰nšÿ’÷ûœÿ»ôÕ¹ÌóZðòž]så“nuŸ-嚨Xñ;("­»ê†BRp<‡!KcàŽÇSÐ;óýÖr OTÌúO´»²_H0Þ§ùª¦‘¸à¨Êü„$¾ZzsÝÁñ³ÝùÎeõÀÊ ÑMí¦lXaô®ÐbIb"®ˆÛ00~gÙh¿Â)r}‚t@8¿×- Ö‡€ꃻ×ìú{ÄÙƒdm·^—Hõ½ßƒ*Ùƒ ÇÉÕÒ€œH:Á€Æ¹š( ƒåQ„×îT‚¶jA²×» -šäiŒõfn=y7Ç_‚1/ „9s»¨õ5Iß¼ôM‰çuó0ª¼øaT±ñHZÎh¡Éë|ÃÏUHjy½¦;w6Óeí‹K%ΑÝWÕ0ëÝ’k°uë¸óK ·ë/o{m½­h §m¯‚^ô` ›¯„›ñ!¸ÿ$€ü8è'0À/ƒ~3ü2è'0ÿ+b?ú ÌØý?küÕåÿ™þ°ñ˜ àÿƒîÿJ<À6þŸuùsÆ`ÆŸògÿÀŒÇý?küçÝÿåx&€?lü—ºü/hµ ŒbPK‰ª­| zAž×¹#ÏäÝjy¢ñ˜9¾BZû±*›^¡¤›u¢á­\OþÖw··£ïn¥Âꨇ•ÙI_2-_<[ázöXÆ3Xm7†/¿"1æäsUüCŒ9Ο‚,½®úxW8øgœ%y7ª9;¤cÂÎ=\Öϛ虻o’ºªX5uZ9ËËñû’¾ý/P)Žç냢¥cpc”ç-uºÆ 6оI¼Å §ö!ä×ÏÇmÝò~±xmXžzõIf¦¦Øûê¶¿¾Ô ôéĺhØŽöVõÇÒ•ƒáÁ•’¹þIȲÇÙÝŠÉÙ•âîCÀßç?$ƒÆ·T(EÕGïú­Æóö²„¼ªÅÖé¼P§^àatì~ª<Ô½¼ì=›ºïHà¯Í~\ùº³ì‘ KJÞqëýû»GøÂ‰¥á¤ÉÖsñYI_+ý7h-;tBÜŽîã¡]ð÷ƒYûÌHÅùQjå8o4›Ž$¯&ç5/ú¦ÂµJ²|P:T°—48ŒÛ˜Ÿ›&W±ûÅ%Þ™ëÄ1-îüKU,nŸ#=nŸ‹§ô^!©MœGÓœ<î+ï‘¿æîrûRÝËÐIHù’åFžMä¼Ë.Týã&÷ìV¬w²äqqifZÒ9Š<é ~™L?è¥îYtp;ïÿ–ð¯‚OH -Çñi£÷Fp!ªõ'Öƒí3{D6ó×Ý+ÝñËÝzn1ºê%ÏRÜ'ïYjînÓYÔÖªàq¿\vи=7|9µ…È*­<“kj*MjÚÔ4{¶HÞ«£ÿQðOP3ƒRðþqºíÁ°ˆän:©ô|¹§žÑ^ÛŽ=Ûî¢Ìb·¶o§Üz]Nîsa_hl>¶12ò¬+¾×”ÚªHfÙ¹B˜­I˜ãëáW$ªäã æ#ÏG5ŒÛXTç¢0ìÉ©ÂÕìYís»Âƒ}qf¸p´•V<+ɲ–¦—"ÜSW¼ÓMilkþ’˜AwÀS³’Ê’YsÎhKÔˆÂUÜüKÀ×J¿)1(ÇÑGñ=”g£zJÜ=ÎZæú~AYêR_óe/®÷üõœÔW>¥cñø¢ñ,%› Ë;ë’ÈyµüØÈHG]ˆ¥Öû`ABà’%P”cQ—?.ï·{ýuUd º0bP°JGul· {ºãÆv}OÃi‹mDííÜ5[j‹;tåuC˜ˆ…äæI¿JrŒ|áØvjÊ2Áâ´ #B`©M¾Â¼WáR×±Ïîã°š;áWä ¦a ¢ÚaHFõèªÜÀMOKÇppÛÛ5ïôØMC0«^*¥¬¼ä²`7]¾ÖlLÙ }@Ø6'‘L˜-´º^ãtÒC *c 2§{e÷t,ÞâkGì[—þ%Q àÂXŽÝ¨>Á@éÃÔ™+ë¤íζÚnƒFwmq|yã@œvYŽUël‡6ú `ÑoSSf”­Íé”°žQsïýVk«>'¬T4'J}ÄIV…cÙ ?) àãÕªb ª§ÞCÕ±OÀRDZ^ìĘdM;cý2 V÷B•2¬ØL>H/2ãÒ §f¡CegÛ.Yh̺ŽWºD9éáTßëcÇÕn˜°Æ•Å"ŽE¢Ú|y[e£xO7yÁ—Q›Éz1K) `9;;ŠPÓÊÚ"Tr“Ê1>ѧ)Ò|ùYÂö„<îZã"^OWÊÛÊXóäUP¡¸¬  WC䘑BøÛ’¾ýC¢Úfs ÅkxÏñhmÊ7ôµ©RÓ¥.°´P,n±"Q™åÝ–´¨É‘ íÛo4ðÖ.7ÀŠý× X;BŽü„#RLÂ*@¦à±eàähž…Ö¯á/É»·4#ûÌÞÀÅ^Pë 5´ÒÊôè÷…ã‰CÏ\™¤sD…'+)QÃÙd~‡Þó¼‰(rÓ†Ÿç¬ O“® éóõš‡ò ô4 -ЄíÙÌÏq.6·2õÛ¯ˆ_æ: ÕØ´»ziÇèÊ‹´žbMßTÙ‘u™Òàǽ%YmTØ•ÅdõÔxFdš¤Ð} ,·ë¹È›9Q¶óòšØÍÜj¼W0÷3*@YØß—ðýëñ<{n³X¡¹9¼&ºöÄ®@›“Q—¬&ˆIñg!“c …àíKí1'çjæ).3«¿Öl˜ž™›r]A˜¶»ìÞúÜ·\3ÇyõLæ´wm>àÃJ›-ì-ê--$äv{\È›&5Ð-ã8AƒÑ›£Q˺Fý‘=íÎÈã²;7_÷ëdŸqÆ×eÖáOÊè'0ãùWA?Aóº|ô˜ à—A?ùvÿÿEì÷ÿ™þ°ñß©Ëo4þ3vÿÏÿùqïÿIãÚýgøÃƽ.QµPrc<ð ÊSQÅ¿l£j±ÜÃô s¬r&zÏ»"/è°º@Œ“~DK }_ª–IÑÇ8bøÁ`÷é%€ÝYyœw\Á¿îXÿñþ(9بéúõ'埘‚D¾÷[nß+$ãÆ=èñhú:ߨ•ó!öŽíZ:D K9™AF¶Ð¹¸Ù—í¶eÖèãÁXL8”RÍÝq ×Ûµ^æy{­®[!ØF›$xõµ]+Núÿ¿¼WGÿ£àû“åcã}¼UÖ -ùÞÆ¨GU¶w ¤¼œ¾êãsåRZW»Gî€ÌÝaצœÙ½çûºÙlö•ÂpoÖ¶·”£Ü¼Üíõµ>nÅ™ào»žðØ$©óUÝÏÛÞÊCû‡ä½:ú×yLV#ê¿[¨­‘ïŒqŠ·ê]i¸NÝRçLù‚æÝã…Óf®’L“ÎF#Dkë=a0{³^)yF“[x»vøx/€Ëg=ðÞoÀÞDZ7ŠKžâñŠµÇ¡¶;íÅÓ$gÙ®7 ¹7Ioÿ%ïÕÑÿ.ýšüôeïýZœÑÏ/agžMßÖi¢âD¯{:͆sOºYôAÛtdÛÀ·zì~ kÙ&Ó$Ý *G}do½5ˆ'=-R/«Ã|~“o¹²% E{+>{ÛÍKø8Ä ¥¸…¾¨¸…¾tü½?P‹3z8%³ÉۺÖ|´]ìë…%xìæÛÌáeЧ¦…C+×hÏÚ¶®f Öv-?‘–r žö’Ç÷Û¼ŸäÚS b#Ì_Ðú'å/L¼ßRÆÇî@5ªÕ°s($û©p{YfÙ7ϳޙ>àñ:s87šbk¶y*u­­’;ë°Þ©„¾Û*ìØÉr7¤‘Útp„îxdÖÚð…LEýš$€o^ú[¢ê¨á~m¾wX›ùUTë“§ÐgñÔ}<¼—¯9Zî]ª)=q&*yÃEѰ‹ -oã9nczê¶ÐµÒ°«)\«·’勤‰ëÕk-@UÓàìn×àÊP¤±n.PR>Ö“ÿ5Ð÷FF%ªQÜñÑ|H©û*lV®;!;¼ê/üă'ÙMÙÅÝž™Õ6Û‡/®×Ȫ¬®¥`)‡™P7PmÉï{«GØÎÖ‡3Î÷Ù‰³^°mGýIù'æÈBßÛcLIô­š– W¾»?Wã wðãî(Bï4³ Ý5}¶ë)ªK¸òRZ1‚”Ò$·’®Àzþ@ZppFezå¤Ét¥Ý†é‚ËÕ7%|ÿzŒW=Ä“ª½w#qãìÅ Õ}œË©T°\]ª7àhŒÎ'†Ç½¨Ž+–Ûž+»á–×Xû"*×<Á'éµ™pBaÐY°Gƒe™Û2ÉÓJi'S“Ó~G©«å†ŠÌ¥ö“õÝï’Þ ‹c ÚïÉQ=µ;Du«— -”\©z-,Žƒsënæ÷­, êºÙTæ!qräÖbÉ/¸z)M1waÌP/ÌaIÐd$q–[Âß»mɹÃm~R>@¿`*G(nš)ªO<ûÁñÝè>?ó¯˜ê¾×Ç”9i -+v͔ڍx EÚŒ `Šóî¹èw’N2u’„ô=ƒÛ×9‡Wª¤Š¹Æfã³ý¾$€o^ŠA±ñÍE z¼ïŸÈóQ] Ì@©]ýk!<¤No¬<5oØÕáöS[l:Ó-ïæã=¡µ£ƒ&ï&×5Ïv:›…¢tßïF˜ï¹3•{T„33“8}™¥1v›É þãœE»­M‘I¦€ ¬¯F½2ôZ7jк[j|VÀ¿ -â`ÂN|‹W‚kaWµ2~wv+k¼ûÄeƒXžÂbÜcÊÌ©:‰ƒs?Î'¬÷Ïh¯ÿð‘%ëÞáÈYð qÄ’ -ô„6#8e„vÜUYòª¹Ÿ”¨v]÷§i6I#iãL\óˇÚ2+l§üř㋠V`¨}·%4Ũ˜P9®Ñ>ïTÛÃÓñ=ÌEÛ…€£ìBAø…žçÖlìÏ1¯âÏKL=˜ò¹HøWÁ÷åq6ñ§v5Cí]™usJ— ÏfyeÚ‹©¬€”…¸$Áðë^{<’œR\ìËö\¡\ò¡€hÏUçöbµžÍÕv^¶Ù÷J(aÌè ºŸÕ–3{zŽÓÅ¥ëþ$€¿Ï`¼Ì>ͬ|ƒ]n›£½Ð4»÷꣕©8 o¢¡¹Ca½"P¢ð¹s+ó*àS3fd2ÓózÅN¹.ËM[+N® g|±ñ×Jhׂ´q0ü¬˜]÷6¥ç+TÒ‹,.œÀÊjñâá÷SØí¹Ÿ$š]EWý ga,Ëzv*MGÓË#=žvšþl"Ûù¤_ÙÂãp·BÆ -,cãá“ÀFO ÅGOž FZ¥G“;´ø—$€¯•~M¢Ê—£J'|Få 3Ž*„&ĉrxx0™Ëó@õÂu¶˜7ζªŽ'´†yÆä -”‹ûdg²á${c U;c º·VÒ½y–âÌtB9ûÂ:î éjÚ”õ"þ$€Ã¯€~3ü2è'0ãžÿWA?ù%b¿ú Ìð‡ÿR—?lüæÛý?jüføÃÆÊý€V2£É_ 7û/ÐJþ:Sàs¯Ç˜l3Æ\Ú1&]Œ1åò3X;ü¦Å ‡í~Ÿ“Œ=ÊQû=ŠIÞôƒ*7³ü;–,ß~\>0ßîÏR¯¨ÒE¦1£ÁGÿi?¼M>Ývs×YÈÔ/Eè88žNØëxöâ0’[’3-îW6€´5 ÆŸú~Oë»=zæ-ÓIMmÓ¾á'“‘«Iô•÷wI×Ìë®pâüãUðÅò æ ‰ãiÜ{ÿªõð®¥GЛÙëFÜÄ9ò«ýSB ¯[q©ÃÈm Nº¸WlÀ˜o¬ü4Üî±b˜Î´Lâ0õÌJt1ÜîZól¸‰:³Ëzf^½oJøæ¥¨ÂKJlw! ƒúÆ"ªŽKû¨ŠxYeo©ÖµzÙÛ½Þ‰ik'\³Ôa…C¼“f#Õ2 x³·:aœWîKueo’mä`xýÎÉ ÍÎ}wN6#}À£þ:ñ] ‡ÖËL¡–z»/Pñ­ô²ï¡qµš‡Á9eo“s¥æÇéoÿäE7л®FÔ!j¬xgV¯¨– 01è{ÍþS,ÃÓû®A?j£Žv"]¾·˜pãªE¥j%ÅÃÆKTÑ´ÕhÜ4Ûïm¡q:z‚°¤›FϪ>RÝ÷Þ{­wÕù± J”;|ÎYß¡•Ýìô½å·ãPÆÓ`¨¦»½ -æI`ù¾ž¼³Z4ª¶Ât cÉA ýû’¾VúÿAÓ1(5zocŒAÍö.ªe¹{ÈoúÙÛ«,W}ã–럾/|ŠrÃ"’–©Ú¹âU7©ûEßñ`ö½>¦dPc3™Dûuæ´sU¬:t•ú7äëyº•‚[Vûqù‚¹îdâ{h1üØk}£ßû-õ8[æ®!³÷‘uª^Áˆ^ÊFšK†ñúýpiiyiz£‰²ë¦:Š>hu –öªjµ†›i¤ ¥1=ØroRÐ¥!y×D5tW àãðCU,CýêóÃ8­é÷~ËMT—~è£{H¥îïwÙþ¹!Ù°ûl&‹° ôî>b¨ÖH5Î[ ¥å¤•û”8+š oŽfŸ–x+VÀ"¾R¥Óf¡N`´…×jN½l°r[´GHÏï°RðÐ[ŠÐÄâ´—ï’=nœðëýÖrTW—Ãût<™žÛ:ƈhÞïæA7‰¹¯ò¯G ƒìòÉ/rÝäbXÒôLó$:Ûq¯W°ÅË'Kä{ߢ1ïÂákÈð¨Û›AÏ€! m¹ã\bÂ÷×ÅïÖõT©‡’i^Ü‹V>§rß -ñø¢ï›ÌVÌš#ç²û#ߤûÇÅÐoù4P"dÑö“¸—‡2+grh·¨yÎUàU³]…^Ä{‰°ÒîAi¸=·ù9ü5Iß¼ô!ñ ž‡ÉžxC9IßÀõ.ruÒ -ö‚6ènóÕÝB%D‘óéâ–oM¥óBf&epK8´RþÇ¦Ëø~¹n ïý–îŽæL+pZû½”©” -àvWª€¹|©97µjïÇ%euó+¯Î1Éàt\®«½ŽÓ£üœéªÒD!ä¼5ãùö(µ\${æŠ*ì7A™= ãÍA{÷އ Wþ žˆkÚÄ Ê¼Æwë˜ É9ÖÈgÀ¼håó3² ”\âÛ§§FõsθÔ'v;<¼^:…ê¹ëëÈÔý,Î8EÅ ³îqü˜vÅZÀäëRtá|éŒ^%Õ~ù­­)Óž2yמ½kL˜¿n‹:o}ø~øð÷ù÷%*K&øÝGåí)%Ð{Õ6NG¯@ŒIqÒPî6&75ªäûgü¦@Ç£ó~¦½Ç]PÚíÕ"þ0pDÉYCÅ~Ùùµ=½‡;; …ŽVãÑ_k? w¼ð·BÃu7íÌú¬$€ÃPyŸÝ_¥8Š‹ùûwòBT-æíXzQpÕäÜMmE1¨Ð¶6?žŠeÂã‡+öfΉ6œä$G•=ëÜÖöÿ£íÌ·ÅÙG}û"pçYœETQDD˜eˆÞÿ‰Õý;õZÕuvŸoX©îªg¿IHÞìR¢{1NÙ§ù…@Ç\¨Ó™spò¤Ø>ÊË–¡¤·ó¿ÀÔ|ÞŸ&WPL§ ¦²À\ä 5‰Ïº%¼ôã¨Ñ³³OàTÊžV7«c‘vYÇçæFÒ×÷h”ÞÜö[N¼ó°ÎÓ)W5FÙ³ad8ËÕ¯s¨Õ‘Œ{”Åîípºiçýt7Pâ ù¥àwøOÑVƒ¡ÜQŸ×Ï9PbP’qÄE™lÐé² —–[r¤])Ïg&ŸöV_È=:¡èm EñêÁÈv꥜ç[35¶ñŽq@='_Óå[¾tÜ™ÍHùWøŒ•wo °sƒ]¼Eß»>LI^,(é½3(Ù\¹Ça.ؑ׊¯¢Å¶{ñ¶”möÝ™éݰÕíÝͲÆyRÝè7g²=W,[:ù­åA•Ó¤(Xû‡Ñv kÕÓ®r:¤º³–ÿþS“Ç‹°ÎýÏ[È«ÏþU ”ËG8"Ç5b— â|¾êg£¶sç%ÒâîîôN¸Êܘ¶´^î®­q>-ÕÞ8½>&TisÐêƒÝþRêŸä¢’è–ê—å’®ŸbìÕ8"k½¹/%·pž,ù­s[…¸ÃŸ#|“ŸŠÃFþŒ-Ï·¶“áfm=}"üwø©ù+ßÃ<Ì—­¬ó *IS‰øøv&ƒ§Ëä2)K`•ÒÕHÜËg¿_©ª$8TXš¨ìl©"—¬ñZ]lrp`ï°•ÊžXyêl´ ôÕrÈŸo=¯š¶Æ­øÌ…ýWE,E±Ãg¿å$J–Õ‚u>˜ƒJ×ÃP7¯þØë»Ž]¦àX‰iÔ`5¦õ¦èw²(¯_Ù’¸£Ÿ¥-¼ËÜͪWÖÜhß\†LµÃì Å$ÕùâˆôYF>ÎÖLì¯þ_ÈïJÿS´6Ï‚2AÑûb*›¬ìý½æÞʲýÜ!qϸ!¢»^3¯½EîPÔóI´ÂöTÝ6Xý„Á|Ó4±åíW™$5i-¼ó‹{ŸÌg|nMó¥Õ"½nÿ;€b›AœM؇֙¿MH6A•¦1Gf…§ºîCÑ–D.À•· -=RÇœÙÎÈ êšŽÄ,³¹dvííˆìRÎJùÅp÷Âæ†¹«Î˜9Ó™®—0ݯU"vV#8ŠÍѲüWE²6ŹeÁ>´Iö[ÖAevGmÁ_ûzû"9"0}^¸Fæû§trþÚÛ 7!¾\:ÅH)Ér³Tj¹oeS‹‘:ÈÌiÞÊOmwSžðËIk,r 5Ik5f/vâ:ÑêÏøB~)€Ž>•’öç„à$¬ójTœp„4q+übe¾^Úéz·vœº9Oå6͇;Q^D[}„®FÞâ^²^‹1ðÞóÛ†-yÚÙ±tbŠ£ýeÝï50Ñrxåø‘¬¾ØPÜŸ·‘J6(—ŽoØ}¥/“kz4ŽvÌ—Qß°­°Ó êdܰö›´í‰é¨äo*Ç«¿Ù}°Ð•C4/?=0mÞÆ©1n¼²£Ôè^èè¹5È3.E]+íœLJûÁØùœÏÿãò]€âaÂüµÃº¼¸Å ráòÏ!ÿÊZQ圇†eRïvY^{‡}“¨b¦ð9‰bSÍëêP,<—¸jÏ«½µ? u;ïKôkD)Õôp´J溑¬ -Ú¡®ÁÂaÉÇÅ”¿/äç=(º›ÕgëTÀ£a!t¸TϹͯzJè-‡ù˜³™ÛnSO ÊŠ|ÔÅü¾:ÏkÅûmÚ~”ÍñÞWœÏc0ÔøÅ—1’ɵH!%ª(–š¤Ù“äìçÿ - ”SYPnÏ4Ø*[¦w]U•{¾Ö3X½+„)Í÷Úrhc#á"†óðÇ ž§þ~æñÑaÚ9¬ÏãÃ%e@óÓ}8=h`´º>uÏ¥jqȾIóùJ‘Õn2ßw´Òç¸^û¾Ÿ÷(­¢ å~Öcþ˜ÞHŸ¼¦¢»vZÞuj5Á á_ýܵç«a’d‹Uw3¯ë ØÆ&²±ÙŽ©Jy7Ême8±m0¿. ê‘=¨r¯k“ëMÅ#kX%ìoFYÐo€RšxîÛ…ïâ ùqÁ,-cnz\>ÒœSÔC´0>ÞǦ.ïží·0w©2'–Vøj訓C)óyCp˜ÉþœdFÚr¸Ný˜ÐÖuK=Þ¢DUà<™dWÔ±ïŸ/Köy²nô[©ÖƒÄ–CtœÊ¹Vü¯*õÞà>´œX÷_LŽg2~‹4Û¿}8‡K d•ÙOÊ‹e>ÆgÙNÈÁ`0š`çÑåð e•Þ3ÒyŸh²Ñâ—ýíjºî·s#žÈ–@ôR]bKÁ÷Ýî 'œîOÌZ·ŸøB~)øÉõÀÒyf}Wsçšÿy“ -‰‹á–‚Eù*"3±Y(MRÇEmxGÍAy?oS5¦Öƒ½²9y÷ûJ§ˆ$ ¼ÊOðÃ|4ÅIlºÀ“'jÙS»ÛbÓC8BìžÔþ_ö—fít8^Hâ(w@Ç.¥å*#»2tį³ÎóäŽGiðb ØF9I6£ CÄŠ’'ˆ´\•1_ÅS]ÿ¬ƒ¶z#lÙëêËÑVFäÒÍ_ˆAç:#Æ:Ùu0•Xü+uf¿‘Ûçž `2Èr­BÃ5IÛso1•;E8ƒéã™8X–£É·×'¨WÓñ·–à©K`õF¨íuuætó¶u®¬òêÐõc¢ƒùÇtû±ÝgÚËö>×.G{´e‹«¿à ù]éO´¶/´Þ¢Z8Ý–1€­0˜âI“¼ÌønÌAèZúzHÁßW5bšw”÷úÎÄä_ˆm¼F{>'6´ÖÖ}TÙX P‘¬WW¬ÒŒ:›V’~@èÏ{{[p¯‡êÅþ.~h™Ò+ú M8ƒýoE¿¡ù…ü×¢ßÐüBþkÑohÂÚÿÿ[ñ%ˆÅ§ö‰Ç`ÝS`ZæöæŠj‡GüÈ ¯Ð•äÕª¨ßÏê^zØìº™·G:Ó´2¶¯9zncÍ.cÝÅâÚ*9iÞ*í ;ÓjUsu¯_ >ß~z¸e1‚ÿöÑ2†©þã»(®¡Q òÛØòQÅô&$ŰP…1Öî¯ÐËfáœÿ­{Ptd@Q•ì:ú; ì«ÄŽlì@Ì-æTXZål‚3­}B4×hâ`VÕ¬þp;u.£ëKº¹—I%y×±Äîò;À§åoJ:¹aŽ–uòÀïògÝv Š”úcæÍé'ˆj§n&è´·E¿ÿxÖ=$9ï9—Z 5Mqb=æ-Ú*/Óp6n®YscÖæîá.Ìãcƒ€ëƒ‹»Ï[/4ýËYDÍ3³˜'÷Ÿ¾ €®"@Yz›fÍþäÈé|ùïu[Þák€âQ=5B‚ÎñRô|µá!ýMÏ™ÕÞ”MküÄZ’uÚ´;`mÖˆÛö±YhûG#ºœïωoÝ¢m"4 jŸ™íü -ó—æüª©‘U¿‹¿Eev ›&éÀÖ>¯ó £Ïi±<ÄëŠÇR±†’ ÄY¢ø…ø‡wFôr†¢lž²‹Œh¥Y_˜lpÍm/ÜŸw¸ øÞ¸·½ði Éž{¾ûëûiãzú¿Ô…ôÁþñ…üRÐÿ?¢Ë¹ ˆCÑ«0Å·»ùœãp¥D;ŒœU€a¥[ôßg®æ —QÏ1:äÀ.¥OS«2ï2O¾±÷ήÍÃ'Ì»˜”¯IàžŒa3iž‰õãÔÜèÐÑ©#d¿ÛOkcñ»Ÿ½´ÕE`§÷Ye,ýX!9P „¿8Pš6à1‘aH ¦˜Oí¶wšuÛ6Öû&÷šoà6]©1½è\“Ö¯í÷úÌxQ³‹ÁI HYW”~Z=d¬í^¦)Î`w˥ˋÚ2}îçÀ‰âçeéþçuþÏ&Öñ9¶¦ ?l÷×3Ù/fÜ‹2@áÓryjf3ÔÛ7˜Ÿv¹ëé‹FŠÔœÚrtâ¥âLí&oÌo[…J8ÇýÈ»Gð%öràE¿±Þü_ÈÏ{è˜.ÿ%ZLýϹÅÏ[ÈŸåЧÈõÇN€7ÕÈ; ‰¤SÒ9Óו%¦+—kõY=¯û©ÆiëuÛGp9öÊnŸõdþ5ÞªÖB¦ùœ$9YUýþš¤rÛ‚õ{õ]ü­Ÿ8PlZŸS< ”vOâóÛ„¸½厤†A»ñx¦–ÏGñË®¹{ß´sƬ ¡gv9ÁNõ´t$³Zõ0Ö‘†|ÓÖíÝ -«—-OÄm²¶†r·ÝObv›ðì5?hÉÌòãò @ÇBåoQÒõ?oÍ¢Ÿ©ô>Çp2±Ý9ƒø\ºúÙìÆq–r*6E²”¾ž,òùRÞ®”9I‰8{¤ŠéÂ!ß)ar)dJ÷ŒkBTà[[ÑýmâÍÏù%¯7š¹\nÆÒ’ÙäzÓïfâŸË_¢X溽(ù„Ýg‡¡LBQfµŒk±$‰ÜKó -ûisD9º+öØ{kϹ•T–œ>L÷RF.{FNjÎSy¯ÓE^ÅÓpn¹9c~›3ª‰!‡nŠ öNB0©,ýÁü»€†•*ÀÊöç]j®wùÏ~Ë(«[8îG-b!>)/ytÜáfmoùç-»í‡:[·ã^™%GÕ^òª\|K­1žÜ¾uá‡V¡ÀÁ¾È2³Yc½fVý•k_hˆ„®M!.á ùyÿø[´ãAQ¯Ew‡Üg¿%}éPJÌxL'÷κq¿>^ÒÕ1½—§ý<ž’èç{|LÅ%ÿmÆB?½øì ’àŠ˜„¬ÙF5¿jÚÉʲsKv¹=˜2ñb1[v¶‹ Äjú]|~›PnÕàS>%ÀçfŠžXç¨4ÌY°_ôWÞ<mD.÷Lª{»TIÕ:×¢s¸î wç¡¢/¼¤Å“Ë›€+å<°®;ýäRŒÜ “hí°/d‘®îôhãèÓ.¨Zc²P&íñwCIÔ&f¶°³W=PŠ ¨y4㺰>Qcþ…¸Œ†ò–ÈÜO×¹“¸œ…Êä~ÔN¡¹¯ÐŒ%Æz×ÙfȪË1ƒŽ·n<ø€yµýx1 7©ùåPËÎnb­<+e×ÄlÑu'óé#Ï/éäà ù¥àü-ú( °µ]P.YiPIÎjQ;Ãþy×;v¸`ÇÂD¾Ø[I=îýpo¬Œ]»Z» éEâûK1ó4×|îm1ïFÅYdòÇ`޾ª`j‰dâr™Ò¤ñÄ»çÉ´Ú?¦æåþmlHÕ?c%FаõmP^¼R 2“*Ák:kyt¾GØíÞ‚¹ÍÆoáÖzû£>9©²·Ïbÿ\67Wº¬ý­p]'ý¾È–ÓÖì¡i=…Fc±þHŒä¶G±á[ÆZ÷nÌFqFŒ…¡:þ.ÆL½œT:-XçÙwìì5ô9ܶ‹N=l4ªˆĄ̀>dNK<"á`éƒ$ÓÑq›ŸÊ'v³œ–|r^äfüef¾ÃûÄwgö¸W)Ãã"H ´IŒëxkK¸³–¸Ïa€ÎÓ_–ß”þÀT¾ŠîYedu‡Ý§†»Lýå.··™|ž±kyW£Î»{‘9ÎÑLúÉ´ Œ^»Çž»òŠh4”Å4ýYë›Ù¥6Âн1Îgm/7“VÇQ’Ù®›dyˆLI¦Œ.)´ýZ|»ËmPêEŸÝéåÅÞgäú“Pº›d½öXÉœ 4Ñ9^»Ï‘幘IÌÖ›ªSãW¯VVX\lTžWs¤2‘ñ4:âc˜‹ºõ/!¹ŒwɾCܱ~ýŽv¾Âö4á—¡_³WìwŠÉkÖ9*‚JÁTƒ×n°BûÀ^ËZóz&ä0s¤OÏš,ò§Þ"èìjÀyKrº"=b±À˜Áj¶1U~ü®¤ÄQ¦­sžÒ©rµb’5¬ô7Ý\‚xrhŽh?{UˆcÄúFìþwøB~W -г7ʃþ&väPøl ]™àDŸ2{=–RVBî¾uL˜eŽMΟŸû+*zŒÅ}8Ÿµn%fL=Èåð’ˆù½V`½PKj©’lµ«÷=²þ ‚nÖ%§ð¸ÑNã»ûÅqæØÂñe‚ú.`Ïû0”³¹‡õó­/ä†2·tL3ŠEj×ÝkbD*89NÈ'ë+DÀz‹’6…MÏ>NÆjî9^œ,ÚÁ’ª\?[-µ©Øç]bO„XëDtGAˉFÝ⦅øBz -³Èô¨òý.@¹õì{FBÆ®Åâ"8ãós^YÏgë¹Võí}\g×È®2ܺBÛô¶±i,úc5êõ‡óц”žÍéN¼iÿÙçýöŠ]’AoðWŠñ~·»Çqê кÞK«­{W޼î(…ÿˆ/ä—‚Ptñ²qwÔ›¶¯ãw¥—±]j=\l—É“ÂÆ«Ð\êý6€Ž"2•;·üx„´KCìq®R,J6ÉæîÝîwøNìÌ…¿s"_p’¤§=U¤èÞ°B­ºçY‹ëNPBìæTêÐ1:#­3‹§ÆwqEû˜rz¯Y)-»štªzµ/d[V[#V®r›å4áª4[ì<¦x<µGúøæ–•^DzÛw¢/âzš)9+ãeOÓjo„Q®¾$:Ýi…":W®Kuh´;î`wbÞ~ ¦]Ö î i—ejû]œ^ûãú`œDò ™·ZžÅÝ\n¦tía¯' -îoFy£'R6{?ôƒÝðŸ02U6p*Ü{§™çtuæäuó¶|VBAó¥Ägi/ÛÇ|ËÞIh‹Å÷pÞó>Ö›žré4yê†ÿ_ÈÏûî&R9œóëªöX¿9îµÌóØœ¯´j“”Òk ±»ÐÄúD|ÐGPn:ëPbÙ–+\‡®'¶íJírèì[¶ä[µ×>ù›Þá¦7yòvm¶R棜§!Ž¿_Ì"|} ÿغ ¶k 6×Mœ,€röYÛ+¼·9€¢ö ÝŒ‹…i²¹W*ØœTô)Ì&?ð–ß“ó=ïUH‘Þþ˜{ýb<óÚ{éñÔÆ=Öš{—ò&º›¦Ë.¸óhD8‡¹·Û£û7ð¢ßÐüBþkÑoþý³ü7¢ßЄ3ØÿVôoÍ`ïAÃ~Z! ÐúÃè…†šÎ•úÔþ%†©~?PAán[‘™€3Ï!Á&0 (7¯ú»a³å½¦©ž·÷bÒëSÁØK„íWñÚäS‚K½›G7ÍO®NVš8¦)ÄöMÚ®×½ñùÄËŸ -)ͅ⸠-ÛTbßîgy¹°î{°j™¨imfœÈ$–PsWE‚fpÇžŽÏ1/c-ï@¤/I=î1´gî`üXºi÷Ê9ÚÜÛ9…!6®UJeÜ[„´Æ1àô‹§oF¬€Ýž‹ÞêPÔ){]K“Êp2½ÂyhKžzh:q)sC6j§ƒfa~<›×¼w&ßòÈ|@¸jë1rÒiæh®²rƤÂ;ãfpüBœ¬‰Ý¬ÛsïÛãȺ&›Û‹nÔ_'Ýž•oš&ÔlͰiP)ÛñvŠËí^ÆLæó…\LÚèËͽ¸ÄÄqȵégˆY¨wˆ3m/ùHw˜ÖÇÎ9ÇÏñ†_;9ì´³/âC³/RÇ{p²ì]ûívÉqIM³&ø^«zeù¾Ÿ÷¿ÃߢԪ à3Q€¡œ?A1UþÞ\ |x8ÃÈü¸` äŽ+¨9P_Þ3UOéÔº°¿¨žGºˆ©Lœ±Ë2¶‘¢7ölÉl4û¸™æÝö¯û×å~Éí“gÍZãõyÒEµS…oHÿ󵬽mÁÆ 0qE©¥ -Bå ²q‚¶w7.o˜wØð¥Ô3|£¨O\65êÔ:n†ÖûN>7Û裾°J,`Íêìîϼ¯Ýz’ÿ9»#'Ggm7UXÊ’rP³…dÒß(ÌA -ü¾Pû*,8‡ È“ŸCXwPójAMås¶m\Yvتäƒ'¾#^ž:R3n.“Bm:\ÖL[5›æ±ß½ÁÔ¾=,w#Cí3‹Ë¹ÃðÐð 'MUítYI!€ßëùízŸ§Ç«ŸøB~)ø 蘎@AѺËxÌ?ïÁç<ãÒçtSòsþ. J…ê)ô›Ûs_x8ÞI)„ÎüŽ'¬uq‰<¶Â%#4>ùÓ‰¬\òÇG]Ç&|W³B‚ÔjÕ)}jbËíqw¡v‡bòû‚áÙGþîÜF¦k±l2ºïž6¸Âgâ2aéó2šfµz+©Á~_=ÊåAKQ=RI Vô^¯q›/DÆÌÒZ²÷wFäf.¶ÊÃÙ7 1(xf`Ë<ÔTr!(Õìó:ÿgÕ¶½ Ÿš+=ÉlYýŘ,/öêr³]sí\Guê©ßƒ^¨¹ƒõK ƒ[âH<Ûˆ’®Yù½®mJòu3oîâf°+k%òœD½9ßÊ~jº%ø`ü àÜò7¥ p-BÑ·+þT“AÑküøê ö9/”ˆ-½Ï;CàüL?³s˜ëôd>ïÒí†`™Ç¥8¾™_8;êîð~**Z`»\9 /ªµ”ät®9Ñ/È%!,é¡Ëf ™âMù#lZ|ZRÆ­Ç¿¿4Ñz‡}—ü6¡À`ª…ÏÖ^äJÓ§Ò\/½ü3µµYš=Þ~òdÌâÓåÌ57U>“eèL­ý|¢y»uyŠÏÌ:¤E%¹íçÓy~ÎÖ6Ú˜¢6Yw -#Æ]£‡¦êˆÖwë¼:- ˜"eXç}¨y¾ç?ûW;a°E†ÏtjM»E!¿±Z–[FSÝ:R§“tAÎÇôæ}ÙÓpݭö) -‚`oƒ”Ïàm“ë.ÒÚX`ìBuzli”®­ ¬WºOþ#¾_ -þe‹8(6™Ï1¬ƒ'(En”…Y'ˆC—ð³âzâT;µõc?O='wçæ:­¨ÉíY=ÌFжsjû³(r®±UêÍÏúØù¥8–­?×–x¯\6™_5ü^sù<î©ås»íCHì¿Ø4»/€ê-Ϲð9ÝtìVHJ¯ll3ãF@HV×C+롽)w˜;RÞs—Šˆ '™ÔvÊd2ÙË«gë †fOÝËëóF7mƒ}ˆÝǺvtÜ¥à­Bf -ÈâÝs«‹Ã»A,Þµñ…0ød@@Œ¿€æ©7@cªŠ8ä—v ”‡yv£üTïM·tYV‡HÍ®3¿Íœ·Äõó>rº2öV^”žvb'ô ä°¹Ü0…5çKmå÷Wäg…"5|zÔDßs]bóé("茉ô0Dzô©UÂÿ_ÈÏû¿D±2 E] ŠZpî\V«é0$rlgíÝiòƒ‹¥½æ§W‰^*³$ÊÉõŠÍ‹øõ"ðÆØ±ÖÊ–W['{`öççÁ¦ÍÒu>C]{VÔhºBS¹iå¾nOÍÆ¹7£÷VovuÜî7ÐÚ,°!ߥÜëʈdƒJ²—øB‚=m%= Sóv«¯6nS²BèÍ:V‡ù½7#w)uŸ"»­VnYg¶« É Ìáèìè3?8Îî3÷<­Ê›û¤±Ý<ÇíÝ)=Š¦ÍÆxk ø„z_ÈÄÔî7P|EU‚³E3fïõð90µÈ±Oç”Ù/“Eci_Z‘íÊüäwOeG!Â@&˜M)U]­[ç2Ë(±¡§¸$Ì–Ut?ñ†guÜ92×¾c¼!©ÈÉajQ® Éí†á‰uo´«oºÆòãÐ —Ť‡É]uÊ7ÿ†gçá]s[ËnWnáÍhÙ9}»˜ÕÔÑíÝÛ³³ý@TrÍ1•Óõ“uç˾’bè˽²š­(n3Ù6q´¯”Ϭµ6£ëÚ¥¦¦’  -»r 2–@ †Ñÿº;¦agǰÎû;P©-H*[Õ©‹Ÿwßî“iÒº¸A7©%¤¤”ðSKr£·ªo’õŒËp©´Õ1}­ØóÙZ.,&ÇŽË’0Dnî2&ç ÉU§o­âDÏËýZ¡:í›Þ"‹oæb?.@-3Š/81./fÛ¸‘ðwæDËûy7á.Þ¦v×H‹GlÆäN¯[&éW‹s zoÕïnû°¿®kjÆ%¯ãI÷X§GÔæ¸è`¶¥h¡­Àf]5úõAÏ'øó:I´Ød‰M4o‹9±¹ fßÀg¦4Lg@IºO@¥°]I/76“ü¿xû«¥‘‚®,gmkÝ”?¢ynR¥Égë ¬SíÌ6Òº?~Ù×Á‰¦óÁ¼UX“wÛö]ÁT ¸|ö(F÷D€ãåv¦÷еFïe½WcºÁ{Ÿuÿ;@±±„Ãä 7Š|<¼˜A:'­e®Î Q¿aÖìí­Þ}q-Ï¬Òø£Í$W™†Å{cL-óíát{êS7=!YXö[ó -ƒJö€'…KR ¯«eV‰îH¿aÝl¿Ðéfçy¡;"SÛø¬(þÏýïJÙ…í²Šß3ù…w©à4¦¹ÞÓŸŒt ó‡íBŸšlÔÜFË3ò@h«ZĦ1(‘¹P¢\­õ…PÕݵ×Râ€Øå¦Sü€Ö=5ÛÝöF}ß3-½›+O­ÎEçÃÎlj¦;h²PißÔVëåâ°û ‚ð/ÈÔôR*?öO[íHC*Có–MÉìθܗ5ÐëMOåÌ%;Ò«¯ü`™e0Òcjµ~ÇóZnê8N–do(RãîÙ!èní²ù°%|!8ò)í»¼¼´™:o¶K7=lY‹+ø‰æä 7®SIÍbéÝ~Ó/œáTöÚäkzqº~o8a™?~Î…£k&nNÞòÔMs²9ôI6£; dj™Ä•ñ(Û;Í[h/óhUºÓJ«Õ¹r-¼ƒ=[ƒö²Óµl¹»l±uŠoÕn£]Ó[ðZÓÀíã³Ö÷ü„î\‹Üñ÷–²F91W'¼7Ù—ÖJ3G0×Áf1ç+WnB¾Ãíð:oKTÍßÉýλvÄß§·Þ;Í®·®ÎÈÖg‡ª-úºÎGíÇ–}·Ë÷Ùé¹Ê·jo©Üä)©Ñl¥µ^#ÐnTC‡³†8I/¾™ÜÇ2¿!á V˜ Ø|Ó¼fÃ5ù†FòÕYÀUšã5èÌ'ùvž"ˆ?ÄS—ÑêUV+›æÚÞÙ¶Ë¡¹k±½Ë¡U{™Úg ôÓܵ{CÝœF7ë‡_H]ž9¯:F©Úû‘+Ô”%‚~²‡z¸0.p®Õâ§«adÝe³¦r»ëôÑ6¤ìµûêw^Ý$|ÂPéê‹m¡Cׯåö²Õ®C3´ÝôH¯ÙJ¾ú qøÔcÝ×å©;¯½o÷EMY¸l*=·µ”õÚUGµH©f|D‡@_ð…ü®À [9æF9Çm‚|èð ° -PØÂ1 0-Ÿ„,È'Ò*ÈëN|‰Š ºž©¨ø¸çC³k”Ã¥so~!a¥îöÓè,sõÌsÔÊ?½ýXõuuwß~îædsÂÅ4+ØùøE>ùÿ[ÑohÂìõÿE¦29®‚|.Ù…´5·,Ô¼…YP²-P衦œÞ€|½û>ÈÝêv<³ -qtó’HT ¬B¸Êªå°ÂƒªÝ€UÏTP/Ÿ'OO¿3OO¾?Ó½ØîÝÝtÚºÚrn¸Äõô ¾_ -@N·`D÷íäÇ¥ `šhBwŠrMva™Ð‚B¦Îƒü´¨‚|pâ9RÑâðL‡æãކgW œþ®Ô… œ?ýÌšzz±:{ò¬Å>[hRöEfpwåõ°§æúpˆ‡v°ÿ@Î~ ^ú1ÈKí.(?ß‚ù² Eg­<@û4œÿ%—sP`¦[×[P” ìýT|iã"áJ<£sÙU˜»µŸþ!ž¼Í Ÿ­©°ðıõƒgNõ"gÚú‹¹™«lö—[÷]8ig,} GPÔ¿`ÓœÁ¹^b$ýµ¼Œ:t –ÍN4(›ÔA,Þ P\õJ°P4}€Udƒ¢N°€‘}@QUÑ`¿bíÛ ´Œg:pG,hÌÖõç–výPÆ)_*p>æw·sÎ¥óÑU¼§e-RÍÇMšTÎF ö🿋Z•îp'·XfÿŒ¿4óÙÔ¤0 •O2ÀºÃOŽŒ‰6•òÀÆ5 n~P2u‰àÔç²ãð4ÅMâÙitr~Ïz—¼>¯7Ü£$öÜ´ÁqJ¤íKWßÚ…Cívo—F†d‡ó-RÅ“Û*o¾c4—ßÈW(Z puú0Q¶¡èi|–—Kd`áz °sˆª±¬[ \|°ñÒSޝÐzl™Œ+Y½›˜Ub¥šYÌÚwßkôo¡Ù˜ÝzÕ­d •±z.MñÔ8Åܱ÷-ê$œÿ_È ÈwÑ È/@ 3µÅÔg9”þñÕ4E꼊ØyM:GJöó½êj~d8…´­%÷Þ´nqNH\_2k¨õ7z9#võ³žŒÇ=}vlÎÏ¥5#©BEç”ðÂ÷m.ÏWýñ7ò£ªò'jN+¬ìÚásLpô9ÝôÇnÛ«JÄS ƒps¾r®ˆîôe›™ì4“»<ŒàÇFß¾èÄÁ×çgœËHjUpËÏ[Uº âí—ìAÝ…Ì>_\Ìv÷:–ÖOwø…ü¸üP´ãDPTîLŽ ¨TãÏ¡¡Øç<ô_Ãñ³O(´—5k›É’`6ÍÚþFæÁá 1rMTÓ…õU³_ÞãÔÊNcÌdåG %U´r‡Lέî'tc cãÆL²ϱÈg6A¬“ä7ð+— vv$ P›¢@[ì}>*~Fã•gu‚VîûÈ–;‹Ç…6ý³ËÞ•åüx!ž-VØŸž9O=‚}EW”H»²é¶µÏ»h°{ ?'´ïÊûTAb“d[äËë¡Ð-t¨­¼Åú¼"yø7òbï - -T) °2;Eò ‚R~ðY ½ç£MëP‡ãËAÌt]ÑöZ ç÷}ˆÏY"»:Ûê‹; V^8º„|Т½²/„¨¶[>…‹TŸâ¦Øjס[«'·2ÏV¶‰tÐçÓÕ¾øÞ&K.;?ð…ü¼ÿþåÛŸüSâÆ€¢øì JV” mSö‡vÐpëiÏ ú«á 1RÝ4/´ÖbË£BÊ,ì•“­ÉËL1'J.ºÙ‹ízþ´’nðGŒµ7Z–6Ù™™ç¦Ñã -'¢ÇÞF­[tŠíoö¡þõó»¤ë 0æ4E÷¥Á`r>(—sÙ@Î Þ”x”m§¶lßßvŽ0æÂixnfSu? i%[!ù1Y¬¥ÍEÝ‘^·}ótØhAëÄÍféϧ“Òþg vZM­«u³µrüpZ+n{k~?DAá½€šª=þ|yjž%/®M¬ÄSÙt—ö¯…/Äòª~ñjûR–iòJÃ#RKMöÅ£?“ü4ʱ0\mSÔu³É§Š{g}e]=sçUCá¬e[Ó^L4i”!Mˆ¾¨ÿÄòKÁO€üc|hm›ýœŒ1þ|à ŠFG'j^÷±¯¥Ð·cÞìŒÙ[%Jׂ4oœùE±§’¬Õ?ÌS§ÁŽÛíÇb×1¦_È6]{Ó›ükºf˺ï6ÖXZv¨ªºx½r·IöCZ= ‹TºÚX$ÃR}AµoàoÍÉ1÷c¿e~òy]Zå¶i‡ÝâÌó.Ëd`»‹ yWRþ²š—«'P½µ}°éÉ ûbÄÛ3n -¦oÉOAi×V@y>‚D¯zwï™È¶Kn¹¸ýÙÞ`H<¢•cwv+3Í· ”®ÉAÕU®‡ø Ç—ß!Âp8-lJF¿´no‡5æxb›tÁ:¶f­„OZÂa0’}r6L×òkJ¼%ªPm^Hz³y’EªY$¯üºIßLýwøB~)Ã1AK°Î‘yÌnŒŸs„‘ÊÃÑ }k½}L(êxêš{.+†¢böÞ|1QɬƒQ®°Láµ=ŸQ•Yõ­5ÆÑ&Ç—Qß6ÈÁ¹4ŸP³+¾"KãšØ_sp›Ö“ؤ×E Ý^îšý¥¹iü°³WlPÜÛ%P^”Æpï}ø´º|Ã0º« z–> ç«ÝÁ:o éÅîm"»í9‰%å»}Kgò³ú*ŽãP«ŽRA»5˜Ö#‚Â,cÒ·yuEx+UƒÃó‚‹T?Ä»¥$œák5ÀŸ;ÿ†,]PêaeP)T‰ÇAMÇWúýJèèØRu27>Ô¬ /½gâi;×¶w® gp_È*œ}_‰YÃÍgǯ)ŽI¢2˜ö-ÒJˆ>§·'ÄöÖcqÉȽWMºöúÇDÜK.Ž•^rHÓ=â Æÿˆ/äç=Àü8åâ¼üØïz¦Á8÷ÇY^OµÐ?×ö¼UJ·ä¶Å–¦°¢|¿.Gd`ÓËaá9ǽhœ²O‰á´De©_(‘5à4ˆ@ýœkǺ6é½o—u/9»îˆÈÝlatôÈw¦ÂcÔ͈™e7ü™?ÁúmŠZt6JBê¨Éôõtœ½âÇ"‡Ú£,j{ŠâWô…e{]ä°Ìå;]mðִ׺¸#턃ÅBTµ6Eú[©X z}PÁûËD§7`Õ º3Óálç^hÛ¥-k¶¬¶·Ö™iù iU/ÎüÖFšˆÆ¢Ä §—ãl˜~I;Ð8y‚1+”7®r®¡ÞŠ1ΛýÜñeò.Tnð±óùrñ L¼rc·æoüðê"½ÖB»'[éÌŸév{™) Zö´N·jþˆoòmViçë½Èù>…EBÆþ_ÈËuº Ö§Ú§Ly-v½¹Ë ØÔOrÍU×)!Ù-çMçÊM¨DZ^çc™bëî±/âs k˜Ñ;Íóó‚¼ûÙ´[ñݲwòYƒUVX#е†8Þ÷Ýœ3®Ç×ô².Óu¡NYíкÏäèp—pJÝ̈¿[Þœë<üh5Œ8î/–¹ 6ë< íñ(=ÆÆ H¾Ý2ù˜á©Ë’z]¶C×Ó|{Ùò¥Û»š5…yåjT-×RN»Y=q 8Šý¸ü -Ú»’LL;üBIwׯ¾[NÞªI×îowòÞÕƒÑ4»S6›I÷şÕQëê ­Ü¡k«úgOoØnµj/´û×Jh7c}VBŸÓ:Qpÿ^MY®P=±É¿Ö?+ú¶ú¨ä£†W¾îƯof|q¢Pç逫­Þåjƒ¡Ó©Ã,`³Òx”BùÁc!ìÈf˜Rð·ÊŸ{™[åÚ¡«ŽÙ²EÎiòýþ³!«Q]ž&Aí}}'kT1€ZyùjÆ}`}ãÁ¹’¼fù*¾q˜¾¥Ç!?+URy¦hŸ:Ûod·˜²Hù²Çèr{ &$Õòãð -òµø¬(^K(ÈQÈ¥gs•VðìíO óÒî c8OQÈÐhd¨vdZã:È·½ø¬i£8Ÿs–‘¡Ô÷á]«kA­ák¾`Kš« 9ž,˜½þ¸ü#þSôö‚¢NÔ…ŽÒäWŠNê°ìÝÆ@ŽnCÍâl²'€ìø¨lîf‚Œûc/\FIBQ¾”ƒ¶ý2´]4ãs¸%âÉê8†¢!Í©Š–«Yí¹QogONôUçÔ{*ööŒbíófJ¶ÔÔ@-'@>‡@Q탼U„ä3õy|\„A@QjãJPTºœaP 5 AæŠ$¡k£ë­v%žhíf<‘ý8ßÓæÑ|ão¿ðnNA­ïœý޼:»)):Úù€Ù›Kâ-ý0ŠdÛ¨ÙoBÍq…t¸…R÷ -Èò ]¥ò'ßϯº §Hski"Èš¦²«ÐÙZ€Ì»„Ä:]BãiºQ‹ó«F;ºæTd¼ö‹£ûÀáÙ:’î*mT³§Íò°oé¼[ü|!?ïmî@v@ža㜤`(( ²(l—PXh h«•@~}ìœ'Ma¸°¥:0žRè€,ž±~Íe¾xÚÊaqÞ¬6¢k¯Ñ‹æî`asi2§P}zÛÅÃýÆÙÖêɬÔùÖ¥³¼¡€=ûg€¬Ãx.†çO› æŠø±ÀüÙfm‚‚Â2! g«èÃ&p›A[_„M „¢QÚ§¯0Ž d„DôÒÅÂÇÞ¨†K8Ól&ÂgGiùä“ãƒß±0w2¯³fVˆøp­Äö¢¡ýµ>^ÌŸr Šä»çÏÞPha*tÛŸUp (çªBë³×: O¤Åè.]´Ðj®aµÒ÷ƒúÏm&ƒ<[lóÅcªæÅQ¦çµòØUøÍÖõÔ¡§\÷©P¼Œž ö¼ÈZ%„CÛÿÆòKÁ€\a²‡¶» È‹næ³ -EYePÐñ Ø,@÷b  ~åÂ<´5‰ 6AcÿìÔݳ߳„‡×§qÏ=®Êñ⦅´“e¼=½$ªv¡_Ç-Ze§®ï¯ÄÃõË0Éj+j²P}'˜%•ý Wg G¬ñ\öÇo -€ÆÁ`£µÿÙfX7nÅ¥§H… ÚŸ>%¬´ð‰ïžjͽ3YÚg{ŽÂa·4-Úfµ‚»ohï[( -™+à6Å+ñ´zÚk½¹ìrrÌÚâ¡ÊDéËõÁapäŸ×PT~BQªc'S#(WVö8CÍ0Ÿ—ÕT3lÊþÄ«[XûÞ@¾OœË_ÛÅ®²5kvbwïŒûÊõu µ¾»]ÎÈܹäjÃPG‹Tê¼³%ͼ¦†ê3/”}Ü`:@ÊÆxEȨÑÇœ[þŸ‹‚Ü ”A†’Ÿ@ÇÏrhóõü,/B}TŸò±ØòF©~Ï™/„U¯S8…Ñè«â"k#£ÏyýФvgf®a——Ó%LUÈtƒã.ÑO)¯Á±¦$ÙÂxŸÓ8rw?´ iÍD]ѵÏí?䦛ÈWJPóÍåa¢¬O¬*(ÂÉb¼j ³4”Q?-9çNjŇÄï¤ûÆ9—] -ödv®ŒOÌÉ;u9µs3Å#¾æöJ*ÝÓÈ«ñØOšƒP6–»œŒ¾z¤´"1±9:BgK6·»¨Üø3@ŽÝAÑQûþ×3˜Ì@q~?â{Dò~öÙâ^^P¨Ú@*wPQàLéšá¹¶~W„Æ™×*àhšpôát­²ûiÅdL`ä]™œ¥ÚbbŠ›óá%u¼±Íÿ^§àðåÕ7'òTý¾Ÿ÷‹žfÏI#¬ó5^'PÂW^ͬÐGnÍ·Ã4¥´) *zÕ˜zéR¬VÚÆÇÚ*Žu{Êà¸%÷%9’i Ì%'ƒ­`ÄxÞæq¬¶2©¼Ò}x<•£ ›QS®sú‰­qÓѴ²TùÏ€}HU@>X=>çb`°ÆkóÏY‡_Vï™ý|ÝwŽ—oç»&._w²˜IhÍÌŸë깤Ʀ]W†²ÓÚÏkhwgU6}©Ñɉ’ç[Âè®øt7+l²ô‘› -„Å^c-ÉÒ—Vum¢Y8"¯—ÏlùƒÒŸEPÀ$ö¡lI’†¡l¨Ñ¦Uº>SõÃŬŒc—Vx=ã­[Ô>{ -í Ì_ŽirZÞ_«êÎÆ¬–ø¼×zÛÍöù£[mr“foÛ-»6'+yåÐ{cÅ•ŠñªÝÊKw,Bh\þ_È È韗?HÍX¹^ÅÍŠ®{J(ˆ/ÍÏèCÝ©Œß÷±0ƒN®ãs³ÚO©ýc3w¸,ë˜\™´Ëb°ŸT·o\oòÃq·ÇŒI±¥íç+3+72Ë­–Ü2’Q:1½ùþ¹xµÅÅ«ØG!(Ó£fÿ€œ}?‚ÂN&±!Q†Ms5σ}°g>- õ¦bù‡Çù¦ÕK%Ó|žâfö­œÇvj_"üœ´mF¨@å2?j-kô[k»0ÃWM<7dvËó|qè8ZÞ¹”ìù™{¡4b zðB _=psù?äb Š>Œ§0-eœÇÖy =)”Ù:æ"%˜RÀ®³VÃ8ûõŒ§R™Kt˜ôÿEØ›n)ª¬¶OaCg‡¢€ ˆ€ -¢‚-bƒ-‚ „¾ÿ kïqÖ9kTîûgÂpTeÎŒöû HÏÇ¥iö±Þå >ÙqÁÔ‚bÉ-Ìê0¾•š6Þ´57/vUyO Õag½ ²:ö.¤…)Ñ#uà኷¹ýÿà]¯ >³wPˆì2(uí5>¦GÉjü ø7 -fnX´Ý2ÙýÕÁjÓháe`'D'=ó*bž‹Ü0­{~8ëÐä 5¾Õ5¬Í6•£ÂY¬7ÔMè´†ò¡;qÈ”\Ë Ù­ˆ¹XáÿpðLo@ž-ßA±¾‡š»´ölÆ58¡x³fF°pí둵Ú8vZoõû¼¡±•¹b`¬úÕ´¼døÚrýM5W@%•‰Ròý·»Sfß“f¼oŠ`KºâR^"˜ó¸d¿D¬Ûè|3ñ?—_ˆKÝ…£RïŠf\$‚÷âTYîúgÍ\„LóÈ5Óß²½êlí s»…yL_ív)'½ûó5­ß0{&—bªïÒ -¢3g¼w‹Ø¢LqUšw×-‘ÏL¤Nغ°S>ka§n"a'°¹Žò„˜ -›Øÿ“:ß*…Ò:†“¬%ÆóÝy`¿~³‹-òx|˜Ô¾ÑŠº)ë›K¿v]Ïæöù8Å×öÕ¬ˆ³Pg*«x]ĉzª2éÞóÐnœP9‰k$•Ž«Ú a/Ù„?*{/\ÐUûboÂö°"yuÆçüÉÿ ·šíAÁ‰_ ¤7+ÏÏÖ.ïs69P&{0«0uš<÷ƒ¬‹j¯?·çŠmIùÁÖô+-OORë@¤¬P=wŽqÏ|æ’î,Z¥¥t®‰wºÝ¤$ä3—Fûz8 -Ü}ã¸:F.ØÇý|gÁ¦ÄÕVãçƒÂü ÿÜÿ Ï<=PlS £|·Îí¨ÎÒ·C-`žÛ6È9y-l,¦L¶;Êx|í–†ç¶wWñÔË’¿ö&ì1„mlQ©=E—<|:D_Eù Ñ,µmâÆ^µÃZvÀ$åù’™¿é€™*$3×ò0â_Õÿ µ. „-Ò?ȵUñ¶Ç|›r÷ԞعÎI½¯ÏáYÄ®fovŽ8(‘>¢Ÿ±5DÌÔZ+ ø®7µøc·Ý8ÞÄí¸wrwìÍëô m×ÂU‘…©4Å€ÕJ`Ú™Îû-§[_µ:•{ØrŒG¥•uRí– 8­ÿ Px\‚ÛX—gÇÞã8ÛY–½q=n½º¹îežlžY½™µ±¿Œ`3Bì%a¬ŽÅ–åìt3=þ zr—чƒý›‹›m”]¤êEfÐ4“‘±%†[£¹§Sëf™Íª‘MbtiÓÞcÝù?È¿>øƒG§Î¬ORndî¦Ãƒ¾AÉŽ¾¼÷»ÖØk‚^¢T·ÜÙ³n­mÓÈ”æÛAa+^TÊ¿„òšm‰8첂?¢ˆ¶Íì‹ìÇý€ÏQ-âö`èËÄáþÌ©p¥ÀYŒjdºƒz k›µ…Ò™W׃ö¦šñ•S}ôÃò~º|—‰—‡‘ÇEºBj\¥ù¿±¬Å²é:eÓlçÉ¡¸Þ•¿ëÉØeÕK&¹´—òóΰ´Ùð0ó4òÙðî³X?N<=nœJž©¯æv|€IV `Sè2àÊ#@3¥ @.Ì - nÿ{yÈð¤›NÁaê@*z U¬ BA¸‹oØ«g?àVÑ=Ùá¼Óž†l‰ùÿ·æŽØ쌦ÞÞ5¿ëcãŠp¾ã,V ¨=O¬ìAÑÃWTýŠRõ)@âöÚê´uîÐöþˆŽg¡m»]g4Ô,ª£i² ׫W^ל¸rÉ.G37éw4¾oVgóWt61ŠO è‹ÜÁ¢$3_-€mà+}]—€MjJg¨ ^m€®òß“ZQž¶Zè:ÐÖ·zX½Ù@¯ë,ÔË`Ó‹RÍþgy™˜mÇߘ_{@T±àk6†"^ض.@Û6üßïþ –튪Û+T~>“ÝåùIä.‚'ø»R~©Ó|žmG‹oËë4 -6÷UÈöÛ _v“+éÄ#7ŸYõ¸h]{¿â¿¢ÏdðÔælå®}äJ©)È! ï»\ÿÛÁ +SxCéìjêÉ×g?Èëxa6¯ OÏ‹Bߟä†~Å>ɦãjÅ£0gÕ"ªs„xëÁ‡.Í|ù2°.Õ›mÒ];H"#0´Òýß6ötG°Î‰%À¹óÆРG¹iÃþî?\gâ¶$`Éšõ$¿¥Û¯¨Ï -˜qͱfÑä6vñgpz´Š¢.ºâ3dߣL°¦ìüdì¾£+J½%j|ž°½‘·^.ûû½ËÛkÖèl˺(ü -€e&¬sjðCú rÙ  O) äsßMÁµðòÝ?öHÈõa$XÎ2å8ÌŸéÈê ij+ha›÷GA[ÌîÛúdsÇÄ¡ç÷F½ÛMë ñõ²SÒ×2±%ÏýÁŸZÔSó²yEÛiÇ]×½IÁyÖÌâÌš æW¬x¢ñ»|W¿€œÖ-|χî‚ü¶Úü¼‚¼É~ž÷Ïžf”{p[ ÜÊ¥u—‡šè«éœúƒÜJòit¹_•éyJáóÓ &³ÇeêìStp÷„þ9lSVmß»;ÛáS–6èØ^/{\kÕi€Æ -É>Àá·¿à5c ë¼s¹ã´òûQÊÄwÿ²z}U›™gôÚrŸGºi!vM -~±Õ¨^G¥mëlmÓÜñ½1Do“#ÕÃöù1öï8ÝìÅrW¼¯÷[=¹ד‡ÅÌæ±\ÓCŽÚ«ŽšK¼2£æç ]™÷/tõWLˆÇ­@ijïAÖ§(Gåûr·e2,—Øf/“y/÷ ˆ\û}»ñ ìB#SØ@Ê»’'‘µ?]f7P[üÖ(ge·îúÚ˜šÎì8[¬—ÛÙnÆŸUG3‹Kÿó~\¯Í ô]±Ç5´ëUéùsù¦}&ÿ]l¢NÈ€…Aü}9Ñxù|䛇(Õ˜œƒžu¿ûä+_šÜç}ì\^ÙƒÒ¬»áuXÚÖ -XýÙØ9³é°F‰[óü[Z‰å[)§7æBIV‹ù™{楑™Uü jEθ<}ÝØâ”e«…ÉûCäÅ4 4ø¾S8§>B÷‹eSX(ªz‹xÖD7áV—v÷br8]ãÝýè²Òã *æ{W™¬3.¸³Lu -ë,U–ûá˜^¨LÔûŒ43&¡fulY“Ìæ±šq4ï¨Òdf -ãM¼ÏQiA|ÿ -€9(m¾7°ibXç\)VyÝí±µåË,P¼ÚêÔ¾O=ŸSÒórEÌß ¹Mœz;i®›Zu™[œZ|a>t’ò¬FØ”Ï8vº(`âdaƒIFáæcñìžÇ¨=!LhÄè¨øHË·°Qá\FÿàùçþÀÎNNaB¾‹MåÖ¹EU±÷|Ñ|tÂõø>àsÖµ‰T–Çî4Üí‡àpvã£wwø¯dºø^ô/rzvÄð·Ø–Ÿ‚n–vk@êM9³G뽑ÆÎLãâ¤vưO匄üÄhþ@¾Èþ -€çš ÜÒw±Iø~[]ØL ¨gÈñrµo¾åj§þ…äŸ{8*y0æÀGPp>”*…rÄŸ†¤ÎÐ3¢yèA?U<ËüúÀÕû‹‘[Ø ¶<Ÿ¢Jgc´ÁÞ¨GûãàÝÉ^úñ,T•vöÊÞÕX}d³8Ä»WÄ1"·õÄ”J.Å”–OÄEØþH­jH-‚N~ ¬FFî Šùê;©>…°×}dË»óo>½³:ûÒ¦s2«VfÏÅǘn3é¸HæWÆ„iºCv?Ú÷ÑÊõ¨0æÖ3è ìFÚ ‘渀ˆm·Qê í2-t7š,àRfñ~þ BW¯¦:Ùmt„gåý+`gǡ苻‚âRKÅl¯˜ºpÁëxôì×u’ÞÓ]…-l­¦Ô;n¡•äC9‡¶Õì{R›436Ä;j“<•æó^åÃçW°Lþ‚äÏäÆ(ô÷'ͺÑeÁ«Î1ŸFCU¤ö[®Tð^ëž^V•²ý‘Xfâ…’<‹ÝÁð=[ûݽ?SKlyÙ«··Ë¥ƒèh·[[©ø[N-öÓÖÓ­7J¸ûàì;ÍS;-T"¶aº|†«ÆâïùÊvй8¼Z*a{SÛÚ›GÁv?ú÷í?N/Ö¼¥‰–ãÙßÓž“Pf­’jë\®Þë÷2âP%§û±s3铟­D±Gïoû:óÞ{´+ÀzsTzM°tâÖ˜y;#¶ÞgcÚz_úak%ØÒz—ÏDëíð_ë<·÷uécœÐ~Cß›…»îòéþp­õôé²>½»³UñL½r3mšÇQI_nNÍþažî¨å{S½òiì ÉÁ±±ˆ‰Û¹ ú[¾,×Ü#‹ÜYûðÌâ‚âLªÌR-Á´¤V–¯ØÍm6ûhº×>Þ„I!Ù”ð~ùWÀÑrBMWçö#Õ;” 3ÕM#QwuÉn”ùSݶ¸m»Ó°r7)cóÑùU1ßW¥¦„™'Ï7lW…}¿ã­zcÄŠóvà<6œ%Nì\;„ _q>°Ä6ã ßÜÍÒ¦Üí÷šD5;§½ÐiÏi`tþÆUg£Uû×i3'yU‘\n7n¯ÎL¡=o¡aÇÂéPûÉFJ£–1†nPKk×Ô4¯4´tµ»Žä¦Ø›øma¸ÒºíVpö9;a7°dÖásßrŸÏ[sÿ~>›9­‚ÑE.7†ïi»Q^!u×ì-uïËêN‡äBÝKqùWqn»ÉMl­N·1Kj=í}r¬IÍzªÑ–ÞàB5ÈÙŸ3Áñ.Òž“s®@ñsd¸—$ ?»Ê×fCõÖŽ+ÚM¯ƒ­é³œ=4nZÖoT`¢CÕßm¼yN½þ]-­½ž’Y›oã]e)ˆ#ò+¶6Òlü ëœÙ«Ì^ëGyšË(彡©áîH®údÛ?(ÖIºÊÄâ1o¤„Qª€·íÊ¥Ä~ìaq‰ùlÊ7µZ®GéT¿1b#*\y=vŸ›ï¤§¤ÆrQu=¤ÓÕÌ])T\óЬH&WÐØžA\œ_±î)Ҝ֖…)açóÆsgåõêY[ÈŸ™3—zXiõÝŸ|óv<õ”/—AÆí_-⦽›…°“mø³þ}’¿H52HµÞB£FmÑ{qß­«â÷Ñý~ ß“r.9äqΜH5#²ð9§K'_* ;yØ_þ\þ†ùTèS‚¼aÆ“eѾgú¨…´ÔuÛ‘Ú¹ô« -?¡öÇ¥[CF"ž£æQ_Né!Õ_6ª€q©Fº´‡^Èù[bDt« -E/¨¸º÷]ð>e"^gI­µÏ—.ËKµèo.­b ýt á2 -E¶ üj›Ï«ý¯˜îD1â0óú¼ü$Ýk>ãÔC ZU4šK'Šqe¦Ñ<£f÷ †šp<_Kv™NõãÅrÅ®{å½9ÒÊD4ÔI­©™¥!ÇM‹¾ÓškHk]˜È-(ràß’o{an¡ÞÄ'p0B %"óLÑó+¢-³ÛÈ -!(¶²ÚÞ|弯0ÖÅ{ÿñxt{3ñÿsÿb>!¨¬þ]·…¢sâ°’EÅf`M£É΂¶Ñ ujFM ºjÐuÞêõ ðþæ{iÿ–ì¡½Êæ â츒8Q ;) -šáš ¶Œãë%«?¢T_ g·|ïWüWSÍé°(Ýïn[ôFþ³Žmd -`ÖN¶ `ú=>­Ë8@εv<@ÄHH 7‚Õçý-Èç½P3` ºw…šÇš˜t‹œöƒ¼º4zöùò(ª÷–Ùãê}S˜Ë¿ «”ë¼aüw9”=€E˜>åWM€Ò}€¹È `­Ô×÷o1 Ún(X¶7 .üGˆ]ŸDj/¡²¾ƒ8\RI=‚3HÒUúKžøçéÔø=‡‘¹?ŒBfWÜ{л©ùŒô~}³ -@qé¿Ë¡˜3>¼-¢_¾€¯˜!Àùî -`—î`’ †íò]Þm¿;-”‰™i²vV ~èì`‰ymåòR"óñÊ[·ô³4Š -qŪ0Q}AKúÔÖ‚·‘×}¹,«WÙK§è‰µ@‚œ -ÐîÈ„E‰.`¶ì}ß(ß¿;4*uÂ48À¿kÇX # l䓞yk¼Ž–'¼ -‹•òœ,=öËŠùjáj5ïúˆ¶·øaÉ$Î5ƒ -øŠÑ¾‹ÔÆ07oIiçñZެé´.Zoý -Ø}ŠPt»€¢Ns K¿Bzå±ÕyˆëFÊ‘õå²}½Juûžvâ³å¸6-´¢ [õ•0±zÈÜi°N¯—ow÷Mßîh›¾¼Y·BÏe.ÆN뜚³”èeöž8FÌvȨáùçÖyMûϺ-žSW€À˜; žõÈ Wä:EäJÜöuQŸ×gµÝyD›¤ÌÌ&ÂTÜ ƒìkÞ%çÉû=ãн×ÎàVx:“äJÓùeTXnÏ̹žì’9²€.{BºÍì÷=¤½vÕæ&Œ>”cïÕ_ÐJjjÉø¿[íirš^ú³žœA>5œ¼®F͉'ÀÜ?Fp 3]1ºËâ3åç)q½~*äÅ”êü ÃN­Ë¹iUöÖ»Âàà®ÑÉË67ûývåï4-ƒmá„Puk}"&0Q60i:=@.Ü–¾o¡“AÊÇæå™q‚nÖØûš^¯~ÿ»ÚsnJýäøÞYÏù¸¹CW‡““ÂרÝiXd·WˆÛ²•ÒÜÚ›n&†ëkž2•ˆñ…e7"+ eŸiÎûëOgvC(}V‘çŽmžêO+¢-|:'(˜‰OÞ“BfÂg>é_°†;øBÂ:W _•^ßåPòU}HL”*Ü… 'Ö»·#«§u©lx8 íŠkwª¬áhé;­ÆôW¨[‰ÇÜÌóQvfhBÎw[ˢʭéË–ä)ŽödÅËþ$}ñ3c±µO›»Ã"eÊŠñþß'S´#M¬ïáг#È£ä»JÆOP†ÛÖ¬åëò›;ƒÁEðöÞUÛ“ÓÀt£mfæ,“ÚzµMwËœœæ×ÔÊŸd6¶©K¦IÛÉN>ÍY–X¦6­ÑÒŒ7eã94‰š{Óì{TPoãB’ÀøèëWlA® æx 5o.…ó¤#U ³[ƒcr}²ªŸ:Ü=m]ÞNäñÐù´×“5‚Å2¯Öy%gíìi•8Z-{ïOSy-»‹òÛÜ÷tt”Ó¤q–An;ª>"R½."½úñ“a¸>Ây8Üç¯X˜@Qу¢Áý -0àHʤ8ãeþ^Úà…˼Û,yR¤w÷e½³¦¶vš¹T·Kk^EƒÅb[ëLßÿ0éÔ…Ó[¤î£Sqû4†«AJÙjn81µAÅA«Õ¶­n-胊ô„=Dˆ¿ˆ~ìê¨þýîuóú®Š3(tâ÷«6*bá.ÓÇnÁm‡œ6ç#~Tžõíôœn;Ní­r\q8÷Çí±m=ÆÖô|–d)9¦²{ìF¥Æì¢ß½Îc8måÁ õø 0_’…ŠæÔØ¶&<àÄšÝnZ'SŒ5!ƹßp® "Æ /¯ °É¼ãçúž Ôr>uµ‰.8ÊþŸ·ËU)§²y«ÖºkúÒâª=ÕYcˆW’'ñ²Ÿ™ªU\·õÒÕëí ^âûÖ_>¼§Ö1µ«- -Êa¶”|U*ùZû¤(£C¨(½m §ïYßËßg"J¹Be ò»ÖÓä;Znµ×ä¶Ñy=¼…^áÑHoãV·èlÖjcy'¼Î,e ,‚þÄý†©)œ+ ßxÌujÛwóKzßϤægu7WCE-6ß½ÞÂå›:iÈ•kF…xd£¤‡ò­c½Òtö;¾g¡¦ÚY¿M@z7Pl7ßNP oš¾œ°ºzÝf~âry_ËWªº0FkÏ©®8íLWê8Ÿ‰‡Æ}P3õÆdk8µ²ÔÜÑf£öõØ»¤/÷äÙ_R°ö¥¸ÜªIMÓíJÍñÊ•¬ ¥èÃ]ªÚ¾ÿ -@ ªrž¾†Msâ?§ÕIxµVâêœÅªŽWœÇ‡íSEŽûòà˜¼¼x™ò,îçh+S®ðc•»£Êa­éô(«ÚŒ2Ö¶ÃôLÍ9Öº7 «»î /’½ÙGâbqʈ©4Vîâ°Ó$Õé]$è|®¥@dŸ¥ûü ÿÜÿØÙ­ÈW.(œö÷›éÖ9Ós-ïT²gûzQÛ¸0¹­s¡þ™?ÄuÞNá‡ê¤WO³#_¨vtë<ì xáÚ×äBÝ€mì̧½Š!λÓ+êŠïôþ(¦7¶ÓÅGÀ^ϯp\›WÚÍDäó‡ö;$ù€8ŸMŸ]è¾Î0K:¯8A÷ŽFq´7)Ó}·Ÿ Ÿ:ÇeEÏÇö;'¡S,6K¦ŽœiýYþp¡×µ^e©(C ïËag7î6'¬-®îŸuGž@¤>_@öŸvésÏÿ ÜÍXÎ4çœ!7î6ÌGíÒ0þ -8áÓd ƒí•FkÊQÖïÚ¾ÖRTw¹))ŽÌµ'Kc‚of¯hwŸîôïé æM©=TÎyOkJ'×VÊqNŒù´"-‰ñP¶üTØGÉ‚×FÇm{8õ}®V=&ìdŠå™ø¥1Lœˆc¢ c+ô‹¥–•øü ÿúäÞÝ™?l×ÄSg;“ö¾DwÝeé,®½†ÚYV=k0KÖ endstream endobj 44 0 obj <>stream -Â’rÔq<,¬cÃFÞÙÁæ]-hêhRSªø³)7±>/µ_/©³kX0¯”€ñ¥!9ç‚:ºe#½2¯™´Þ´k-_>Ójï7V«m2—V:o¾Z©³þþ/£Ÿ¦°‡³‹ónò2¾û“ÛLk1¶ŽÜ,Uó»Ó{1ÆeÏvŒY¾|l‹f¢Åé=¡Ø™'%eÓ}¦Óãòþ2ó”öˆ2†Ü¤eÌØf$n6Q¯-!5NšnÇÏ5%”g~&z¯XMôp¿Ò{*›ÐòOÿŠ›ß¿rž—š[aéõ¡¼®/ƨ@Ùü+לô?ŠÆ9UÊóLêM¥Ã¤gå.©nûØÂÅîáN -gT§xÝaÚíºœ—ÙW&0o,²ZÎù¼n!åôµ‰×ó/ZÙ rt^|³sùjÑyfxiôÇ“¸Q*8éáùçþ$Ü£Æ.8ñ”#µ†å…iû9Å:}™/¤G Çlávl®xŠ)x+¿û“3µuÜÚ½†Ÿ¦zì¢ôàÊäFP¯P!Ï4ë±DóµÄd”)fuÝœ­«™ýý\‘òس‚F:QÞ[ºÜk J™Éêoø>Y/ËË|ŠÙíú93&éjJçìÍG+ùԧǘMZÚ‹«^gX¢4žŠ/:Ç¥5“q{5»E\ÁоŒO»†ooÏT¸œùõx£GµdßMj\NÎT…÷ôb©¼ŸˆT™x<©1›~鲊ìÒP¨lKålß/úÛû8’óù_aóœhü­s­A¢ÞÿÙýŒ{O± M`œÌ‡cŽæܵɸrŸmõ©5?»Òe™9—Êi",ÖP&½–O½JÞî»\.¹…½$—øä(·‰ó_1ÉOq ³ k%bu×™òCÐéíƒK4¸ÝÖ<e¼á[‚¢>ûBmÑÕȪPhT*R¹H}ëåñh…סU"?3¶8¦|!ÜšR¡ë½¼­êýÜb¨ŒˆÏ½oB}êவ=âhò±^;ûèX.m–ÅwÝRdxˆ¾ ôc•³²ZoV_v ë˜HF«¯Av7¾€¬¼{,ûAA6sœlN¦A6eñ óðz ¥ 9Ó3ˆé÷9ñŒ›‹!tâJ‚Ì6GƒÌ•cW¯1 úƘg±9¥ãKÒoü ;hCMË„šu5WÔ|_v­Š „û}~M¡@œæÚªkh»ºBÛ[²#Ù^³ ²Ü  ²µ••o -ÈæñȦ™9Är²™Ú dâ)ñ€ÿ8Ûb©Ûh¿öAŽ}£oßî5†ŽjSœú cþìÈ¢ãÜð¿Û‚™Ï sëP#®”#;ÐU„šŽµ®Þ º¾Èú$²{¹ú­—…Å@oO„Þ/ d•ÊdÛÝ%ÄÁYªü„pˆD²ïÕWoy¥ŸÚùÊ>‹8¯BÃføÔal½Wõ¿d7Ê÷™w$€¢ACh×ôÖ<¬f†VëÝó"@Gó1@sž ÓëAþó]®Ï5àÏI6ôº {@Ìd—ú:Ù¶ÇÛ€èyå–%äY<·É¸ì·©È¬uÙÇcZaÂY÷Ö¼wZdíž…ù7€ìYïügez@¯á÷ŒØ± süј¸ú³w4ñ èâ ÛD»4ͧ²’9¦¼7f_^j.½”éF{žn+ãÙ7n³g‰x»ñõD_£ûÍJ=¦“y.œmwÕ¼=&hóã–ß}¨kÉ*?ÈEËäßëÜ„¢¦üg÷2Ö!—{Ü|€×e £ž~+³‘)ÀÊ•}Ò«—‚—š¤ßÏó'ÁŸd1&c£7` Æ6BéI¾ú°ðŒ¾xtÎ3â!xKç×}SOPG&äÍ«%ôõ\²©sýò({ïM=çñ$Nü °û8°N™Æ÷”3¬xüç :Wð;Àïðo}×ÁµäÓÖ«xÚ­â{upŒÂ'ÿxÄ+ò&Ã,²ËT)X=@=È æ¾ÙgÅ;šÊª¾Lc#I«›Ê}Xb·íå}¦ -[üø:LÉ#›ï’QŽò;uÆcÛó»€ü °ÎO°—ÌXb“ÄxoçS€ÐñyôV¸¼ -ת#ÚÑç±?çNÖi,“ÒN?²£ôÊã§JpçVz/¯WPn+çÝQ:Æœx¤e»&ÀHéÏå_Èý),;Rž"Çßówm'ú¾×/Ÿ¯3æY+§;Qó5PC~èw™äf~^îUWõÓùaâ÷“ýÚÅÇÅq c~o=“/ùƒTªû½$1{â0êî´´;Ú1òàŽêj¼™4'Y'^Œ°5G³«,~J-%uòþZÉBMa­ýYeü®HõLëŸ0ÜŸ˜ùç}L×¢ÖcÕ -³BFðI¹úÂÕ<7›o븢k¯3!ö ì/ûÜyîŠ )ÙŒ;ÚUˆMø©—7Órì™.¯“÷Ó†ÑxªØ»­²•I²”ÆËÌ"'µ>³kí™Ì®íÔ_P½,l}„¢¾eâ¢| Í=“¾6%bÚFH˜ž‰5ÿt_4¯f-ÕùANœÈžø¢ô½ï­Ý Î-·•9Øn¢¢sržoý¾—ösÍúg%D$¶Ü’Ty‰TvÑ{œs-a¶³kÏ gåÓýc×^³Ä¢‰Îsú<Òñü ÿÜÿìç (ú‰5@4os£fßÓ8¹×3èÒ™_³Ð»ªeò×`#ÕNíq…ö°“*ì‹üQÙV£œ¾±ÂéÄa™úb Ëí -mŽ0ßÇ;ƒÛ"ϊѼT¤>3½ËöÝÖìzá"ZS}°œ>ßÑeÊùçdyEž“t+ŠÇ8?þ€>9 àÚ„õ^€Üêúý.)/_q³~O‚}‰þÜüá>{f'NΓÉsu_’QÖ},[¢ª3e-Tp}…Z›Éâ˜âónfï{ö¸ìú–•·ãé¦'oÎ)LÒGk¼Amó#&y0wu6ñú€³ŒT’ý+àp$‰ h\ZÁ$\’AU{>>âôq×ät|m´Ùä(átvI JÛz¼€£¥ó.ݹuöZ—–½óF_U}6bü©M¹ÚÜjš­Í䃦¼IúóôÇÈó‘˜r5 ë¾Q©òÏ«j äáâãd tcâß BÿÀÌâÿÜÃ^4„%ê³CØ}ê+gs—WµQ BwDßüòjçŸSÆ1ôr’ÚUodn3?pÕµHL›Keñs½ÇvíGw¯ZöE2¦\›N2·Óæû{AwGÇJçd\ -í‡îc|J¯*óÒp|Á…!e?Cj¦qwrƒ°7' Pù»*Ö]ƒü¼vyR.€™EÐ[¿¼ëtHíÛÉð¶&Ë—k¯|ÔÉX©ÒÒ{ÒõùÍš3vÔøÃ剣¤´±t›fNh[Æ%@–º¿‰và ç]ñ4xõ“JôœÅô¹¼û~Ÿ+d=ís)^¾8A”ÏÀŽë. Ĺ gŠÞZ—(9ÌŽ÷âÕÚœ—wíå™ÜiWÏ)ц»,2«ýî”_ qFJ3ªÇ6,_³“M¹Üã´#úÝ7ÊàmƒÍlðäÆnñ’OÚ:”#-[\ ªd ”*-ð‰*í·{xw?A<þàø/ü .°³ï¡¨µƒ¢ïåJ½k”Êwv·û»¹8!sn¾¿µ¯{÷U*Ýì/µpŠÍÂÈ+Y :S›¸™^sLäžühPU$=è¦Õáô<˜žjÁK½ª+u;AÊAG%çµÒ½~a]éõ‹°Ñôif1=öNýåï8÷q¾Až9¹±:›ë””†çö©çJÆIÓÝX4ÝMû\W^šLæ÷-“µgä¼0ô°2îµZÑqX=,V„a³íÉýÕˆ×4!L‡¨’^vò-ï^势»æ+uÍ€íC¼×?H× ¯{ÿ0¹Âv½¿vöŒûPz -òúÝõ/ËIÿ¼ÔîÊQÆõ@J³‘›Ôê+-¬NËÒÍ̦ïRÚJ§Xl|P¥dt­gkzøQèAr¸þZ´$ ’ŠrÄC½7°–\­6]Š¥fÎ~ŠóA*Ëâ|8_ˆ¯kÞ“è'œk!öÃ÷›wŸ„¢jÉŠ^§Õäò$âqt¥ÃùÒ–wÔ9ßßðϳ½R¿ŸöÝzG0‘1KC-¯?^r®uª/D»–zȱ¼Rd®ì_ì~w¢ôÆ?ˆÔB[K‘KqÇÿ’‚ËžpÁåÖÁmË3ˆàÐá½´×á—¯Ãß’UA.hÛ7¿´åNN¥Þöò..ìÆ¿ù|,ÎȽ±:^ŒúáÊN¶ÂeŠŽÌÈ¼àŸ´AÕËØ%–Å~'@*ª²7½ë edsì‹Òó )âRl:› Ò«¼ç{a#às“-Ú>Ñ=¶}j6­ö‰\ma&Þ³"ï n‡¿ö¡G5up±ºå67fœ Æåˆjk}¨ Â²ìǃ٬:™YY5{;í»1Z¯!Óɾøìç`¯TÏQ©g8b­½?Mi>ty‘Ï{ÂŽ+xÎYíþ'·ån–ÏUfN– X®É5aƒÚÜå*ÜíÈUjž÷7€\~7øAüAǤOÙöŽÚ߸íÎßj½¿k•e…ì5g¯’Ôµ:³¢9>%Ž3ªWñóp©È>¶vZב^­·"ºöŽ-K©LBu\sÁ=²'ò…S£Ïùrqòƒ°¡ÖpYjÛ»1våžÜfš'˜$Õ¦•ÐÁ‘iö£¿â¥7ÊÅμj‡Ó‚­n›•°ê w\–oÏ¢ýÞn+š9µáŒ|>/û#ªT·‡«áf×Ç÷èU¹uG±µ²Ÿ.3Ý`¢²…^N*üé¼o¶õÞHàÌŽ¦²4Ñ›0LVsZË®{k¥ŽXªéLަÓ_›Â+pš‚Ÿ?ý .ƒî(ÔQ°änœMÊŽ‹5 -˲¼$ìw…&¦D /š#i¶ö‡åÚÑ‚“æ¨q9È6Õ»Këzê%¢Æ*óƒê•ÅyREK\ˆ^êlœ»´™ùn!·VG×leбÓD«è…Þ…£-OIŠ–­¼IhgÓðú©á”¿âœ´VåýuÀf÷ʹåe·Äg Ž SÜß £ ËâÃŽXL5µRšìõÇpÜÞÄ‘²÷Æ©£”žÕfIÛ¬¨Y®q©æØÖ¬2) eZÙÚmvSÔˆV~õƒ4NòüÒèçˆT£ø8SâÙ1©«6Ôðž=Qåø+¼\/Stç””_©¯):›a¯Ìɩф@3ì¹FÔ[O#ª³‡‘’ȔȄ liÕ䥂 Úµ!È`bò™òO6÷– õ)òú’­ò~þ¯ûÿŠ&8õt dÏ ™Üb¸ƒˆŸ ûlåAv6l‚l÷¨€,þ™‚Ls”Ìqp™õæ ­Ã4œïQ‚­€L绯^vȈˆÑŸŒ”=CHqâbËÌkŸ‡f„ÕGŸG’ùìYáÈß2Æ™†EYšgSHýÙˆ*ØßÕÑñшF¼"h€d× È^XÞiƒ,+ï@–°|ùœ_ óÈd!(XC'¹ -±b@ƹ‰ ³) ÌYâJÙËk¯ä¢çqP|? IûAâ!WÂb2|£‘? ÐG8gÿXçµ:n%€¬r€¼í9@‰Ì Bý$tK™Ä@h˜F#ˆ8ÿ.×¾ëɆ€¬ü~ƒ,WÅ@¶Æ!,*qO+6‘úg)ÁÈôàÕ•¦OmªâËË £Ñz™<Yy4ºs4Œß*Ú+ ¸Á'ó/À¼ò{Yi$݇ËG xkP=\tÎ^ Ü@å˜h®ÐÈæ§ÈÔZBeï˜te/|Öx)yžt?÷ìãq9¾ª1ëÙ K¬¼­v£{w0ŠjÞÙ ŸÃ×5«ü3HÙhæ.øaöž³¾ô˜d®'™øü ;(AÍö”ƒ†Y黉u°RÕ=÷¿«£"šJUºùdŸ _ªAÁé@ ÝØÀ>§(à3aT$É#²Ù­`¹ð•.–C¦™« áÚA›*A:åOý­Oz·Ã‰z\ûFá}ÑÕðóƒœÍᜦfãå½K÷èo€í’†¢‡C–b­ûçay€9ª °GtxZDv¶j/íT`b?8v£Ik¢?ì´4 ç~Ë…õ²rЧ }Ëw‘À^þNÀÓ~÷YÂoS)Ýòã:uíÇ]ár+OçqlޝÞýä½;÷‡—–ç¡sìGû]£íò‡àþ7|ûK]i´üä–Õz;‘Óïy±»ï C€/Wè«cÏèñ^Ô-Pc¹ËwI›~¯¢ÌnZ†Ù\÷¼w1œ×íL±›ðdM6Éñu]gŽ0Ç'`öºR/U/}EùƒX÷DÆÛîòà|Û’éMàâྠ|Þw¬õçò7Ä4¡¦žmLûÿèúÏ%E÷qü>‚9‘`Î"*Š E‚¨`ÒzþÜÏ·~ﻦæ~òj¶v§¼è¦ÃÕÛƒ›F8[‚\øÝµ­¥yZuÜKÛåñ„¯ØÍ›¥pÛ9à—CÜœk#}ĵ6Ÿ¦Ø‰|ž§UõWž?ŒýnW/»<#>·%žûl„­œ÷®y¿åI‡æt­\VK—P=ß1ãÓÅþLóbîhŸÍý_$tÓ@ãfZŸŸ„O“еù}»ip5íöÄóÇÍQèO˜w7ðù -ËG·öƒ¥× ,3Þù,6ûA¶Üë mÄýR÷®±ì¬oÑt»ÆKÓ“kL¦w'±„ŒCBvÑvˆGÇòX^²ÐãÙ]åp0÷«ûÁ,üý²Vaw‘¶ø¡ûí/899¦*‚<­¾¯ ÝÇ‹cåt'ÏÒ9d’çí6¨÷a% á`¼YwWnn+®Õõ¢Æt¼Ö–ë.ÙËܱ[³Ý‚`Ùðh¶±°þø¸bTúþƒ˜ûØ€ÌrýZ]ŠŽÄ!ü0Œ¦ÔÚéê­èkñð½Õ–Ùhó„]€©õ¿³byYÂÐÚ?âíŦ3tw™»‹t{á2 -&û Ø•K5tÓpåÒZßçšÎ;´{¤Ó#k[È3+?~Šæ!ð³ü\­–µPÝÍ£xÔµªk1sÇ´îqÒU­nv¡0ÁU3¾²UF-:í• -ü軑ŽE™Àn«ñ÷¬Ø²uëuG›û@¥ëQØ9Ç—ºÁE;~:{xwÆù¸ÉÁ -Ó+ÖE›«™ºï¥5&ôDjY?5i£eÁ‚ÞÑbUëÑþJµœóNq7áU?ØGžR󚜫ÜÂ]W Öü¬E:q¥cþêüEúhb=kœ&ßÿM›‹´2{V,[9'ÚNýÒ qõ­±+¿ø9ú%›×½õr`Æ,F/åÌ“ãT–‹Ù2ÚF¦§%—R߯9£Bû¨ ›Ž.產½(õý"ñáè1¿´Ž…¹”uéyýÜÔRŽ¥ƒ¨ô&Ö_¬\ëƒýœ‚üú¨€‚ÍÚÏŽ»]†3ÙŽÉ|ÉôѶÒy¶N3¾×uwtÖÂÃbÙ0ÏÒ 5îu¼ zXÕÈ‚ÜV³f«§ 2Z `¤ãc-Í/WÛ£ƒ¹ÛÈñ*èLˆV‘ˆÁAá“,f}Yýãùïú?Fái é(òHy½âò6.Ýæg5ïOÙg‘öË霱iGUËu2‹ÀNçþpUY×_Ë¥Bz¯PÕmòebæª?ˆ\(Û‹Ðûó«NOÅÓæ…稡ñI¿áò}gvæìxŸá†[»Ïe1Å5S6ÿXþEÚ‡¨4̤Á‚BR»Ýø³âŒÈ#™KGü]ro{Z\¯ÖWÃA·µÍêTÓÎKEŠîºYoÕM,HÉ!lqP.%iŽIõyº!DœA‡<€o,÷‰ö .{Ú¯fhøL[ŸÝ)ã„ÍÇù6ËP/žeFS#…_þÿ`{Žü·+–”…`q‘ò­Îq…=ˆ½wÜvý²;lðsiæR;U±öpÓþ²ŒáØ¡–-~b…nŠŸÅ±!R]Ô â"ªÂRú´ùìõ9*R§3LdD¶À2ß>£ÉƒÃ}#…¥Å¤¥ úÜhý4ÿ×ÿHxuòÂŒ{tßêøÜÎ>Z‡ìZh…ÅßÊt¾·¶äÝÔÁîÔb5Ïú–W‘æ,é³²#ö÷Å©‚'R£@¢Î1¡—dË?·.õÙæÍuYÖ莞­qtÅÔ©ÌÞäîªáD¨ÒDÓÉýtTS^Æ´•}ë‘vv#­Ï£Î_y ©û¬POgd†)ÕwµøV÷^%wGëÅØâ·°°¼‡¬®[ýËZƒ\&Pjæ3OÑèjþ3ÎÃÜØÙfù:SeŽ^¹M_,l0U*wf¢çïòØ„ckô¦»é\9²ó1:² WjôfgRŠ­{ÖZý “vƒ×éÚ¦ÏQ{]Ù{D£º;ã×êF³•5$7ëö¬‰õÍHÙ°FïXW4„²´¿NY_’;ÔI\–»Ln"} v¿Q1Flã%ºÂÍÉstèÍ5Ù±;§ÖÑÆ¤Æjí4ÜÆn–B;»!…VbÊL¥Æcáÿ‡ä¿k{=‡7êª5&Û(ùϵ´m¾¹âÚÆà¼“¯œË+i³Ä—„ÈN´õö!*Åw)E‹'šùòžwr¹jýÉòTð¦C…‚§Z­Pøî'»çÚ8Ó·:#¸,SÃ]$ ÃBic¸9|”ë4(§ :(ò œé«ޤÿ$ÞÑáµ’;–÷éè˜ÛÕæ\Þë‰XîqèŸ]-à"²ìfs5={x‘Êž3g‹Æ)¯ŠÉhaóëg¼GöÄV>ïˆVzækÚ9‘™±]Øh´õËÔT]¶‡Å:?TJóY¯Yz_–ßoµÕ÷Òo•Å^¿'\¿…ôÕ~óÕVþ"n¦ÿî¤cýtÅ¢ªØF›á9qå¬Uå2ç@Û¤‚rè+ÅâÝ‹€Ü‰¼wk¦½r¶kæºë4?.“U[¸‡aŒP!÷ùCq l– R²µ~¿ÝÓ=ÝòÕn:vo»¦ç$]sãt»ýú‡íöó}õ/~EXØ<Òʼì±í"ª!kˆ´²Öñ†}–;O´iŠå¹}…¥G-©‹V²ëóS“bfü2#3² ¯¦K>çM2Áv?ÚL„+ÅBíxX6?2Äi^Ù¿÷Îõžñt»Ý8ÐÝL (et7ܽ½¸wët¼Áv™ž:÷Þæf( fÇA=iDdëžOú†tßʽԅ Ð!FïÚ´CgI&«5>Ë5žx@;?ÎÞÖ,^ä"¥}„ƒ?Ù%Ýh¥ ìL_øÇ¼u­D§¤÷C>GãhÞo^Cn¯27vawƒ‹óÒtuœâ¸Ù3'ü ÃÓó¨ –íøXðû]rxî‘£þ³KÑ]ˆ˜Px¡ÃL»-|,Fx¹´—Úâ<ï¶k½í£]ëív­ÐbZWw*·ú?È®V¯Áî:#C+ɰ^ºc¾îJ%G§1¼3·çšg?ú—ÉÈ߯ÓËWmx™¾z¥ô ÉNx”zoÝ6zÐÊ\wMÚ9svØp6ÒÖo_æ×f+R’aKÁ‡óæc³u›:Ý»7u¦×jêÍͤÙqÏbãÕ •¿X[+ ²ªÙÍË Qè®ðõq87É]!ï|-§3òx‹äTõ6‡XAȧ­Tú+Qïô¨–DuÑdÌ…ÂlÑáxÉÄEi¶n_oҡݤì{ Ÿz™¦\‹ €—: r9ž62½£V§ö­ å•©Ãü&Í,ê0Q`ëp•”þÂ.÷ÄH„Ü]9ô†×ùk±ÿ_Œt<lǹ©½(~Ã#{õÞ»?Îr=£k„ÿw:üŠàu¾š®`Û-©Äµ´O[n¾(bÙxONc ƒúº= ëˆgj4v-TƒY¯Ÿ3ºrôzE˜N÷•j.þ”/7Ÿ(_ÜÌø/–ú£’æ/*“æ·ó¹:rû6þÅ8 -êºK> '³7„s7ßÍg:§"ŠáÒ%)·•ϧÙ"ÎÏnš/_ *{šÔ'ØŽ­íäÓ¼–\µj)Ù›‘¼zå«›¤ùKé¶ËÞK*CÁÅçaU+ügPìVÇB!¹­ÂJn -dëÿ…†áJ(i×ý‰Ïs™4B¢³={MêoIìŒPîTùÒéû>Ø~ãÑêÂÙW“Ä’O#{<#uôêç«{M.VNæ´R¾:½z¹ ÷Ú%uÚëY}PHN­I¬õ¸<Õ(9OŸÛ94ñØÎ*¼0f0ÿžZ=·Ð½W¦~´ù¿X´ˆë‰§àÓqµîý/F"]l³h·„¥Ú\M -n#{ˆwµäúUç÷±7:•Ãε„cPT좴] -deûÌ;Ò:Égo^&‡ÆjczFÝÛ« -Ê OmDœ\I¤–+±ðb6Váfi»ÉÞÎÈ5«Î ‹×%ä/¤¶k ›¾§:ÿm6fÍ7 ²«¢É Ë„.Èðd‹2 Ç¹ - ¯ÐÁÌ$:jÊÓÐ4¿MéŸÔî)k’¤Ð'ÒŸè›]‘­tK‹å2%ÞhÐ<%ˆ€^Û -8½¦ÛÃñËé/´ï§Òfd//d“âÀè”I9¨ bÈúL²òþÞËn€l©›Þ{Öàô¼ê:£nJ Íì’b?´¼|dæs)ãZʶ Õ@LñWÉ8sß¾èJ|Œ)<dz†Ž‹{-Š9>}Æþ¿ÐgXÙmÐúßVãÿŽ0 ËH‡>Àr à®s0Pðô†ìÅdg;d©ì2m¬ïs™>¤Ø·ï½ÀÇd‘l6…(¦(éÏ A‚Šü‹æ¿¯Ö‘xï)¨üþq‰ÙóCZÎF¯u¾Grú í 5#Q¦Pì|÷`i:‰¿ç—K¤ ì*ð黾læ<åÚ®ï& ûN$ÝWÍ”Ùdû’Œcý– ËmòÚÕ÷Ð‹ŽžùW^Í×â=>ìÆ¥¥N?®Ãà›‹Ý&‘s{ÊïàF£S”Á9ZUísÔ¿*§/ç_¤Ui×Ó¡@ Ö$èJ‰< öó®‘‹wß7Ûç4Á”t¼eNÏe|Àžn\<ƒçéñ¹pöÿøAþ»þGZ™ÛïAÛz(Ëwò†§Ê÷ýÐ.@iâІ’­º*¾f9±WD|ð¬ÊÌ£U…çwÁŒ[ìbîmÙ)û -é3Y üeÒe\è–§pH­òuÓ‹‰‹O‘ü™ÏQú1œ”ÝÃÅü~wû%¢yûî–ÿó@Z&@B‹øbý~_ßT€UúÀå’äoÂ+®Úpö¡\å;Ñ‘ÛÑ{? #hÙ¢ÃtN˜_·Ù–~Íuëö…•+›óáX<œ+Õztš3õ×QæÐá~¡Ë‡Nã1 -ÞHQúA|×–>ç¼] Ü­ïÛΖe{[,ά_¤$”j}¿ÁP}€•Ù÷ëu€”MR¸èç§Œ%áÝ¸Š¯È1spˆžŽi^yÙ·”öYXµû'‰ÏOŽíÜ“;tžž´7k޼eÏGoë¯Qçà#Ìé¾Û Ù]N`êÛâ*a¼Kç){õUÞ^·œ½é>¸ÝÊÕË+ó?~ÿ®Óʬ׊i-€ÆµÀœ+rMÚH‚Âv󔟟ÍíM0A¸Ùܯ—ƒÈ¿NaçŒ SØ“¥B-&€ðLJÃpG›KzÜ4a[*Ëʲ¦²é]ÍóÙåa­L÷äP—(¹}§_‹y{8,Öz k<u E}í/"÷Ò0'n `Üw—±™@ng.ø²%V÷þ€3ís·/•çhwì˜ÊyaŸ§?EåÌ®À·rÛr?Wó®§cÛk€uÝ~èר*œ“0Šìô]eiÛ/ݳÖóh]¬vˆR7÷ùGº†1KZk±^uuY5sŠq¥ù/âj=ÝÛ»9cK¯Æã9<)º—j4RÉH·‚ñä¶ÝÍPê²¹”Aüƒx­¶’qc«9@ÈVœLÿÔ´aÜëY“©A­v¶Áš{Cœ›%`,—bËÛW= ˜ º:øôµçiÌk†(ÉZ·+,ÔŒ¥/óÿHWJß"ms¾ž>—0þoŸä’¡ð3ÃÅͱj³kš62ÇîÂäƒÉð£îJ뉽ipñ~M”WÇn>žöÈT>Ö”›`+¶W+›|)Ó\ÎѰk„d8úAô{Çgu\:,´.wX©«K&PIIŠs¯µxdLå rË“ËIü"Șäñ¿HÛÜj¤mÞ$@NJ¾g%ñy;'|„ñnº†9Ýåu?BªôŽÆ|Ó$3K7 XÏÉ2—½µ½HѪgŸfÅ!#"dTFe-~U*¸!„:(çÆÊ(qòf˜SeÌx 6ƤÃlT‘ø6H?E*Ç]^ªÀ??opîËì56i ûIä®y:¾rOæ“>y™·£öÁÚ•Ú>«xÃMxĹu7^*eäl‹)[óÄ•Î?Ȳ!¯"ý‘t_ÚëƒT» Êºm—å-&4D÷Eb6•*MZœÏ5ÛÃJ+ou,ˆ-–%…GÑg…GëÎ}™ýÅ÷·9Nç&À>bäÑ*}£È9~–kÃò±'dËÁ¶lT·— Õó:fL;nЛ[>,MñVrp|½‹6OêG½ß/§½äís -¿‘“Žë|u~‘ü#î6ÓÛLèìó:o–iŸï]ªYîm>î½M¹÷NfÿÌ/–½7A®¿"céÌñ4,{}n?º©y¿á¥t„iîÚœFÙÓgŽ_UF’¶leŸ–Þ=RžêÜÂ@™L勜¶îÒ‰yùµ³ËŠ·Æª(<Ëj“½¹÷ÉŠ4u1[[o†f¥× - ­”÷ø™¡•"=C;õ?X¤Jyû¬µsÓÏb¨¿@ý`YC¶—ȯ“ŠÛtÆói•Ј]FR_Ö—¡ah™¤ì(Óòf÷ƒ,íÖAwë¼ÙÑÂó6þðɦ‰r»\å²Ö™¡26bé#>göUÕaJ2zcJ6ZOÑ)föަ_& —{M‘ö!¸ù=o¹Ýõ¯Ü°{5Ðp-»¬8»i¯4Ä~FU;o约tW&1üˆÚ°ÈiÊôv_-ïéZªWß¾xwí“°l o<¹ÉÆ•Nx³‰~/±…¦ßúAŽ?i‘¾óÓ롹š.øýuºv•éB$Èi2½žÌŒ‘NAXã%p$~©?ÙÔ 3~ ‹3›Ûxñ^¯ø^ÖÁ`¤øƒ¬D!À—82¤´¸Ÿ)Û¤¼XÏ–!ÕŸ[ÔÓ Lè™ô‡Êùëlã_Ÿ¬¯;†oÎsô¼Â×§ržïM4XfÆñô¬—yæ4^&Å”¤7ŽÙa:ò‹ñ—Ñ/Ò>ÔlÞͤU:i'LŸŸ÷öZtÞ^·P{9^ä'®1³¹’úê–í«vAË\3#J‹Ü\ÓU½Å*Êú»Ÿì´ƒ]{žÙâÚ¸Ñr’L£Î<ò­Êø·ˆÑ;7¢Gƒ¡¦Œ ¸PîÆF²#FŠŽFƒqõOÒ>D6Ã|@¤™ÅÁ’ÚÀ/‚]²i_É—ëÄáÃ.xÓ‡)ÛÑ«ä+|Úuäø;’BüÌ‹qi ™ÌeÉM´3ËoÐ-sjí´tÃi{ԌǯÞ1TAÔþA(ä” éÚHæÂx7ÌE1:Ìí¹öÎ'£ÿõ 3øE4²Ù=2ü$[éÈÅë•›yØþpšQ&ºËÏh£ÀËÿ -ë‹­EÁbü–!""7El=P}‹©É×õôŽ.ü‰ávÏã•Y{ލRü¡&Ø#?̯‡!ytD²ºÞ¬Éª·É’Õe·I -ñž"…ónøƒü+~qOÐzñó³·?«í^Þs×]xy~¬Î!r1Ì>}ÐÆ½ñS™‰tѨbñu¶:t긜{áXþ#,討êÓŽàÙé3öÎL6ãÌ ?Rކl-¾xÁÈ9z©õå›ìÝÇu¾§¡¾ÛÓ0?ÓSßT³§FÎ eGõÔÀþ"drcdïîÑÿUå²&ÝBlN£h©W쓆N>,¢»«tÓö@L7Ï{Ã^}¶'ß}æÚW&t{Ù&I료‡Œº¡éR¹O-]‚àB^,ðêGú ééÍK¥ûÒsý¤Û»8â½mÂníÞ„]¢ê„õHÂz8/®WèUi,ðØÁ"ën*™ÇErÄA>é‹íÜxÁ-ÃÙ•l:&—W##[|º„ÇÃIfˆ2#/9Í)FSµa¹AZƒZ¹¶!›õò©÷<‚{7Ù½2Ä爔 -m™³I^uÐG’tPO­á»î©ïj·!¾ÃNiëÿ+þãå«`ÛX‹/—ŠãçjVBýý¡ -×Úùóx”Yó¹nÏžUÒI‰H>¦uDB'v¹_‚AÕçhÈvP§™9Ùâ(ýéw¦¸ÓëÍšAw0)‡Äˆ­½;S¡WÀýãÅÂn³Íg›Ÿ\+íò;Ÿ&ÈJ~Ø:ϲƒ_¤£å>Y¯îåØâAd¼.öù»?†}ö’–in¤ÄÛl9®¦ý´ž £à2F  öƒ[~(¢§ú qðzd{kŒ{ ÇqÝÏ|"w³3|IŒO¤×¡ÓLŸ=&qûD,±vuuoµP3-[µ§7o!j5–tµ©³N³=ñ¯çؾ½¼/Ú墡ËÒaÑ\P[a¤X.{qæ´MŠÛ9Æ ä½õ ™äôWj$•é÷2ßä©FwT¦zÄf>w|…âñƒA*xL¬vbü–,8÷æýzÏ6;­Z½±ÔEªÑëf•ú;ž½úºPÁ£]·(hð?È×Îfzx˜Já}Õ¼2q\„UÕÜÈ_³—Ã{5%á¾NqWU!;tMë®Ïe«‹ÖIŸ mçÑÙgïp§4/Tp!jâméY#[· I·ÚþDüAšFQ4‰`¬ýäz®;Ô³kªXó„k¿†† ¾†‚÷¦º³#¸º£êµ*ˆ”iÿ«ºU õ¡:<ʵ±á‹ƒéÝ›U[ß÷ÁNI|¶¤xÔû¯ú]$¶þ\êp¹ÅÅ'uj‡#nÝY jƺ›oöÐs­a1÷NÝÕîƒ:üÓÚ”)ˆUÿPӪŠ-åVxÓ8U*ýÏ»/_­™Rº­‡zñ¹k¤kCÞÊΉ“ÏF§u}:Œé®|to”xWä´Þ=ºà æŽfU>.CÆÆ3+­6ÊcñƒdH"¿N‘.)¯w†ìf3¿Pö”Ðì“S*h?¬H¥qqÛ€F¹9€*Sd^îdv™dV dËzJœÎ/ #7ia)>EURöfÊÇ™Y;ý™™tK¹|@†VR¢Èð­YâVXùµéOõæÔÕ¦µØÇ!õ‹ö‹ïNoûŠ¥áåÓ@?Ddó³zÊšÙ 4С·k¹âÁ@„ÎÓ[CÃÈ€Bú¡`‚L¨J){=åã€LÔØL0>¥˜ï½Ÿ,ÈìéjârÑðµ1.öDŠ‹åŠþä'‘öEV°@–ÁZû€”KãjäA–¦K +éõï.xdW£È2Ëï½d›wd¡Ú@Gî  õ!­y1Íü!‘J+}¢r)'@ÝÄP¯ºNïSÌ0qßAœŒœ,üÚÂJä$òy,™ÌcÌù=¬õ»üÒYìÞÒiýö ú šJ÷Ó@/zZŸ¯ `⻽LX<€ÑØÙ°¶Y‹‹@–õ!mg* ["» ›U§É¸«ò?H‚OyÑ-gùÊ=öNè·]<ëfOqToOa@€g2òú$îí":‰^I^ŒúÍ’¾ïY=–wj8ÄOzWê/@¶ßþ~§pö"ÜÙ–¼®´üXŽSÀÀ'ܰ±ÜÝÝœƒ‘×nW^ùkŒÇ¥Ã}ð‘;ý¬®2âc‘ÉȆ†-ïJúÐÝÛfsw{ö©Óp¸û%Ô!õ·Íf˜U'ÔO¸K^aÔóìTVN§j,Ÿª‹t¼6—¿Iy˜K#4Ó0ÝK žˆ|ÿž^&Q  íýþy±ì"ŒËÉ‚'/á²Oõ9-è¬u Àb8hùäÕû}GOý÷6°ts4Ì⦚ O*êÄ:+ƒuk¥Ð®ž1Eç5 - g™ ;ûMÎ_6Ä¿«²¨ôWÓ{}´Ê¹•‘ŒêùWü"­ÌKhX/üßv(7I½˜Ç=9£Ñì5¼ðÂp|ìäV³}ƪÉ>FoÌ-7l6µlîè5®hÝv·/WíÒìÕ1oFÞþÀ‹ª=œª¸µ¶UÒBž½¢»;É ¬Ä2gCú²LS–œq‰†¸Ñh C=zŽºb¤®xÃÁ/¼sí²e€á…ïÿ%a7uøªL»Ã›]Éá—#[j^K¦Œ®€Ú±¦<Û̧9y}×Ë]ff‡”ª›~"kҽƫëgÌýØÂÌÒʪ,…»Õ6®ÍÀhHFWîåÑžj«+8£® -\MíN¤òÑö¤âtœÞ_Ä/æêYU€©ÍÎëã½G'áÛ!=—ŽŽëǽ¾+UØ®m t5î¾EpZIµá÷ÉZùâbcä~y&š‘2O£9‚ ïE˜f(ªšœ^åã?(…*åxÙYKžÔkÏÅÎÞ” …u…r¡û…ø’ÖØ¿â?êy€eµ´*ÉÎÍÆíü¥Ú>'åÕþìš›Ùí¶´iì‘–› Ì“-T‹®ïææ±KjËù1c²yÞêò|¯-§|ºæW­v/VÜZ#£Àl#/cãRmÁ¬]é¡i©<Újsq×9Îk2‚‰a‹mŠáÀêˆ2$·ÿ µNfû\}FРt9îf¯ähä®Ïýç´ŠýÝO6×ø®»]½êd¯ÕŽ•_z#Sðº3#ºÇ ]ïËšÖ;wlu·ÊHäé"ìH%¾¿€ægU-ŠáÒh‰Íh74•~Í-oˆ_Ò¯¿\ÔÛ_Z‘öóA¨jѺsƒ/}#oÖ*GÛsãüððÀ„·H-ƹ¶LqF¥+%ãfÁ´Nl5^µãº¬ŒG]¦ ÛYÌ -³­$ ýã|þiÜDùŒA 2(ÿ*ÕêÜkö¸+rTBÙ÷›Ëgn,•gkøÒúÒü4ÿ×5F9€í3Õ댞G"r¢`"—¯;îåŸ7͘ ]Kl{Jds«òÙ¨÷.ÜÑ»üf¤:j‡UPÿ",ö„$K¡aÌ¥åÇåæ§5¦‹û o²Î“³ÇVv¶&­Ò Ñ|œ&ÍæV†ÉƒÙ“ f¥b -Ý`EøGýép4Åâz«•KŸÊÆ}ïäÅ/h÷4GÞ„{þ¸^Ú¹³ƒÑsuÌ{ÈR¶»%}Éœ[ªÛõlòš,8]b¥Z¡1oË›*í•ɯ0fÍ9¹Ÿ­<š¡MòÃæ+½SÊ´ÀYcºZ©¥ë±éåš½ÑU┣«ÝjýKí/Ò>Äc7+®¿O7üs ›§ìL÷î[¾lmº9Ü®ªöÅx¼ÑD{'!¦l¥Qeqœ<ÚR]HQsÑ1Þ˹âDœm5•e^ñŠánçÍB‹gÿ”v¬Ís¢z[xüQuÜmçú£•Á‹£Õ’;Vf9;J^NõKå/fßË×:{îºì*ˆŸVå¹8ëõ÷7ëW'8q–ÊózÐûÇÝSæ<Û·J’dMQ_ô»ü{srë¶JϦ!)°ÅqF¦Ï9 Ïš;¹óý8öØh,!3ÊèR‘¢’#AÁVn†åt=FÁ6€(X¥Êu+_Ê¿x†G=_ íG€±ði5–G×¶Þ¾_ÐÞò¦ˆK}%s;ÕÛq‘¼ßâi1¹`¢AÐþ£•Ò1™É ;bžÑ}žOÛ°¦M:oÞ½ÎnÉ­+…­d8ÝÑ…A1ñAq ±)ñvPœxŸ»l–Rºå¿øAnÐGâí„»ƒv?y<:8ããÅ_ÕÔÕA­i¨“kf-FðI -gñKŒY2d»Èa½Sƒå ±Ë¤)ìpzÛÆôÄ \a¼ªóÊhxj-©±_ù¾ñ€^σ٪“‚Ö@û——ÐêKæ›î7ÈÛo´Ä¤/Í2ù”rùKéaî±~í)crÝÖ¥Íɵ£âÞâHa»ÄÏV7ü9í•r¥<_Ir ùâ2ä#Þ½¶Þ\®üÁØJɨС?Á§&ÇÀ9LÆ™¹ÌR›Ro>ô±º>8@E—Í÷¾¿p‘Gïöj#=¼/5º†ó˜t wcw ]M~®!€|ט`¥®ÑÍq¾6ûOŸm.Qfú„æâÕØèƒcÉüm®ÎõÁ[ìÅÃáQä}šÍ¸GÌ\/K·1¦81Ç…ú2|îÄY.TNáéÁ±ÖâÉK«®ö£jÑî= -¹ ûBrñη³Ä€“j¿¨Ž{q—w%>:î"Æ:.‹•þâ@ÕtFÞÖÍÃÙuêƒÓê\P|#í:žš`SRæ-…ßÜŒk[‡úá´‹ÉØ -Èh’¿—¨üÆmKÁ²?¨zü¸´ØÞs\_t2·$>\ÚúÄÐ성CgðmlWpÚ,ñ|ÿ áù¦woGiëf©èåâ/|_n^×IY>ÛŒ—]ÝjˆÌ8 I]ÂG?.Øsî:KÇäX^H°öVc—9íGÓÊéF±ëgXŽý*r¥7øt bMÓŒTïLº™UK `­¢v&¯ž‹3·Á©}Àµ¤]^>J-ñÓë·æXi‰÷$jÕ2]Á6C”)6¯ õ›M$/Žg÷Îf¨I>ðöž,¬2–¸šU4®Àçt§+²£éǧ©â9\ +Ÿ‚3(êD¶sä£O”FP¯_äÓgÌ–§ub­:íª3èä3/¾:R[è³VK¢ä}3ÚžÍv¾˜k<öËnCçq©ñX÷¯N¢BÎõT¬¿˜Uáë~¼¼®ÎLpHW='¿Sf:éÎ)¿ä|r­ÐÒU#—x:ÞƒI/ ¶ûy]g­­»p5<Xóþì0:áû$—ÇËd¥Ö®ŠÖb[6ï)ßÄUYkñaûƒÔAŽê¤3Eê*l×ÜŠ*Ô\T9Õ¨Eô®Á…Vƒ3Ÿü/ì‚Q<¯FÛWóEn-áeßâ±]¤1-'/Ç>\çì¸G.¶Ã¤Ã6úT»>/M[Q}¶hÉï‹ÓjíëǦŽOMÞ sz,4úy¨Y·ÕJ¯Žim½±äš *Í•O• Ö2•™ ×*%Ú£+%fë—\\ÆìRˆ~îË"ì5Ûo¥Û8üf’3%OÈc…Áõlѽ c“øá†v›ËKǼvƒ«;F_®gÁlUóüÀ¯a­×µº{VÒlµ0ég+L(VʲÙ(Ÿ[—^¹æ!tIN×î¥f¸Yµ6;Ù%ZXN3íB¯î …^Ã<äózåVÍþB[ÏÝbA\>uÌ„‰¤™:öZ«ù „l×e‚!^™%D“ˆxmü­j€RµÈj¥¢­ò<¡7¥ˆ‘¥ÖÝ ‹-ˆ‹ò„ -&ýÉåß÷R5?õ‰Üz+Œsh㘮“±]¦·Æh÷pÃò ‡î«gNDgo"HY?~!W¯øZp‡Ó[¿(Ú$kƒ¹t"ŒFí‹Zê7ú:Ù®2ø¦^ž_°î÷L¯^ZŸ»¼Vìòf,ìÎ,–O¬Ê‘K9¹š„ˆ#+Í÷áëvidoûù*‹—å5Ô­Ïýt¶ŸSîI†Äù”s'Cv&³”d•!‰Ù1%zeÈn)û ‡9‰yªPzµB¾û–é -dº·È䪃ä \ò .FòQ‹›äñ—”H>|K™VS–xÊ™L>#0N>ãZúãÉ"eµLyzÉgÒOLü ÉgN§³X’UfÄ 5fƒ˜Á†Ã˜Ä0Î[Õ?þ(+¥´@%Nÿø,‚LÜÆAf#Af~A¦Ÿ3A&?Ú%Ÿ}ï%’Ͻ—OÑê)çnòyÀTJ“NaÄ”•šr³’ϳá§(Q’Wø…AŸJ\ÀPüYn•ÈÇé‘§¢öIáŸÔ£V9 ÿñƒüw%¼4ó2Rv_®i N» Ué¦Kà  Jq2¯ï>øÖÚƒÌ*ó™E/ûÝ/ƒLš‹€ ÷Sº“Ä-N¸„:ŠiI`ÁÐ^›êÎzM®Ïmì·Ðs\йÌãLª¥{È{í{órÞ´üjxÃ÷ *Š9f-Ëäo4ÔÓ0ƒ ­Ï÷-²*²}¥þÝí¿ [¨³ºó*€\ !súîƒÇ‰{å‘ijåהך±Y1+m†q1˜>yî0{VÊwéq¾Àúc.5{x›ìï²s}Ý:4Vße¬ñƒ„ƒy“¼ºQºŽ” uEZ÷áeó8ü&qûÙz”"¡™ÿÛºE-£!È^edWdYlóÚ¾âS<ãüÛó¸9¿Óv¹Øò¨?ƒâ=2^Í{ ¼‰ÛC·ÎG/£2‹Ìno¾­é2´Ö&„N™è²•{È9Чõ“ÐìuR'×;(½€8´Þ6±ØJç?~ÅÿÅhB ȘŠß7tR¾ÛËz(&sÐârcd>Ó¢wdzÐáF$ï[´jA t* fé¤põ -ùú¡íË©ö/yº1úAγ}›=`B:U§3ó(kÿÐ^æÞA2¬ýϤÑò‡~©³ó -O|7>œðš>¿ø¿+­7€]H)„•¿§—ÃÉw?y·daôšÝb_—"2!¬ÞÅÏ£ûó!û O'ðŒ ÝûnW9àÍMqoðn-H.f%™æ ¥’«i¯áW­î+îº1Nkl#}Ø…Ï]Eé0ÝiRÙöìyÍXíÄEè\õá½ø¸b|W.49ÐZ1i =ŽÜ OŽ< 0—™¡ïÕo’øÉãvË= χ½M²®Ÿ¯¿ì͵F›ë'ZÎð¼ßZyC½˜UzmîÕLÛ,¨ÃÚ¸¡ ë<±{R:–ñüüž°bøìJÂ=,ëB§XsùZØsÓ}ü 3wÔƒfðV«²±i§pØi˼±Ó6ša§ÃÒŸ´zG.Ô¾œ¢z7Æ£–³=µ×+¯ýÈjnÿ³Ò­Í¸æÉ§Nßýä]á¡%ËÍGÍŠ}TÞAheQj¬Rf1²káaÀSþÅœ9¾çžn`nÌÙÈñwì&CÆBÀñ4=:ÄIó&·¤ù]ûšb¿SÖé“ü-À/:A2á†@χ—pØøô¶èlÝÙÒÈUµ½3©¬Êxµlê_慎P¥fÓ—¼³òð¢tñòó+ÅTEå]k „p&~¾Ï¸Cn8Ҙٸϊ,t&˜³}œMOtå´L¦vÉO¢Y˜D\A›Dêó<‰´q’2Óú]ùM²›T²W¶=íí…æý »Rð°=œí/7oÈVA¾‹ËÙèzln6Zf#Ÿ̨?e>øÌ{ñ^­Ä'ǰÎ9Œ†Ï¼Î¨Ïî­1³/9ú”Cº:,:ÓÆ¤~˜(V/}ÆÆOHÇÆµG ¶YŒ’Üâ0&è|œÒc}ÛÿÍs!õ^— -µ:cm´Þ\OkËBÞ°§8'›ó±<3:©¢ ¸‘£L•Öở¼xÝæáÖ"ãYÞjÅŽÚ-«3l4j±…F‘`Ê¥÷®a3mBÇÅD%¢ÕØhFþhÕyß¨Ï¢š¥œ¦Ù †G ŸMŸrÚ§GJò¢>*šü ÿŠÿ¸­€”¹÷ófÅõÔPX9®™VqJÊÆ£Ùa´Aðž+Û²ZÇÝ<ég±ã Ÿ¼~>RuQÖwGEæ`¶ô¼ý¿oЭɉV\Óã%jÎGVF7F™ÎzKÁíu8Q?™!V×»¤Èvaf›"Þ†Xmÿúÿ"‚×íë¡O„»íi¹ëå`f¥3òt2L©-ô>ÑÊÖÛ -‹ãÃÔ¤iz"AÓGÞ^æoò8vÖœÃLåÒ)Ðu©MÝ 5~µÌÞè]•Æ#¨@s‚ŒÕ!ö»WÎäay?Hš­VËý³s“åûÙ#Ë*"ËGñIòEã7WVú\¯°ÜnÏÈu!‚°¬bìÆ=K*:4' j3sµY’„d[µxªœ÷ÓfÇ^/li§>éëh”™*·6!Äc)í=vc4K5&ijHwúì`Ö dRÀ‡v_ªÓÇ^4ÓãžRF‹½ÖyMõZ×íµvµkON¸øËó?È×g©ìýük´YÇáÚµ·‹†mJ‘hèdÆ’ä2FOæ8½ äÒá¸Mi§Ï˜p·f΄q¤›u›Ä2¯Nl:üj2–ªC†·×i÷I±ß¦û ¼!õÔzÃì>gã KœÌ'±ª}òD?2D¯YÄ*S»æ}ò$ÌùøÅ¡/f‚mÍ©lÜÁQr­Ÿµ cRÖ~ëwD©ylLxw‰÷f¬ˆMØÊ )ÑQ•´¦Ú!]‚¿ØeäžÂµñýÏÐ?®sž¡Kä|D4úávØëÝö¸/”¶AdŒá¶CûÑ•,úƒà›¶ÝÃ7¸»Â7Há„{¯É÷.äãzw}¯3yýtÝ{·rPÛ0üO°>„J¥}$Ûl"T -©H)Ú'RÔ÷´®ë¾×=Îñ¾ÿüæjç&æ<æÁ`»Ú%#wî-k¶C«Çïјœ|úÒ÷qQΧl™ÿÔ3>ߎ6ÙY¹;ûFÖì 72g/z2eKÁ™‘æo¯Âð÷eðÜ–1ðŒ]±$»è^ÚßÑê¹vNãÞ9©þ¼s¢cçOãÎðÀF_¿ü€¿ÍN…zÞšéœvÖƒÖfÚg—º$jºjætEÙm-^ i›àóñ e=ÿÁ2‡syÄÔ nÑkõ¤-ðà ե¹ˆ$82%hqç@Õ0w gëJ¬TÆÉn¥F ¨Ò—§±d:Èíô¸"Ó1ŸGÖ Eòs‡Nÿ‡ŠÁFaü/›—z+ÈÓw‹îêàÛu:ŸÜÕjÖ1,ÿ€R3&¸>=!q¦þÀ[TA…eç ¹Ø™*ñ†ï‘ë>v8·Ì]°í§üÀÊlõÝ­ -Pî›õε†w‘{‹b‘Ž$áÙi³l¿Z¹c{ Ó6©—³•x–K“Ö»nù-{áÝZ¶z¿D8üã±_"“üf¶¶ kz5ÔÙèïTé¼·Ä~:;2¬4~`ÔcoáÅÑèJ}¬+ûž;I–'££]¢MW÷:ÓÒò‚„Òî §Óž7ÃŒP«mõ(¢õÙL-¶x2š®ÔðšÅ“7¼qµÞðÔʰá çmC¼'—$ÇÁ—ûY6Þ2;ÓžÏí4¹,ÆVõ¥É‡4R½Ïî»$M£MÅÝò´Ži^@Z£mwgv¯ðdÛÀ¶Ö“©Ùâ`oÝü±¹]£a³Üß ÿú}Æ]£J…pý L½ÁMƵk\×ÚÓá½zïU¡êúUílÙUÍMÕ ÝÿaN¢ç¾Õmu^Æþ§4ß7d®.857Ĉ»êF[ƒg&!®ÜÜ•ÁJ½Ù<Õk·V£[›[ƒíµR ¥Ô¨µÖªŠÆÊ¾ÏÝ{eÞÿ¼+D›­@ÉÝA!Ë© PÍÊε|,³Ë P.Ðdç,m«„QꛎWêóñí ±Ê:t;œ+~EŸŠ³³7äD1hôñ ñ¾w:VÉ:ðÐ+7öÚ ¬ÁS¾PyÚ ´B~·5}HYž õpU.¯½Å$Û{VÚF‘S6õ}q_ìÞŠÒÍH‹Uë §ÝGÕólÄ¢U¸ns·ÂDÐÁ wˆŒdÖýhìÿa²ëe–õoWC| ñ˜Û%‰6ô‡—–Öét*ÍvÎVëçܾXE#8Ñ»b¾ÌÊÝ’WÄz%hJòEè À3ׂㆪÍëb^˜R['?ß­ßÎ#à »B† 4Ö2ÂÅ÷®Õ @P.c•ýÙdpÒ£Â4ZX›ðEžPè¨<p)äÑ#ào,õæéZþ+×Ì—¶¨ù.îx¥Xû¬Úà˜Ýa…ÛvEäýLä»ÙÄ™#Z0 ЈÍÀÅŒÉ8ãd4Úp3´k&ðÉКY˜•ìÿaž‘ÅÊ~ï¶½fÛB0Ð?$Ÿ$ŒR †q”|¼síÅɧZ($ïÝHÞ‹LÞ£XLÞ}\KÞ„ng<öÉ›ìÜ3†IÆ.[‹eÍJÞTµ™Av2Ôì)ŸOÞ4 d°³Œó.émàÇ‹óèÜS8Òåx±µXzuÿÿIf’…ɾO ø6¿¿H>)”&Ÿ-%Íj'Ÿ^ÒK>5TNÞñHOÞç““¼/cÆ0ÊØ¾3ÞÅä}m×2¸vÆ Ë8ö’÷ “Õ´5Nd0ÿ_<6uŸ"†±Ìfý0šTÊÑ5,Õ¢‰žþ þ%Æù,8·ü—9ôý8ýÖÆ½j -y4Ê—|îæ(ë±Ïêj&¥¼ùÖÁ/þ3ù Á|â4ÚP’«µ— uÚ/þ@¢¯¢"Oñ"±OH]HñŒ´¸æTVæV¹…¡Ø ¿1 »’º¸£è=ày+ðÕâµïU«Yâö¨\‡SåRQ6Ð?dq¡Y„:•E¸b¾ÏƒÍ½÷Iš‡¾fh§¹pO¥9ã-¾våëø)ÍWzÜèè֣ݜ¸‘6Ðat\ÝC¬±yÞ¸“Ð ÒCX zõ¸×"zãàu+|Dñ*°+í¼H"è.0ÖŽ:À‡‡˜’a¥ýkvXú«Añìm†p²ÛMZ…]9ûòí^r[õðÛäþHóåIæ…ï8-`Ì·¼œuu{µãKnG]Ä@î4Là·~»À^¡M08Ÿéíø/%ýˆ:’u0ŸÂfŸÒ~OéÂåô?¨ùÌ|–z›@Ï{¼¶«ìv+¼ƒˆµ=”Ô±{®»Í$(Ý×a§ûY£fáíÄØó혯KúY„Ë,ÌËî–×y¥…h™ÿÞm \ËñÅý€áb€‚ÂÊΖù’Ô6µ -þ05„Éòäßæ—´P¿iáØkxào÷\Èúx©ø'}¬÷+wwöÄÑ$ÞŽ†°¹JYVM-gÙ9b«Uqѳ7À°oƒØph iºôÃáâ\Vë’³ªYÎ~Ùk·y»f¿Ìàü*™vžÍô…`<ŸÝ•A -ÖU—‘nïPïÉÆ]ïéVøGÖ•÷k -"dø¸»Ôñ&ºû|ã¿×‘1»1÷N _ì\âºÓîíºŽu÷éP¾”³Ýb²vØZî/îâ´ÚQ‹F¼áçí@—L½cNf‘iÎ0ÏtŒeeåëonwÓ{»Ç[Ï ðšÆWÔ8Ýá‚9’è0ÙK¥û—`"WÒÛ?¤'òÀéðl>Èú|ÛóƒµiûB­8ñ*Gs–^® Þ¯X­t³¼kðZ*ì÷­‹æ]„æðoš¨ ¡3|–Æòsú -%Qâ‰Æ 7sº[…ë)”ÿ'²Ð|¨§ýTGrÜ_/Ëéx²loÇ0¥Þ¾\G!4üåòËøÛdçxáÚè|=ŒƒÕ‘0vº¿eš“Ýq0ºU­‰42V…ÛqmI û¸¸°Â}ެéìÙ óF6ƒAúçe4õ|8èhÅ ANå.790ß7ªg¢>Q›vw9ž<èí(ÄÇ·jyŸaÜCZC|+J2Ö%QsgÅ"s—/çÈö9v¿0kwiõö~-Œ´Ýñæ ]mYÀõbnVÜHR-i-Y ÕiïÌè»ÌÈæ&2€µ˜j[ -)Lýn©29–‚Ƥ޻ j _“ãéÔæFÆÌV†ñu­ È_)–ò<ÊŸkë)³°V‘óÝÏÖbî&oeLŽ’{Z¿œþá?ûÜ¥ÛãsPžû‚ŒO·j4UÖ/ÙçœDò–ôyË‹Iû=3ãûzFžV}07MXÖâì«Önoõâ/ -ãÀ 1rãZ£î±ƒ‰kƒRl¨-*ÛQå‚@.¥þJð~Þƒmµ4¨dI“x„çsñˆÔü ôð¿ìÿÈr¹Q¾ß,âlånzÕqqâ¢`_qVóo‹;½¸nMÁ|ÄúdFõ&–¾±¨­V¾dGÛ¨æjÛ<Ç“FØ´Z’죢ô„k[^¯®¸´ïجÇJÁITYJ>l¾7ÂEëܵ³Ê ­˜ÄúÁ¢® íÂÍËø¾ìÿáèñZ€ÍžSW³íÕŠ'ëjÉ+Þ¸åé3ÁÀ¹~çù™UbƺÛlš¤|Ö“ÑÊñU˜_GS+.ÅçKY±§œÌÝå‚´C'õÁ¾Å#ƒª$ÒbƒçEAõ&Zÿ¬œ~‡{œù™×yóØàŽð˜t™þ€ül?vùÙÝÛñÿ!´€xÖªkÇîúÌÕ×®ê8µT²¡Æ-àm%æ§g䱤 ¨Ä˜ŒILJóÁv4¯ÖCj÷Ìr%ß]Dr±=L%¨Fåµ\›¹bK˜ðM¢¯Óµ>ÿ˜Ác{‰·„Íû6^ »Âô»ÂÕ1ûž#ëŒ÷ GÆÓÿÃÝáÎÙÞ?>oœíUcÆØÌÁ¹ºrçOyyNªœßó¨Á\J,þäRŸ«*"棹ßr†y²k,Î’§ÃRë¼Äq Ê S )õC,nð1c\Ò½1? G©É] 9›¯3>ë›'S„ñ:SDÈ!ÃOÓÃK­³ñ‰ JÐØ;ôfàr{MkúÚǪ=ðòBƒÞÜ àD¼ìœ¨õJo­êòhÑoÃïsoäþÇv¥ÊB:ê:A ŒûQåñéw¹°Ä”Wãè®ÓaÙöœb\Ù•˜â9žõåÚŽ>ÈrD+õ|…V œa[? }àÝ—mÚTv\]ÖÛƒ‹ëÂ5*Ýa Vµ†_™cÝ+§—>: z•켦뚥QãÓmMµÆx@­[™ Úw‘ iAµÄhGµ¨‹ûgMŠ\O–Ílý²!°›¾*]ˆl†­‡³UÙd§ÃÍ–T2ÅNG#.Ë!wÃÑ`¤H3.cvÿá‚[~I_Ü -Ûì†2cfו?LYUʽªÌ6éFŸÆ¨¶Ôç¿5ØÍtB<Šg‡ÀzÅ þÊY þʯxërs«¯Ýÿ/oº·ÚÞúâ| L†u*#Ó¹IKŠQÌ‹¬ªëÏö0=Ê2d<àppÞ”8í–ú'|b>æÜǺ¯¹ül¿gR#uŸF sC³ø+qŠ>ØØj1Ÿç0†X²ÓîSžm~@¬÷Ö6ÿ°/‚ÕÕæY…ç+ÁpfK8_˜ÎV¯†¬Õì3²ˆm[R½$N÷b½ÿ$˜ŸJ:Ë£Õ+L7ãc¦Ý;£-}éŠÇìk©|@!:õ$»c$GÌ¢‚[§.‚9^—Æ -•ñ°ÛçÜêyù:˜t ™4:@Û:¨ikt×6ÿ°×6ֺׄÖia˜Ùh‰[þD÷ô»¤F๧l«3Xˆöx‰·½s‘±Cƒ-‹‚‘"\ì _Ì„V?̜ҞèŠ|”O¼4›åçp„÷ìæÛ亥î6AšÝ2Ý'Ñ*¦ ¾5Ø¥7C®ä#êz™ êfI ê›"jßvV:Û ­@]ÿávmÙZ Ÿd¹Qb6Ë¿¸ñtŒÄ‘m=éTT;ý%ÊTyÒè[¾odk³0…®¬9ÏßdÂ*ݦøGJ8sÎo0·U†Ãfaä/š>Z7Üd»úÃ>Mg>Ÿs£´Ó´IGZ*?àïúƒIP<Ë\‘ÞKjDêGù®´^öj óµ ?mÆÞÝ~ííA{)“6U/-Zï'í¶rpjåÞVÔä™Ã§±ÛÕ†(ôñúþ~Ȳ¾ºlàVíôÜ\j£¥X®¦œXÑþ¬Öˆ‚Õ?,»kÏ -ë`6m«9uÄ Pù¿eï%G0Þ “‹§RÁÊžšCîïí«åt¾×ÄÞò‘6*•"X?D|³®Ì–x½Ž]¹Ú¥XÖÆ^Ϩµ¦ § Á¡ªåZQÙh…ʬµ+݀堧™çåt åËé²Ê—ÓÉÂ(§”oeÙÞÿm~1¿»Ô† >ÍÉè?…o[÷˜S銑/çÖÄ <(!F9z·»7*“MXÕ*‚Hz–{)ô.çü~¹œ pi£}ÈÏà˜5¦Z*Ö^Vq÷$÷E¯pŒ‹â®]*Š×€t ¤ÚTª»¬õná,ô4°¶Ø®ÀšzÜü¡W'[sœtSYQcY «ž­nÙú…L*5ƒÜzéR|« qI½m²©‘Dƒ2ƒ®E¯â†Å?.‚ǪÊÃiuó†.}ˆ-¨-F.´SÏk½Ó*¹¹CnÖçâVºBÀBƈ óÇ1@”-@6ð7@6‹hÆ]H¢aþ¡š÷ªžíWx$,lBdå®ÀPøm‚wgº?Å^±¹µµOMý\ŸÐëÍD%þv¸‡õôú‚N3-L¤s-¯¬zîq3š91€jôÑ ‹ÎHÕħ…ï€"çŒõ H²‘ŠE96@ ø{FR(ÍÄ2Ü/Ù¶|Å‹Äÿç!ÈïSóÑ•Æ*º‹ÀÏë¢Ò,ÛûB5Àwù£óqÑóâ¬çA^ÛYQÎL `M» @#4”1nteÚÊp:ÙÇ„úÖ`;p?c4ʸÎíì2Ö!@w±RF€4f¨ÃÛŒ]ÐD³ÁWþH>4{È üäSiyÉÛˆÜä]²Ÿ@äþ&éUË%é.ª'é´…%é`ÚÏÕ$•ÐeÆd›±?%©œ 3:I’2 1Éþ–ÙU3^í$eÛx†Ê'vá8NY¿ - -{Šý6þ€qE¤#ÓOÃ<‘Qcßÿ|,% ÔêgNi/ù¥mò>"Çä=S¢äÍóÉ»Yj$ïâ€HÒ›“…'|f°Ù}·%x“ôÞŽ2„4ÃÌgœ ÄÖïõˆïí×z‘ë¾8²Ã<·+AŽ}w»|ýç>RGÑ- ‚Bj6„:z…AšÜ•àõÇ·›L÷ƒ]ò¹±Û,d6‹ÖrnY/ß^ɧZ/&ïÛJÞæo<ù­ƒO3L+Y•N»$'ÝO/nsº¾@þõxîJ¹äY–òÙb¡Z+ÆÒ ­f=VñíÇð6Âé¾µMP¿?˜¼†Â% _‹çmE ñ¸ÿ’;`?RŸû)ÀO·)Ðd×ÙGkŸýoµ¹…’O ”¿Ûbä#™„™CƒWIFOÑ„f±<Í[S¹Ñ =D×z‰&K& ïæêŽü¹Ç…1x7·VõŽ 7$XòôíSçG×Í{ã\¼Úã|–ƒk¶÷Oãœy?57£àxãùà8-â·?R`ãežœ,бIs¹Ï)Íå©G†“O7¬%ltžoNÄulÀd;ŠÂðÒÞã{W»(½øV­ç¬ÐÄØ»9šr¼±ãvuM#ºöq7½xÞ·nUIëgeTïžÆÓ¾pD8o¾B‡­Ÿvã«O/Ü‹ó‹ç¼ÇçÈâ:ì¾õäÎy›æ¤ŸæöÌ5ÃŒÓÜ*Ì¿„`^z\+(ŒÞÛÆl ;ðÈ[áÆq×RÄI—ÊëaG?©]lq ¤sDÞäî0cÈÃËK·ý¢¯f³˜Ÿú³oK÷ªœ˜ÇGuW®¦Î¶ÚªœÜáôyÚ\¢íá?¨³Åÿ! .ñÒœlÓ|™:¦yR¿¥yá¿ ‚yF÷pü¸SÕ4;’oàq \dX‡N“6Ú8<æÅÎþ5ý{òQäü üÜ´8ò¸Ò1ÅÔg"$ï—õÊ2J­­|)uÝs»Bÿ€nS.ñ›É¹"¯ÃV]]ë>2sâ*±rÌÓ`·JšÓËŠš:OûÓ)”lÆãïç'Kñ¯–Јñrï5 ÚþCšo7wi~o¿wÛÖWÇG€Œ×÷”Y/ÇïÌÏcµ8=SÏò âÜÛ .Fà^ÐÁk3 ÈÜÚ8µKγØn¬R Œ¬h®JØŸW›µYª3°\£=²²èlé-¤,·æý€ Í–Žõð™¡iÞ%y>ÙÅÌXÌ·³.9vøÝßüCšW››,BÝ¢²¼ ƒ×ø| qåù–˜%lõW†Í½¤­ø:üeï,Ö‹ëÊ^Ξ6×>ÖVå‹K_ƫˊ‹¶ÃŽÍ/=”ž«9^0œ™ß7TÍf€¾2žÜî`,ŠàCObY§OeV[ã3Cc“(Ë©¬c–)M·ßù‡lwã‚w7ôó•Uóþ†Ê¦”}[eÝv@Êk¼HÎVL£êØàÜ/÷ýûuqêâùß|Ì;bÌÎpú½§w¶·ŒçGA ’Sº½÷uà=iãΦ[7ÜLËeè2ñ÷Fn")¡ŸwUmPU+£½_¡†ýi¡0òÂ竲¼V¹éxXgÑó ·=—{¸ ãoÒY^WœíVc ÊÒ‹‘/®ç0ú&J7¯3+> ª§úJŽ -Ú¦ïChoS1磓=îS“ªˆê0zªã+^±~ÀqÛ¢÷£;í=Gzu cHš„¹ÈØXC,5—_¤e¼çd~ -!}컣©°=omjnÞ¨“ÌF¨Ý¯7©¥ÂÔó›_̶Å4¸ÏÒHo£ÎAÏëÓ«VìÓá’ºÉDÞtòê)iCãk6Æ0ÖèŽôI›>ž2Ä»CCYªöF¡¨z(¯’NéĘ̈hÈùfmñåûf–¬1ÿxuÔ»íæ‹ù!YÀC¯B}Y )'¥WȪå[KÅv»óéÍáfOl®V85ôB›Xj¥Z˼t¯ž×—‹Ú|£1ÚÉ82êËüpÞÖ!%‘õ–B¹&.;¥½ ç¥×DâÏ {െçx HzÈ1¦‡ùóËìèŒk­GÎöl/”·®Äÿ€ëç¹G­¸†[•ù´2F,<{® ÊèA]QÛªuu*åž³IÝòíq€©›QsûQWé]‡„ÐŽkÕHeÇ… -r>…ªR¿‡¢Ϥñ`ñŠ¨Ð»lÎð Œù% ´JCRhµëãŒÁì‹!ŒÕéìp «ó|ë~ÿtS\M8Ǫ¤]¦px¡JHÉÄà´aô„Ó¶qÄM«í·¹Í¦cm(ÌGÔ^ ɸUèô¾—ÙÇî"m››‡Tšî>ƒÊÐ)‰ÊõÐ.P˜E3,úÓ+5ã#xéñFÔxñÆ£g‡¼WŒ/úíüm~¹¯ÄÖêVS¯r³äÍì~áW›NŽ\ž”ü}Çh¬ -ëš¶C5dZõg=uRä„qg†/ª¬+v|Ëë`ãHÛ“¾øT+r–ÁŠ z *Åú£ÔûÈÓÀùéõ¹çê£qdrÙt{Ùtwﲩ§Êþ%û×bªÿðrj¬‹<Ù^¢´^¢[Þ¹;™É“qÜž‘» ƒwšVÓjCŒøèÜp©>¤ììÚ©2èO ©¼á—ƒªGmÄÆº¹Ôték÷OÄ?º%€ÇÔz…[Üi”}׎íÝ ³>ÎÈpº2œ¡# 7ë 2³Ž¶ÿrƒÆ¾µgï¨êÞ1ZrBå,ùz¥æ¨æ¶ôͶ—›Öðk^…‡+hS§ÎÚnh%/¯ÄïÙJ£áàã5qdÕLab|Výû+ïñ{xâñÚ-ä¨Ö9eWRTfs.3| az»^Ø+oœlvî£7z/ ­Œ—Ð+ï†Ú—é?àos÷Ô¥_ŠóãÍc‰H+ÞòË1¼£fÙ!ßÔ ½ ¨ÓÊð=šSLQù\˜¦ìê & ŠÔs¨,6“ÚD€ÃϬ>c›Çï—³jÞ‘uZNð²yÉy1EÞ{¢wlÒ ¥è(ÔÈRT“þœ©f/©QMfÃQ#›P#£ó‹úÇ©+¾g»S1¯—‡’d:R°&e°ƒn㜴šÇÏh¾c…‡9¹$%ÕÁ‘ÈwÄKê ÓW"ôO<âçϳÎYooÉ:ñ|ÍZ†Ï”:ãko0Ç´2ê|\Ô©èãätPᚘw>þ€D·h• £¯³„A¼ÆD¸¨NˆPÕ?ôs©oÛ‘5r˜ø)Yužì›‹6CjƒgTÏ*‹Âå—¡$uF©x¹‹BY­þ<ü ú¾ó‘³/®Â®÷Æ„óS“)—”U¯š£·ôgO”J!Ì艼Ô*Ñ 5_t/}œ¸¶t̆ç>f#J³«¥½S6õ*–v®£?² v{—õMÜ0Æ«R+Õf¹ÈÇ}+üRìÖ¸•1è²›…Â0»•2èíçô°WMûÝÌÑ5Á —Ô‘úžxÌlïص›àdØ+a«ºŠ`9õÈw¹C![Y+Ó-ºkàŸ.ÈãTÄ7JlîÇÿ°µŠÓ5 +ªuԂᛂ^žzØø1–ª -xè|²1〧Ê•ËëVÊÝc‰´KpOAë$=&ê+}|ÓýKN6—Xt–òO°[t·ëa±/Òn±TiåŠý^.ö«¢Tìç&£?Œmc¡M‚t?n”s0->ižÕßhOÅ‹ ¢§¥ zâ€Æ[`6Â4‹|¨[Öz+›Òºî»E<îA\@I)<2zX­kPF„Ž/¤W.Çr¡!†f¡1`¼üµrŠóm“ªæîøƒÉiéPÏuö­`ÖÒlý˜uƒÊÅ?¦×Bc2bè¦ôŸÂ7 öˆÞ5'#ÄJTÑÑ ÚIŸ|4¼åäZ J‡=D$ȶÖý5(½µu¡‰.¼<Ü·Ï9ÝŸ}ïPEµc Ìs/ÀÝjÀÏ* àqÊDa0ˆzb¦_o‚Ñ_¨¡f‡1ɧ –<øcLᣡt^ÂoÙ;Ëaö#>õs =Óv±m͵¤Q™¬îÕNés.ÛÉ`_±‚W8—»\8—\€èU×!I€Ð¶@ØÏ ö­@ÜÕ2@¼ƒ6@B] - ©ƒSfù­'[@úf ÷@†À ?î *¨JX¨*ÓùCV„›ô[øfNj•"­ -Š¢ç¨¶íÚ#W?îqÅ,ÃAiss.à£O?`þ¶öÀ<à¶‘j>@ö/§ÿ¢€\Ð/€Üj@>/Ù÷•@aS Fy 6ºÐylе ùŸm‘þÿÜ<:g<’o¥—ÀK$oçâ$ocm%ïº`&é¶3KR²½H’·°M’•}M&J’„íAÛN’p%&£/g8ZF¼L¾éf0û =ÛûY³‹ÊÛ<_Neó~å‡^áÉ_ϕ؃8†fMúqÀvÓ‡œ”èL·ýh `§ÿÐ\7Žÿ¼?Ï,ÌÐ_f±jfò.`ú·{ÚIºØúI*Â$…ºŸ$ µJ’‚,Ð#’ºV¤ŒCè&ŸºA7 •4w/‡n^ùuíü샵 öÄÚ#†œâë¡ê¹è,•¨¹Ÿ¢?`8­çå¾0³»A.Ý{·}Úñ{¿žåëá’•Úɾ,’÷AÕÿ-»^eÝûÜ'o~$éu‘OÒåoÁ2ŒlŗБ£¼ÖØA{&žùì‡;ö»‡Mýôqº—óÑdô†B´rmÜqðÔ (hOÜVÊ•½å.Áõ[å¾ìô×ô"vAó¼_dÉÃÉ~¯OÍ|Î;Núåóáñí˜Å½é¶³µ˜Oi‚â1›…¹s‹ÚrWœ -‹­‡óHY¤^0RÀ$²Ÿ6c'ûÉÚ%ë¢wŽ•Õó†§Þ%HŽ`xcË÷äZ<ß -çƒô}ÄiÜ4êÇ)=GáÚ!]ÐÉÒQÑötV|K¹M|ºþ1=g’·=–½ëˆ×]©°J·Þo¹Š³ìoÆÛÓt=ÝïfkDßÌœ5gŽŒ_~Àß&‹ŒÎÂ;µô4×1¬4×;lÒ\õ°å…u_`Ëù­ BÖùˆß¶G­aöOc|÷Óí<õ¬Ë<®fWvÛ¾ÝÜ•üug+U×ø¶rº°î°d+>·£Ú)hëÀ¬[kW|'òœ»3“êàjaº”m[‰bçª5Ã↱n³öYH}3ÍI#ÍùîòYý¤³è®w&÷E$*Ù‘ìDÿØ1.Ã=¹?šÞ†8nvå½ßV½uà6Žëç¦}]kZ‚ëÎa_uÌú©½z©×îjÙ~Óö[~ñöª^ÍQۚŽÛWÛÃR,”’Å^=v~ÀùÉ( óÆ“˜š×mwjN„úÄ„ËõÿƘ–QØ&‡7+29î“§k”¿x·y›8Çmåè5Úܨ‡¹6 -÷µ3/?ö++ÍFKÛ)íC;ßÛ'¿9ç­"——¢Ö †ò"ççb“Ÿ7¸bÞ*“™ w›YØlÜgèôT3ðM»§[ÅáP§nêXûL…Ñ?|¯&øÖüY³Z³€,çqlÐb§!¾ã>š^ñ4j¹- Å×]î¯Ò6®ÚNŒÎ­lG;Ë y e’çç(Ÿ·êIlNÕÏÇD 83”JÕˆ/-ø4ˆ6AêÉ~ð½UÌkÎø°ÐX=NÝÕ!7-Õeb2P÷Ò¤Ò> ¿(êᾑÿHó­Ø _ŽX8Y²à °µò8´ ”°šÉ Æíµ6§¬rÑ,Žkc2WÅñÜÔØ¾=›‘„kÿQz£$ƒÛ’Ê a‹.—'´Û%éìwªÑˆŸž:÷ôžGVkó… xH’2ÔÛr ´'¡WòÆsÚ—Ð;í˵F¯´ßÑWæKïß 3¿×;ÖÏX ³ùEx™Ý¹^¥+FN-¥“¡±<Ž;:äIïs“7[, Ÿ´0ÁBMAD˜äŸx_/E oæ|{ñÇ!GZÛ)Û[¯ÌúµÞ0 u<õ'}Òþ«Züé*ÅÁÔñãóÔpÝ5¨—\¨~«QùLf˜=êoþGü‚¯x8vÚ¦,™uM¼Y4±ã¿Æw? ©¢ãÊ–Ï݇]žéèÎ/;(”kì;µ"!£YDšÀ«OøžOú%žÅíñ-yÎ[Dç“´ˆü´ÙÂãÑÂÏüËïOÄÊÝʲÝJ³\6ëM&å´5dÀUaЬŠ~ùTÎ t›íHc¿±±5’̺¥¤¸A«@ÞÏöËñȇFc”"—ס„¨ËO¾EÖÖ( w8QlÏv£Që-g-R9mšö5wnö&Ò§±O7ëq {šõííü¨o³Ô¢¾Õ\¬¾•\ü—𷱆½ËÐ|Íj5{cóì·dÈ B·È<ÙþŠ½Ò­h¶Çíófîùü¾_ó6ÎÐN+ÉO÷­åù6?­$m®îu°É¬úõfž™£ 7ÈF˜ï~ÄFQ@'uq/.ë¼?ÔäKùY«©fµ:ú”éjÓ˜M*7L¹VnÍn¥rM¦ÙÊö6ÇÿXt{d¸åÇ`r›Ýè!ÛS[bÐFA8~ëü½Æ˜¾¯vêîÒýg«eé­Y£DçµcÃwjÊ5òª¤p­ªñü>)W ¸u¥¢5žp¹µ((šDh†¸SË57åçÆ Êd)–R_ïþ€%[n K½Zä•z… T¢¯R¢w,ñÇÌiCM›âx;=éè‘íþ²†CŒ,6?d,±1Ög‡×NËÜì[éâàÔ}ïiVÕ« •_ã—ž}ËJz,JL \—rgìPÜÌÕ{Æ1-òd®Xä© îö Ü€(*ýuC¿°‰IAžÄü)âÙüÈçùѼôÊŸâcö»K ˨¿d£åÀ‡êŒ¯õážMÖ1XE¸¢ר¨Þ*à¯õê¨zçÖzc°Wß÷ÙUnlçå峬ÝÞfVš3ôe~¼^­ó-îºÏÅÜ-7½a¯2€? !ûÐÝ€D†*]·°È°Î@÷ÒîöƒÝÝf t=ɺT̈Œýe¢Iif²"3œsÝe›Ö`§‚s¦ë´÷Ã(;+W³É©.»Î¶b4žv(póbIz…as©å´5£ØŸgÈ+[nv¶{ŸìÖ‹,Ùä¼Õn8aS®"à Ïð%p«XúÞå÷½w·¥)€ïë~Æ!ðƒÑÉpñ_F¹C,ü·ðí€]:HŠM|ÃJÉâ™¶˜èÔÎn¸‡ž8â–¸º½%½³ÌOŠ÷9€s£€¯7‹ï·ï+4<€È g€¨.îÑ ^!c @èv u - >î f•q¿dÏ,¤ÌQ©”´ ÇÈ!g¸åŒ[íi\£¹,&ÙSO"B0´޹ …B‹•ÚqíÂRWˆÄ~q×}n §»íæP;gðÈÚ€ð…@Cï¿õÛÒ%ûz²ž}•:øäÂòZh|ïéÇ@ñ Pæf PgîPO°Ðhp0èfs›±‰ºå3šà/¿¥Ï$½€j’ö÷J’Hh?Izò0ã±L¦·Ï8Æß,Û)f,ÚO2I¸®1'è¯è,^tp^yÅsŸnýì?ùK|~ÕO{0ÇÑü<]¶ôŸ©š>º5û&¼qàá[-¼ö!¼½úÿ¼YNOÞõê8IÝ«œ$©œ}½­Ýϲ;» ûžWÎe ë,¡¶QïÅìƒþ“?_äØk]Æ?`,OýqèDæC~¤Ö£6/m¢QZØEM»u -o=ú›þënÅ;ê—Ð`^üt3nɸ¸ºYÍÏêFÝBû’·ÁM³XËÊ·žŸÄÿD›ª¹i’¶ÛYî{Û$‘gÿ­ƒ[…v) ¯ZÇ9øq@îÝèŒÈ¨¹¼²áäsBØy)w£ðßÑ]Nb±<ðCuuKl÷Þ¨ w¾:m-¾æõkñÂ?9âì‘ ñ ­|õ¤ þôT‹íìÌÍo§$ïPP³hËRòF¯ýäÝ•¬{1-;$ë¯ -þ*v¶÷cï4|?”©F-ŒªÜêñ³ a7kQ7 Öä®Î¯…ª\úªž½nœ+8cê4µ9Ž×£Ó!hÜÓbMý¬òàÿ]wº¥¨².Œú -ò"A[úN¤l;;Ôû?dε÷ÜcïÔ‡,«jDo¼…Aô ‰~Ä5Êy»lßçõþàÚû#ûb]+û|‰¼ ië—¶õŸd¸é{¯àÚ (þÚÜZÈ4¾NT-¹Ì ã™´›â÷,tR¶rì -xýГ~ß°?mjÔ¾…Ô¸ø’ߎ1QcµÝíiÛ;š£ÙbéGœø·þšn‚OÖZoj¤¸ÚqbwÕýú -]Tµð ‚ÿ‡ì;ŸXÙ—¯ËùOýßÖÿ΃AöÕÖ£çæZvnºÚ¿¼l×I -–àžÊ“ÊòP_\6ñU‰ö1qy¦;ú~yFsæl?QÞ.ºõê¶p¨77¡ÅQ(S…õÆs”5ÌÌ•ÑÛç³ØªÚ¨¡5ÔÏAR¨ ËQ"Ћ‡à© fo+~ÖSäðê¬ôGö}MŒœ®úèµ™±~侀¯nšvVíDìÑXbœ/}Ç\ -NÞ.ßÉg¾YiŸÕzk¾vkä\8¯L½‡ÇeýÚ ’5_†ªQ_¦; _Ž9·x<J–¬¼_´¦¾`L"¨q…ùê3Áæ%ñ"Îôã¾3«ØøýÜ}sÒþ#/žß}“Kï~òçâÅŸ Øy-ûõÓ6€*û¡Í#‘—Uñͺ ëÊÖÃS—QÕ]ÞÜêrI½›Å¬MüwH^|_–ŸùêØÊ/ЇWÜ»è|Ý‹ñy霰³®+3ûnOšûn&®Ó~¨Nœ°ÎŽï0ÚO¶áhíÎÿñWÂÇ¡Hè—…™r§jãÒúÝÙx»Ù¶Á|XX£ª‚†-W –S´#.¸DÐ}ОôêLæ^ñç奾šEsçTóäö>ÃtzǯéŒÀÉU9U&DôlïF3?óC×óèâå'#îCñ…ÂI$†2gpÿ}Dö_Þ¦ùâO>Å¢ÎÄ,V¯mWY\Çÿ\5Ji¶œzNÁÿÒ~Í“cŸš#}ÿ·]ª£@wnП.þh’ÁlBN×Áxš›Q†ûÑüvº8_úörD¯: @T‡r ƒ-äˆmó´ûqEúÆì#ã5m~°vc{dþø¬D_Ñ<¬šj^À€”öãÛ…Ü®•CuuÒâBp+%·E¶¥RoÕ¿=çð¹¶óiL‡.DO(áÓ³ÒGyóOoÇéH…ép5ûøCø[Xÿ@½Åýx%}“#^öq!í&8¨YCeÃXx¹¬÷Æ#gÖ£(åaºëjº6Y 'ÿ‹Ûh‡çŠJ»6ÖÃ×½P«““ ü@ S®žrò3óÑK§*}ÆOO.ÞÕrM£‡ëŽØ–Öš:¨,£ß™¾}Rø±Ýô™¹åøÐÂC#î3ëb>…ùÛdƒcÙða7„ÄÈ£‹n€ŸGÝÎÝOºÅU»¬¯•5ñ¿àÿrÍ—qpXSت§^=²²Xï|Gɬ^E7“ÛízsÚùîÏ<´\2tÐ]¥ùÜïùGÚ`]²­lBlô&“•m>_Û‘ÉññÜðÇqhÏôЕx𮯗8¨—‹z]‹Ö1¯u5ÆÖP=¨ûÛ¡¨ö¦­¦Ú Ûø/Ø?Ðßá²LF<;T¸ ú°€1¿ˆ3{Þù§É]X„£"F4c΃!g}«Ù)ÚB¯Z#FÇ{S>þtõÊúÚŽT¶j›gµwñÞjÝyW”þƒ¢•Ö*6ä«j­åBåvmä|ZÿER -FÝ]ûõæVIáÜZòϬ277›’vû÷ÊÏ™_7nªè1¶ÏYšZ£ ýô¦›wÉœÇﺱ8=ñn¸ÛÓ]\óº¦D²¶‹ö†V-D}Õ’OSå¼} -†–òÈbŸ2 ;°4Þ‰!:ùz¿óz€‹œÏ§ózj9‹æ/9í7¢A߈ ǰµ?[dÖ„”×8ó[G}ýAµ;ômÌë¬{©s4=LKª_许>’ר–Tk:âvq­êp”jÝ9^I˜¶¬`CÎG7i(=èÞ\š¾¢]'ãß·?AAñK÷[¢¸yJ¢¸]ÎE°;zŠâŽDs´ÆÿRÿã:\«e&p‡Ó–_=dš~+QGºÆƒ˜]Îí!Ót{®¦€;|—n©\{ë° jµ„¨Íʧ® ¥&§›#S•‚(¹ÚÉ6P]t¤>ÍçõîFìÔüK{¥ž¶‚´êÂvìw„í¤3¶Sù‘“!ÂÖm4Úð«ÿËž¾¸Òú¨v™¥w³Zó=Û)Mfàíî”´Å®ßDõ|eÑ{|#ÇZ¾×UVÎJ×#ÿ¨™‡õ]µ“õGÞ6°<~-*Ò›7$v¼ ;Þ3ä;·‘ÅåçÞ‹äkÜÎg¢PØ~Ú'¡"N>|ü¸WysÝøšVÌç}¾¦Ã)_\˜7½¬þKí_vß‚)­†A/ZnÍúíFÿØ"7@ ØìC{;„Ä&/އݤ~±îÄcm@Üd¬õ’ß÷½*#H³¤×Xw>kcÑ)\§;Q×öfuÏÚHé[ DøÃnð&Brƒ.³×CUfÉÙc¦º8¡_ûÑšž÷^)õÙbEj¡/JDiƒ«ìþ¢DðP‹N¥[ù—µ½ŽÚËBåLÏ- -ÁÆb± êé+ùK|멎²®™Ò<†»‡í-úuÛˆK®³dœøcwúà‡Å7¯±ÄJÊ–¤5vŒ?pæ1ÉX†É@‰öpØ ¾lD‰/#$WØüLÊ7¸@li£ElT#´w)"¶Íã—Ø–kbó)!$Ì5+üý‡¿‘v»DÛ¦³¹–¾öhžð¦‡jÒ$ÆŸb¦¨4Wš–6o{T’%‡Oá˜gØ•¥¶Æ›™U†E†Ý¯ª´_ß¶h@[QyT·Éu-ÕÈÒà÷;#„c |_³Ž¸yº±BÖsî2fï/kìTp3ìx—±ãáù8Wm{ÆfÇM¼†Påõ»ç‘ⱋœ©PwëqõDzB-4ó1™#€šÎ<_ø€á4eFû©·¡ÛãäD…ø ¤ P¦”I5ÖëÄ®fD÷´ðCy¥àu#±±þ ñZ×–·ˆÑ&kÞkÕjsr -Åæä¼š“­žG°ÍÉ0‚sNåæDJ,BŸgI¼àG«åíãx/3‹ÍÆQ³ãR(½-hÚF˰Í9™mПõE¡dñf“š}»OeAä7뎨Þ?VÙ„7ìˆ%­R\ñf+%¦5z ¥æƒ]õšŒÿpHF ~¾ê‹÷©‹‹_—½Y]ìQ·º(p]¤×åñâ)!MŸ}ƒî¹ ’G}ì¹õêv+ߣ2qŸ«¤}fü@D†Ìü¦÷(x±×pÛQu —Õ~kœ8Ó5܇Í'þŽ›³•6Þ²5|¸6„m€6€á¯DS¨§½¶f–ù¼_+yßu5j[÷jez,¡{VfQóƒŽÐ=ÖÌC¼e·ˆšc§ü/³Ë°®Œƒ#Ìö—+›AZδþ+9I‹î~Ý®vîûXaJ™ 6~²#ãjÑ ªÔÃâ¡[‡ú¯AMM ÓZyLÕ]§W¢©¢ëÓ =hµEë©CVÎãS§Òz•,ä:·}dÄÏå{ ƒå©ô%Ë Úeú½NÊôÍcË2½h#åɧ[ùc:/Þ½y´õŠèJ×PΙÂ,·IâGwk]—tà4fD/VœÖ´½¶`¥¬W«!U° ÙEÆmÞ.?BqTvå‘_f‡íTÊ’Ï©ä9õ{I ´BIhŸk%Dé|é©pG¹¡ÕNˆr¾H1Ç-¨TÇt¨·vœíóÏš-$‡«þ1–G e€ÍuÚ”_÷ßm÷´—ŠýU*´ðýYŸ iXØ#×ú´&“~ÕÜLä¦ðJÉï;0„luH-¨L=œbôªzÅîœ_QÞ:€ûïæöö0Ö{r½Ð?O™BkXèþ@ÀïÏbò>Äԇ¦h€èÃÜÏ ´|©Ih£â[h[Þ³Fë's‡Ê~²Û¹z²ó×>¡÷'Æn8¶jH 5«öz}™@ÇÕüOÓG os ïô` -ï`ªã7ÀÔ*µ?Ì ×iÿó,u©¤5øÛØ,Óª'ä­âÚ€8B_–=³ëŠsÍÌ·Ó£г¯ Ð/l0ã =Œt]ŒEJg€9ø€ËE€%Àš•6À.W}€ ˜0¿vÿºì=¬죫\±9ÏYª¾rLèó"NZ=v¸à Ò…'-fφüœÐ¡3Üün·¿€¢Ekö“Í`êb~Í“¹û{–¿¼--ÿóD°C¯Ö—~3¸ëÀ•‹w€kQo€S'ùùæ@à^càÌxù¼Ý¿üŒ¨æ<%€ß¬f¿eŸl^9_ð7Ó›²·½3²wI“³LxpYÖÛ9b7ç0Í2‘Þæi–uP0§_ÿ'šI•<@ß4õ×Á­— ÎsUc'OùÔ™?á¾<¶­áæ7;zuã2Ú&wãßoG6ùܬO IóA…L‡E¸wMUÞ½ŽËÓuÎ|óÿ"{¯y1KÊ2ýHåeMÙ¼„¤šó»ïh^F4Ìq~3¡ïOƯëå—¸«×ŸòÅÛf‹} !Þc¢­Þ«S®{;2ª}³²ž“&BošÇ»¦’\ÇP]Ztº0ñó™d=®”ðç ÿ—xÍ8‹7}œ3žÁ‰éþqZÑéÙ<ÿ¦iG,{ƒy= L^F$ÿmÛ²r^^^B)~‰Ÿ$)<îÆ­ð½Y/NF ["Í븈Wr±·Œµ/ÌŽ‘¯Êè ”­ó²)Ï £MOðTΗÙ`{0ÅeoÃHiߨލ8ѯr<¬|º1¶¿ê»´·ÓþåwÏáêNΫÕ²·+rù}`ˆÿ¹ÃÈ º?áæÞŽäc™bþvs%Ãøxa¶q^Îýêy^Ö·ß38ØC'9Ý#Ç-U1ç7Õ…ïmÐÿ}'W¢ø|ŒEky7F÷Fô°žNÄ\j‹­G¸—-ÿh–6KVÇ6âÛe7`4f×+gøÈ>ÆYÊ+U¦s,.gÑÉK½Qó›áÐ{lã¤û›çW*F’mfyŘ³ÉIá&þ!^Iáþµ£89ЇÏ;únìIè HïÈ ,pû†¬ÒÖÛL«[™·63¤×ù…‰ë÷é®¶ßödUßöá^š à4cA«Rb–—øMÿÙgwòÒdöY„yA˜×ñFºï— -}03úܹ^™Ã¾;㋧黉$ÚÑLÇ[,{[ài¬~ ÄY»õzaת8½®¢ãâ¹êÚG Ü_8ì9ßjpzÀXÐwny- ´%ÑÝ9‹;©îü—ò)xŸÝðÚó -«6•rë_²oùÊÿæ“[/á³<•ŽÜ Ÿ*êwà2ÛÜ¡c¾±s¶Ë·šÛÂHa×ZIV*tC‹QÁÙÓ'˜ÞòŒÃ% ¹Ñâ~ŽO ;¦þ++|ž.æ#Œ÷…XÔ5Ÿ‡©'Ìåñלm^š?C†­§[å© M›ÓÆÆjü1I4¹þGöÕÊâSgÜÕ=Õêç’Mô|‹G·j½®^²‘ýáûZY¿$½~s9æfáv&ßSÍûîf–×!×#|œfs…½,çðû½™é"°ŸUÀÚÅ5möåÖšýâÔvüê$¹ïÉÉp…+ã[É©‘|ÍÀúœ·ò­9~)­:B£ÿò—ýN'ü‘:—å OAúhæº)­„Ý*¾Ýâ`—Ŭ||á±+{Eל—gzVIÎm·‡ßÕéiò2òºx½ú“‹'Pžo±Ó½=7½ËˆC&/Ç §M—Ã`ÎqCI»Øƒõ~¸äÑݧßõ„Jpä¿xôJ¡x)̆Ô´'Õ²•÷çÏÊZm“`TVKºC¬üñÚÍÕí'¡S˜ž¸<¹JHc|4¨1‡í£y “œOÓœö•´œÂHt†ÒCv‡ùâ+¨ïù®¿ko’¨o€…·}°Ä®_„uv>ª5÷Aïºî=²µ.åDeó~õKÿr#¦†x2V"Zºóòº† ÞK×¾xjóMhÌfù´NsñðMHÓxŒ²–8_ÒFµÃüÆ Û˜ ²ÛéÍ{ÐØ°­`5´h;µ0-YöFq¶ë‘=25§/»`dü¶fxÖ6øýkÚýZëkW|£yßÏü_\y÷ãÞ¤µ‰±âOþíwÒÛaßw ·§Îd\޽‘§,×N]† -“<ºp+ôMéV¶míR³’ø†[xñ’–½±zá{(¦kóQøTêB«év¿W}ÝíîI_¹ñKW¸ZYÛîÆŒ¦› [«Ô ­B/5~mŠÿE²ª}Û;ášëz£Qú4yÎÑrw7upÙ»Ws0šÔd¨„5PÑk[ûDã‰uá©gïÖ¡µn—ÌYYªoCÅ a%QÝ $ñÝbW•ô| 6ô’å5=Ù{êžv?jÞ…»rb&EÅ~¸ÒŒ–†|1F;ùbÖ3Ù©1…ÿøãïVBT|BÐù>`_Ê܇Û´¢ñlΜ@©Øƒ¨ úµt2±.k?ÈklbÌvæÌ®Æû}ÉŒBá\èJÌ¡¤¯½sU/ç¦Ö•®”ºŸŸ9µ'å”{J "&òeã¬e‰ÎÒ¤WüJ4í4;Ϭ¦vžïê¦3‹6Ïœoá7;ZF -çþ8\š,»‰Þçz0íáù.`o“»wÝŒ -£Ø–ñ‹eŸ¾´aj}h¾ÈúÌðßõU7DéC²Ô›^J™Ö½i z¨Ë%µ>óy_é§ZK¾R%;Ϲ(ݹMW¢G‡açE•n­źÿˆí­æì%±Ð[baªÝs¶€X°ŸÙÓ…"³¶ -óÚ’ï/á™5®?Ðx,ÖC5'ýú¼›—quVLî¾±ŒÂ6é›^èk»Áf£¡ßûI­?Ï©’Á/ùºxd|ÀÒTɪSF°ÎÜh°¾*+âÂØbá²™·ÃQ9þÚÐCz -÷€§¶W‚&À×íóoð;³¦VkZ_ˆ×1ì>·ëHÜ«AµM­QÕMž0:Ýp«:RÇl­Ê‹yœ¬6:r `ªÉ£u–ÕÁMbìÑ»3¿¸ ø%²Å)ù˜%¼lv"GfV:¨Ý0«¶uÄü*ªõI eXýŽäq ô$—„×Ì­î;ü»ˆË6ùÁù¶W²ç5oͪ°Õ=B@ö[7ë©ÎWlÌÙ¸±â·kʬ˜seXì½wÙ!çžÙa+Ëþ!Àÿ°Õj*¸¸\ŸÇ—8ÎÞêy¯²åï!ÚÉ1ÄûHÑ‘8ÔÆgØ–ÓãP“\Ç´:ïÂrÚàÕRì Ó]{uËÎíý½ Û¬ú*B³Èïåk’Òâì¹ÎpM~©°Ãe:`nPuÁŒ7¿ß°c(égeDÐOt2¦ŸUáH?ÁÕ‹¡ÄcöËßO¯MµÕ"–ݨÍìý-ïè±oGÛE5gC}»åeÕžy¼<>6Ùλ]‘:€Õ4ÄâÿÝWAtOˆ¤áZ¨œ—~?HR¾–¦wšA®ù*!¬Ã×,ž²43¡t‰~Î<›f?©G½—ù„íwv_ª ñÕ†¹!%¼‹qŽý¤üæø~ ¿Ã*!bÑÙTë.~åu?ØøaèA‹ëkµ ,É“*Ëv|}OŠÅWÔn—‘½*t'@ŸïÍ‹S¾® !ו×2Ç ëœÜ;Kô·_frÝçÑý"/Ušsß8õáð6Õ^è&<<—”fpLBÜ #Ö_¸A¨Ë«I¬oû-±¾³wB•ç¿„cëNø¥ë·>™™Íl°_NãÞ3¼¿´w>Â\N_¹ãçKÝöêºìr|ýÔ“¸Öµ×eGbà°dõí3Ó~iÃ0-êHÏ”æE;£«I‘•%C‘páÉšRÓ ¤4œâF7‰ð*H>~ ì°ŠQìpÚèØa3Û`¯˜âhÒ|üËò­oˆùLêãû|õ[*™í 4×»-Ó»£ÜÞôö o™-œMK/‚qÁ‚@gJY¡ù gçíòõ'.Õ)†—¹²+RiRObë´B'úOgM¼šE,fã7kÜÙQkȸyTüFîÍÔ÷*ͱ(ËÍt¬®š©y¹6ÓÎ3ÿêrû#Ÿ+£w¯Ž]uÊaÿnMIpÛÝôO3u ììÎÜ*‚qÞsl:«“´75©ÎpÁ’J°‰¨~5=);ø^ æxPWxmï°Sï”býðÆšI+oý–ƒsÕæmÒ%›:’O¯4lÌ;¬¿'ÇKÝg:pÝgíºßª/ë~y–Ô½Ot¯{×àö‡· ¨É|[D ¼žšßF²Õ«âÄ•ôÔ•’§þfà¿‘À°;”¦ŠÉÇ÷/’Àú1–7>%Ê­Qqa·ˆõyÒœ–ï~“NëÛÆkÜ>5æTÿÞàžÁ·þ™½ú‚oaõÂRjrqmÕ MsQÝhˤªUðüZªåýƒ¯–û_-Ûí|TUœ{µÌönÌK‹ÉÏ l“§ëÕ€{­&n&ˆÑö„«³óÔ¡B°Îè~@¶F£´Ù˜¿t}‰…ºø€óÕkmÅ6ìšü'58-ªy£l«:ú:W‘Gë‰Æ®QDM6¬¡ÕÄVl±­Wšài†$ëÎÁË{ œ.²œ–‹`‡òÁBé`SîþÇÔÝÁyœ<¬‘­bO0¦}Ù[y†Ò3±ôäó ±14è…ÉD>ƒ­t åk®UFÔJ–ÒBk{Š®ôiA¨4_¶Š\6Û<0Õ„¨ ‹òí&FåÉd””iúý@¥g†”J3OÃJœp–`?$¸-G[¸­èo(Ø,¾ ¤»=Ü®Ôïph=ÿ°oÞ4Ö¶uU1µí„pËeyÌd‚k“ÛÅ-_#cÃ;*5|Óàkªó +gÅÊSõÒ‚¿àž†Eû"Àb¿¢Â`‹µ ð3œä—¼wÜáÓâ¶èEmý¨gA#M°*µkã_€¤òû=‹BCx· öÃ/4H~Ÿ3MÿøÛB»÷‘ n¡|*ó\m×6"`Ÿ 6£TÈuð{ê5¹¤®Õ4ÕêT’F•]Z àåâ†C¥<˜Íû„ËÏÒôÚøV…<ªñî]_Ì9¯ÇýÑÀŒÍ×*þîv’1NI¤N<µ·´i ¢¬Ü¦W8 jÛIݬ\ª–Zfo‰K¼Î£g‘)œÛk ’>ŸãwrN@¼+vŽ2ÈÒ*È2¸È–•Ï/É}ËÉ[@ -=Ç÷²­¤)–*I®ÚÙ±&)q @ÒÁ÷ (íq½9ÓJR0oaP¢®Ì"UÄ1mZìlé×KÙ{Z¹|ÿ·ºôéò:´Är±J™ m?y€¼_x€K@U;FŽm¹”øõÊä75^œj¾«]¨³î-€®*@×GNÒèú@ɯ…nÔÝœõ ›já£;õIe.j¨hœÍ"ÇvÇoªòœ¥­WÅØÕË®²¨Œ´î¸$:S ÒaÒ›µ•P#V¨ÏXèÖÊh.6Z}Ø¿g`€ÞX>@ŸÃÀ4Ê€‘{w€™ÞA€yô[[«HKå×ÂJò`eè °Ö™X{bl¿äxg _ ~ÿø{–º³õÉOš¿3rYg?ÝÏu„ëÞ!׃?ŽÈâ4Ì?{:do…Ï‹émKh×s–øo Yþi[6^_Ù=彿?´³µ½éè|kÌŒ[Š-´ì:mðâ–8ɺJ%ñP»qþZ&v“:­p=É·a^†—.ŒoGß;º½o¬w›±Ý2Ük`¿œV•Èm}©ˆIÉÎÿ‹ìíòyKgÛF– ‡µ¼„ãæ_¥¾¾R"Þk¥“&lѸ¦íÚèâÂ¥y’ëátOïè$_o‡#â®Ï‡ª?»îíÂì'ò$‹±íØ+ShGîü õ°¾Í;b˧çüî¤^òœ£ÖZþv½ÕV*ÝV:Ô‡WÈfß̹`a¬E­èïðGö>bDöf–•œ$/m–äu«/±{Õi£—G'¬%ünž_qsÔ>Ä #ïÒ± lïÈEÌ1s·^3ó·ü^m–yе]pÿ­Wl5YÃ~ã¾Ò l…„bhÂz%8êc,hì·ürØ€ÍE:\¬ãvë¶  -Ðæ%ßNÿÙlü–ldï0«?K³7|ý¼ï'^s~=)°üÚیųu]geýùv»‡2좖ښö²“49·moÒ:‚ÉxÄ*À˜,u€Ñ="¿ü@£i·úÉ>£uÃÌb%7/`?dŒs4ÿàÁ¶/7ù:äkýÛO‹)ózú|å zbý‹ÌeìÛ˜m„œ!P•sw²ãVRæÖœ6jòàšœŸSw‚q‹åøú¹îƤY¾Ž¦'ú3bf Ô™>ŒÃÇ‘3_lòÛ{ôÞµNìdmõË•»ÕÜÕ^½‹Q|þKzÃ<üÔõÔJ´v¯•ÕTö‹W§0÷;hÏ׈nÌôug8=‘Â|â”ô혺›ç›ÙÇÆy»8@àÃC -ÐÁz5¥4"úÑøÆõ+Ï›hïç%Ý®}k¶Õï°®Õª¹ëžÓO=‚Ç>ædi£&-!¢1;ß}ƒózI÷#Lîÿ°z·®SÓ|ä÷× -߯xƒ ´çÒ…ž;&Fî M=kÚ¿œä Áæh¶Ÿ öÜ÷†R|Šêérîw¯—»} oo»î¾@kP†«µ«×{£jëGŽ1§M©ó/§«s"v?ÓÓ¼›‡GQ·™w]F#H‡ -£mfÌTÓÔëAC(ý/Σ‰G˦X\5)ìéw:ÎÖ=y,þÁÍÑ+Só³8b½¬ ¡´`ö å3²­º°n}Û»¾éSl·oæ4”2#ƒµ‚ÁëÜýVFծصôð2º<¿v´-P04-à&j õCµ:8•#NŠuÛç½Ri„[GN>ÊY\þ‹£µÝ·¶%´Q ¨(¹Ïõkk=¹ÕÒñH`NÝ¡ü¼ ý¸j ¶õ¶T Ë| ­‰ÉÜæKC€Â¨»Ôv§¨ ƇTWjç—Ÿ¾Mo}a5ƒUÕ¤˜rœ1¬bsUNa_ÆÁ»'¥§ö^÷£L¢0®Þyþîyåɪã†Lò¿œÿØ“¯vý÷}| X\øÖ0uOÓk8z/†ÎPÙ÷´¾9šóVTª?ÚFö}hÝå)ëw‹âê% -Yjú ‹ÔøCUs!§Ê ”ŸJSѾò¥4.Éx×­J7ôHü@å‚bÇ}cF‡õG®èQ8r°Ï¨ôOF;ÀÌ çqú/v^\®­úZZô!¿L a´t‚•0ìïç„j]Xž5Ÿ´›÷JCˆÇ\·Ø7;Z$Ú†ºW†Cµy3¥®ù2Mv2ž¥'é6|\%ê–=;ϰ v8/‹ï­ÞÛ•1+ûToK Äik%¬½O*¨íAY(ƒRÃåbs‘ãr–lj:Çk5$Ì%ø÷4õø%‰‹¡UvK¼Ë=øÒSM]åj”†‚§Ö›5UiMK&˜æHšxÔ¼óØ<ëÌs'~ ó(¶5û*°Õ»¤7¨ Ñ@UX¿(J(óºÄG߸Ïw÷dÀê£3wh`×K>"×»\æ\ïÚÙçè‡8´†Çmé²@úé嫤™ÕZŸFŸyàÁÉîÝ•P2ü|ÎÖÕÐP…uKé'-“Y[¢7k½3GN}ñcÞ&bûôöÛ!¬ÚЈ„ U; ZQy -åõ¤Àe¿Ì£»gÞ_8«Visõ“ÙcÏýÔg¼|b[ßÈ\ƒ9Ç\ÉËb ¾g[ÿË%Fè’ç†Ew`8G'\dž}ÞÌ,sÞµ:]Èìàê¡Ô”V"Õò{l*Ñxg®Ñ¬¸K²Xxö̶Ìåƒûf1sôüîxÙòF8r‡kéÆY#êÃ5È.ÌÜY“ÅØÇŒMƒ!ÅñŒ~Ôà===M¿ôô¬Ñô4Qó¨žn/QÎ;¦¥ÒþÕYÖÐHR…)©Ðû¡æ.gÖ(V{†h1¢f´Š-kt«Òtù)‹ßÖ§%‚älÃj¡-h;Tãã*aóUWšpG¶ïç5Öð›´ƒ˜¢Ï+“Ú@ÆŒ›­"C^ºuÚ%|†fÅ.å±Â„âßñŽü.˜Œü.‚üè˜üN&kŠ¿âÿ°váuË£^~ÇY÷÷vƒªoº=f]x·•½´$h¡â²5ÏÎkra¼I,XΖ7ׄ_&;tʋČIïjÈPL7¢Ÿ°¦ÝíéI½+¯åZ(•ßAÔD.“H!‹DiD¬îà ¡¸ð+'Á¥Ê@Z«Íd÷/ÁôÖ@çÕ›÷-{çL›ãÝ»ZÖ ÃMëü¸ŸZƒúh^?L¡9ZŒjxf ÆtÍ™ FÝûõ aÏõyü.×çÁ·[ŸO^ýu¸nÿÅë’q5K&©í´ãÀZêP=íyMš¹×VçmŒkEJ¿6)L†§NÉJ>Žav©Ti%N³Ù -ÓÂó˜µ9–WÝ&'ýÆã‚¸ ×a‚K»zv¯uoúúÔ–©Ô–þˆªuÄ‚^]­ÔYUQžÇª¢pUáWZUÁ™ü³ÊtóU`°þý¢xyehÒ{5†K}ÖŠójhö¥É è·M0Óƒ\¥Ä4)ÈG(¶.püm<{ùÒã=_Wë>ÁêíJ¯½¦T“ê£V|¹N~–õ<žWU>[UËvD£Àx ])†P´Aâ•Þp*Wê(4FN‡ÉéÛ€ôû‚„ô¹t‚œâ9iü濾ûçñùlöÔB«gŸôº~wÙÜKB>´$-0Fƒˆj)Š5ÜxëB-ĤBµ|j#¨ÁI5ý8DÅg\¥Q½(Èù˜È ßt¬¥xåëg•G‹ìR&;PzWõÒtÓhÿ@p†Lð|_ßÀóàô†y9`à¹=Ás% à׺º…_3xãl>uÀšîäCWÛ”JŠß‡Ð5þ¸y¶÷S§Éµ7±s+­7é7…ÔÂs¡1E~ dØ~KìP.±úµ¿ÑžÃñ°wè)°0][°à§0ÀR´|PÇO_PQd‘âÜ2EeÍ[àö³X€ú’NA}ü©‚ú躥ÏÁm¸]ÿ@ù!‰Ì›K»qÐŽ‹ÛBzC;§]qV]övd*¬*“¦¿×ùR–Ïø½X¿óÖ'ÌÝiQÚ(ß)‰î¡…먎…—B¹ˆL®h¡jv’ë9#ÀžWÀ^¨’£Zí]‡››÷ -à¥J1ÇüÌè?P~ø,ñS¸ -€J€ÝSÀLœ£zí÷ÑÎõ<( ³P°ÖáŒñôxHVÆÅ^ëvokõ0ŠÐÃ!ã2P(ø»»`b;Mм£µß³|§@ ÷@TÊTÁÞ“r‚.@´!@t œc #싸 KžÎ¹ÿæ±g!@„yäE¬Š €p— -@Ìä9@ÌË—âžÇ–H ýþó4u^Gu½”‚3ìzbG 1éYÕZ9ÔÊ;p00ÿ“Oú@ÂL—üòZHë­¤Û4r¥ôò¸œþæ“ß Šò ¨{P’U(«,䀲;[€ê#ßœ„¨ù(¨=Ô¨áì”s{hà JÅÓãÁ_…÷™O…O¶dØvú¼ºßÏò‘¡Ý… Ëõ¯Z4Æcé7Àþ'Ÿ,Ö:ÿIÞþ±ÓU€ºº@}’@c  yÑèA¸è¤uæ\·‡̺¤Ìi;˜³vþÝW!#‘œ0o 0ŸÚ&gœÿ›Ïö)l¸Ô>"M€õBöIö8õÒâ&|T=8¯œ¡!,ß- <ÆîJsµµÿ¤’7pÿ/uû{–¿'oÿ¶s˜ÌÝÿdp P°„ºØnpØÕ§ p|¸ybÜv°¸ ‘ÜûƒÜg-‡–ß»qkoGCÒ„¯ÖRlYm]ÓFþóüêCeÚ·,ÊIfˆ¿Ï¯Š½ó×¶œ³xÍNò}·ÌáxˆÛÐ{”§øÞ.äh߈ZæqÒ-ÿò?ÅëTrô2¡Gúµ„ާ'f>.dã{u”-n ÷»½¦íôpqám’x•åíüí-_gñ¼üþ@§æƒ'xì—ŽÚsV9Ÿ~mû­}cÕ'ãaÉáviw&íÈÃÚˆÜÆm´Í|µåïégóu;õÍ’]²ÿy~Õ·™~ú%Ë<ó·„¥Â_ëS¥³ö5 z7.“f:ÈÙW2P;I¦IÝó²ÆõOòµ5>Ùyº·|Ù:„Â`—jáfGÆ‹]äÖLj9m.[¯µ¹oùëù¯´Rpºr¾—ßßúj+"ä -Y1bh–FVp4Ðu`Wƒç2±_ð2é£è‘e[á·„Eà JiB÷ž·egñ°-‘‰YÞ7–¿ï€Ø¥j“ˆF“ÛzÍRgóu*ÚœÆZÎ{µg¥¥iK%/4¡fµæ6hÄøa9¬Q—%v2³Å¸5)úQTó™'Àx§›óï\åŒÒh¾ìlÞÿÐ)γ?ò_f^‹‰ßÍFt½Ž>ÓMÂGÈæ`š U ì’Ö\&]ƒ\bƒ]Œ¿{©-Èd û.>Ð}æ6·<ž=þµsçKþ¼œƒËW4S rân5âåêÈ´<'evjJÏɴƘÑä˜m¯{»úMݽdoy‹Þ{¢\8»þ@Çèobü2E,rlÄß÷•è͆ÃÆj9b‡Å8ï¾{–?ž—vŠÐ1+óÎڬϋêŸ)»1=+¶mWle·â¦ÆÔ|?ú““¸“~•ÏWã‹;8ŒñwxÝvÒˆ®NiçùìŽN½m‡¬™ ý´tþ‡öè}z–i¹¼ÀçpD§çí½xëòin…½p+'}¬/nóŽås”îz…²½ÊÛªŽ³rs|uѶûšöB˜œážô·{t|Å/ͱs{“£;ýfG´ˆÎKhê°ýá§ç·ís¼„XvÈ€ô7RŠõËIØ·w>½²«¨ºÿþÖáÀÅÜ0ñœ¶Kÿ½%ð¸ªÑ„»xl3ÃûroÁA‡›CŽÔqF·7mýɃ&Ë1‰ùÑh:ŽÎÎ빿9RêÃïF>ãi'ËçÊqõÇ¡5;@m.?üX -ÝÃ÷8ø}«‘ìˆÅ=PÇz¿;´ëv½ÞÁ{—o‡3ï5[5^­¡ep‹Õ¤»c¿[PŽ+]ŠŽ‘u³“¦²ß÷%•mä¥vßxAE}Q¬Â°¥Ô© šˆ!·.ÆTµ[‰¹§DÖ¢s?é@_æØá¿ì^ \Ùr÷”ra9Igþ#ÞˆN~!"CÚŠ^í]g·’9%/ ƒ/퉮ˆ^]fÞš¶ñK¶†jcÕ›sµ - ¡b •HiàÓ<˜ ¯2F¯2i4߃©Wj鞥;LÏUÄŒóxLäó{©ýÕï·ö·›¯›–ÕI¯½¬Uü¶xª,ÿaYÃHŒ… }ÇéßCÊcVÄ­ÁvýíFö%z䦆ó£ -vEGªè°Ì54DoSjµ'åk1Åz9ºœð3[Æò%¯”vös‰\Ÿ‚ÎC}n;LZ<ŠÙ¿‰¥|Úßõ i/ÕÞãš(¬Æc[P¨ïR€_Ö…ßnI”ßF„‘³ÿÿØzÏ5Uåíûæ ZÉ9EÅ„YÌ9€YÏÿe\kﵟÿõ~ùá€34ÝtuÕ=E1Hq´åèœú0ß͹f3‘2­œ{Á©6lTë„?.VÃIÄúCÿ€–³#2ãzäqð$.ØÔ=f¬4^P̰—J·CÁ+) Ï·Ó1ŠFyh€lkª—ìNêÃèPnu,Îbp/æÒ–·2©y³³¦aŽXQ+ëp !~HqË«Dçê*Ùé©Dw2V‰ZôÅ|ó؃1ÒÇ.w‘͹·­ÔίU”E…Ig%D×s.Fn?v5€Å9.bŠíQRz Þ¾ºªô±©—¦‹²Yq½hŸa1×ʵ/͵r¦ºS·V'Q«È¨Ä™Á•C«%+ôõìÉçžÒûI½X{¿•[N/ýäl¹åñ]¹U4G¿~ñ-y2œ‡—}x?|z²Y/W/gCûêÉîŠWÓ9þi½ÍK½ð(õ2F -’×'eœÒ¡ þÀE‡ÒŠÚªiÙvq}µ*¹Z5×=•„¯‘r8‘ëÔÄ›gù|©¿äÖ$FeÞâ銌Iò^¡$µ†ñ!(ñ!¦ø â¶ø CIªò£_ ÒÀqeåþd×Å›ntSÇ ¿Co~€(‡8^³æeÎ?K*¿¾dŠóí*æ¥=®´ £V£“¤’fÖPêGÜU˜†\“cÖåÖg<’ùè¸ø¥« ¤.i>ÄG£ˆöÊŠêE7…wgÛƲ² äùŒ|Vôw·)ŒÕ÷P3×Á/úßÚ,}²ílZS¼× ˜¨àúúñ$»åó´ZaüÀ%µõ~èS¤q):ÿ©vF­¡•¼Ò *¤÷{Bjl7Eé:Þ[R·’­H2A5ÅÇÁ苃~w&jên'¼?™‹0Ž HÈB]ŠŸ.2:o¹Õ:pPj-¹Å%zr¿î àn#Å¢Ï#Ùq·Œu?¨^¦…UÓÍÛÕkïh—-4’òÚ'Jº ô©µ¸ó9Qw ôª4B$]æ9Hºåé‚$ûbz'‹O!PD­×4„2ó„(w« ÆéY_ó3¡±æ­Û:æ–ÊûùoƒàÐñZc7%¾ÆV yÄâ‹Ò•Å—žÄVrlÀú¯ZõÏn—ÝÐÞ8s˾àŽÌí ¼€µ*ƒÑÀòP¢"ZñÓÁñXE”«ÝÔÚMäÄHG¹GÏâP>ÂgºÍ Q¥ åøÙ¨Xäm=pø\fXå–Ó}›ódÄa´¼üÙJË:°?}2û‚1A¯¡Ð§WìÓͱ9¤›–ÐÍ -ÐÍξBŸ®Z>í„îC´Ø{´?Rw4ŽÛ†oL]ÓÙ3¢ð•½uëÆfÔ€Ôné¬ ý},>_¥ƒ]K~ŽØ/>畳œwé8¬³bØ­¶Š%–XËÌÁ L[öésç4¥[2º£¹§u£.ÃMŽ’2(OÞ§]ìÛù~Šíé&Õü–%ûZ³LöÙ$$ïÍl—¼»ÏN?€•Û7;½z «·ôPÃæ²€/I¸PÄrHFi.Œ‡¤Ì–‰`fÄ#·‚[MýJæð€Þ ãò9:Fÿ3B·6-žæÃ•F]Å»MuX•’J› •©nf+â]Í&Ę´³D:X"Ó4l|ÊÅ]ܼV÷8ÜsÜ<ˆ.nÎ{í4™u¾èŠø*n¹å设æV§aÞ‘-g@êµ ŠŸ¬,~ä§^Ñ…[#ĉ9Væ{ZÈO.”,Œä 7¤f?Pâ“ÏSÄx-‰„Ñ®êDV¹øôv®ãV?×ÃÇNçKÁ]÷ŽÔ¾± ᑘx–0¼Qk£{,³FkÛ#‚î3[Ã…bÃñRû‹èê1àºö gÊ„M3à -dÎØ‚•Ý2ûo2Uš,mh28„u¦n¬}s¾ê:LÇ*ÿÖ‡ÙFqݨ -J{4 ¿Â7Wñé,})öÞäÈÍÝq§×¿`ä4Ê]7í¢Õ1l¬Ûw8ë?)n9hF $ddBê~1»d¦^mÇÍÌFdRï"ãß[;°W//Pû”)@M€¨N¯ ¨š|T “€²%@9d%…Þ¨j§…çÕŽ-HµªaµC=UÒ5m80¼w?˜VOÌ‘c²“ÁÝ–Ú1Œ*wî¹Û°pAŠ¥] CÎîžÅzþtö èRùK -Ð7TNáZ)vu@ßù` Õ0°ž~ïqfý =@Ÿ©aŠ8ôz€ÿÀ€ŽªéÞèz’ø3n¸p©YµWúl„j‘iSî3ª"8Í.Ï2qBQZK)à;É¡ç|!›ëS7.Òrú{¦–Üs®½óZB)’|z6“) °ÌT¬—S8mÀº—`½Ò9Ål¹)Ög«€5âô@©ó¬$Ñ€•éZŠÙ°T²(cRsiÝy¾§¯êx \9ÎM×Kp Žš¬Uê!¾²J4C+<7TžcBÍ6N•ÌpDN>¸Ò€\å(®šQ×Q-ÀMF5Àí à®ÍUŠÏ5½>ÛC±jЏx¸¿ÜS€ÏåDÀ½¢ -àÞþ<Åúàœ“Òª4¥¤‘Öh m)2íO#î®&á¼Y c‹˜ÓâÛv•ÊVv0þ_=YÛá€oÅ­„O£ñ[WÀÓØì^áÌ™¢ìkC¡<†L·u®“ȤàcÍ™fåFÒ°;¡¨fÉÑ\ü«'[õôÓn«þ•n3÷"q QÈ@ô+ƒ±Ý«q¼oý©é ¡µ9¼ÌHË1¤³Ä9Ÿ^†ÌÌ" kµ‹är¾œb4²Ÿ[¹]IJö;Œ5¾ÚßJHn1çØÏsL•ntêÁê ½™{|vݽi¼ ýo{|Îü«Û~ó€Sõ/~\éˆ7þW¿Äi®Ì -(…ú(å×(ëTVù­¬jÏP‹ƒ%PËΨm^Lñ -€¥Kˆ:ɦöD»ÿ?Jè¿J¨Žÿyˆÿ_1ÛÕõ9̾Á#ʽs÷YáZøïHí‘NæÓ[¸n¸‡|õ¯°~Å»ëR{æÝ 5dªI0„¨8„õáùæ:³síïNÏÊù~ÒŽjáøiBö1â*íeþà·ôÀoVgºùúA¿rèW ýfkÞs˜‡YG÷v\ßÜãúxÅÃAr¡úÝGÒxAœC8fCôÜKÈÓ³R§a!âŸÚPú=Ëi¨R»R: ÈÝ»emç6á6€NãÍÉev“ ë¤j)káØ¶ÿÿ6©ˆ¦  òGý˜{·6Šü_7 ]ºì¥’4^b+¦tÿ,/éÉñS…Çlýµ9 íó~ïÞÎçÿš'Û Ó¹þÉ_m"c°N¼ñóWÔòÙØ K-¹‘”÷Ùþª£Ÿ×~ŽL:Ðli¾˜™¿¸ÿƒÿG…À#ëŒ2Wÿ\È&ì`œåy­pŒp‹=XgCÙ£]ÍÜu7ìü毅õ)øW=­ Df²÷DtBm=6j’‘>Nšhc,\ÖÛÑ­Ë¿RÔàQO¶ øù_µV‹öïKí"íOO»¶? ýnKÅÃ:Ìו¼Ã>KíDæÆ…!çÖƒäfî'/M7¥¼6ÅçùÒ$ÈÃvtò3^ÔÄ µqäãæ»?ðèÖ’F£ï̇Ïn}7ÔžýdWïAvšÁú6’./ÈŠ{Ër´êy…gœâ“ü÷…wÄÏ]¶¿[?Þr¸Ý+öª‡[ #vÓq™-m4­däÅ$@ŒCtòüë8©úïÑ­agGrbç†CÑ. µ{…Dj…ï¦ÔŸM­oÿ>Y¹´#¯çåNõî¦üìvñ=:é´° O­Ú5d¯{än'½®ÑU²ÝÞÕIŠþâ‹Öm\Ÿ?²÷å'á¶ä0o ˵¸ãë‹’}3fîSÑ't1Ɖ¿rG·àÜùCoÉçIV<¤.Ïä²ïyðåüw7îåÚÅ7—g9¼A' óHxjä±°É2T; %¡-Üu­u›÷Ý–’_՛à Ÿ»¸jDóå½i¦>Ç„J}έu\'õÜd}q9ìíO:+ÍÛã´zŒ.ƒY9f½¨µÅÔÑ}­2ÃQg,ösìØèa•kµK 2a‡Ž2ðEóö¥ÎlÚbÌZ}CJZjV¿7G¶þnê9jLÊ5´aòSú®Ï»;©î(°¬Vj-(ªÝZµ{XÖHE¹Uoˆ©2þ̯Ä"Õ¯´îø Ò†Øá_QyËžäýlcæÃ±¼mÚ«gнu§MtwhíV+(ý«Z¿>b»3ý­þ-7ßiç43–Ük˜Km\_`ƼîlýM°&ýCP~ .µ6xÔÈ(ÕciŸ«2óY‰‚\áßö»â¸îËp!*Ü걬qö{Ÿ æxF-S„Ý?ÈR•ÎaY©?—“|ou²ÝÖ g¼J]ZŠùP„²…Ö“3­Eçš–Ãðãë(ÊœZ­ï5J ÃjcܤñK’íEÁîÍý½Ùúrks,?ùÛµ¬u?oïc+yÏXy´;ãgškÝßYÚnÏñèÇÖAãnÖÞD­’]©BÍtÇ&È\øÅ–›¯³í -¤ñË(“AëÝz:|m•pÙfé¢õåçêè¡ò ð± WO‰W’Q•¯ˆ™~ÑW¨‘]~žÕr‘;7½hrïz¥Ô˜¸³enæÚ½v–[îàxÊÍÞ<:idaWwÜ:`„h—Ð1ÏÒ©e¶kar«Ì«ti½ÕÒ¥Ý JaÔú‡Õk\&m¸?í¯µY-”N|± ûY:Ø^rHÉÛï*7Ý^*bs›ÞÉå—¹H½IwGx¥ä-¸s)_tsÒvʨTq°Ù°«„Z‡º3°ÒabÑïùÚlۣɧ¶¥Ô©KpIbúŒqÿ@†¡Vì@§HKã»>zvÓUL×Gy?…Þü‡¥—±&’ãîñPª´ŒR¬ÖqdIUãúö6x–G[3ö&éîÎ#þã”ë,bocƒ´I£–úN³¶fƹe¶V‹4~)] ë´;®°$õòC£ÿ*L uenõq¡ëúõôL!ŠS{¤áÕÝ×íÊP[„T¬¹Î¥X{š+ÃX š|N_ÇaGhCéYË|[®5¶á?³ål9sÝÝ…›?;~vÛÕ×úa1»0ÛÔ>_º?tIÎdc`¡š¡!‚¥=ÁÓ Ì -ôÌ¥§Å¨ˆÀñB[,óÍm–Ÿ?°†r;Tõ§¤¨âVÇUö9¨«ìéýI©ÅF©%¾“b]ÿ‹Í4˜Q³S<ð2ƒAûÙ=xõ -nH•Û€ÆËƙɸî@¿Ú»Ûâh5ZÚÎd9I{L.²wc°õ ýC˜n4Bº8MzBÑŠöEm ,Í| uѺºažŠ‡n¤ìK‹­À™«B}D>5ŽœÜ,ê¶ÌCWJ¦öNJfRJæ”ý§›Fý&É(<õ Äí·¢aß hýáøUðË8xT¸XìÈÝ—nægm ÑçIÄçµ8So¯"’Ƴšauã´ZaJ’²oۿ­P·¨"ŸfIKn:èPæw%%ñ4–ÂÏJb©A‹wò7—ùXo‹JۈʈÃE¥u*‰J[þÂgkQ¯«ºg€w›vG·«ìħó%ºm1góX}¬Óìš”nSvgÙåJÏÞ{;m¹nž5 ïÝU‚?JÐ! -Íoq¹9½°Ò¹KÒ%GRx¶=Il…uñ®Ÿúb?‹ÎEeW°Kg¥0bR(¾º"¾ÉOJÛ%?1y~b…:?±³5ô?íµë„÷SØÀ«r¯.yÏ~uÐ?z²¨7NF±ñÜçP°Ô¼µ¸R‰wøí±—˜È­ ÷.53+u¨:*Þ›-Rìs^TÁ½(¼¦9[YbUЩfG§eÄOFøž7õÚ“‡3·7ßûç×€sê9—gë0ç4í"çx›ÊOúÙÛW¿ÎÅÉ&TÞÃvˆ¦?r+¬[æ¼Õ*•߯«fŠsq¸Ö°X\¤}™É-ﲕ:ÕíA¼?îWQ-‚·ð "èU2d™å'ÇNêÁòæà`òpøÜÐMÎÙ#._ݬÙ5EÝØò3BÙ—™>¨0;C05ˆÌÎÄf'^ŠÐg Õž?(3Ϊ n¥fí|[”Ê“M‰uH™ÉýÀ¦¤’O}2NZ!o¯”ú Ìe¾9šˆ©³Þnk'èÉ$á§âêÉ›÷k–[¸”sQ†æò]d×aKg}qî²…û½Îì
¹Í%C¡IB·Eø¦Õ½@³²U¦âçcDůó“Šã¹H³lÑ£Y´X¦®ç÷·ü¢9jçìëž3ÏÑV¬½ã­?Eg‹ž”F–[Iiø=5X +ÍxbÖ鸸uõÈ®cóÊúã·ÅÂÔf;œ¡êŽ>1ŒJ7bÓ¢Ùq¿J%ƹC…1¥„yóDÞœW†ì±MŽ”/Ù´s.ÓñìV¯¤¼¯ ¤<»Ûd"RkIvï9¯Ë׳F¡³ªUÔ«Pt «3e^£12»ëS+ÜŸ'Y s+q°Ìùé§0æÜÛuÌâ«ë’ ø÷Ž>õ2gºiKO*Yi*,ûyJL$òv=ŠéYz½Œž:E¼G¼r­1\ÆDÑçöÀ‡o<:^ªG&±B‡¸ü‡ø‹—Ê#/é+Fg/Ô`# -n½Zžnª}ÜÈÒ`>†‹‹Óô‘ΗS~’ä¶ŒGkÎy¥Â„ Fƒ!Íu3òî5Vdì‰Wg}!FÒõEŸD€¡PÀ'žÏâ&6VqhÛ…4 -Ž\rÏÃ[”VÖ·øŒ²Eݦ& ­ªíºLItÛdt [–/¾µ¾SG0_öÐRC‡0ŒµŸº® ý“öæ,‚ ´åsZkÉ}xJ]¦ñN{Ÿ<¨Ô‡Áa—]¨q,äÃÚ [‹ƒV¾o¬ÐèÎe´†º:JnÆåü±òó RŸå™ã6ÉÅM5—ksg%Ç_½¹öðr­SXŽŸ¯Õ\ë‘·¿ø›îGúÂu‚ÔR—ž[‰Ð‘›©Aµñ”zûYÌϥ׎­–Ê+ºµÎÌIuéŽq‹ø 0¿½P*Ø,ò'ú´É³£ç)—è-f¢™Ï ³eêÁ"·zVAzŒl#rÜmÀÏöc Eçk÷Gº\<úôib(â–S(*,!ècø¬¯6¬µ¯N-yæ:ÞòdÙÖ%‹‰Fô(EÌ–aùâOœ{éZÉ•—БŽëΆtã>þ«äo£|3úȦð«Ö\Á#ª·KqJàâ9û‚A‹†  oãPéÚã!¨›Ù¹BU²ö«Ñˬôx)gÍg›½Ók•·ibüê‚Jr»€ÊvH€mWR¥Êˆè·\b‚[f_$}îæI•) °¤†Úû+|3èu&Ÿ¾¾ÃgÃx¥®Ô"ß>SäþŠ"XŸ†éèCð0XgÖ¦¸ÉÜìÜRjhðäfò‚Q)*JŠÄäÕ褈W€¼Uӯ܇­È…7äR¸rDq€lÕô ·ìø|#õ`[ý§UÒ½‹¤í𘔻r6÷U¼Ø€>©Þüu! žs¶ÛÇ>ßúÔ6ÈÀA–°Q>-²K*Z}ÕÞêXÜ¥΀z,ï€z‚  ‘"–â÷=¼€¦#…_Mqš)ïÍ ÅJ4WIÐ) 3» ³;Pw©œ¢Õtkt¦aIqbë®´’•$·£ÅúÁ8ÂW`ZÂ&€0#Î}l7ª_óBãqFŠP÷CyëÅjÏ- /Í`Àé˜zKÁ>Ã{ÀèQ0å ˜¶&§Û€‰ -Í `&Ø9ÅÌÔV3Ûéµ0½Ý0ývúÛ IL3—~½éÖ{4KÆ~ïjÔg¤J£Ì‘ç p–¤¯}©±}=òb­óBtÀ? §ß2Õ›Ÿ6§¥Pü+`íöoÅÖ^|ÒAø €íS`çØÃ@ìý¡Ž4}ÀI‡.àde 8+¹ήçàFŠ8=àŽN€+ª9Àé@K± ' K1ã¶Ž6~j-¥ÒÙ\:£2‰& d©Ü¤ DrÁóR[Í#ò@ TªeA¿¾ÿêɩܯÏýEÒÈî3ÅÏe8À%ð^9½ºæÄü -ûÕ`ùW8BÝ€ÀÊLŠ«„â| ½y‚@“)ND«â½2æÉ¢£ÆÓ¤.eP™£Ë’Eé¥Z±°«-¥|÷±H ÑV–ÈÿêÉnL~™§@™ü_ñö‹ZGBw$ÿÕroÐ*:aˆ~ÒbdÌø¹Æ@âº9 é¼ ¤ò=’?\)lþV–:²”âZRͦØnŠtÉÃÞå·I©Iº­*Îw.ž‘24‹P¡)›ômÿ,àoIà_éö~›þÖOþ÷·ïßJ¼ƒ!»ðÈ×ò(üëý÷­iQÑÊ6ÛÊe}j&í/›@%°.PÅá*Å'þêá_|õ±¯‚û÷!þÿ³eæßÚ(W¿Ýg…êûŽÔÚð mÖЫ©W¼Óe/µG[HNZCNŸn1NJ]3¡©w¾9ÓêY^/š§!¾ë}ß–ÏŽ#ŽÙvÿóŸ·e­*ÿÊ ü©6ð‹Ô Öƒÿ(¡4ü|Û½ŸÃ j=Œ%R¹[[*¼¹Ga|ÅCjñ_¨>¾MØ1zŠ…Yör–—·ûiˆÝÞÇOõ’9Ç3|Ì6îè!)Ä~)Þ™ÿžsÛ½±U¶Ôì`lš¹WyøDk-ì+ÓUÁ_+9^2ËgˆË¡˜Õàïæ~÷þ%ô«1ßgùKáêŸ[Tr’!NŠUý|3[ÞiˆVƒc¶.µöK¡ÐÝùÏÇ`€ãxs2wÓ »Ø-×!ºÞüÀ«[e±_õˆÝyù¬ïþ›ÁzƒÙÞ›[o”™- Y¡³†7Ý8ùá_î“}EÅ&¡ÿÿG½¡µû펹ž´Õàz°NÖkç?hhKE¶a糺ù¸|@[|š÷Ò¸ÜùLºûsdp©ÍÜϹ1C£{8­@÷þdïÜÇ“Í-¢SÛFì‹Ç!S|þª£í*<ê føìsæP{?ǃOT;ÿOêêóWÿ´qð¹}®þ©u<÷²òt†ÄhXzù¬æ£…_sëö>ÌÜwæ:Ý…4›ìmŽNå,±»,1Nj8;N¨0êqˆ<’/”>Jxzú‡à "M® Œög¥Æ°ÌÇ‹ž—ߺ› -üìVˆZ¡³ov'`·ƒu¡7á©Clàïæ‹o?^ñfùpÒ–§Ñ–‘•åÀ­ÙR¬ÉÞ$œ¨‰bµqHÐQáÆÃg»¸|zÊn`¼ŠçþL/ÝúvÆ{õ–V9Óó:ÒÝxUôîV°ÑÙW;l' 'Rxj¬ôMÎn;é`¶ðP­ž,›Ïh“4‹°‚6>KÔnD^£~ŠGÿpC§—ä4Üûéµ+Ôæ¶ÞT'û’“ZËqâÃôðÙœ²ƒHêJý™6.õ<¨ëu7δÞÅ×»n'À£ðij=Ç«vÒzìÚÂõzlÝz™KK~¾ïÍç(ÿi38܈,¶Ð(!>_ŸyÝbÙ®½8XÖòí=9³Z…;\jøuÂÖð›hW÷½ ø‡¸cÖ“M ÑÃy^•½ñ徇à¹Àûöá”öŽÐ„»øŠCÂSÀÛI“æZ·§¶ä‡bþÀÍah5>›J½Q›a#{ê¶Úב÷lxéåžk•à–Ú/•úTƒq1_¥ý![iîOEÿ¢R?|·ûå{%(÷¥ QRY¥ š÷:Þ«?ðws„g×Ó2ót:ãëÅ. àÌvÉu‚„ü}µ;å£-.zïæRAc¢Ö õ¹ÑbƒUÐWìµ2kU}ëÕÈÌ=¨Ö­{«Êй^¥ÕÊ*ÆÝˆÑG \UhF‰%I}»Xœ¨U,½‡¾6÷7MÍÁ?-w7VjYmÇjÍBÊ6ipJ5|§=¦f§§Vx—9…I±))¤˜_D?êœÛœ:¾PkH¹¼¯>ïOw¡eNNÁ–WV}MÌóB–:qnôëôZÅâQ× ï^œÌ*©å/švÑæ«QAsZgV]]Ï¢Zî¢ZP[ÙåìšR]õ:ò‘?Måú:|ÿ/ÅuŠ–ÚÌ $ñ>”@Ä’¨¡‘¢—¸6Ñ{vüÀvùì4Pç*×÷ -ñ¦ÛÉÃñÝÑjðÙeéê–ÆÆÃxõõqî§…í¼×Î[Í9ÝÏêšwµ0-egI9¥†˜¸|ôFn`-If.+CŠ;÷ÔZJmKlIŽÅns¿e]{ ÏÌž¸¥ ƒý£%hõÉ^Ð#\œ”bŠ’ñÅp:ôB¹6²ë¸õdý§1B\wáܬÝ>˜‚´_ƒ -1úõ̹ß-Â×ú@Ë?«ÎB©U[{ùxì'2ËŽi°µR;¼æÄëý‹Ýɉ²««Â“íØÂ ÙÖmBôùO5\óù¹óÆ3Àøìð£òÙÑ žFâ3ÃÜp³’™î€tïÓ¿üÖ•m}<Ó¬¼%SŽr*¨z3“²µ7žn´Ð³ôa -3Õ®ê/ÉŽBUá¡Ü8©ýɯ$!’ŽbR/?°ðtÔ‡0ÌûA‹Ç(ÿiï(>*Á_‚¤Ÿ=´*ܬ~èp¶&.8ä]Øå–˧ØÈ¬W3j,FÃKÖ«3hŠyºÏÙjìr~)öñ!6gfÞø­8mV(o6’ ;¨0·R¯Fíô¨R™kK¾ßWñ%ʧp×”BuÑoëÝXxî3¡Hï6üçü8ñ%þ}å¡,óáf3áì Mp9zÁ³Ëó£Èzc>}3ê-f‹ß§LåP:3•@bˆô¡˜õSçôAú`”E†h´/º§èÖþf§Wz…t½Î;cqöõb OÈ®h×Å™J fOn†×vz‰N»!¼?€bõùÒȸy¡±äìÝxÏ®¨iÌzÏë“ÅFH–ÙêÆT 5–!vc…>Ô®6]W¤M¿úcê¼ÆŽTËïd¨V…æ(žXzäåèF?0ÅS]ˆâi’§Z¶£~ÑÕf£^O‹~1ÊN带¸Ú]ô šl5/&fróRèŠwgÜFéΛ9´Á9<ÕcW]|ÈaÆT=mØúéÃe|§ë= 陣b„¥¨ÖÒ•(Þ›ä•ÏdçªH©»ÚE|ƒÜ™!Ôµmê†ê„ü³Äƒ'•/Ú/£X¯mK-C³s%ÙtÒù”ÔÌ ):D´Q‰ë}*Iл+èF­ÅÃ4^g×`SevdÒGíÔ§™è4¦â*º¢ø#¾'¯iDvY÷IJŸ!D<¢}˜YŽÐHCÇßÇA‰ããÆ»‹œ³Â3—Ë£0ÖÉÂtF˜ËÞ÷ –z|¦ƒ6gޱüEËÌZµS¦w5ê`/V?Fqc­\7rŒl§Â[½öxsï·Ùuñ“ÎJ†|ôªTœ„Mò…]Rvšb§„ÖÞ¬ñp;âã;zÃyàY'Èfù]°ÖO¥€„š‹-Åqsù9†:WtS,`hLŠ(>-…(>óSïõï8nˆš˜ëJrµãûoò$9Õ©‰–ªô..0ö¬’íÒZºz“©YÇ}ÎUÜ©un *a›5âÙ§«x”dxöFw ¶B ¹œ·À–«fêc^ycXáô@77 B+½"ŽJ(ä÷¯S)¬åzîŒwǹæ¡pN±Ëç8W’‘ zo —B6ÉqúË5Û”ðEPOH·l&MÁŠ›…¼16àߺÖë*zVØìo ¹ËhÆ[íÇÅ[F‡f·©ˆ^€Go¬Š­äs­rÛvþÐYuóué2Î3¶Ì—â>×òì$Çc½7rÙrH§K0)šED’“Ê ß_ÚîŽX5\VKy zÝG5èõîáÔ;ÍÁ*²gᾫ]{g»¾Csf75ѺU»¾ÔZÿy–…@Ù -Ù™sè>ÑÍ®Ð%{Ÿ}Ÿ8vê]òYPCI¯_ÉñÇjéj­"}úø­¦ð òÚÀ‰RxOè}Áиq!!ƒ£e(siºYK:…Yøi¬2‹áõ•qy—ëîÕÊä÷ÛqÆ%öï8ãÂ4–Âc|£k”ìg3FŠhsúTšù[,ËØ–ϱÕ¼Žy_²}"Cl:…|»ÔBÉ罞Ìz~ºõ&UêÝì¬nö³6Sg–íê<=‹'ô·ô¶Á¦^“HŠð0Ru@t–b÷ø}–”øt¾?>ß¿¾PðMÎC“§nÞ–ZŸµˆF`‹·tÏ.ãt}jàðM] »)aaT@™M¥z¥NN÷<\ôÐ…™•ãô@/ôqãG)Êó£- ³¯òI1 CBŠŠ•â“Úd@"Ý sÒù Ÿb^ÄÇK±¡O;JsèzT,@ýOµ¥·ß¢®l®dï¶SÞ9^¿Oq…U)€2‹ @IŸ ä$ý zð›=HÑoÏêøAјtZœFÒ>ú•°xWß<¶b²(8‡ÂŠ»nQ5–ÈÃ~Îa£0œfO{ö¥×ÐÐk h’ž§(m-…G@›ËÐÕçãWOîr@O› w7Ð7£ô'-˜ÌIŠ´¿éGô³”x¡=@Ÿ×韈Ûé¾c§84Muæéŵ¦sòõjëPXZÞÈ×KíXa«&( ¿OȪíál,처•¤íÙV–€á–[ÀxÉ0Íç0#ô ˜•ñ›ÜþüÉ f™,XUcëŒUÀ -`£.`—Ä*Åòù«ô®<°k*=´~Oî€í¨$`»ÀNõÿdS7 U£“EqÄ{ˆQìãn(”ËôŸÈnм¼þ¯žl7n€-¾g¹ÅoÀeÿI .QYÀU¬<࠸͕ܥ «Z€/Þëÿ)æð9¾ÝƒÉ"à'™t('‡5à§ àgJº¯·/¾¯õRoœ°DKVGã}Ðè›0„]ãô õÛ”? ˜Î•¿Ù¿_ñö_‡2î¿r¼NRÒÂè™<ÎÜ5Oßš†ÏSï«TßbVf@é¯qºw6ªeNÒ6ÿIXý¦ƒ~Ÿÿ&Úî¥çSgíƒæóidÆOmƬÙráx[’ïäºa+>>—Úý ýV]e·0^ ¿ÈŽ*Ò|¦ÛagqŽLiáÿ¦®óUöêŸó™¤ñXÃç[iP8 ó]ö>~ü…z°ÎíÒÎ -ö–š Þ†ªkaÔÿä¯>ƒSw9¤oÃŧ•D ãšÌÙîg3·žŸÃl©#—:Å_Ó -¢ “½ç‘“ۨѩ&{Îì~U¸VŒC~š ×.ò¿?þkãÝZÓׄíÇ›“¶’·{4¬¬6'#·]‡9(^õ -¹çrH …qÉ sëþ&gî;f¦#¦ø,V¾êèCŸP««Êo7bwïÊŸ VáœG=žŽä«¹åê~ðŽnƒˆ¡þÌâÙ¾ËÕ{ËòpÙ[úèåRWü×Âø[h`¿dA{-,vµEĵÙRNêÓ -Øw&|G'÷³ŠØ-z Gä2’ãç}ø 10Ôî9h©Pn`¼±Â ;¦©ÔÕ¡¹>2Säž—SŠÝM¹dÿÀ]|W©vöµY§CNQxj±û½ÖžíPÉÒmáu Z·±}Vÿ g8³oca\›o÷ŠY[ñ>C>jæqÔ£ÞâpÈgôA$ÃîÜŸóAßR§çÁü°»q¥iߨ«N@HÛuÐa“Qã{;iWßmáV˶n½n¾%¿Ædó9ŠÅf1›1‘ÍøR®Õ­ÏʇEÙ‹·`Y§Å8ð˜¨xl¶ ñ­õEr’2›oÓ›¸©D''_É'!;0î'¨çe+p·’ó°NP¨0a“ªHí¤má2t[=)ª6ŸƒY³©}¢°ñ‰6ƒ¸•NQ#;ßÏê3ç¶®Ûhö,+Ù8@裶©¡Wðj Db•ºgíJS•ö½›ûI4Îú¡)‰¾w¬ôÓÊýâX: ‹%ˆÚ切æÂ<01+ß­ÜØG‡æ‚S;iq«—Gs¨Ü_HÃì,×íÉË2#èŽ/Ö6µÔÅOºWÝ7j•º˜õÊ©S +M©3ð“A'òCmµ*ߢóñ.+0ôðž 3çÑ>ë~¶ ä–H;uMŸìÞ…r(êÌÎ 1ÅTûbÃÛ¯þ䘺kóSú­™iõÁ¥1ÙÏw èìlêv7¿M‡a±­UÄÅ9÷g÷¨œâTáDóÃ9Nù¢# å^¬)e¥íÞKh8^±W+{À×ÝÒjºP99ö-¿rr=3¶Ë¹iÆÆâeU…rÑ"n—À<Ìgópþæ)Õ‡hêäeõ‹Å´É·GƒþKëÖÞÒªÏwݵϧZMίªLäG•2¦•sßXúƒÝ–û{£–üð2/ëN¦!êÂHtæËç8Í$ûöêr*ÚåÎÓ² 2ê[Õ!^7XºÕ·i)&ûRë”×¢]ì¡Ò®ZÓ¸Ö*‘KÑãSDâÓZÜȬ¥´{ö­/Ãfê]|…Z¿G e­õy†\éxú)ºS,˜: ²²²×lå`¢ÞÕÚ•6‹œ`óèÜ1³BT)1¾Ô9ŸöEÃ誶«?Öa]øû~ñ}G©w1îûI1›ßCÚt#sš] ‘: ±ú ±i6…ȉ4^í2¤ÔŒ®•\-ˆ‰»¯t2;Ø™³†{cBmwV-k·ÞöÍãm¥ãÂ*—U©=zŒ[$FwϽô'-eõÁÙÌëÚ¨E£L‹-Ó‰¬Í•¡YgPÖ–ØR½L3RÑÝç¨l/]ç_{JÞ SÞoø‘T±WŠ ù§›+ï·wnTœlüðâ.øúÊÃàJÇÚ^=3?Þ8Øò4±’Ò7›ëy«”ìÕZIxé¡!_™ž>´ôIñ³tV©‹PÞ³ÛN¢Í:Û—†6[|ÆØÎ¸)¡$œyØæ8xP’ÙE1Û`z>æàÑåàqžþ¿›n}µ2Î5¡*]Áî¢AÆVCGWÆ#¢Gºþêt´|¬©«_Vªó¬-××hMjí> ñJ}BQj!CáÁSSAí²+þm¹G~Œ4.¼¾¿~`>Ó¼!Ü”G ÎÊX"ï»à:ë*­ ›ƒ3³^·`ƯäD'–ƒ“»ã×Ú¹GŠYOS“ÉïZ¥º/o(_SˆãûñÉlwnKýåFé¬D2f¨úì´¦sÜ•âÖÆ¯ÝeYx ‡UAÃ?6g]>³˜ 9‹ÝÏ88¹­ÙE9²®©ÝY2,Jž -Ìú„pŒ?t /.ªô>Vº¶›~`šêIYê¤lyªñö*TãSÛS§¢œhÈ/Ú¥¤e£ùzÛµýúT’³…žU.C µ¤P¡ÄZUj·G”­¦)hžmóFµäsÖ@©¥ã‚h^›u£JŸÙ@•1ãÏ{ ¯ï÷ôžy&tíþS4ó4µÙ3Ô©ŠjTSªùû¸öÉdhnÉлH±PeˆÛóˆÛÝ’"ñÉ’¡i_üÀ­FêÕ„ä -žµÝ¾¬f®{4´¤9/"£íPÙkï–”8FU”§K‡8¥ÄÍ$È`—.æ1ÿå3øþÐû>Ý¡©77¤NãÒŒjúá–âðÙ™LöI#“aŸ€R$):'…¸£”Kô6Ý¡t -kü%^øðÉPxqxpðâ(YáÃ÷ à/Ç¿hZóµZ¹5°‚‹;O3Ô݃^½¹†^ûùÅ­X¼­…jêE6g›D‰Å˜m‘!„•EÓöÜ£šçmâZ—^ï=Rìå"ân++¢Ÿó„²ê]ñWë’ÁG†áz¶"à`¶· -‡mÌüt^€õ7‡’Í; ›w‡‹´wß)öXÁÌ&…ºn+ßj'ö~£ÞKJz“}…oeÿtúÒZ·„á>[ár¦h1[æbÐu¼_¤ZJ«DvÊ-—èלòL¨r¿†ý׋«!ž—Ya²Ã¶³¦Ä˜ -^ØüyÈcÎc±¼îéè:s -Ðò̘ …àqA õ.–ß±¼‘¯n±(¿Ãžw´ {9´<êaAÒßj'Ö™`îFq‰îÿßç¢Úÿ -ßüDþTÙ2YtèÃúnR¼R-ýkAÅÇc¦\Á-X@ªb Smb.\îaùE'B×í -õÙ×-\è´Çò»NÊ×Ì™§LÍ—ÍJ®QÎŽrl¡}Bâ=ޤˆ5¤]ß‘x1¼æX„Fr ¹ˆV/ÜMøV;1Ås|Õ¡M²û#{_&L_x­Û-n>Lª¿z²F;Të¶5‰GËÑqƒÊ¨ØbsÔÑ tuò{fçåkñµ–§B¸;‰Ì ׸ëÓ;ïl‘ÄÙ&HˆBŸå"l¶|ëˆÜ“–}8õy÷)niô -=[ z¶6,oì8Å‚{Cý×"äaãu_´Õ!Ú*Œ|˜Š8ß2B£Í–/ûÍ$œGÊôÜÂÇW§T@š™"Z!J®Ùn”pæ§‘"]?Å®ßéGîÅ…VÚê*Eÿ ½„ó ÞÉkçüyÛGmkv)Xsô`Ôè½ð`o¤5`ÄÛ. )šyAþ,!¿?+BÑk·†¢·ù€q€P–2JÐKzäoЉµõ¡ô§õ]C{òÌÏ4Ež›W¸›&™¸1ýü4›!ƒÑÛÅV;YåÀeyãå’*ä -D—ËðxÓkÂéò²Mû ”m kˆPôó - -í2´±amZV‚cÐV=´Äßœy@ËÃ< éÁÐ ›ÜÇào̷׳2ÀÉQöÙ¬¹ùµnPãH™,ß}‰)!®Íkl¿+'ï>íOÆy²Dé>~Ü=Ò˜Þ침uúð¤Q+@ē̆‚‹ ¤j‚b0òr ]%È^l`À¨ -Ÿ`šŒfÖ<ç€Ñ‡oÀðîÇ;Êt 0¾ô{üUïõ›v3tl3:6Îp~£Æéî¿„oöŒô´ÉVÈÒ|àMNÏc†„æXÝgáÌ«—áóÌ|tkÖLBÀòÉ0¬iÍ4v ŽwÀZ:ØìB¬-¤Ì›€ur+À¦x8ÁÕl²N,›ß–sHÀ¢¡˜6ÖíÌwlUP5é‘uw¶¿Â·°ØŒ¸n_í1huÔ"7‘ÜÀ›—E}ö -ed4RJ0^y€½6ó€ƒöÕï&à± 85=œÛš®xÜ..€5@‚ûçÊznì ÀMr=ÀEè>Á†Ü´ë®g¶WÇŽ€kÀà -ÛMß´C l„gòö :‘8­´äõÍ&b2%iHn‹QZp—09*‘xЄèªUÜI¬žH5oUÛ ÆÀW_SÀ÷Ø%àgÎðûð øÇá^Ã`Œ$ ˜¤› ]‚ÅM¬¯@°[<’Ž -9þ(½ôæf©Qr©© ±–bDìà‡LÏÙ#«[&ªoÛËÌðVçpQ].=x‘š!—œQÖ$ÑjŠÍ÷(\Üçà?Âö§¤ßB>©ÿŠpøún?úí¿rTÜýŒòOø‚«óÐsRûÏÖiåÒšF´s€±ë„ÀxävÀÄ ˜8êS˜†À©ÉóéPð?âÝ÷Jþ¯ú›mû›P,=ûЬýa›ñ=³ž-nd­½¹2aíð»eVí]|œBØ~Ô(ÿŸ}³<òЧîŸ}³*ú.’ëÎv®×ýÍÊKë­×?y°ÓçtU'G`%­3ú2ä†Ù¿_ëê7õ­«ù¡{ÇŠ/ñºîÆe«¼¼ ßaƒSîÕÃ37mîã`ÐùA÷pµ5ØÎÕôh³2•Éz›"¦k~‚.Vu ݬ¤9}X†}YÜJÜsaìUxÞ—lr‡EqæÞ{Ö4²®…)6Ê¢<*¿"r¶àÿnŽõg·¬ÿ6¯~Œ¡·ÜÜεGúv4¢Óm?¢gñ.saÛÜ%b×[».­¤©©.nEÖœ?+ˆ3·Hz6RòW¯r4·ïÿê¥ùÙ“+ ºf†ŒÆ’š°I;ç:ÆÛÌ5óó':’Ö[|r[z¨îÏü #¤q¾iýg[}û»½¸ÇäznÌ—º‘g4»>RvæÙɲC.—Ï•õAk3»·ÝÚÖŽ½VEö¶¿à/Úæ‹ï,~kÜ»ëzw©Îjþ nôS“à©Ñ!»ö†çâ®0¸UvÕþ³±kõû*2êL0ëFÎ=é÷¯åŸgÀ©CÎ^×ö*<ÛEƒÖ¶D ­ -'’á¡&²a]J+ÍsX²›¡6Í5nÝK­aÄì Þ÷ò뺃"P-žÅ^m”o†5ö~1Ê/û?è×^»ËìåV2w¤;YÙSqÒC¼·x²YOº“‡·|;À·jk[¸9­ -ÃäÂCE*‡ÒQª7CEm7nm©×èƨþì«Q½ïØ‹Z<6ÖµQ:søAkZ»U£\ó]õÉ^™7¾Bî³¼ªseæ¼ KÛ–¾(U ‘(ñÏSâ_fé/¾q _=yÌOE¦ß—aÐņüµµÍwÎÍsÒª'ß-LÈÆ]‡ëö[!j#7Å×<¸¤V#¿jU±EÃ«Ì Ýl…Üt‹åUy\)ü¢QÚÖ§­REÞõKüå> êXÒS>CÇ{nã^è¤ïrÁÄœôšïçGõ¼CgÖ¹x«’ –F‚«óÅò¼‚ñ6Þ)½1ÐÖ;\ššu\Õû&:ªyà:­b³Ù¢’'—ÛrÀÎN¥muÿüØE àbd ݱ¤{-ž»¼TTߌZ¸ %³Ðqm7ÿŒŒL¾ïg -y/Wr£B3Ìyôr˜Ê×EÖ”³?oÔ`?¯\Ÿ¼¥½Ìª Ú™À:_3 æâÓiUÊþ ý× å[»[.®¿IöP%hzV.ÙÅA©r;·‚àÓ)žÛù^Q}ãBÇ —ù縿Ï;èè’åFœG®A6 -ÖhÖgdÛ½8^C$?/±zf -Κ 4/›Þvƒrº’]u¼Ã -žyõ°|ñd팺áÌVÜp®ÖW €«-q&Ŏżãt–ºK5zzt¯, …up -’¿ýQˆÚù7/Ôòƒ>^OÞguO¦£Újf£Ó¸ï/àiäSþb™Y ¯C†m¼.éŠ?ÓUX‡ÓB”&¼ž¢=ùØÜKcl¸:´vS÷)¦,Á ×±9ƒ!yr 8Ûãè–¬“ítµ]´ÓµîÚF!NР¾ô²;³%©¼†iò¥$Õ¡åW¦Ía²ÐÌ÷AÙ§‡tΧ].Ȭ·\5½;Í´¨p=¯ÑV&îK-ÝÖ¼´ÿA]=ߺ¤Ôø™²J༹ æ oÆIõ9ٞŴÓã’o͸iÕ"úÈÀ\:ÁÖ,‚[lÒŸ76&o”Et‘`ü2Ê6Š'·ÞX3uµñlè'Ùøx?ž»Þ<‡:½Ì¶º®§ë°xÍùÄ÷T*çyʺ–wõY&Hõ¤rÝy‡ÍŽ“º·‡ÔÍ쉵Oæax:X³½Y9–{[ÄÎFÍ¢èR&ýî%3f”Ý™eð,]ÐkÕt¨KÖq®bç¦5WZSKuOSÛé¦vRCô‹³¾Êu£à’œióÞ03óƒu¾—–‹ÝºÛé?Š©~:—qâlœ|Æ·¸J90|ÎÚ¾*XÉy°f‘*Ö6‹r`2¦›—»2Ê‹ÚA?е‹^Û _ºÔ¾aZó3šº°5õVèeTãú¨)Ϩ)}ÿ~V±JÈñ™MF‘ã‹4RxßäøjÀŠ£dV½7”ªùÕ»”¾Ï.|,JkTÇí -V5J“‚1ÏÆáȶòBÞ4W…JÚd&¾oð¿¨êa]—.aK;·¢Áª…éóTÓÐx¥&çÞ½jÊêMy^òÉRoQŠ“~Êòè ¥d¯Þ.ËHŠISzp”ü­ˆIxemJxõЗðZó$ùþ"ÔÂŒPawa\HqèîõKùÃÄk5/íTJ!*ö´VÎ[øæ™Á1°ŒÊ¨ú£íß]Móî¾ÚÙ#jÖ„ºò’̶â„f_^e*‘þZö¦«£ŒÔà§4•4TòãÿAÅ…·2ŧEê±ì ë¾¾JÎJ…†.pLÐJ{L°…„õè5Æ•ŽKó•OW»¯?Ùín§-'ÝzW¬BhäLNXy†pÕLíR[$«>õv5Õ¼e` < Ç99 iêOkRßµ$ü„ ÅES˜‰7™)î\…õö’Rei³ê:¿'â<_];¼<ÖÜIO:ÁÆ“–¹Æ‹MÖ0ÜÉÂö j1/V; žVVL endstream endobj 45 0 obj <>stream -p…©gŽ(7é&ŽSC¡Û²‰Þ²l–î|îÞâj­Þ]W-¯*+©¬.Éé¢nHÓ­bK„ xb¡¡dEZI—„R;]ûA.Óê\ù2á÷¾J:<çÄ‹ƒðÍ -’k2²Â)·‘Ï^»Lȶ­á’5HåÅ+k:æã äûk£/ éÝç^öÊí…òОMô¦É³¹@W••¯öê‚£@÷.å\Z‹™MòÊõ¥Ì×Z;=z&§æ&.{#÷9¶½z”˜§Ž6X=¦÷(EŒ=[lé8÷ºÒ#9…ÐîeÀÓpq¨(V)'# -[ô®ä¼¨2ä< ó¶?(åS«;åƒÃ«¼o d>“®ÞÒgˆ\¦†wjh­0®iÔv¹’f”;exÁiö.h–?‰üÁéñÜ9áØ[-yÝç(c0}¯èÐñ´˜¦GÕa‘öÄeÒWÒðéѦ¢R¾—^R8Ò:’óÝ=&ó5&©d­J¬.l@ãùˆ&¹ Áfb -ßáKŸ`­õ”zþ…Xí;Ï’*®±i+ï¦ßNæ¸û•½P=$Ô@íó_Ž´—#®Ü”Æú{‰ Kež5m†aœ€—h¯ËhÔ”`L*im\ -/fräBm–Éüm’TçÔ'‚×{J°KkïŠ^e®^e?{¥áÂý^À޽Êk8Ø1ÁŠÄ¨–Æj+ÂŽ¡pÆŽó(>Õêo=èìöí¤iÈ+ÓþWøÖ:E&øÊÞÒ¼{t„íÖÒ¹ µ‘ÙΣ(0 ü¬”¨i寓…ÔV&Ö›ƒN”Ê7ß=ã4^0\LÕÍv‚–¬±E7˜RIÝlp´u"äƒl2%r7Áy<ôEºOÞF¦>@õÊ%YÁ¢zUNÚC<…d’Vð”ò¼ÊÌ -ÊÉ¢ä¯ð]|_\½4uƒ½·– -“’2"²dRá"bð}¦#b§~GÅšÎÈÄT°rÑëú™CÛåO¦=jðF˜ =B‡ý -é5ˆ3b+9~_7<<ìè.ìš»&4š (ãžßPFë(Ф5 ¡I[_ÀÐfpÊåKWàÝéòÁÁ³æÔê©®v·ÚueŒŠ?¨´ÐM_ا)®U  f°Ÿ¨4ØYÄo<¾ßtX¬9¯h‡êhHŸ ÄÞWl8®õ3ðHZa÷|«A‘w µ#›µw On€\óD‚™‘ôûä&]d„LDÉCÓŠÈ^±È>1O=|cY\ç©î­u ù¢tÕ×°\Wí$®gZ–oD—µ6´Eg¤‰N.w¶BðXÂBc$$3Ö©÷eÄiõ ±=,ÜT•r8~‚N9Á¹¨š4NÐÞª=ÕoÒ€ ã* ’®P!*Ø*€*-€ÊT7 Ž·4}ÙY/gºŠwæî†Lu=-öÔT±Ù”–Ô¡,ˆ+6Ç>ŽÝ4bŠ\yC“WÇÂ-¬¢æ|.#`ÝW¡l.0­ifÏ´fÌ €.aŸkÞé’ÝM0œº âІ è*ÕHpXºÖA·“×)ˆ-@»ø9A ú¦Ø…ûagœŒûTë¡bÿ_²wûN’s²µ|Š’iºk¹ µ|Þ„'ÔA‡ˆ}¨:®˜ 9ûy€‘nYÀ8d0y» -˜z» ˜þu ˜¸Jи$x#€–$ÀŒ@&Aÿ³÷3ÎìÓ&‰'0•A0Õè˜E¥2ÆòañD´3TÌU˜$_¿7¯Æ*3:!UÀZyBÔiÓ—Ë4òÞ6=83°] g0ÛÇ£Èò|&¬Q+¶œ¼olûÑl$»-Í{[ï‡È¯lÁ¬‘`TìS‡.€}mxÀžª9Àž1`לà,;d¾÷I;©VÍ^¯ç2y{ ¹ltØ;jRK˜ªè–0£¦‘‘ ùq˜¼Î$—Ô+NY€+\Ë€k¢5À õà–¥þ¯3˜'î ÀÛŸ "À×:wÀ÷Arkˆ žà¹à‡è,Áâø°¡¾åU_&— žÉ“ý‰mÎò­ßn££âϦKQ$ ˆ}çíMQ‡ŽBˆ™¥ƒ¤Én¬ßh%©‚N^[̆€¯v;€Ÿ.z€¿_þ±"„´3B«³Âx{•|‘ÎÃ@LX 6]3Á¦Ä0Ýbpß±4À>Jo&ŸbÅMî5ßG ZˆRÃ7åYñ¥ŽáYâF‹-÷„Ñ=o•§D>Œ0«P"èA€ònÚýSMI‹þDKü?+ðg”yø£™þ v¨,c MIH÷ dvöIâÕ3 ¦u —““Ëç+ÈÕbÈ9} -d¼Œ% §Ì¬në\lwî?(›OŒÿw¤Xë­1;Ë/ìz[þêÛ¿E} -ù•’¿ò×üõÝþ5ßþGÄý—~[{ÂxSúÇm‹-Ò@õ…P£í¨Sw— F€:›@íê@˜[ 6q¨Ã• Ô‘]V±{ÿ–ÀNHáõÍzÆ^qE²Öìò§®¯?ùž¨ÈG8üºÿnêöpø«àþµá~¥Û÷¼ t[í}¿_ýYxçx`÷0¬ù賋Y®÷F¾¨£! ºÿUB¿–Ûo2ÂÍzö!)÷ˆ3™ÚÎç:ÿDх4xnNêä°?óèøÝ7ëã-õï{wßyí"© í°°‹m“7µa†U~ÍG%eUÇ©eHOó‹[i-Œc7ï«yæ7\À¾Ýœ/¾ÖÕ¯ú_eþ Ï> Ð{f9Ư+!K^Ê×®=³úéì4>»e)þÞݾóÛ¹2 6L?WZóc»º’f|}’L¸¸dgÑaùÁüYå'sûÄÏg#UÚÌàŽqšf^îã“;šª£9‰¸I€ÅÖx[(WǦpó;‚ø×Yu¢¾1¬ÿ²®F„y»ð-z}ìÀÃÝ.Û-Fûuù]¿¬êpø^ª‹òQGËir6’Efé´0ÅzÄXQkÂL¡Ô¸‚¿Óc~‰æFuýøWÑêP=Ò­AGQ†ýg;½èÛÏÖ©÷PσRbŽ´Z7Ê›¹äxéú„}û¶”®sW77mÉæº¾Tg³`WÒkŸËÙ¿…“‚ãm†™yf3 Ùç~ÐÏçþ³y¾÷íÛáÕ™;Ðs_ÇÏ^ÏQj‡w}èÎtæ™ ßÉãˆÚ^p«ЦßÚ–ƒJ+YÑvº|ž…ÒÕ½6Ϙk†TmªïFô7uõ‹¯uõhDüxuH¡ÅÜŒ§×+ CuóûÏúLþ^Ä߃{·›‰7ù.6zW;äô¶™å©ÛÚߣ¿}Da]¸ÎCéø^7ÏMh×ToЩqë·†ñ¢ßõçÀ@ë”ajñ¤§~ÔQlïU£Q®útuP™—_‡Ê¼J)•¼8~­«ÿÁ7®ayNÏrQîq—“âšXϽ·ŸíU&~´*ÔöJû.h†RrR½…k®ÑÑ^É( ãÁ:u;ÆÓµx$äkÂÕ(KUª>!4ªØRkWòŒÜ«[sTgVfŽÁ¦TQÂs‰¿nã n"T ½|­¦¢lQƒ¥vá½O…Ûô-n³jæ-tr…ÌßyŒÈÞNJæŽ:ääp -ëX5Œ[jZ¥èe5J¿×Ul*yо—W%..œ‹—¶5—.ñ猢ԵšQ“ò”lºWäi»—îÓKÝÝŽ›2·QÎó’ãÏ {¬œÛvƆ61‘5õÕ¥•r{ ?Ö.fAi>MÚ¢Æ:‚Yƒ ]ß{Y½:$ÂT;Á…™Öˆî7M©¶XM©M©»³›s‚ǽUSçèפ\lïr“윒ÃôAÕ÷&73©Þ4¶a1P¨C‰d -š=>Ê–&ï¡•mkÉßb.žf`ú~ͤJËØ€Nß(£‰¾ç¶K½º¿ít±N^µ“lÄš2®êµtPTý@zÊ£^«)½ÉïIá,¿#š’‡þ=+³‡HÖ2'ù=+'ßÈM£`BåòµºËÇC~ì3÷WÓÓp¨˜rP;mGndYs\P,RÞ fQ¯rf(ÆÆnh·8zgtq·.h§ê¥¢)§KC½†HWm{òX5oñƒ*½l~§Øâè&¿OwXv=•a¤nJ“=H™Zm Îâ÷^œƒ!&æ&WÌEþPœÃqrBžê#î羸Éù™Ã0}x/©µù•i- -÷“3¿±_dƒŸ•8½v¦)]òhNkv I½¾QU5\ÂRž°è)½(•—câ YU˜0'0;,9ÃðÛnoÃWl â+jñ•Òã·ƒÑ6ÁöXó¥ù»Ø½×Y*ßx-¸QwÆ\;oå·«”±£]¯ÏÆ’† -«Þ:B5Ž=Bé+3^ŽË=Qvm%é÷#gbI>Øx6¾æÄy-‹y^i -«ºÕ§1X°HÝàw¾RN£¼`¬%îˆ;>W_l[œ\tWìEÞ':>¦öbxkN.‡‡*ÅÏäÌïL—+Ÿý¯?Ùžær9³$ŒRú±Œëš¶*‰ªÙ…¥¿òhb R47( §TVÌ—$AXíÕdvj®%°RÑã·ñ°ÀWFË -/” 6wäÄdmÉÕ¥'÷¦gö’§aV[§9æÞº¸L×ðëŒù¼Íè×¢ù¤_ËŒB¿VÙIÞÑ%Óuœ}y'j¼÷n/ÓÇžÐM bÕZ¬ìœQ-T­U;jªÅ¦Åä3ZFÊ.&áu©ð† ÁýIó;ëÌòÂð,r ø¬rr[ì¥*eØ–èYíݬ3÷å¾ÏttÆXZú@¿îã7=˜Ñ ʵl¢¨*5¾DÓ”_3*ÝÁD*Ý¥Ôø¾žÓ‡ïJ úuËeÕÜ»Äx×o Š¹A˜¬ÞØøŽú ך’ÊŠ‚4´)qÑ—0¡ä­ ¾š !îxŠHör i¶ÕépI¿u$¦;˜ŒU¸¹ô›ÁrôàfWéT§Ü¦!QÝRéSáI¡Í IÎ4Ý sȱL3L,sÏ ±Ì¯xbYØ×HbXˆÈì{· tl|ÉÒ¦4ÿA݇zéØ3îZ1öæÓ×Ú˜l+o¯«Êé%!ˆK¬L ¥5†}MLœ2ÌĬy(c’~7¼dv®ž@Cí@¥&™‘Ce°­O}þ¡OÎjHæ”âð%Ih³&–}#Š~#ÒðÍ©QÄË!5L°:ã¼08oÔÊx¹£LðͶµùÚèý䛥ޕiÛZæfå¯ì­>ú¬­@Óª*å +/lò*9*ÅÝeÛÅbzæM8Dô@P’Ùy²™ë› -I:¾I¬@Ý#ŠëU‘`JÏ:¾å¥>^~Ö8ßߟ±CJE°:½T°:“-`Òö5À¤Ýèüƒ¢¡¤Ñè9ï–0iŽd7ù *Ó§Ò<ú懘›Q³¤«t”QÞç—%£ï”òÕ¼žÊМjm1¦·õ zø"c*s9Çd½ D í|®é¯x/P\Ã3ÙIPÉaõÚ¼ŠÉRÜÅd93GÃVûˆ†FµL[Bî8M°î!¡¹CL÷Ž!¦÷Ì"ñ¬Ü®Ü2—#Þ¯]«OìÙ&•|‹¤øGø†µ£%å†)UØ8áÊ: <² -7úÆ|§à.52+2ÔÔ:.VT<ÊIKóêÚ¶NåÎë¶À¼H’¡½("Émb‡;ÆSm#éYv™eÌÔÇ£˜K‰ ¡¤·'¡†•`çf+ *]@2Ï Ùâ-AL’k'OÁô: q°J0…ñ®[€ˆËÝ×Å7”ÅÎŽCCÖ6 µ?y?)4k'#®Nc›?â´ÆvњȀ•ÄSÑW6 ìTÇq´¬“wkv—cÌÍ4 ÈÅrQyœê ^. Or>A=)ätòl¯?zòyùäÅN~çruy„€Ü:@îðäÑé9ÈÙuÈyz—ÅÛ•Å–]ëN*È</9þדÎé4¤3©\M¦²;R ÊʈÅNOþèÉ€¥~¡`ø=%ef)@ù]P•µ ¨ÎKT—²5õ2€ÚJ â öî$Ál¨ƒ'˜q€:¦<@ð –« VaòRc7é’5)ÌÕNþ|.c¿:Ð,bØTÒ\þ¾•J×eÞm¢f£¦F”—°„Û+íôCqÆM­hx@Z½ €öXÐg:¬¤>zòh”ôêVôEªÔz NsÀ@Ö9Á œ|6ø’ Òúá^} ùä×÷5 —-`•†ÎB¿R¥¡:XÃ?²÷YÎÿ ¬*ýš½‰í´ibUÒQóyVáñº¯€¥$‹€>Ë`’µ$`슘\d¦vKR˜ó€9¸%À<›uÀò§.`MnX¿±Mðz6[ >JoöeÖ®”›W#À¦ 7`•¥ -X¦Z,[Ù5I³Æ˜K]¿4Æ -ºm¶D^ßV9cÇi?Sò‰÷ö°Ë)YÍZÛb -ΤȤÏåýÌþ`õdÌlÙȶíú¿2.{íÿxƒUº¸bv¸á Üéµ<é_¬ÀSš˜`•< CÉ£ðܽ›ÜÞïnΟ>Îá/§›·VÝ^j,1‹r‡»ÅQA†Ô':¤UÄ®31Äî5Í5ÿOI_“rj_ú•n¹·Þø£ßZ¥Ö÷cÃåw¯¯§Õ2ZçëïFo"4góf‚IDÄá~ÿä8LY ÂöCè‘à¤hÏ…¼‘ó£ÔT¯Í3A¶m²\Ø­µ¨%Ç °5¨¥+Éœ Õ?õü5ÅÛù€;Ë˸_î‘? ¯=d uUH÷‰dTÌ&‡ÝsV@ÆÒk®iYkéœÝiM`@OMÙÈ{ñ³ c‡ó^Db<ĵc¥B­1ì4™Ý®ÿ[Òo!ß@‰¯|l?;²ý—ø7Çá«à~eÓoïïvj'(u]ÿ³ušiÖ¬Æ@µr Ú$ Ti“*Õ -@¹±B‚(¬Æ7çÂ=‡¥]§5¡Œ2KÔÄ huèÿ3EŸ‰ù•³¿…|]ÀŸáµã¯tûÝÍí¯~û7‡w25?nÛ¯óöorÂûPz.7úÚ%èÁ@ONŽ@âÉÍí -è£9ôqÙzë]ù_ÁöðMFXi8}pМ;çïXp«^‹§¨yá;îéœÊŽ·Œ3>sgv°×Âbïî‘Õ.’Àv;W/‡Mñ58®·©ÁŸ}³¤ù^ªë!¹èp{iÞ—®Ö,néÁÌ}¬“Oò4r¦ÈßxhžÁ•ñ6(p#éx%ÿ7m þ¼‘6¾ð­%85ã&zì mæhL7Ê~ÄôÌmîÊ[ër¼²WÒ´ã.Õe%9'/Œ­—÷§0‹fiæ^åÊ42äÆ4ót:ÑÜñ†9ö§“­o“9¼ŒÁI{œKΈuopk•§ƒ[ÛŠÆÓ€¿ÛcõíøÿM]ý“Þp]ñƒÃWÝÃ¥Ñx]~-úËs¦:ZÜòÅÂXös{?¸N#-ûˆro=ž0·>ùCê4ùÉ_Å„aÈ3ÊàV'õqÁ¬~_'½´o?ŒBodgj=xTëv}d6ëÌsЩC®rh;iÅR퀗'mæî“[£Ã÷Ö׺úˆ½èõk]…KùÎ2D{ÙYÒyZ“•Ã~öŸº{#i¹Îþ Ã¹”‡êŽú„¯ -ƒ¾}C&=÷yÿu°þ³ÕñÐ^åvçv@omfuµ*ìjñ{ëÍ6Ï¡¢4Õ{Úit¬V¡a¼_ízßsuyÆ?h-žc™Ú(?éþWîêWóÞæN—ÆÜÞžoäê?ñŸÑÎÜ ±uèv€GRk[X­ -ýôÂ:Î6Ïõ[ÐT/·j££Ÿ_uô™ kßzu;~k#ï6©y(¼¨Á3f[õIþT™Ƴ’gëXyU eæ ;¥mX,—øûuz48ôwnpÁ_|ÃþèÉÃsaIt#+so3óú¾Šë:Þ×íWãX¥Z·<Ä DÕÇÞle^€¤ -¹´òªLXe怦JÛ:)ñ4Zt1¨ëJµxî)Íbh§{I'~•¢BÇ›nóÏéýžwHËÅ«®’•Ø|ÎãΓœ'`D‚¡™`b}1‹++û«'wòÐóœ7ž×âþ~PÅfR«0Ïn©"Á¡q˜ü A]‰—ÅsÞC“¿Õ—ý*tRT0!Ë÷3™w°<›‹ç17ʇj^×lT¹YŸÛæüyí\öó2ÓάZn” ôÙ1½ícPºâÔÄ´qÉê"Yy»´€àX‡ýâ×ð­±ÑQÓÑ·¼¤2¯\<·vùBÇ®~”ÐteS•¼L5²Q±ÔÍúLwœ`·ðç•ÝÆÏ ÷cfÕ¸\>UåõÈ07$No;4’æ_î -ëÕSiÙ=O -¦fÆ~ê6?VR‚6H™t´sž[8ýÊ\vúÕeÓé×§_ç /zñè7ò©,¸ögOay¯åûqõ³ÅâÌñçmIóÉoe˜ –Jo[d:Í?˜jT¼ºizR²òvÏ£æÐÕf”ºM{‹T';Ù¤LbypžËÓÕéß/ÇaÄUÕdk{bQ¶¢æ0eùZ¶°{i`λ¯½™·«ˆ™wÛ̧2Ã×ä>—¸´K°ñ¬+Õep¼cÝžgcÕg^Fàø¸¾wD½’~¦tËUµÃü:Ñê…ìY«EöÕêVI°iõ³n^.ѵB§Ó‚5É7ý¥•*|eÚÔ#;ÒœÁˆß±ö¸>H@uáÒ‚åÛ¢ha±¥™yϲL -ÉzÆj×ÈAuPJºWVœÖôíûÒÖ÷Ò׎iõ¶ÖäjpVÃ÷(Vµ •¤!5•.{ óQʯ>t”Δ©qA¸ZOûóú Ž_“r.#ãõt­|JΖ©Ç¶èæ(Ö¬¾ãÍe:M™töš´MPÆzøfŒsåô]å$êÂñªiÇ&dkòõíª—ˆË©-_ TOÕ”û±ÑQºñH±”ãBDÈANùÎ+éÅ aJIé&¢K¨Z)ˆ3ë‹Ùi{/&m(!y=—àØ‰‚<«ÎüÅ®hˆäÀ_ÞUO­}ÇF¦•[ಱyd}†]‚‡ˆÖÄm 5Ž4y_”BŽT[—VõrQPº‡²ªXõ¦)¿å¡+§ZC_†Ò§’4Aßu)=纚OMÄ쥱‰Öî&,3 -&Ðó†Ìo -H–/+ý.ÏÝø-·ï0nß½æ~Pnß+uxîq™Vº°É C?à åT_wÓ6U “Y%=ùöc4µ3ÁT£aCÊãò~)6º|ÉïÞ’‡Ò“¡pKJíÆIè4–ÅYŽ4Ä)%]’H¬l_(²nQ ïí¿énú|9G̹ýJ?pµp8I§yöôèzls.‡¬šŸ/™‡˜SN37–n±jИ”jz•Ë<îwŽ”’3ê©i3ùäë?¨~ØDMcQZy.š˜<Òß± çzirÎ\%ŒuŸâlê‘$'™2{fÜÛ¼ '¬:…vL‡ÐŸ?(cœ– ý u‡îkëm?¼÷â0‡¶_›m¿ÅQ ®ö‹¤±õ:îí…_áÛØA”¦]È’ ¼ð%ùG¡íbq–"ÙÅÎBñõÙ¹˜ß*Ï'Ï·o1W{!Nê¿p6/šU7¬ÈÜʦÎtŒºÇϨ@?çqƒîÔí(í `èN¢"IyŸÎ b«JFg*"ýpõ$ýÖÖ"ýr®úƒ’Ñæ1,šÈpÐN½äSÑZ0g×Úˆ¦Þ=‹W@aHJSŒGÄühø‚®qç+…í‰;l:ö|¯¬&‡OævéÄŒ©·`ºßká víäAr£%ØÔè’ÏR^«_¡ô³KNywAú‡É…Ä F,R3ƒ(Àz‰ ¢Ý_gƒ¾ÎÕ |í0e‚êûßPêŒP­¯ßÛ\ûQJ—wMyui.9*ÓP›v Á½üâ…!vãäöèÄj}oÏtw¦_iïN;3óII-¦¼s#§ZJ0É,r1H|F¥‰EÍ/©ß"’¹œâëq戗JçÏÞèŸS±Ýá]ĪÆ«6Í+&j²ŠUƒg ۭ˽<âÓ´¼Ü…ß`c7¨;š.-T%%¢œ”5r„°·_µ2/N¾ž“ |†'úµ–÷4؃ýJ¥mêJNGÈ$XäE,vLªIÐb–Ã×†»Î}<\{ÖobÕ]4ƪõ€‰½„žIF›`™G›Pa€*rJðPeº*¢‡ÑùA³ #ž¸÷¨Ñ° 2—7D%pÔîj¬Ê鉗8m sÇ“ýb[íó~Sù5~à{rFì¶ÄRÝ’eÂñÝçW€í‹†ÕT…ÅÄ[ZþA“=E¯¤”¬\GU¢>Lo‘ë¹#í0CoúðãIwàÇa±ƒ{?Nú%g5N½ñBâ=5§+mÏQ†HU‘fô‰6'ÇKu7‡9¥i¿™îsu£Sëü™œå±l®wø&°ýkzŤÂôžéuŒ67W49£tKBnBÉDÚ¯E2èTà§ëôà>NÏÈžÞáÖ}Ac¹ô†V¼g'Cõšt‚”ôÈxŸNJ™7 - îI¡ šà|ø²N|¥¦>º&Œ»€·ÓÀ;~6Á9L½·÷žÅH›ªvƒæ¾âK"UNÊõ&Ë0ÁÅ09*iwÅä<=zLlÞ°#‚_Ñ‹y¹ ÷ÆîÊÓ'”ηހà”' ô"ˆôOpå‘áÔ%åcˆŠÙIÍQUo 4 råÏÕµÉ7{òsß"µ ¡®’§kvßÉ Æ]ã µ+ê3Ó÷…ïâ–ùƒÜäØ[6&é8ª ”_`"˜ÞØ1ðžèe*<?à!´»Ch¿ûÉ·$ÖÕ¤ÐË$8ဤ JJdj`%¸û€tÍ* ÕN³NÐ{RçÙ§ ùn‚ÉÙƒ$iÔo¦Öª\èè¡•)ÿ Ê(ŸÏü¾+„òõ¤3ýeLSS¼‚ë2Žà¢´€Ð«ÖHÏrbØU‘˜=§@Ö×1  -È…Hò˜NF~5D@á P"’”åvÿ¡¬¨(›'hîå 1 ä¶(ÅÊJ¥’˜ÃPH_äcÒä3½1GlkfQ-+•÷¿Q,|}©¬¹/‹4r{²$ªS¸8§’cÿæ}_`P¤êPò÷d“ƒåëI Fò My@mï - î¸hÖq­…> ƒgÐ%£ó)˜>:ÓDí' Ú߀¶:³Òk eÉ4Í%O‡ÁE¿”жêl™²<•;ù_¿·®=Ž‹2Yb—2uíP28:NCþp@$Õð?¢²{dÐ#JüŒ²2“aãÐþã ¶ã`êF0óv˜Ã{ ˜cj“`ñÌR¡¬-À¬‚`ÆÒ -0ÿÇ<í&½µûê¨àf×$šË'w5[î7ã„\!E —¡§Œv‘ÓžgÀ%õàBRˆ)ÿo{‘ùUp¿¶à?"nÆøÈ¦å"`'Ë`ohpjv¸âbùÑ`¿qa\‹•Wêû€Ë]Àùè p扜8Èîó¯ ¨Ö}Ð=9û’ê‚DzE&Nµ2¿SxƒhÚh72,8ÎÆ‰{¶²þ£'¥ä¯xû× ÜxWþGÆÍz€Ÿ!SÀ?¼ ôèü'‚·Ýç€Ðaœ³ª©rÈöñãäQtf ˜Ç¾1""߆ìóEf\ÀO—E{ͽg^µL2;RúOIßj¾Ñ_ù+ }·2úæðå¿ܯzzÌÜ$l U%HÓ‰¤™XH°î©ï€4`I 5ž —C i…+tŽ•‚ÊiÈk¾COßùQ«“UúœU+°çÊ¿•ýæ]|¬É¿†ä¿¿ÃóÇ¿ -îý6š=ÿíÀýt«›j›þ¼/Êó˜ÊË Ü–@y·c œ (k´”Öq ”ö y^ÇÔ¿»1²0pGdEõ0ó*t´º¿’û·®¿zò¯ ø7 ø¿·rûº“•Á¿ƒþŠ·_ßíg×´O^ІAè$Öz-9öõÞ³‘ãÓÈb„)6,¦¢<…“U<&9&̪ÁÏ ñ½~ÿ» Õ¿´ÚÒy~½¡¾•>ý 'u]}|ö‰X½»‰Éí\Þrk~¤qË c³æöa ÎÜKUŸFzÉšb½B*ÊÅÙôd女“)ãm¶ZóËFk$m[Ãa(,–ƒ[ãuþAÆM‡ûö«+÷FnØêyHî܃§µÇ×µú•C[Ûz°û |Ë;×î×å~Dõ{k~ W·Ü¾6·wµúl$öºÓHí§Xg²˜Py3®`¹Ã:ªSîerÒmp«I¯qfâþ3”‘¾}ðÞÈb©žûÖønäjÿI`õ½öªmf³m·*9 ò5”n¼Ð†-~{„uá4 ¥ãuÙ •Óæmª×÷±Ñ1Þ׆ñââz?¥bµx’ãkÖ3«Qþš¯b«S™—ïûJ^dõ -y:Ôþ¦®–5ª}íµß(„oÌÀW«ý^ÄßfæGDx(Õï¡´¯¼š¡ÔBšêyA7ná%9®ºG­n¿VmäÝí2ÕÈ¿$ƒ.ŽAe^¸T*yúÕ(¯Ê¯v9ðAiÛ ¢RE1×Á¡\é9†Š¡óJz±¢åS…Næ\/˜øæ˜.÷JÁ$Sé¿øÖ8® ûuÖ¶*”³mÜš¥iͽ^›•Gr•‰Ê›Y$èíËÌns+m«¸ÄŸ>YÐA]}t‡™bhÆ|Q}cr¡“ŠÕ‚ cfþñnÞÁy?/ì çÑ™ÿ£í;EÚn¯`_D‘sF1çPQL(ŠYŒÎÕŸ’Ýóö|sæýÎÄù±öµ›Åó”Pµz±ª]D·#§÷«¼×a/ù’f“y:¤Œœ?8vrUó}Ïù£ƒAž¾?àú%jr¤'wÏÍÍ*¢W¯rn«zèðÕr8—Ëñ÷¢Yr2ãvÉÂVvñ½¸LŠï%ï34¿)¢~ìPÈóê¹@ì·¼×̼ò%¹ŒæéS“ÌUõó…縰/fƒÁBÏŠÏM2s˜`ÅŒŠÆÚéóÜ™¦cäcŸ -× ,5¨rñÔ –rSƒºúL…Û>ÁèÝÛ¾"†Í<ž´«m «”®œ)fȉY ¶qxËÓ%™«ªJ&Ç]ô|6è§+ìv¶aÌì¬x_;™vr=Í(ïý2}v¯~º—¹éŽŸRƒ"rM%höi=ü8f9Õ -›|ïl5é6O©$zjæ¼7„+ 3o0{“¸Oq“x8Y“x®=“x™¡™7 ߪ| ¶†Õv'V/ŒÊäKuÝÈ62U1Ó6I6}ž>C1%5È=µT‚Äâß:èc•)XN¹TùÂ-‹m4’n½ÑIfÄv?‰#sÞžOͼê/MânžMœôƒ»Ç}'ƒÅ«–+Á44F.W3$â41$²sŽÖ;ø¥vì°!s˜:ß#ìRŠžVr»ee4ÌÕRÃ4)XÖ¡IY@•±d†¿ãæ¼y¥Ì¼|gM⌈ ¯Çj‰RLŽ'è›™ŠûÃl.^5Ë¥8÷jÖŒ`Òoô´oHèÆùh°ùã<¦RØF?¯cg½Wé#ZìxmÐLXZBö›ZB).´„*¿ÔÇUNA¸ôÔG˜˜µ°•¶¬ô;½n2µb¦³!ÖKWå$nõÙÄjà –Z"_x|S©½ãÕy‰sOŠB“±ÃLábí\\Š©DZÓÏ^1®Ç˜¢¥…›zVÔì’–– õ±?õT§UK5WÊû2<+ã„(è#%Ësg—ûÂåù¸jËù4Èù ÁC8å|ÖìÖ7ûÙ´”r°fN°—ÙÔHvbIÜÙ v)PÆ>=Ac'I~Æ´Òõ¦_.û‡~©­_zlÔzj7ºøÒ™ ¢>±ñ…«Î®ÇªVÓ•T {šâž‚¸’qÑŒ‚e¸¢¼àsM9¿³ÒÊÜÎ¥Ò›ÝKŒßy‹›"Š5¡“ùÙvmÇv!캹 „m£&÷r“Â2öQ­3]«˜ŽôäD%[ãŒVeOD3í>Ýß5s_¿¨ã·xTÇçøU÷âw츇’%ßO[“ˆ¼èÆ(™ UVZ ¢TNä5‰%Iq³òrb­V¿pQÐH%¬O9¿ø%<ùÎzÄóZOËpרªËõïŸ3ƒðHqÆ8W«ØÒ{”¯˜åj:žURI"¡jq±h°1=ßÅ´Ñy©éb+Tf½#BÁçÁN.<+÷(S)÷"•gƒNjݧXóÆ@J+BØ3GVhnž’ Û’ÁŸâVŠï<Ú%îj¹-ΆËθ7ì(>»±æSa™×xi1“´Ùþ™4zY1ÈbE0iÜI1i­F¶ïœ¨ eëm†É„¯ÞCq)F¯*˜êâû§Bdô‹L'©Òéâv¦®ÅºÈø¢àñ;a_’Ž‚¼aÎü©¦Þ¿p^Û«oîÚJᜭ”Îxeöîîâì(‹eÙ¤`Õ˜I«ÑgÒÉpÉ  y¦sÙ9Ab:A-§:UìÔæT±’xSŪjRKß«”P¢ÔÏœã÷’…v=óãO®­äX }1š…ÄQxC=dzÁ]$nìDÑÎl…–Vüé<]ð:?\q×ÝtËÅåᎽŸ¦'6©¯Ì+ܼ˜ÉükS`Y•³*=;õ“tnrk1šÈHÊã3Šn†ÒO6p² -ÐÉMí*ää)ÄŸ"H\ ’6Ê‘;=¶×Å$:¦qXö%=‘ØÒj&N#²çv÷|œx[Þñç6éÊéþÆãXsÎ>Ìì‚MN²kÆEÍ ƒæµ€Î­äMt -7#@y©M•P\¢è^}ýF?OV·ɽ"Xû{¢Q…7Y‰?è„$´ªøáÀNáÊ¢ÝÞÝñvyªã‡u¢ÅR§»`2R5a¨É¦¤9Ç%=.'ù·Ü]ltÎgAó;.¥5wã/9çÄ °clç3SªéÁ1–¨Åx[—ä/“)ÑÌΖøq¿ôñÎh¿Ãµäõ„]~ÇúS`F6M¡wa$£ÃýÉDͶP„—’Z™€çIÍJÉÌÈA -d'\àéíÇ=˜u¯Èö³f&6È6“¤ËåŒN3´qò3Vø›ý:Ú;ü¡y÷É€ÕO/*Z „Y+m!ÃérŽŸ„õ -»¶¶[Ì8/wè(6s~Ô¼­¯Èkx}!“$#ˆųjE9~™øžjÀ3»ÄN?)€!Qü”ì|ýÚ|ƒ †éÔ³CÕ›Á!ÓÕéøÊËå’H|ôäfëÍ_¶aÈ Û;îK{&k®64ÅVä–d—D ì!ÇÚv‰ëøÉGïïu€¼y;@P¹¸¹I刴}‡p@‘áX Õ{µO üq™¢^=¢!Á÷ó‡ -öIó+«@x¬iÿT‹ïž^F7{c]Y"[þ‡Õ›ª¼ùþìqãÌRûü-ÍÖ‹-ÅÎË>± +ø­<]^®g¯+,¾|ˆ›`^híqªìñš\ œž€x‹„ŠHz$£ Œ;€d¥ï -H®Êó® îcøg«BB­j’*ŽªFi§µÔ®¦Éëøˆ÷™7i1Ü=÷¸³©Kãò-ÍWÒ«)&j[Bé˜\ß²>ú€Ë_Äõ÷ÈG;@fG@6÷@ŽÏ~Iä2Žro3ö2 rB¿ È#èChx€<Ñw@®çð4üV Sc -Èùä€ĶZ¬Äâ„•RsrVû!{(É_' 8Sb_‡ðÁä²Õ+µ~“'Rœ¯„òìï±’ݡϸ Såºäm -»Ÿ%°!  ã¨Z TIjúâ53tý4„g PóüÂÙÿ(½‹ú P#Z†p­ª=]ª\zªÒË*“ÅS,ko†±”b×Ð~ -ß܃«¢,Zz¼hzY½‘]µqû(Þ‹3:¶›'WÙýç(ÕûR ÇKáhКY£€N! Ë2èqMôjczÍ 4ÚBÐnz!D=µu@Û© ¼èü ´yÎ:yšÃì€ KÚ'S‘ã[Û‘PgŠuUœ.õo€l¼OBÃoÍ;µ]…`Ù._­–¯ ú í,€>Ü Ÿ,Òü؃;Bä þèÉ ,˜[> Á­æNØêKÀì¯gÀê$`VTÂ¥ àMw˜^ƒL­XL®ìë–8/ªÄí™’«zÖøö{[ÝËãIîöO¸®$4wb£B ³{ý õSTd ÀM -0aŸ,·`xƒ«’X·šìyžý.®TéBغ€K±ÃàÒšðIoÐ7Àñàðô°þG¤Ø5sÐ܇_V¼;™’lpÏQEf -3O ÛKèOcÉ݆Dòé2þƒRÄFßp?ôä³úù÷—_;‚ʸa -ð„R|®[¼½ï~oô&pã5xò -¬IþtÀï*àéàW ðã—øîª øª³×²Ä㓞ͿžYþβ 6GbT}ü‰á|{¨Ájrü…ÔO=ù#%ÿÊ -üíþˆ¥ì¬òÃé·ß‰¼åOÞ…X8΀¸Œm€$Ì/@ŠKÄž$‰Ë‰\öxJmè 7Xˆ}Ïb½uUʯ&êè³ðÂc“ˆœ¥Ú¡*ɼÕæ?zr.Q‰XDòO p$–þŒpˆ¸‘lú¹Ë¥p÷½š"p  sPšk (­T(ÅÛGéU¬Á(lB -*6ìW@ÞÙ@jí½&÷>ÞÊôÆ2óÄUÞg±ÉÜ΂jMýàU'¢©£?]Àÿ#Âá“Þð›Þ_¶Qûá»ýéyU^ -Мf -hGPƒÐm ç>Úħ€ÖkÁ%O-5Z޹M\°BÂ÷¿³9à|¬ÛïÔ°÷Ù¨Mg]ùzù“£ÛF ¿DïþÐŽ¦ïF¶ßŸÒíÇwû™PïGé QTBä¶=yFÛâ)†UA€¡±1`°Ç&ˆ…½5ˆ-28ˆ ßñß(¡÷ô¢q¿¿³€¸Í©zÜFáÝ:v­œ°Ô~n=Ùc˜­ªìÓ;¶¶#:ׯÖOš›ÀjµýC6×õ•eÚ^ë¾2\ xeâ927_º:½^¢å° ÆÉÇœž•ˆY•ðµiƒUÊî¡a¢p/<Ú+’Cm¿î¥„~‘¬«QhèIŸØ«h·¬Ý\xlƒâùpÜú±Ç}SÁºGv0Ï|™>ßÉEî¶gæÅ׌›ù鉸Q®æ#eÚ m}*®& ·Íެɹ>ÉMôò2Ž_–-Ç1®£ÑÛV#tš½ óøžx¥UePbý "×;ôùM´ÿTû1—§‘íítxä ÷ ¶xZÅ}%³D;~nîÅ{…9í ª³*˜w¦AÖºmÚ™Lô]c>ŽŸòžãèùÍè=ÈìFé§uÎS xdĺ ¼\â9 =±ýR´¹Mï7„¾ö…÷ÚÊØêž{—JW¿KvgøÈàm'×m´-8‰‰"W^‹ëÕýnÐŽöÈŠìµQ¦i$ãÃz½{t„Cjà¥ç „‡<(a"üîèE2Ó*\¾whåžr$êßÖ°ÿîvâ÷°ß~ŒÞ?ü«ï):oepbÝœé]3Oǯ ¯ZEônÎ׫ò;Q z¹êë*!ÕÃø“YV£Í§ÊᲓßúú´="C‡¹W<ì‹uÛÕOýeû1¨mÚÉgy×rSåS+ƒ Np%—»Ò ¯´åôf+ÕýÚI«sû½Q Z×ägï'å•©z÷|µm åªrg•^Rè–CW•™Ú²ôX,Ž% ~ ‹nµ 3ü3_D÷çEaÞ¡yõx!¯åŒŸñG1ý TÚA^\ñEbc zÍê¡“hV•k¦_éÅ­!„ö‚¿®èó®<°Î§_žä?<Šïå 3ô+¢þ“*ÌkWÈ ŒT öªž/)13OŸK¹\5Ö«}ÔÑÛÁÎ6’¢÷Ùìšigi6£âû|F%ÕCF…·â¢‡ø#Í;*`¤ÕÖ‚FµQÑïÃ\׿REtÕMˆm+÷ê£B¾$NÊyú°n|ÔÑ#Ò‡ sUMžf[Xdqmf98Ùc¦m•¯4éót„¤cø„L…˽JЯ˜õØYËú­äû€Í’n»wIf4•K¢a®“Do…óÇ;[<#€«{çÐr­¥IÉ=HŠÓsÜy eC‰Ë(/^L÷Ò¼šŽ¡R B&™ -ç¥ §”À[Õ¼e9e¿gYìa˜|oÏw‡[Ì’‰X™ó±5óšr2‰kî‘ðl‡HÐ@Œûc1¯¦&8Ê3#˜oB#X¬t£Q ÇFàu÷tâ¡1‹¼¿Å÷üdåüV d”ç^ÇRƒ\·œ‹$ÝêîÌð/œ7ïô·šWXÉ$β¡`&¼^-(Å:ù¸?¬ãU³Ss¯aÓ&‹žÑHïF†„>g±Ã‚÷cíBþ¬Ÿ×½÷®÷*7Nñù¤î¦6hµ–Ú C´AgXÒÝŒ«…§Á2‹åÁhXÌ—¶3})d$Ëòʤ¹à¯H¢4IÝãþìzŽWã§«8§‡Ñ°®oC!œóÿ¢„Šl¬ÓŘJ$Uýìå ½Wª$õÓÊháfTÔµeM}ì×mÕi݇ª¥Ê å}©í·¿{)™¸)(èc—‘çã|OžO¸@ž»iUžOe8Æ ŒGµFàw‹ØBÎgŽþðƳ§`¸Â+vô®aL½tOzï.îô˜üÞkáúqÔ•ÇYKpïP}ìˆ'„ª:M‹T-9Ã(n×╌^‚³> 1y>p,9Ÿðó2ñ|×%o¢ô¥Rº>“t¿ýE«…½*òt¡$ò gì,úy޶ ØõŠX­<Ë®3©»ÜÖÌ%¨1ñ¼ÃÅ:Gêñ…kw¡~Vád¯¦ÒÁV»U¦du«dÊV `Hl'Ïmi/çã±³L<¬›ä9ù—T²j¨Ä€)úÓ1'V³+Yäñ[\<>+4J™ª 1NŸ?lÑåηíÖ•WÃÝ$w>imî|&î|áEnº4ö@#Ûôr–5ábJ¢‚t”¯Ýû뛚:‹G%ø\Xå×Òá=©\ÇVRY¾ÄŒŽp-&n”ÝVä«ã­°ÃÆ{¡±Xœ©°¹ñGNzÛgŠàU×à¸K¦¬r}lœäb»g‰½µ]v˜œÏYˆgæ9SÌ8§' \›ŒõlzŒõêPŒuÆà¦ˆÇ:•Œ¶“Éñ”âÂëEêv™}©“VýªàÁy/­Ëy_ÜæÀRF‹™Ð¥ñikI-³£ UÄ—ŠZžBr ozdEM3${±Ub;½§‰zΨ"1uQ²öx«³ÁðV·Ãý]Å~4ÅNûÀQV²­Ü6ÿ…[(WÅëû2 ßÊ\±ï’Ÿxœ")™?+G³wÙ);:ácæÍ®‡ JTl:gé}¿p}ªhÐCr=|ÈJ­,‰­ŸÞõjýDÔ{Û'¬˜h ~Às2ÞZN’xëÀVp¥Ý`ç¶ÁzHïés^"Nµ_úµßÚ •“é:£\’È'ôÈï­9ZU¿u—¸UpÚbbË…Õ)üV²‚9eÞåטAsö€&,½G§tô±°Kr…³MÔ×Á»á?èï9ÞºÑk¼¸²èÞ°só€aç–Èc½d+Žéï} §ZMã%ò8|ö~BœöF@,6‘ïmhƒ÷|{A,¬Ë¦íK%kÒcF‹õ2«ºu‘K±ÜM”çÀ÷J̆K`Ã%›|iS&}Ý;tîÑPÅ‹ÆÞ]"(]B|ìjà¸áB;á ë5Ñ“¹ _¹+:˜tßh¢rf'Ï©ˆÕÌ¥`î›À}›Sqwg€ek,ÀâLBИ4ƒ¯ÉE6•Ü‹éw¹©ð.ÞV…ƒ‘ªM%ÚvïiÞ¬Ó¬-·+Léy}íP^»2 èKÜ&¹Û»G4 -aoOìÂ,6&èàéM‘gÙ_"cö¼A¬=‚iBüèc™gØØ¶V,“À|Ã…p»l3å6³²›C€ î!€e’¬ŒB¤ã’±Sµ§´g¢…š¸£¸¿ˆÃ¯ïôGç§ó¹í˜b˜Ü€ä ®O4–„·OgW}ÔÁ ôí"Ïã|‚¤´ ¦³ÁàÄ`á°¸¤Ü!´q¡p9mܼ6ž¬M®2WÇB‚kƒÀ¹äà¼ñ8ŽhIR?¤ µ5V"cºÌôfïÈê¹¼Ùgæ¶aÜWyIÏC|J­ˆí„¬nrC¢‰+üØ‚45ša†þp—LàCR® p»=ó– Àçâ B48Pß) -„ÉG^à—z¿€'tÈß¾bà¬Â -À'£ÀÝ -ð>œël¤^R¿õʲŠÍêÔ.ÕøÑ;2{sC5±gÇüeÃdN5&øŒÜ”fbׯ8øqáZQ]r„c0ëø. ùìy ˆ4í¢’>@膈*÷y‚›¨¶ô_æru?^w€È—T@ô: Ì—åEB=æâM-cjÏ!É+/NþJö6ëÀ‚ÅnC/ºµŒȜ؃åôãèîO¦¸¶¯»XœNN7F@ŽÏópˆ pJnñªì)//®@* -¡ÎC8Å©šE›> µÌ:ú$çlju?Ú8‰¶€xx@•Ø•`êô„‹ÒÐ?„o#wÙÈ™lóPk´¼&…1¾$p_⺱^ ¥>CÜX| -òÒ{HŠžÒL¯Yh}öK"‡ë# WÈñ.<%TP3ÂK”˜MAXU%ÅF€"½- ¨ -È·˜äùÕäfµäl!r~èDùÞJẾýÞñè çîñóEjÖ‰¦Ö»YÏ—7„RÖüoÍÛimWH¦ÚZBRú²©x€¼Ùk@ñÛPYü¨šjZû8ƒ—Ÿ| hM!4 ÇOâ­'Z€ææ. yýhì@ŸÀ3xÔ†€:&.€ZÇ4@¹¹a”ð-WâK!ò{GÒ[${Ó˽u!ýrŒo<–ªíÑ1!`Q]Fªõ'²ã‡¨L íùd/€¶:7@—Åô9 0dSLþLSJ¦P*C8ô“Œ/!l¯€Q‹,`4! Lƒ>¾>zçÆ½¿»*avŒA'GGÂ7“»Ïo«w{‡]±ÛxsASõÎP}dÿƒR$*·Çw@Ž/H?ôÛ†ù­˜~ôä`ȽȀÍÄâ€u{iÀžÏ%ÀxÀ»ý°W<ì¶óì\P Ë€m: À`­t°ly¦Ðëf\ª?ö2Ž8–ÍäµްûD§Z}awS}¢Èöxÿ¡tG¤$ -ùÁç£'KÉ‘#82ßú¼üC1UJ:àZnpËwðD® -øÜ²ø¹0†Ðùô…ÜBÀ› à[dðÅyðV} x-Mes€ Ÿ®ÂP‹¤pK% -÷¦ËåMœJ€ÀØGæéú£N¥HOþ°ùÉ=9o—¾ xü|œËüJÁýUï·x›êÑÜ}TÛĈ÷yÄCÑëi@s% –× ¦›ðXJ"ÎÔ?ÎáûÁ“¤ ™ä^øR‡Œ9ùÛH`,ÉŸ6¿8§£Ð‹ŸzòOËm$‘~Ò~íýíØ?#x?nÛH¼ýl£ö­QZçݵþЉÒr*Pä}(t¡@@Þ¬ ׫ŸàÝäÈä(Çfš%”m‚‹üÒ?uÛŸ>à(*!â¥7Dâí·€ü nˆre#Åô§jûÓ쉥m3´Ç®ðqÛc] ­|hÝÜhBZl_ê½³ª'½9tÛù‘ÎñÙßÍuôýûÖ¹#^‘j’#6‘H>†>ªõoŒ¿ÿñÝþ*4!Ú&-rÛ~n¿¶ëj-X1c´ô€Ñ6nÀ°ö<0´~ÄBɱyx±á€ÿ£§ã#Oè·Z9tö!]¿…a10‘«/¥È+×) Ñ.k§Ðâ‡:™ût°LžVÌl9‡ÎnD÷‘÷•EP\ëk·¼pNÍs¤Fcùî4:_øµKö‚pòãy 4–³*>ØO´÷pÛÆNέ^z¢_fë( -6Òj£ý§"wè·ZÜO‘zê¾ìå·Þøñ¯žbM6õçÎ.ü6²šûm” Ö=;yŽ0½,æ1ó:/¾Øû¬Š"¯i{#ßûfµ™šœkf¢ïÂx ²óèbNòZ#7‰F¨«4‡Ä"ç JÔlcWyêý…÷ƒæÝê‹lÙSn·u$‡6çµÊ r‡Fô~aº«Ì.r×Xlƹcu*.Æ ·M ­É¹2ÍOzܱ6H»¶“¼Ž{£ô£?έÎ8Ú-«3”ðæÒö‹å•Íù…M?¨Öö}qW;÷ÚríÖ=w›ï®ŽÈÎ ±•Ú eµ-´Pk¹ùݤ…®·æ¼z(4‰½Þ¯ƒF~OžåH È}áß¹«ŸLÓoµÖïQ°©Íy‡{_Üx/O¼/nc|ïPϫݞªÆ:ƒI@ÎÀj½'ÏLËÍ`…ºåæ¼@Ö›Äk5¼ -ÓkÐ3üÂë~#6­sÇ̺ÖÐÇêÁöÞUåÉ•^ª‘ªÄP±_4ŠY-9U6=¿i¢Ñ.^ÑY¹HjŒäÐÈÕØðŠú°Q¢‹³ÏQª¹%{ a®s»Ó½ÖOïê¡{ƪJx +çAÈVôçY(‡ãPŽü«¥Çì(9y:]|¯¸BÑ-'ëEt[éàa^ Žì)_ÒZxžÙøžó‡Ø WMæÉ÷v¤èùýÌa¡r¿zˆÿ£áAft¯&cµo94r‡¾B±èŒ„B ÂІpt‹èú¸øù$”nzÜçéãíœó»à–«êä+ p$ÛHdV|šŸ'ù ZFE†Vº—Û•Ó126H ÊŽŸJpÊÛzï¸õØ7}ëq0Ðèùý(ÌtॉK´wשµåA²+ÌË•x¾$¢Z$‡fÅ[IΆ=#Ó6&/ áQÌ´“b=}vÅvº—Ñûé–¥}œJPÉ™õXç<˩Է–ÅÙǤÛ… V -@=¡´9ï¦5“GÙ„7d»‰’9]'èw‰ûS§÷g³ÂéAäOŽÄî’“¡Òyzª‘a5Î:„õð4Ô²Iº5œøE`†¡ @Æ“è~j™yeœ1‰ó¢ðz›J¢;6ôíÖ‰û#`Ç«IfçÞ±…ÑÈXCÂz—ØaqDbíbLŒ©´—ÑÏ›xWïÕîÛ:ZñzLŽÙz¯¬"ˆ:]Ž¿ÍlÄ1}iÈ .[órö–(éø%îÛûSœ{¬/ßJhà¼ÞߎÐFJ$!¤9CBŠRì0«k‡j®©„ÔÏÞ,«Ç˜IA 7ëª6¨]ÛZBdGêãZ¨N{¸W-õùRÞך¨¸,§dÎ@ɘ­‹’IÞ³JÆZŒ ¬¾eïBžMUf¸/Ü ‹&¼Sönå)Ö®‰[ý¼8¯õ5_iá:´AåpøE }\µÏ=!$ÕiÆpÕ’-ZyŸò¼âv«²’ÑÛº‚†nRž‚œœO¼«_¸L¼ôžäMZS©”>í$KÑ_\U±Zl–Åj)1«å'V+݊误ÒÃ7²9¾øÐ­g}Á$V…%0šâ+ÔûFú >»ž¯‚|b¡¸GlªdÔãô£Á^. yÞ¿­ä¹m ð;9—Ž2ñˆ]%ϱRÉʼ%”1ÑŸvh±šŠ"ït!ð€%4J±’ 1íØœ§|»g<ªx&xU²Mî|2à]Œ;Ÿc'î|)¼ª•"–e2êm &³VŽ zí­÷§£‹:ž„’Í7Ö2œrÎ¥²0žˆ›dÖý·9«v,òëBÐæB°Ð–B£`¬‰J_8Xg|»R¾ð*×ypç`†r½Æžæb2.³áÉH@˜–ØA뱉XuÉßJk„Yеåt*4]Óá7dÀwò×!¯®–:³waG׳+#¶Ë†ÖŒ8¶ÇÚ³ ›P·æq¹^g V.G3€)_8ízW‹Î”*„Ccœµ§æó#Nͽ5_æºÔÜkÞà€“…ìîNZ©ãµÓÓmæzW‘q©qÉk÷Ý\{©1¯ ‘!gtF=öÎvû 6—볦˜¶™§£ØÌX¥!0Ü5¡§7¥3{e ¡¸¥±VëH-”ÙÊ¿ß8ENuÂÄ$WuªB–ÅædŽX@l:cŒØTb1b“á<™ØÄNðUÁ&"Ûw/9£…vͼw¯‘ãWb·ÒJh)ã¯í‡½w–6kn+]&åqm±ˆuCÕs—Ʊ G-†^Ÿ*˜s›"_¾ó…“«ÉaJ–Ó,Wã{’å+!±Ù¹±¹sQU„P¼Vð=“âÍÍ1À›Û>ŽËy2Á«á²Ùòp9™CÓý[`˜pËF‰Y‘ì-SOç£'‹ðøn¦àrÆó:d^³zAºEgßµŒyMŠjöšäZn·ÈòºÔ"Ù´Õ"¶¨ÕPµ‰Ú¼7&jÁaIM4À÷²rÁ›¯ŠË“Ë®ï•Ø©:)a]²1m?Ü ðÚ…@¸è¨ÍæË¨Í)sÔyjë v¯Ñ‘>¯Ì\ý,UÊ­ («w½¥\vÔÙ™´˜îÒ9mQE~Þ ×ÍÁÇÁ®áòo[É4‰:ª6aÀ4ðæãÙÀ[¤ÔÅåcf¡7ÃNùÂûˆuÓñ'¦£= -½ÎÏ*j7“Y4.¯;ÈýlzȨwy"I£)ƒ×+@8|ö€˜tF+&Ôx½(ÐÚÁ_ -Ïb}fmø®?]°Š3“™mÓ3/Þ¥–îp8hì« -v3\¨6ðý€oàòãYÇN+¬…uËXÓYŽdôz!4à„Ùv4ž{„ÈhÅ£H²ÓÁ[ƒ«›I˜®ƒôà:¨ WrhÒª\òU= ¬ýLæŒb´&/J}§§Lçãp%Ÿ c9ü¾Dš7oÙ4/1ÍûM²R4‰íº -«˜Î4ñýœ¯ã­ßÀNO¬éeÐBí˳ƒ<ŒcÝŸC$éñSð.YkઃH_Îo€Î„èYQtž¬AxA¢‹Å £² PG*NéÚ Q³Ø¾É±n¸%Õô~û”*ÏÃQP<Æçâ»ÚFDdLš{àõ÷mÍ:fjö­yNѦŠ®KVv‡Pƒ!v«M¼ÆêعiêO¾‰&4¬…|ÕXrÜXq׃ð„ód€µc3í „à °ŽŠl<6QRÖðãnÙXŸ‡gЫÏáky{°‚-DùÞšS®àr)S¹G©!Ü w\³–œ3èöW”WÊÙ=åz$·ÚDƒ4ñ¶ž«ÅjMt˜&j7øw`)±ý³pBB°&ÚK€‹‡À%.üE^~È—‹i€+Hç£'+ãÀ™¢é=ÞK~8‚£à¦>Ü…XžüÄñ–,Àö¼"`ût°µº ØìëØx¬$vÊc)ÐÌ)ød»<`ú£úwÂw·àãìëñ~~gœˆáå„÷Vã#:Ž›{Pè]ƒï:}SФ[oóüÁ擵…9|Ì·Ÿ£DriåöK|B$OFâíèœÜ#Y|nÝ|>QÞ^º</7À½É,àÎpÓQ¸zE\ÞlЦÎsŠKD‡ûäû™;Þ/·oõ7³O¾IEzrdŽ$ÒŠüíþŸ9?">ŠéOéö;© Õÿ¸mÍãˆË̈#÷Ä6ÏÑrL ²z ˆÈu„`†aX³€ÐRûB̨ˆQpv”™_«Sœ²¿$r|Lʿғ¨£‘”üŸ‡o?ÅÛH”ü¹Z¤˜F¡ ?UÛñɶõP ˜ªò¯ÙN\S?º¥lG±‚í\ÙŸºm.‰¥‘NZBäï}ȴó -´Uc ´.<Mw( aé4P—Ô¨­Õ‰¡cõïlll¹*(¿¥OB×râß}ûÚ#}û?nÛoòOÝögfCDä§€ü3(á“‘ðm´µÇñH°ý$|ü3ÆHì‚8¾‡«]=£=!€¡&ˆ]m›·}+HèÿÿË© -ÈmÝÊcKglÞ‰¢kKϧ k-oögŸ„ºº€&%‘’²ŸMtÕ|#½ÑÁ¨ˆÆîd?ï*NËS³UÒ¯®Þ©¼}Ô3`Ö²ÃÃkž×á²pÐ$¹TÍè¥J~[pkàV ë#¢-4uÙÎá­ƒ5u:ÕŠwê‘ý€éOƒ{f`>€= )bçÚ:9©NLkJn2ÝÙ4,nI¼…-C©_µ“NÓç;ëÕfåîÞAv{ÑöIƒ¨Â<»8µ[ÒãÂc°וŸ.ׂ߲{cÕë3îðàOÁïT Þ §nÿtÕ¾ðß«Û?]µ/ü÷êöOTí ÿ#£íïTí ÿ££íïTí ÿ££íïTí ~ÀûO"‰íÛ@ûÍ};©S.ýÚ;²2Ê#”—Òèü§v}nXö×ü•‡Ë -ùPç¥z7•’}Cj©Äk™É颳K껊š_ª~vð¬ÇÇf ³{‹>¿PXÌšžšn!ó-;g̳~¾[TŸ…K -%.ÓåRkÝ®,´áâSÔkÝt,ºa§ÅDóŒ#µ¶è'°²óCw%ô°>v,Â1e§zFq0Š3ƒáõùØ8Š»}¹‰8ÙPÍ̧ÊY¶¡,ç÷Ë•ñôÁÊ„…Ô×;¤ân˜…yÜJÌ8 ï^»CéoφǞÞÙž÷üû ÿ]º–3t6\â÷îYm¼G²êÜŠÂô\ú?à™_×Àçnûù -ÿú篫\X¿p¬³©Ùø¾aù$§ˆOªxE:/ML®ÄëìÓθ¼a*G8P \ÜÏ®º´}ur -=Í÷Ô|^_iSšºëÏÍ3ŒÆÆú Ùfb­Ïàt9u¶J– ׂ©Špäž+Y³´tr6×ßåOû2R; ¥Tqù²wõ«Øx·®¥2îã3‚ùÆuKµ”šÖj7$rÞñOácŸêg£¸=~תƒûÌô‚WN›AÑq°½ênév«LŽïÌ‘ôh‡tYÚ«”â«R~-‰h߯Oë-ù¾ƒ ;ÛÈ»IÁÉíL½wÔƒÔê3´ïç†qWxûÍü¬òÕÖ‘ÁóhóŸbÆÐØ«•A/»{Ö³ÜÃeçâBPò‰í:¼|F0 Š›¥‰UQcÐð•ù”·ÆŽEfËä.¿k¬õ@l‰¶ðÙϼ›²äÊîYS¼v0Ñ0}rЭ{—ø 鬲Û6ë«{`•J&›ž‚ZΚ‡¢8.%*¤][ÕGâ<Ö:´/‹9 {BÛ#½?ÃÑ8“ažð*2Ã%z‡"u_ÆS¬àõÐgzuXmŸOu§Ÿ¡xø.ïó;¤«Æáx -ûȵöã:†ƒ²éŸTêy>KFÀŠæ²É÷´kY§BJ©—¿ðOõ:·™VßÓäe/ùãctÿ`á½ûwêøOW~¿§Žÿt¿ðß«ã­âÎ’6¹Sp´ßgžœWwÆ ~‰5Ù;Î6Å-»ØÎ­\Uçf!í.½*=YWoð[9ÞÊeb,¤YÂÙ…åÈÄ’;R79þŸ/üçï‘è ùë£ümø¯4¿ð¿MôOVì/ý4a÷ÿÝÆÿú\þ½Æÿ£Ýÿßi~áÿrãÿø¹üízÂ{å¿Ûøçò/7þŸêþ ù…ÿËÿCçòÏÔvÿßm|Dó ÿ—ÿtÿÖó ÿ—ÿÿ;—ð‹òï6>¢ ç0ÿnãÿ^÷ÿT=¿ð¹ñÿ˹üÓWÐt¦ô_iÂ+ÿ¿Ûø¿Üýà}œfOqë Ùº°žCÒœ×àb~»ûМþBsûfÜb»»?©Ä.¨Ù$ÇBE&¦}"FÇÄi HnÃñ·á ÿÍ ¢™c{¦Ü Û^yg‘ûíºÔ¯c/Z‹[m±½oÙ7$ú ]Zµ¨ {ÛãÚ«@8{Ž‚ l¤©Elh4f?×.×/Ÿÿ n§Ÿ>ôöáåxdK¥à½úxð×u´ó!:m-ûÎfæÍ÷”°Cf²¾¨°”Ã)œ~NLxªï‘‘Vªƒaªƒ)Ïž6’?ð\þà'#ˆˆîž™Òþ¨Ú±üßÉz]çú‚§åÆ­ù³˜õgS~M‰úþ¦Y7û¼W¶PŒ:ä¸Ñžƒáât·Ç“Ö­=Ñë?_øÏ߃Lª“ÞY»D°Ò«T€^+Þº®÷…eß)4çcÚôgè‹z¹žèQ“JŸíž¤ÀèT¿¼‡W«üÜ üf¿äÙµ? ôSb¼c_øÏßÿ6R¶v )mw)¬–ÜæsÉåj?øü»Üâ´ÆTYšO]ŸnñàÀžßލÓJ«¯‘vß‡ÆÆæât¶‘tñØ[<»n¹ß þ(|áô“Ûù,• ¦&¿ ¦ÍØx½\ÉG[˜Ò˜MÑ×zJ\—÷‰¯–ð±h:ïÑ)ÀCÛªÁ¾ ÌÛëd§Nö¾7¸mw͸~§¶L¬ÿAøÂ£Û|ÝLØø¶°ÛÐoîi›=;÷–_¯\ÏÝ&\_…¥œ”ž#]Ü…ƒQM>Ú“Âv×Ï¡émr«ÎÖ(-Û{…Yü„/ü7/üEØ’¢fnåËj[ÐNº_Í´gKc?âæà4ªÃ™R~5ZM|'Ž8ú¹Øm·:GûuÇwýY·»é%ÂëV˜ù¢#,cÓÖ©rûsð…ÿm¼‡œØ’×ÕrS+êºÑL̦ŸægX˜«»¥Y|9¦èÉ9‚ç0Œ?σd?»·Qjí÷–óøޱŠÌ:õl{Ú–ß严…+矆/|ÔpcSŽw–~MåTï<›Ng32ä§ä‘®w´?u;ÿ0:wÛ×Á£´ØÛé%å÷<´¾èVœ÷´Sï;“vk”·º=0lÚéà7ð…ÿÞ« -üêùªû5K^¬¥Û\Y<úøtZ˜9Òx×Ê—ö.?†wäÞ¤´êÁv³¿OL¶ãSq;õËaÜní«ÃV÷,ÙM;8öiúà ÿßß_ïb}-cë…×OKÊ,7×§!öF—Á±0"G{ðŸËe7ý|’]týAwÚih„Ón óa«§äáQša•ï5c·‘\;ÿ4¬:TRÿ½~7¹XŒKÙå²óùÈÈËìà¹Íf ]hÙS0tmÌè{=F?¹à˜ƒká Õk9ýfègºÍ/uNþÙ©¿W›öïÁþ_ßúàÅ®alñ"‚å¬`žd§÷Î×Ètñê/z3³OîîÕÞ*IÁŠõVŠ9íòå¸ÝÞrv«·ô{Í=€«¥nÃYHí†U`ÚuwukýE€çòßÞZŒÛ cŽ1o"¯âÄ`Vàã½ Þq»b¡ˆvöéV¢³ &µ.œíc¹>h]r\¿yKzífÂíµc,ßjX ¥ -±v=3Ín##5¸Ýû8»ýkó\¥ýÚwÉÛû¯ú²›¤æ ãÆžç#ŠzÏÆvñ6£w8͸ƒ«;_¨—XíNíŸ&E»|™Ð„Ž×mí1®0:ø |á¿÷ꀟDÿÍ/üoýðûçò§ˆþšpÎÿw‰þšpüï6þ/wÿÏ×vÿßmüïŸË?Ýøˆ&<Ê¿Ûøˆ&\%ý»ÿ+Ýÿ‹õ„³¾·ñÿϹü+hþå«åŸª'¼ïÿ»ÿ“Ýÿ;õüÂÿåÆÿú\þ½Æÿå«å©§ih‚4ãjDSŽh~áËnQXüO¢÷ÿ ÿCô<ñ´ñ-\ÆkOŽˆ>ÇìEà¹üÿ?tÁ™&„¸Ý³û$*íÖ•Í Öó‚ÀzÙO=©ÓÜ\½^³ñ¶õœN[ï»»ˆ5B—4Øý‰·º„zÛ¹Žýx6Wù1æÔ8:ÚïN¯¾ð[×zhÿ}»×NhNýÝ‹Þàµe·%ÌqL8ÏÍf)»þšfKèÓ-ðûd%3· “ÚÜÆþvÉ=R§¨·#Nãä°{š¼"øÂþþá›fRè¼o#a]?—ë±Íí‡&R…4ÙÙ‰²§y’RÐRo½§¸ò~¹Ö~MʲúoR—÷˜¯÷_NÃIƒÑá¢àÃóž&½ûú£ð…ÿï½ÜƒPxÞÌû¬‰ªÁºr?08$:’a=yù<{8HÓ î’ÁÀduëƒ KȘ¿îá ÖiŠ#ltl·¨H` i”²­FåýÏ@8iá/<|Ýk/HtR=—íX°æúKE*¨0ƒD…Óìõâé¬î`îÒ’°Éš_`ãÍËÄ=Š“‘`;¼lÒä ñš#ö¸û û™g ü„/ü7/ü)‘×µNF©GøZÚ•sy–€4§kØx‡4—2¤“Op¦ô¦DHtænQU‰I[c¡«á£1C‡WFÃáéeƒ0ýè-ÆÜ³·8öð?_ÿý­k.=k†“eÝÊçòÖú]òÞÁ¢ ѽ_5N³·G§³kH»ÅR†×å&æÈ³;^ÕÂkp>yçvªR¹ô^xê–kΩËf.ÅF±¦[)x8êEH4±]s›%çJ쇨6]ÄÝ$ÚL"œ™Tjáì»&2Ò¼þ}pÏRW{üúÙ·ï®tj#kÛÞÓùÃoà£ý¿¯þ8WÆb9D9 ÓÉbá\¾&6몺‡4Û¤y7 Íwá8{ï*(¼WFrh½9£Ó<‡ÆÅ¼Ø¯æöПé¹]¯p7­_µ›gÒkb`ýçá£ý¿¯žÄìwû¸ÏŸ .ìOƒµ ¨š¬ÂÚÁ““—b<²CA(_ø› ÎjÀ ÊÙ30i›ÔæEk¿‰óZ "ÊZOÁ.㫌sé$ç½ß3+{ fbk -fI›æÄ2·ø%gT+ý•v‚¿§Ž¸êÒÏ铉¸ŒÌ3¾öèqßµ)è66ö&S ¸³•p¬ž"³í€Ò½ƒð3‰ü±_ú¥8|†m]õñË¿êíÜØ7=òh«‰¬Qz³âñ’jG¡¯ßx<«€±%K?½ ýî4mj¶ç 2W-*„}EÜuçv±RœŽòðöoJ ú¡ÁxTË·Íõ-äÔó­*o™G$‘2Ð5Sâ¦I -[=­ógÕºpdk0|Cr)%{ÑÉåi£ì‹«§@T¦ ×—CkC:Š˜r¤‘gæð?’“8Z7ÜÛ—.·.§Ã\cVaÓ‰kÔ3ÑóÇÎã"sä~ub{Õ¼KO ¾AêcYÞo3‘@â Ò8ݘ?˜Ðèm’¡Ð.ãì.”È_ -ˆØ_ÿAleV¨œÄðÊ8£Ô:w’²‰½U¬ ¢ñäÛ#‡Ÿ©3`ÄkùC -eJ™Èj¼î‘§"à\þµÔ¹GÉžìÞV(uÌãEDžeÆW¾Ù:´˜^¯˜ø -¥h).É_\ÆÕ&¾¸Œg; 5žÄÚ¹-‰2Û‰öRù/VãG‘øßˆeYrî˜×;¨µÖã:UL­y?µ²Á&þ°ëtÊzÌ(uÚ$¨¹;âI#×”ö[Å<ÕQ—ÁÓ'…‘œB¢àÚÙ£½ÉdðlC†ý‹ïÿ‘ÈQüÿÒ‡˜§û&m¢m3šÙm\Üö]&Õ=ÒT.Ç%È5J÷ÉüÅÜ훀Ú'Y@BšÅt‘}2ùxRè½.hDp$l¯dȶq$^Ëc°xIý¹Dç6FcŸLëÔ¦ŽË ·°“a¥IÚ+cN©©†ûûMß3X ßW¹9Þ$èÖ™ÕwhàïÉo‘Q Ù"qgµƒ¥EoO³œ2KðNÇw1ècóÇ¢±&«&ò[Œ _í.”7u·ÛlÐ_ƒ¼ ‰jÓD&A-DR•Va§“Vik-¶•mqߨnàSª¾>§úÓÆº><4W~œj®.Wª½ÚSèg$}â/…Õ§A†Õ0ÿ -«Dg}g¶°ëîy%e”Sû…^ËÚ¬Fl!K½:å@m‡n 0ÁÌ—ŸîÍ“âÆ é]j»’ÀfÎÒ(t\)±–èhuïm—'·Åó&7ÿŠ)BÓ×ç¿\[p¿Lϧ¸Hm‚‘Í<„|A}¶•éƒ? ko¾BÕÓlkƒ=èg¦x¡«§ûûƒ¾ï?½³D„µ^€†‹5Ù”IPz¦×jAú»þ«k‹Ç#×ö-fîÛPÈ›©ð°µ¥znâ|5ßM³¾–Lƒ_y"ƒ%æoj,YîŸÈÓ{û§fÀ½NA<ÉÍɹ­ðD Üñ[D½ÐZÓ‡4µ[j^¿ú¶²˜Œ|Û„`8hüp²qÞ¡Í_=s9®q\–™žpNSâ¡x'•å‘sHt&1è—‡~'Áˆ/ µÌÄ“\Ú‰LÄ“½PZó˜íóß][ôÉk‡Mvf?Ã׺vdÙ ¦Ÿµ’ô¨uÎ’j³îõ‡îïWsþ›fâÑ&ÅŸ„¿?=£¿¼÷ÄÍ7PRñT/¤Öª—è¶•ßµVR’ísV\{¡à¨½%RlŸÅ–uCJbê7r±Î…d¶Ð‹ƒ_ÉkÖ•8֙˟ȳ¹¢°¯˜Þ(Â\ŽG˜Â,éµûc¯µEíŠSº$%%™,æ±”‰ìÐ$G<¯/…¡‰ÖÌ'øùLÝí™°¡Û“(×õQzóþ/%ýõñùÚ\yÁ“ j³{ÏÚP>噎í²{mÿ©j÷½óå·§>ŠX¶sLrúflç:ºÑBVð(hž;í Ó­…»¹ª…×Èž‹c9ÿ öÄàO$=‚9úOâЙß{>|t˜áìe”- €JY\#§kYe\Ãÿ‰z£_êãüQo¹7‡–A”¹FäƒÞU4˜u4@K'0{ ØùåOäA]§ðƒŽ«àX—§÷Þ˜øæmÀwªŸWMÌ’®CUT†âÈýb‡Â÷ç›ÛŸJw†“Fg:ñÒlrÙ+ê„M‘ò7'ô( nÄ*·vŒ¿–Èû«ãŽ4Ú=:ǃ_ã{?›Ã/CÖܾQ…w­Ó`y)3¢‡“” x<+>Ø.ox´ð(ؤv[FþX±q–"'ôðoN(GìXø¬°@¯Å?‘›ÐV`¤ÄuÖ'@ûÞûƒ½éôðñÓ,²M ‰œ:Ñ”PZ% _ˆg®uúÌà -Â7»¸úÞª¶$>Z‡È e±Ö6ŠݹD!œAѰ`Oix ˜ô±ù›rÍzéáíåŽìûºwö¡L}p· -›]V±›¾Pr<ñ t%qíÖÐfž^Ü¡“nÇ WEA&NëÜ?Oó—+櫪\ɨöp‹‰ åÅg$:—_roI§rKÖËæm¼\6nÂS\M8Zê‡ÂŽšt9ªz‡Ùž|çþ´(LRò~;Ç¢šH²xý!Ò1k½çÊLª{äÉ„2,ŽуøÙú›wQ<='Êþ¨Ýùè±a”SÚî‹J¼ †J—jQµ-ôŽ|ÛPšÖý‡8¿Ïï‚î…÷ƒïî‡"Þyó•i.ŶZËýsõºžljÜÁxìcóûò”¿‚kuTþÀ4f&üŠYÒ¶‘Jiû\Œù¿Ô/²•„<²ùø—jÛŒ³=$q¥.úâ«ñzŠ%k§$ýœwï”÷Ϥü`Žä CŒO˜‹ý¤õ?@ ]%þÅó¶µMd‡’Ú~PÓ?ŠX•A­ÿw×öN%»q"É£!ýñŃe™[JŠšŒÒ7r^VÎ{}°=î—þÄø›Ño˜ëZd¿t¾ÞŽu£<‘W:V†@D±¦¦v'Mºh%ädÂψڪÖwéËwØëu¤ìMIŠž!3Ôó¾_qâ…°¬Õ‰Ø¶ÛƇĠïûŸ•¯ ú=-w¿b^K/[HkFy+/tl -íµ½±Š#0ºPê,Ù¡y1—ogåË"Å5y0›–È)]-”Åë¾ÐM¿ˆ’O^p§=9áˆZ5?+1臆%š=ð`êëþ’Š?m¡åT¾Ú¡„i$4’UfI]”a›NFßHÈEvh"²CŸ W†|jzj9>(øÔÊGNèê™7ÌsægŒ¨•ŽS!0Ïa„Yë€Þ~î)¥B[Ø‹‘Ï8ÑÏêùWJTïSðìV„Dø±Xþc :Kãl@³ò‰Ìê·¯Nè>é((1t4`',æJ™ÕÓw‰A?4üµ|½ƒ½C3íkþò˜zÚïz¼`œÒ‰‘Qí¢°N A–J哘{n¢·okÐ[H*`»¾K¥“Kc_Œ+r´ [@F= ! ƒ‚¤!œÐ>#ܦíþ–Dîè·}ÀH­Ãê3´ÃªÑlƒn´¯úËkêy<2Y ²m0¶ðJøh2âÊ^d—ôùú…¿GK»Ït²˜d®W—‰Jÿº »[¥è¨®¯¹``žòðxƪð˜Iþ¦BvÖ2q€éuæzªú9+£¾S¸Z¾Í£².itº9 ¢‚ËR8ÿ‚œØ›Ò»|,í&õ§ì‹DZü²ª»Eæ™hù5 ¿›; -Ó`X"?§Ün¦Òƒ~Öú3 ké­Æ€v@kõš™Í}·ó*Ü ÷ÖOJ úÖ;TýÊ‘¯žÀ2JÉ`x>×5¯CêÞB`‘¨ý/õ–ßVu_èš"à±½#wJeAíÒüšû}çmžÍázùl® ýé…ëZ¦jÎuï<—ËŒùà½×½ÍÞÇ-sHK4ÁþŒj_‹.÷ui÷’¤>ê-‘§šúR"9Š…±S`b¿KŸ×äß”@N޻޴“t<(=›ÍÚá‚tÚ îϸ_Ü÷Íg^‰r‹–Vå¢:E—*³ÉÿQtù¦±ö ZÚ=⯖ðÕ[bp*a»ùŒý©€ˆýêÐwqMÝ^n“¿Êt檷‹c«|ÖÆÒú]Þ±ÃâðHiK·Hª6!óþVt >Š.Ao‰-#·}´Â{ÿ¥è’¿±¢KX|E&!b;%pþ®€ûåu _öà×Aš„Âã:C%SU†ÇWfTþèˆ3»»õ1\Â/̾ï3Ü2*º„±Î¬²EY=¹C·µEFm|ƒÄïÛ ,¡Ã <­µ7;uŸÙ€ˆ- -áf—±¯Û¿)ö £î§d0íDŸÄ Õä|ãóìýÚ£¶ãÓ¬ -úäW²©¢C¯sCÄ`A&ÝnN[§òn•â«ÛÍkXÝ÷íú>Åk›ÊìúQ~ÙXc¼ÚüR~y¹­Õ~·nÿR¢zË¿:nY¥Ú嘯_ªêkž\èFëI µ:Ñ)œFèdqÝÂËpAïL{jníUÁÛ *í¯‰ýà¶¢fïûªý¼…1hÉ6ôײw<Äᔈ/š\ ¹EjLÓsÑïdæ“m-û7Ŭ.èù5¶+¦…F*5§­ÞÊ#n[·€ÊF²›¹ÕjWò®ø×§ìŠYÈürpꊋ1ÿVæòÕ7çiˆ7gGÚ³¬??MMr~žnJ=oZpÚ—iAm_'p¶žb“Ý%ÿø›¢·Ó[,+ó¼M}Ë"l|«=œô®ì*¤2Þì7 p-_ÓÎf®žàÍlÝÌ!Ót&&N''Õ-ÂŒ½Ô–ãì\7úiä_zʈܷµ¹é£v±wÞ”Æé¯%ýòPXÙAs k-¬”ÍrXæêA ØÃŸ›—¢ãûüØRxÕOæCÖ«Å—¥•—«Õ ÎQýÄà¤îgi[mj'Œù›˜å(×7Ä"]…š žB•¶…Já7…¼ó¡Üz÷ß—ï˜Ñ8ùA?ƒþô˜‘Cò‡ Ÿ@úù¹üÿ Æüÿlâ?0AöÿÙÄÿEÄþ‡‰ÿÀýØ?›øŸŸ `L̾j¥°¬½h -ßýÌFË;¤HF (ª#[ԫ툧“˜ž0 c€ çNjÓõOJPçO -%"Qö©íöô¢´‰¯´Õí]¾÷WOi(߯¿/a…Ê‚xR;€Ù1æ9Fãfyó3{tÖÛùÀÊ4FˆŽ® W·YKì§52æY%cGmu“€‘³ã›t”ÎvåÊw§Díùòí2›í=¤¡}¿‰)œð>$}ßÿ„FÄ“Z«aeê‚Ä»/êÑ\lîc,LrÒ7xm§#¦{Ы$djDªæƒÞÒµƒÙUnå·£ÐKő۹9Ë@ت'÷³_£wÔáúrXCYç³ÕŽ~Û+R€J;€Ú ¢ÏÄ¿@…Âé¾íuM#^lNóÆ]k€xFö²§¶ KOa ·+?|Ü‘¹öЕûlþ,…a½M8¬¯t§BÀ8õë2äò¥—yü}ùwÌûI?š(üˆA—uqŸ;ÝIººf·:ò²YÍ#ºÖL•/*•q}åžX^"ü,sâʃTÕáõ,F†/ŃãCLéëÛaÝ<ð{ò‰«1ë»Ä ~)_AèWÐJ¢ïAm|»¬ÇÀÛms‡6:ZsÍšÖ,•½¨zù -î&˜E>¸–"r–4W«^T®ˆÓ™ô:¬§ Ÿ/g‡õn3‹ 2-ã·$}lcá(u‰@ÔWÐ —=ÝýaÛÌ[•Žö]Zó¢ZÆërÕNºGÕ˾ÌkO?òÁ/_ -—§QU5ÔÝÝk -#%Îyq{IÎN ååLúÑxª¿/ß0wòWÌJij?˜{.½I|÷šfþýZêhä3zoHUƒRá¬F5Ëë@Èò‹7çÖ8Ÿ%n—éðo‹îO:%”Eå³×Ÿ´þ €1; +W"Ч[ ˆg?ЧÀ¥Oú°ar¹Ž²î^»Ô³ŠLÊ®òX”CùUÂ/ßí¹‹ŠÓö}¤FÎrÿÁ—ï—ð»©L½$ݤƯ‘ò‘_¢‘Òµü T -J_AËûŬٙÓcЫ™…Ák¦»¥]d5 ›”­=e¡ ½¿|Ìa~Ÿ?„|óøäO«A’mí÷!=XÝÎÔxÊc)k¸A¦s+u¯Ysù÷å& -ö¤ ˆg7a‚Ä›}:=°a5™ës€N^˜vÑj¢ø}GáØf(›·”áÈ·e“‡RpI}¶Ô;¬?#¯Ö‹¼Z{Ÿ#{a•ª -±QóÒÏ$ýòЇÆÂ$¬Æ©èx€xv!òNg[׋‰º©ÓC›”ÍMº0Ö] Bõút*ª·ÁÚQøÔ` -õ¬¨ñ,è“á -±A~ú¦$¦u'³‰Fœ8ÖÉ Qš mü¤æ üÄ–4©¥äß@X›Fo¾€ ¢³u§kQ5H:Ý)R4M¹34*«¢×¯èAk—¥“ò|KOIÙT²Bqš¸ýìú¤…šæí×yÄQkx¢Ü0Oí9˜·¨‚ÞÃPÃpâ*ý¾Â2ˆgž˜×ˆç, âÙ~®“ˆ;L^)X¹õ¢ƒ ¤»Þè^÷ÎiÔºt’Sµl X«mœÇL$dשGjHÅÀÝe5ZÐn¨(ݯº(]›x(%äm”…J%<å—¹£¿8›Ó4ºV žp¼Îæê/¯Õ·s;Ÿ3–®HM³d8 ýÒjв4ɪ‚õœ•ûûÉÞæ)Ÿ£+ú¾åÊ—£RKy¿lTFãÌ «.Ò—C aiOû}„5ÏÆAþÈþ«Vè2y¿•°‹¯óóÐ¥Z­ÔQu{Ó¶æe„%0á8«¼—²ŽœoòWfp¥NTz#¨Q½¥-:gî€ÒðxÖgád•àd÷jÁÉj -Ì+áÑÓ3~_c€%p­S.“2gž¯óÀzœ/ÛGü$ -p^í¿Ýv ŽÇó†'R+†kozÛëU?Š.õ½5_Šõ–¥–Èà@í?J$wJzÁîÒ8*îÒ¼gîÒ«ûñ¯%rÿ»0vèJ ÍD>¬Ø5òÙèœ@N-¯—jóý6ðý) 8^盥ü’e W’៞ÄÄóŽIêVB!N‡÷µÞeÚ¥ý—*ËI‚ÚïfÏÑ~—ÖwÌv•GÛÜÕÐ_a`âªV×P.Ê~¢¹ëµá[è@?šæá(+Öú›c…9Ћ)- Ú­9J…uLºBä2r_ê-{í6 «w,*‘Äv -Má»ôÝoužb¶ù¼$Ä ÍoIXåT?ÐC!V‰rKÖeì9!¬ Æ7þ€ë³#3nv2”j=†Ñ÷-ш\ç ’Ì“ÿ¥èZLToIF¹Œª,K_ë$S…#¶›Ã¾ÕÏì~»â æÎå?Âêy@ŸÎ!¬z…ô£c'ÎkØ+é=‚ÅJ{2R ‘Hkà…{x!×·Å çfÂ:7IÜ#0 -»Â•¨ÞòD =KŒ¾ð½Ôrbæ±Zñ]öˆ­Qñö¿/·ƒ°l> ^bž^XHz^ @oIš%©Ü|®Øñq¶&wrœ&,áàíä&‰Ñᲊчýåàî–ï),·™1 ¡óÉnqiL¶f1>Ù®åëd[˜y³ÍNWg›ò¯Q‰X~H ú¾ÿ q«ðvPS⪷.ª‰å¼d£›272½yº½8m]1T(®d:j^ÝM€&³Jgwù¶žÛ–²óü7 -ë3/®ëg§´òWjyÕ:Rååmº«,h][v¹i}Á½;ÍÏJ ú¾CÔÅáB´}r %¯À`žËó U©”;8KûS45È pÎ#˜]q6Ò6®õ:®÷}ÚYuNgÉ>ŸçÅ«a]£ã/̧Ë9'ÏSïác¦Š½p¶hWÃYÆÉ¿§ZNý¾—“¬c•R‹P¨y‘H]…BÑ|¢fM’£Ýž¬·µæ_·¯ýrhrûEªZ§æóQ’›é”/Ìr®.O­¥¨N‹"¢MNí¹>¡9© ½ãøœmœÆ˜PrÇõqÕÿ.1臆_Š hS™ÜO'ž!vYê4J€pj¢éC:¾;-OµmÝÑg+îìÌ2V˜ÏÌÞf>µ õu š8ÛnR«[蘘ظ™"ÉÑ•EèU[±£¶9á‡÷ÍD²Éž4ì)=uöÚÆoI úØ(âbȚ׃L†Fè:Ù9šæ0.í¶Þ«ÁSÊ,ÒZ¹8=!\u‚fíq‹{wG´ï‡œÊŒ‡ƒ>9ûÕb0n­ÖƒÄi²íËóƒÀfŽ÷§áp߇¦§5'‡ß—°¼¨×Ã2±ay¹I„¥·°K[ÛºûJ5ƒ®IªœO³Öèt[àðq¬ßYS‡Å(¶†^ÉGÍ]/tm •ë¶V«¤OZõ™tµªö>«Þàæ©^ýyU½|ò®zñÊK!Ó㻀Ùë6üLþô˜1èA?!‘£ø‡ ŸÀŒA ú Ìô'þSÙÿßÄdÿŸM|XF»(ûZ6]Æ(7 ¦y÷OÔ5ég+g¤[˜ÚjwLâGÆÌqkM?µ³¶^é™'Í%6G K á,a&OªÇùŽê-ýL_õÉ{”ý|.T®%ÿ)÷ö“ëoÉ(À„˜°Œmâa9'/à âI2ù¤¯X‰ò¹šNbý4kž?0Æ6WSõÊéuÒÎQD±õàÉ•£zù`toŽJ ,W%r–§6Dýª6¶Öž"Âa ÅçñËÏ$ª¶ýÅ¡°ÌŽ›aÙÍF ‹7/^ŽwòTJøÊåU—êñŸô}?¬”änXéÔ(p™R”s— û/ýѼs÷ëlE¥Îç"]¶ßõmÇÊÔ•QÉOI½>¥4õ–C/ -§Žã’<#“šE“\ãWIe§ƒìï—ï‰EX¥’Jlœ’LlÔ’HùQìçùß’ÿÆ´AÌÅþµÿ×VÚf/¬L;4È> @µ ˆç¹ =öÖ%’r˜ðÊ;lfÖ:ªejîú+Ü,k-Ð'+¡{uÅ­¼¤Ç‹ÀƱ\FD;ÞõˆR¯¶‹R£{ä: -JÚ†ˆú# ¾íðŸ•ô±ùÚ¤¾º‹°’.+˜¸“M#~Ù5Ø‚Kç²ÍS²jÏÌS¾N(D°bÞÆTÞKÝ\–éeÎäR™É¥–°@xX8Åm0ß))~UŽ:Ò¯ -ÂVT¹wÃoIX™¸ý°²ëƒxvÓ½ÌnMz6ÒMðD¾ ïÂÛß${¯¶;öüÞœ¨ƒ!1ŽAåγ̩aÐ"EYû¢QÐ[;NÂczDîfúNÜÍΡ½“õ»'pCƒG+V„+êð]À¹ügÃÏ$¬l/hijþÀm,¢5*|X%TïÞç;¡o¡ÏœóZ7*zÔÄÍ|<à®÷ö† é+}­qxT¨L½¦|¬ëþ0áq€»4?¢"[QØæ®Ž¹]ƺJênz¤Äß’è]ÒÖ‰C }6Ù-ÂjaÁ<›MÊ ÒäºlØ L¬¹‰KºsÉð­ú¶ÍŒ`jN§ŽSüËW$ó㇀ãršŽ F‰#¾›w b«ó,ûá(ªâf»;ê›mÙ£ØÎAù- +ä`J#€I©“†ÆaÕ ˆÛxÑçN¯~Å<ÏÓ2 D—¿ÆÍ73&Œ*µ¸vÆäz E%/špz2‡]»Iòc-÷‡·¸Slã_¶â‘ÝãGaí@ª²F0UÿAbÐÏZ¿KXzœPI}‚P–»º0š{D27²TSS%M;\6#™(~@ZÃfn/]ÂmÙ elðÜlj5¶ÇÚÏ$Ž„[ýâ-¦6)ì‹­¸:÷Û<î1›ã(%¬‘‡!ý–„ÿÕýx›°Þ‡•«}gTñ·ý\æXÜxUÂ;˜€S=†‘—uŽÜUÚ'GÝzö -„të&–3ä…=gHbÂÌvê«1Ûe[©ù¶P1æ›~XnÊyv"æðÝÄºÖ Ùß’°š‡ºaµ±#€x÷Ûx¹¾ÚËÅé¤ÒµºpAº0›•AÄÈʈ¡pêîéh¨ :$–"Òã -<{ûµÝÒ!ëÛÐhl`úÞX»g£µ®çÙÎj¿C{Ë@õ—ôr:Z<¬ÑtÁmwëŸIä(þâPX]Õza'¢¶8Æ Ê6VZÇ]è&;kçg$Z‚7x/?!Ðd‘=ÀÚhjÃÙnÿ¾ÝMÌç¦j@o dâ1hEž½äòž— eoJd<7ÍÎßïN~.’ÍÒ|RªUg -Z¨Ïæµf÷·ävhåJaõ}WÁVäiYï\¸>`ì{¬RŸäILï÷ð>Sœ#ê ‡ìÀ4’Þ"•…¼öK®¶¢Ø\ª¹ãb·ì¹ÜáÝ9„m½ÙÂú³ì¤L×lþ>-ÔRÏÉñ’Gõ–›lj쀀ü–8¼äžéêmÛ}ˆ'Wï¤FâéÆ \´:¢¹ñ.mÁ5¶¹$B|ÅîjøBjaø|n÷`¸r¦¦–«sÓRŽ9LN".M*÷¹2>£CmŒ×&Ö¸nöì‘?n8£–Rõ†·^5ø¥Dõ–ÿÝjO­„¥·Î]*ì™3«d•9‰C½÷}YÅßÉ]ÅÒëÛ~2\ŠùÃh¾Ü£Y!ÜŒ§å 5ŸÔ´ÛbL¼­Õ(hóÛ4ê <|ÔlÈš+bØOÈÁ+9gfÈ âa[èKD[ù-Ñ/œ~òôcÅúÌ_#ÚÂÞD°FrvØâ¥º³zÒâm1‡RÏÙ¶È%&x³Énº0b·êŸXÍÁ[U:±/É=2ì+Åí¤?Sæ³~z¾Zõ´p¾í-‰!Ü5ƒƒº¸Gý–HËça_{„¥&6giØš¼ÛÛúI\­Ï8:× jŠT‡1ÿ‚sußã# +žIã}íÏ ÏgO‡ï‰^îìA] ¿æºÛƵØ9QN¥ƒt¼F§ò¸vÚgîÞoãƒpÜnÄ3Ë¿–ôCCX<Þîa©ù‡¥–`…¥T®ÙûðéqN“Áã±+ù³ªÛó -zkî¢*¿qšÅ*jßùãþNÔqˆÕØcxÏNL ÇŽc=ä¬t™ca’ªÛ=ÁÐË£™¥¡³âICÓ¹³VÅÓ¾VVʵô|Jakx¥tè -ó#u<¬#ã³î—ÒzðK—1õ³à: gtBPò@_ð;JTþ²Rì–‹cï©ý4‡ð1É{Œ¹tw_ß‚”½½¥¹ÍÙ·7/=êï@Å¥äÇÚ„ýÄ'ºã -™ÝúxÈs5ƒ/™/í³òï˜ïÂ7LR˜Vp|}úƒ‚Qü»¬nfÝÅÏɱý| ·Çä٣͜¶õSŒgG/@¢W -OG%r§‹Šûï§Ì€ ÷`tFG~—KèZÔÖc8ù»Ä ~°DÌãa鵌üÏ,H¼¸ ¢€ƒ?¾ŽÏÑÂàÀ{¹ËºåV]¢’Ùa²¶Ý ÍÜc!éH;½K:gä“V³ë¶JpþÇÛ_i³ÌSœœ ï°†šG˜‹ÁUo‚Ê63‰¡Ö]á³–ÛDä(fáeXΕADŸ•| Ë„ù¸¤ÌgpxÞ3ã­º^0žú)r}LÁáÞÌwv¢Žl G ÏhŽºŸå>^)¸JÛÙÞD©bø‡¼ÙÚôèë óÂPDW¢‰Š@÷§oþ³ò–kûÀ,€xZÏ·†„åUÖxÖ¤D¼˜R&ùYmT=×pïÄZú☺¥03/±Ê††#ÁYmÍrŽü(MÎR(TÃCÞy9¬§,uW”iVz -Ô«|æ©gñTb °?H úY뇄%O~­×@<­èUHÝ |A{bòã6hMÒþrÇVÏ¢uœnº¾<ʵ-f®ñ#§;ÜÚЈRÓj­+é³$dã—úýº°Í^ݤÙãY¦E Kþ@Êuž'!a÷Z{EV¾bö¹õ‡Ÿ\õ£W!{–Ï5õéž§wÁS¾Þð*^ÅjvÛZ^Úc)‰YË ~0àfÆÒ.uÖWzò,ôbüÁЙô‰=H•œå¶Ò^Ûùý²ÔÛg6C˜h•Òø´„%I}€¸&ˆ§_Æ.Û°’î(a¦î÷îý]çóõ]J;¯ç^'‹å)Ü×(;Åšµ†Ü@k¹¡/˜%þÄÜûöq¯/q›Ø8™(.R~ʦxÜ~¥X ·/Hžú™Ä Ÿµ~]ÈZñ|ÃÑËÏÇ&¬tûRX±¥à~wSÁäåWýBzµß¹·®/Ÿ’Ñ/“™+Í/‹kÑaô}K³ñ”°æbè d Ê¨ÖH8+a9£8Öä²é¡ka¥’X„•`Ç…ÕCè>¨”f’]ºTE{ƒ\KàVYlÓêÐ3Beßg˜×FäȼUãÑ^»GlõÕˆÜXÔÑÜj¥ì¤+!À£{€ ‚…Ã!Å ½EýRbÐ a¹j¥¿‚bÊqå¨ÐvVG,ýlŽ®æ-Ù8ø€N8¾[·*Xc¢ŽÈÖT¨ö;sæ]Ó7T–R±è«‡tôiÃhÕs¦Ââë*Ö'×06æƒR¶Kh'ì ÎšÛIÔŽãÔ‚ú¬|ÅTÁSæFgq­ôñíŽæ(¬†qøAüšî$íjâý0¯Ž±qY¨•úU6ž:µ©Üð8&Ü-´Æ‚ÄEF©-¼›ÛWl›ÇzítŽâÊËIúú4@¥Íf¹8l³Ü”‹A[õ±`>+ay~ˆe' ÚÈVQå¢Ot]ú¥Úe g´ÙÈ2Žšê ÃÈècŒû~Š´ÎÇùPoeä)Úד ªõ–ÛõÌYovCY;’G¯<î(,©öA[áÛ´†ýµ°)æ¦Ü_K ú¾–‘w.,ßÔºQÅz*¬e¨ÆUokugœH¶ãP¡üÝpêí²‰'ä;ø|'X91=¹†èqžµùþÖÜ”†›2/cк¶ÛÍV„KlWMÌ –Re}ô /ØÚBX’hŸû¬€œ a¥ ëó1=Pf›€op¿ôq-0ñE¾ „@˜Â¦¨LVšo §ÜÒ=ô¨"Ú³<œöýòvóšT?Ör¯ü8ÛZî´»xøýÑ‚ÛNç‹Aû€Ìôþò†=ÌääšA›Oe·Ò .S"ôgsž¼†ŒÌ3¢\á´%ìÁX”…úy’DTŽÅ»ÒJD'Œ”àݹîòþ%Ü ¯ybݘ…©U»!e–½2ZX sýò\œ4ëó$[ëÌf¯æºœ¥M›æ‰þ„Ä MXiÕ°²+l¢ -ëÃ5§h°ßæ¨:]å¯Âþ¦µØ4©¬HôZƒñÞùM -™ávVa§l+yÈ^ïk{wÕaŠ—Å+.ú‹ø¹Ï§¹a8[¤Ê`<65ú4ͯÜäØ*T'Û[³=)1ëñØéVŸ•°B - Ë<ÖV·„ O¬]£S§u„‹Ü/irž'ë:˜×¿NØY` ¼ÛeãÔ×üÇôÅ©‹‹äð©ÎÓeÅœårˆ=ÝLæç‰ÍöüIùR½±bþ5ºÌ¡äˆ³™QóÐ,ƒf¿ñ!1èûþ/%¬¼/Âe«è4}uÄko¡ ÌY€°÷ÂÞ{­ŠäèúxWX3g·<¸Yx®2K\cëøn'râäxfRà‡1ή¢•çCÐÕn†Ö—µãL¯á »aï6à›ç`LSŸ0³Ò$dmªœ¡ÆÛg]¨Ïñ+£·øÕÞ_z&¶F.lMýä/‘Õu÷Vê.“:˜YIs0…‰åx‚ÍŠÓñ¾~žè¿>ønȾtðj͉Áiƒùþ Qr}ÉŠýéh¨öTyhõ݉ûY9.æcQ^)úp‰S ³¬¡=™[t±qàná-´AŸ\w*îjðÜ^çú²~OêA.5ºq~vøõÒà­2ÕÁ$‹7ûÊfÛéϋȠ§ÏçãÞ2œÌºÖh¸ên‚Þ¶[„{hçT™SXŸsß%ýÐðƒ(¯5 -¼Ôeˤ«²o™aˆ*ÝVnwZœ{ë1\.“"»›m‹ l‚78zÄ êÜp˜|KƒIæ¬õçÅŠA½U?õrgÒënȵs¢V÷ÒÙ¾:•šlãƒ]¦]£…–/RÕ9‘ÚÍ›zèV„ -:›Æ >çDG‡)tö0í]Å,+¾Ó{ÎW,>9cïÌèÆòe€7o÷•u¦ßÓáø¬»­«Î‰4àNå.ám¼ Z¾@q-rÌŠÍ›B©MfΚÍ^FrbPãi~cXPoõ·í¼ë"üHVÂB?´ÂÂ’J†|¸ - CëæƒT>,K¼]bУ*Íß·öþ‘¿ò¸îO*•ÞeæŸÇÞtÞ^®¶]7ãöö\¸µ1מ—0¼ÉÓn™ÈónyÝ–7140žƒÊúýÈAqÏ]Îw‰A?4|—Ï‚~3ý1è'0ÁHéOA?ƒþ6è2u Ø!øù:,¬ÎvXHä aa-‚?î“rXWnws îûWñ-EOö<´¦g«¿Zœ‹›öƵoUÄ­¤CÌqùr°¼Ç:5é*:5:¥95¸í!ïÇÇ.˜òxáË*dÿ 1èg­€1@µ#¸VõíõvŒ“JXðn»°p©kO{Ó}Ü °d¢ìK#¸}™ë¹‘—/‰³sñ2\¹H²´sΕÃø+yº%út¡ á´/ÕSSó¬SS¯^`pv3åÍô¬›¼mkðnc~BÂW¬®0Ïø6Ê>ÿvÂ×Âb-‡„ÅþÒxÚæ&¼7¯‡tÐ U:ºž±JŽÁTYºÈ¦±qÎL -qêü?í,egÓË…`ÓJ·;¾äØkêaÊû)˜ïë¦nžT÷Q7?ûÔ>! ” ÀÇ5Ó" ¢XôÂbFi„E´‹…E–¶ž§šð¾·†^öú˵äCk®ï­Wé鹤2+·ºiïœFåŽÈ†DÚôcJÛÝeÿp|¼§êŒë, j`%^ë›n>]O=Cõ£ÒÚëºÜUõgƒ~hÁlŸÂ"fÃbEGÂBX¿„Å¹ß -‹w˜ˆìeº_÷æ®ïܨqÑÖÜÀ+à•…‹.ðC$KȉԽÍìvÔñ‰uÀóå8 ’õ.• ë×ð¸¿~èVÚ¾¨Ø£x’oÑ”^I'ñ«ø ¡:€0• ‹] ä9Óõ£· j²–J{ˆf>OªÞ[a*{sêEóìáÙŽ@k(³;]• f³qj £— o++Xbñ¥Zc²­d­98¸©uõíÈÝÛÊßFÌ÷…yç tn}ø„€PÎ]À˜Ï‡ÅÙ ‹Õq–RõNäÚbaiëO'u ïí^>}±Eõ² £9rôzaå\X9Q§*`”Pê”9kÒ -$S陆93\×X‚«UÅ;EOzž&Gq2¼jB¦L)¼å—|iñ¿”¨ÚöÛ~X,oÏ€±@á¸$ç›`œuÃ’(£aÉzèaéXïTr’âeªrÑ™ {FÒ cñSgÔ~0¥ýñ Œ5ñü—W4iOŽz˜U¾©g©u•ž·ÉIPŽ’~Èš_Ú&D®ÊØ<‹‡4û ˜+O¸Q‹ô‹Ì`’ËNäû–ó Hóq¿šL:ýiï½µª²ì_Ás2‚b ‹ˆ0‹bBŒ´Þÿ¯{ï±ÎYcÏyÖ;Þ>Íb=cúµºº»ªhÚÔ…·9¤ýÆéÚýX‡$£x{u?ž„Ù+2ßn±I°%Óýs@'C°å4k,Æ[ÏÖ+sžùÉt䕪ãïy°éêèÐ|Ús(Ò€^¡pä<6/hE³â[ÔQ%`ZÒãU²"ä>½äÙkSnW.¹éÆ0§ë 5‰ÓóöéÛb²k:ïÅ–œöwA×4ïË'î_gý8ôs³÷z<‡³‘§Ê“áóÀ‡’@ŒއÄÄ{#Ÿ±§êÇ¡—æBÏ]\Wî?À¿e.µÜ¿ev‘74eÿû&ÈëWu4á§€Æqµä‘ÈDþ¶=1îLnõ¨/©½Ã›aÝ]k±®¯Ty´˜áY†§š›ñeÔ_ ŸwbæMÏ׉—–Ñ[›<éŽåó]qkû&7îžÕK“mø§¡Õ‚ ´| +–},ä}›U¿»a\›-Í–S°m–NýBÿà?2» tÍêä>¥`6Ëoñ®Tùô7ßy'|¼Sëç Ÿëè¥׋ÇÙKQ¶g®ª®”ySµ‘2šTîŠÓ6zÕ~q;mõÄ`:êü±g•“…c¹ìv`¹èÚ¶®ÂÞîsæå‹[ÿ~¿®ÿ½¾mhϲ> '=¿å»ïëç¼ -òí‹ûº=Èí}ilãøØ]¦ÏÏÎŒ ÃÑ´´ÄH˜·ß·Ñ¿¿2^qƒªÉ÷Í´ÏhŒ iÍ”ÑtLo zbÑôF­©Ý½ÞÃ^Xÿ€Þ‡ßµ2Gò Wí|_NRï€åÏìnCù79xJÉmv#ΧåÄç?G¤MÓÛâ’a– ´˜÷ãÈä†5ËÙ—Í™Z{\ìÐÏXÖ¯«¸×в…Q§2Ì^›rÉ6´WÍ6æ/Õ2Sˆjš -ßü'ô-º\i^¹NÇyí -Xï…ü`,¾øå¶uk!ôäèöœ.NÚ»‡,»oú9³“v¡FŸÓ‡rÌJ÷Þ›îØA^÷vw©1ÓV8¼¯_%Üm3OjС4ªßÙÎ«ÛøÌîšãÿ¾ÕÑÿuã/€\êu5€Bg3öyó{j=•Mƒ)w¯/JÂ¥”eêG”= w6'œÖ!{Ÿ–³ÏDZái|¸HœÅ±Ähis¢Íùk¶ùúøFµ€7ëKgÆ¥ýa³™Ý·®£”VkãÒƒñØEŒÆó·2w/&è'Þ147Ð!ºŒ¶‰[·‘|‹L7´.ƒ×ž¯µª%¹Ff¦FƒËTÛMQ­ÙÆûu-x›õ¹èü ?È» rmö roú×Ë4Î÷ÔúùÕ˜á9ƒ­;}ÜWMoZž .}\³z}÷tLú ÍNÍ`œZt̀ߵKoó Kþ5‚[lºz3—{×È]ÓŒ •¯žD^ªZØÞP/Õì¤Ê¦ÃêaÈ÷µö’êý@SVáÌ.É`ôäç«Ö4Ž‹u«¼uš³ÂkùǬœßÆu-Ôܾ}2ûïÞÄîaWÛëö–ãIë±ÞMuy%¬ËýqW×¹Þ©F­‹Wq°¤Z襳ê`‹Ój©Ý”;µ×¿Eº1VnìH-²{÷ä@f=”AÞ7ê‡Ф¶’¦–»ìÔ›Ž0ë9FÝ€v¹à$ô?.?¥‡'™n×Ú‘V›'kƒæ‚îØ ¬øÖ‰ïkÇnwUšÊ®Ê9•H-÷rwe¸-¤AU y²4 -7U– Kžô¢ÙÃò·€i¬R€ _¡N¨Qp£ÎÑ2(¡ØTÞÞH²{çÚj½ûóò†2Ã:'tûÏU³õŠ¶ÍæRNëõ¦wj ó0´ügÞ¯O]÷Q+;eò}Õ| PÜÉJ;’S”ö´z3%eQ2ɳ$®¦yõ0Çkú8í#Û ¬G‹%»0ñ_¹ynDŒV†ã’©Y¿ú¨Í.rþt¹SnÉɾÐD7|¹v‡­äjY~TU¦õŠŒ÷Zò¬¬rº§ØRm+ŽÄMº2›Ce#âB÷(l#÷öƒm{Ÿ(ò‚þœ+¹Ã,\oÑÆŽÏ¿ ‡3xtÛŠµÎ=ã¨ËÓNùíu-•;7ˆÝôþƒÔØK)©ºü#­ -Ÿ*Ö=JÖð^AjÐZI zš$Mº”Ø`ôhòf¢ xÖÖF•¨Ø[TúûÑ®Âóãoñƒüu½µ8ÒZèò«=y:ý¶×ÓÓp<§,ÔƒÝR[¸“f#ÛîÖŽæ¹_½zŠc§òg•¬¤U;ØŠ9:|ë ‘@ßZ7¾Ç7^?ϾŒÔ·Š–¯sƒ*»Ú¸Xz¬ÇBiÔÜVK"~k•Dëý,Ö×¾6™WÕ£^óº-_˜mOÿLÎ?Hçjïú\Î&õ°­g5îªðî±ò¼Q,Kèþ&ˆÄy_å®×àÙç ]±%Ã(_gm»\É4½Ò¨Q›“­:/úíæ†ûš{nnZNcÇÏÂÊ^¦~؜рÉíñî>L Ög9Ín&ñ[‰;¶-tÒÓ¢>íªMd Wb`d÷oÏ•häA+_§FRz,«©’ˆÊHÑo•qî³çrœÆÐy.sæÊ…ÇŠô*hùV¥ÜÌϪÁ†cÓaéw±Yþ?ȯjˆ‘¢wFÑlê—5 ÎFb/bòìBÁàß‘b”Pg,y1³;ú,ô–ôc0u -÷Û¡^¾{õºt{úBõ6. -Û8%Ãüå&.të -øþð -£ÛÅÕÒÇ8«£ó¾,Žƒb°;x|kógüY&\÷ÿÿ -ý2ÿBù VoV -Ýd·€d¦ …”H4oÂÏÏÑjůÃ9÷yöËìá`ýƒÜï¥NñÎ×þöLå&‘„v Z¿NyÚ¸NÙ¹*Ý¿*“Ù&Öæ»sÔzw¢S>Šö{‰Ã6áÔ¹¬þ h12S€V*´è !1¶Ú€Dò;@{ C8Í’uņj«$T×—yfRO®|&%à°wþ oㄯo2£^§‹Lãªæ’ö5µþôâ…LÀ,)^pò<^“C´•ôèd Éy?î“Û]­Ñ^î|óÿ é”ylîñ¤(@¬½ kÚªà0úù~—ƒíBµn„~ïöbÏ…ôãJÈûp ³·I+_ß6*]g©LíšÎEÍx1>vãZá:øî0ÇY¸ ]Ö3:ç™Ëþù*îw‹£µv©×,h/ÎÓäWó[²,A¡vÈ,…>× ÷£ J0Û¤T·¯ÂÔ…I7pïÏè\Ê>ÊÇ uÂMXþ¹V‰D‰—¹ ׯ«öeû½K³ê;—&œ>/Ø ÝEdÌÆoÑ8‡éòx·ÅÛé写tüµõ)ÿ @Vk‹¯ÅhïÈŠ”dîdŠ Î€š¶r€ºD: v¨¨jæä"ÿñ´_|öþxr7¿T/^g !ÆËù±#k¿yiƽî¯öìh{´ÇQ»±ZGm‰¸ü ÇïÆaº:Ø»b¼ZŸáR·*=¢Ñò> -† ;Í4êÊÔj ½¬(ûó-/û, %´ èByú­Çò9aÌFˆŠÜÇ™˜¹)j§|Íø)F+sí¢Kv;ÚuZVDõ*Îy”éÙ¸áÉ÷PÝÁN9oÖ}¹<_Þád!«Š7ÿ¤‹Î/ü ]ÿ €t è¦ÕÅÝ6 È[Ñþ–—¯€= -€>U»€^÷瀶&—„ÍpïgɳлDß™[ªÝ¯Ä+U/úsTÿ>KrÄöù0ÊõÏÌ4;<©üêd-í8|[§K°³ãÝš#ˆÅRíÉ|vÀ¼Y}²ÌPyjÿ€œYPfg}üö¾k°€R¾EFç[·e‹ WéwA&z€~î¢Ä|ûég¹xÂïÒg¯‹ÃǘþQ£.bçc¹bœÙ<æœúäÓ?ÆøvßâÉë°Šã®/‹ÉEóg+dïMõ«1ð»Ù’åÓkæÿ wÞ -ud8.ʹ€>ÎÿÚ žs'íï)>üÏ;\÷“^sóyº‰ßeYe¯Kl'ÄXÄjQwlëgöHš'NÙ»G‡ß.÷VöºÝŒðóêŠ6óJú3t2úaWr&=ñemufŒ‹ÔÿÄò·¿Èxº†f=A¡×¨$ô@Ž>?¿›XaÊ|ý7òyO@îF@nQN?o™.q÷ù)”¹zó—Z®þzrŠŽ¹ï¯38ûéh?éò«°Š·okNÏlçóRv6mµáä¸N;c{°ìÜ¥Ù>Q¹;\ûÏä{eîng@~¶ß“[há1¹ÎûùÝ$ÈFŽ›€i}¡hÕŽI§’ÇóØCo dAÇAï]ŠL›–÷r¾. -¯Æ¶¥²Í‘ 5g…«ù\Í.üp^#°qFîA·~á(áL(™Ž§„IÇKu£ÖŸño¡1ˆEœJpø£oñîN ÌÉ»˜H…2£Áþ9”î0Rº§§åOÜÛh”¿Ï˜ƒ?kwz?]ÞœÉDXUN)e¡Ô }¶òš®Ÿ« ãÑ­d:Ã1Êö½)—˜^jn-;o;릭;kkôwü »(|»ÆÌ^Å]ÊÐ99(ôSx¦¿ü¼Li€׳—6÷OusºêúE¹r”n¾k“cjÃrn)W²ì¬9yR•ƬÜ>Ͳã©Dd¹µÊÜtÖk·ëàx£=Øî´Ö Ý›Êÿ@N4%´(ÕJU kÖ'¿ö[ò7Àœ¿çï -|°Ëµó™ÂìÖèMv—ËÆ§Ç[û„úÌMmbÉ.•̈ƒJL¸‚NÈç.®EÕiŽ[½Á×ÌÕÉwìM¶lãø}“ 6¤n3=¬ñð¡´§Oò W5'€Ñk7À"wò»-Ty]#«÷HéÈðJa›u4HL¸¾²ÁúD~òXÉ¢fÛ'ú™ÝYÊû.êè³=e³T³lÅѶ ±ïXq°o[±¿Õ!Nö¾qù…úÃò×5 ¤ Êå E£<´èlò-ßµcÀÖÓ8ȧ=é9&UýÖt^ýËy{ŸŸäJîö¯Ây=zõ.ó ö¾ù\Ú~ §;æå›Ì§_T èb¿cy¯›ò«Ö2}¤©› o4{‚áAÔõÞЛþBíÏTó¹”\„K« ÷>L¾çb\¾û-—’\¨iå¡|õJçnävp”«À©ÂeM#Ó^8ŒF~»8»`÷Œÿ4U}ýébq•j¢£tZÏ´Þmtñ†‘¹êFõSªsN‚ñ˜ñ‘Õê? ú© tñh³'Àqžž~G8}2+ýâÙfî¶™P•8_i6OÓúÙÛõì§¿–§UÁΩíz;~¾ÙÐìõÁ."òÉœSùK§cIχ~Ðæý ºCôÖi{n´÷â½ÖiÞZ'X½«ÝFW!¨ßãùÕj‚…P­…|(´Oëm ?í½³"“½Ò*w~IMeß~q½ òxK I¦¾‹Ö–ú®ÖQÝô¦.ºë’pÔo4skø‹æ/u­Å–ëóÖ¦ÝØeC/ —µV‚£ýÔ>¨íîö,·÷Ø[ý3µ!¡ÌM®•¥H„£œ‚|ê¼¼e>‚¹1^Ççñeû9ʰ²s}µ¬”왥†ãTå5qŽ^kj%*µ0,ZµœÊ%l¼{êµ¶A3i­}ÓËUO›µÕíЪ”Y½ñ -fZÓK&ª>î”D˜KùÏÔ9¿Ôkô¹+–—hÊìü¾ ³ˆÏãó}g¾»o+»°Îô=un.ÍÎdÒ)ö½önã†'ŠC³‘¤ý¶}8Λþ†ÜÖ‚©u©ƒNFöqE-² ·j¢Œ¡µ‘C³Öà'Z½º˜Trq¦4äT_ü3¾k%"ͬ¾uÆŽØ›Ñ{¾x«÷ÝÚÜÓZÆ FGô¾Â8ž˜E‡zeü™åT¿š¶à…]So{½—]ºÍwð˜ÖñyeS=wR±ZºàˆÂj•Ø<ºCÅ.ŽÊ)|·Êèo½º×w5-ØÌÔzž( ùÖ ÐÞæ¹SAÞ÷ô“‚í‰=]íŠ8ìÂ\lÙ.€óô6¤3c-Î1î‰/ð¶8 U3xæ¢Rèè)ºÞ«·Œh¤õ*­¥z­æÊ#ŸÉ ¥â²\_²Ü Fòs[ñ”JÊ3”øÆëª¾4õt½W_ÍohƱP[KvŸ–Žoïtß²Ösø½°8Þ”_¦c¤)¼]k_#ûoÑ,š;b%w÷[]W7\_êÛ‹ÜÕN“c¿Z\·†Ê“Î/ää²RR^ÒÜàR&zòRæòJïéÊ“Å;iÉ£š×’Æ@ÿ3} ¿³%Ó8fOãZí°cKíøýóÂØ¥y²Ø Fú¸¼s®d÷ÙŸ=v˜²l¡s•zâ÷t¥¾{l”sçjU‡ˆÚÊ«Ð2åwFñäçRRBq]ïÞó§Ya«æJB;™b£LxR5½êKêÒ¶þ S>׳›ÛÍv†ù«¥øsãØ -&£. ; 0ò>7ÿAúÕ‡÷-‡¾’Ž[]KµñzØ9PZ4èpÕrDT?»’åYlפåªÑ7SÅñƒ2:hkÔމ7n!à™až®œ… Ê½ê€ß÷û~¿®ÿ†Û2¿X…¤g;«YÉêÍz‹Büþ¨¾wTIv0Êy5k%~ãÝœ·G¥ù¦¹Òža>J'»Ý¯êHžù³nÀÙRÎìRB•¼H\ò¾ç«|OÛ•ÈVì -·oLÊWÃÊ.]Êå Ÿ*—ý€+=”ügìW­žµ÷o~ÆZÆ8q-·_Øžl?Îö¥ÎìîQmùBªÑš|»ÆF³^µüEÉ\Çrf»ZˆÙÛ¡ÝØóGO¹ðìK{U¸”*»Z(=ÖF¡$âžøÝo¹Û7‹~7e•\eÌ}N£Dpü3ÖãÔ]›1Û«8šß`îÂUòj¿zO‡f¨M®—½&zõƒ§ê]ªŽü Õ«Š_U(+‹q“….·Ðxöi4ÿU u«5»ôXÕ†%«MÿS5ŽœÆz÷_õÏB£ôŽòÁPU ÚÚ_øAþvãfûí¼<šóÙÒ §•Þzx«ìÖŸèŸqsÐ ‚«Ù;V’éh.gq+¶˜ì‰?:ÛkŽWÙUÍTiT¯f¿uPâùO%”ã -«>[) WNùAl8ºlW2]æ8s¦L¯º ˜|æ1ù,rý3FŸì>?¸ ¶‡v—ùo=ÙØêzfº«×ØÓ\V…äÂK+„nþ U¢á°Vv•N»$"B¯¨¬Ãi9tThä~>\yâmØ®p ™£ž˜žr½å¢Å5É‘[š¾ü.t…$Y걯T(‘ihÝúŸð–0xªêÌЀÕ`÷¥ðªä¼³ˆ^¾ãÏ2"ÀNÍï¯ccã1üsi½~‰úI€†x ó•¾è'¾œMU†ë¯Ý{k½èJjúd¥ìæîÖ›k¢ÔÖqfüZDÄQŸþ ?ÈßnÜ–¡P^a^ -53(ÀKçÀgÆ à»Ëà.B\“d¨Ö0¡ÚückB­ÏÀˆìj-`I+‡P ±Æ ?È+”¨Ê«T£ý¢;Sûi®n‹×Þ·:ØÆÕç{µÊýù©×úøÀýºðb~ò¢¿Ø…Ñ8ÀtàïñDö ðk‰†jk -T;îCµÛ9ÀÖ—Àúðÿb2“IZÝ4þ -WoæÕ•Ò¥#â‹Þ•êÏcµf<ÍÇÀ}DùKp//Ç»ø³`h±`6Yzög¶•áŸño¡J°ýšè^†3ÿ©ƒ÷+r›…Z jB™Úíû‹8’¶!“¤uE×>s'^ÝÞ=ÿÊa¯Êó8K+Ï^o<Ù#ß{D c gÛéîšøµý¥!m6§# ‡J_›„©ÆÚýoøAþºx C¡-Zô6 ®vH@HÍ 6© €ŸýWÓT»€Îð9A/Y¤Öç±K²`àµïÜ‘Wnt ž'ìÀ=óê†D‡ ŒÆv=n=l‘ܯu~ Í}œyžwç\ƒ[ïŸrÅS½õh‹-™ÁŸðÈ„Zí ôErwøvFb=þŽ’w€¬-ÒðêVÄùÙ€jŸ RdÕVn %§>/£óŸV~™{æ§ËÒÃ> ÅG±>«Ý¯Ñ¦{w[ïáí±”vñ|‘>ž¿÷O½2ßiÀý ÁÖL;›ÜDZþ €'}(ÔŸA¡Å3ì‹|Ìü«jK.z! oÏ, yf¥ ˆ¤8D«²Mv‹ -Œ.^O?Ï™ñ¸\&ùGñÞ)Ã8ÊTî[oÜžä¤-âéUiᧈìÔ÷ûçD\í²—æ$ ³».TkyÇÆocËotèÁÞßÌaï¿N€ãy¨‘ìªÕ.¢=P”ÛäО@µ­m’ÚýÅâ¯Ô³pÜ‘RÛ,Ü=³Êÿë¸:WÀw ]s.XÓ9xD vYP›nŒÉÄ[ NhñR}c>cÅöŸa eF;(s>2+EŠPæ6óý5y³q@@‚7®=@•ŽS¨vµOºJëþŒd$óˆ¿Uû!Uçn/V®àFÔ¯Sží]SºìÇZx¸Ê|¿C0u¾ats¸baþ*ÆlÙJµ©žóê ê;8ì“ó:'²kå»@‹òУ#è1£ÿ&gêÍÍÿU³ï&>”¹ù¾Éõp;ê.PõâM -hùªjf¼h&vœu¹Õ9÷@/¿žs¬Ï\}¼ä/¾=OGYcº™¯Û~Çj6'¦ƒkÆ÷é›y€NǾ–Ð9Õ×u Ðhèú8¼€\ަA®Ò©~ÏȰ½ž¬^çqxÜùÊå.>Ưë|‰fãúuž»¸X>É^>jcýà¹'œî¶»ä×#ŽÅkÚÌêòÞðwÏv{bî¨æ¸8h#'ªÆ¿eŠé zÖí[¼ƒ2Í råsü=#þî›ÏªßsÿeïêÓǨ·ªsÚÇÍ;G†¦ƒ“½\d÷‰µÃÂ Ææ¶#áƒöçZß0n¸˜ Zî åֽɱÀ´ÇvúЕ£~m8*V«^²(ÃùWó[b ›¾¡=S(ÔÇ*ß*㊣nN -`ˆ³’rã1î¡îM{iÓ˜ÌÖçHÞ¡óý0û¶îÁ>5IÖ‘›Ê¬Êƒ1µ˜ÍÒ|¶X·§úÜèMØé½=r§9ɲæ)|Fuçã«ìjÍÿ €XÞ¿gA dHzTRá¡1;ßr(ýkKÆÇåç°àè÷´õâö5ï/÷æü  ’ÕN?m·›sky\ñå0^(Kì9khNÊ;25éait]áxŠE¶î¥rÍÕÆGÕY½ç²ƒU{â`KÛÿqxaŸãPf<-4 Lg2ŒÜŒ¿‡†â¯8<—~û‡Ô«¾ÚQ”ËõÒç<ÜmmÍߨ#j±L¸Ìf^Oc{bNã‹9¹Ÿ¼˜öüPaÝ9ÌWœuÁ×ÛjK+Q²÷¢hXŽ·s[²ò ?È_×ÿ…Ž˜5 çÙtN[¹øü­2Z`‘7ò>'ÌmÕcùËñµªà¬Þ 7o¡Xϧ·o“ÉŒÇ|3l®F÷¡¹õ¦Ïèä¬WÝç ]²pÛh`ŠÍäI¥oÙO±o Rb¿P|òýù¬X—ý¥ôg⑆2W¥ï¯Ê’Ÿâ Ї•.íæì’ˆER¥ÐÁ¯­ Qˆ.ç«rÐ"¾¾=ÆZ(ý"s®ª=‡£Ç%ö½ê>³ìVÅmÿlœ.V)<¤{ü«-ô†Ì7K‚ÍTì ‘©Ðó^ã -Ä´ÜóÎÓÒŸÈ ™Ua »ø9ê*A¯¤&€e‡—+ïŸ÷¬#}b¦Í±z¹v%lwïêæFöôefîN«Ð{¡e{5´î·í÷‹©ÓÚLºêѨÎJ¯nýxæŒL…áj—ç™+UŒwX+ï¹Z4¥kûàùÛ_€Ã‡ñá`ç7€ê·^ ×Aeè—¥`½Óî*Æùº}Ñ5]FË8Ån­w_XMõluNò}âRΰ*1¦Cy(ÌÄû¥<æ™SNv1u´»þæÚê-Ët;W:ðâòªt‚BºÔE›x©[·¨bw¹.pÆ¿e>ÔPçQò-wpºbÉM¢Åeu_Œ¥MÌæñãY¬#ï°‹ZÄf8b‹‹ ½•ü˼ª;Õp‰Ã¹Ý¿â+Ü «‹ÓÖtÍ;¢Ÿ/âŠ5=Û©èvâ@‹µzÃe±KŸ‹í°ýâ:Äà•ÿ3Y.øPm;4|¦ð-ÞÍþó)«îu×¾#wЬͬmí“ýAVZ*dfæ VO -ÉÃsÛª= ÍzOéµZÝg-;½4OžÖgWùX[e#¤žAL¾!-úå&Oö‹z1˜qº ó­<»ù;àwùß7à„¤Î錷€^f>€ÏÔo•Ñx¤é{û 'Ý“z‹G»s)Ú¯Ánš,H½ûÉëK8¥Ÿw–dIgYínï9­uYKúÒ“¥UÏöå‰Ö: »j÷eªÝp(iשÔÓg³Tó&×{ýB³rsØ?*¬Í…_B+)iÀæb'Å…T¿†˜)A´²âÕÞ$)m³Ô1é6Œ@z´Ð–¤s"l_Ì—JF |»‘rs²«5êu%65êãŒÔ¾äì”8ä2ŠScêªUÙHÕp>„³¥N©Vç:…zZ¯3 wÖP›üá{Ä ¾…“´¿=ö-;õÙÙXQØÈ×›±$Fê|zåú§ñlé¾~÷àèHÿ%“„Ù4ÞL'>E]¼å…úšhZ÷ÀtUû†Œ•ʤ´—Åp—•@íÙ•L³® Ö±¨ZÙM¥šËö¹j˜Óóÿ ?È_×€|—€6Ü0£=évÄÌa«éëZëòÅENˆuÿñ)F«akëö¤ñµŸx0õ‹•îpîŒÐeóÊÖ7g±¬™£§¦:Ãi÷‘“Ôt"}ØÔIÊ(*eÚKR…JWJ6Óº<ì—$ù^¸W”AaÆý€’ ÛåÔÌí\^Ïf!•J¦›aèÏàxɾ2ûy>©³0‹´vh¸ÎůúÓâád¶É£S r=Õ)§ë­ÛœÐòs± z8 þ^ª%i7àŠXòæ&ü~éJBkD÷Å:ëwÄE kü ’Â-TiòüŸ¨\ñ:âï±âÊÛð£ç`‰NÃYa›_OÒ;7¤†9Í)­§ƒ~zš^˜TX?vJ÷ùíÑÓëÓ³ÞÉÞZá ªmÑò¬S)KÈkBÇÌ›¼ ÀJß+KfÊñ…Õ+§‹Ü`ÞôÖ¼)¬÷ùêoñƒüj’ã­ª~ËÜxy£ókÓ¥“νÑj¾<ÖxæÂÚ«Zu¸G}ö-‡×ÍU û–CÛÇêÕ{ÅJ2wžrfWL‰A$úöÌÿ:mó»{pµËD–› ,‰$÷)‰iN- f·Sz§r¢¸æŸq¦GF`ÔÆ¢éêU¿|HªÃõí¡;ÎîÃõå38 ½VªSêÚt¤¶|!×üÚ©±ÑÁRÁ‘?«ëXjP£¹ÚZÀ÷ø"´Ý‡¼•+Y&UñUô;†PTè½ÎÍ-Ìå´Â(ä4®‡s™*}k}ÁPÿ3¶Çj³¾XW2¼_f»•!ÖÇxû]dkÅ¿Ʊ©Çí‡}zfÎdë]jHV¯î‡Q’Ù¨$g¶yþûþ>sS¾Ckî-éeøoõJ"VòŠ -Ušss³VvûV@o³L>ðÞl¾%¨jžHÖ½<k6œåRÌ‘µNRöK>Yðg·ùC3w"Ùîr‡ÖDŸKư¶ÎÍæêKUÉNÖP÷Åå¯üѹ=+¿K¥ž)ú-ãæã2g¶X@¯´ô«úƒ°á¸j²ôÛ™ü»þ™Mǹh-§rvs’ËÙz¡úgø— `¼€æéþó<£ŒÎÆ þ¸ÝD—ÆY«ïS`ªœÙŒkBhQ ž½Ç²«L¬’ˆt\î–Ç…••™åƒÁc•'îÛ'æè'ÿ)‚¢)„v[dŽzìEþWi“LÎE&:!}»ºýA~5¿Å°^¦»2Ñ1ƒ.EXKºp\=ÔƒBµ|ßçd Á)!ìÕñJä%Dé1ÑEWX.sÌóý¬°¡w‚±%sœ,”\4TsÅì I»M«M=v–I‰´5 ýÞ`D|.ã¡•Ö!¾^®xC$?X0-áãÒŸ°œý}Û@Þö ³ù ?0´,ŒõáÕ;è…tÚ§ª.€R¯@”£4ÈÊ2šžÒƒ¹Ò€H ƒ—mÂOAˆ† ±ò_Ûgoúdœâô#˜Ð1’W¹´ó[ü -Ðõ¨P|4Z€¾Â,ÀLS8Ã?iPßÓ±|@µª€VÓ€ÒªUçP­µƒj¨ö’ÄB²iò4ÄTˆœïBµ½aÒœûóç¡ Ìﻟ\'FmtYÎN´9:ß·Ÿ¾ÍÀ*.zšÛ-ø€JŸ-ÀøØaR…x»vë >Pë.µ¾T€:¸µòC¨µ½hfüÝ ‡<ö7(ùkÌõטãvJÞ*ÑÅ>Ä|új?»‹GŸÎί¯›1‰3øÝ;ï€`ŸŽTÆú-¦ùÐ,ÏÕ÷DTZùÝp!À–,pj§¼\<Ó<ì8KAµq`Dh\² Z~µªÝ¢¼Ž ÚÓ ϯ)O% -jµÊPë¥Io”ànoñƒ<™8¿¼ mog¤÷øæ«î‘ÛŒzûG7ßù-ÖCdÃ@›0¦¾‡ -«$ÀgÐËðáÜ™ž àÃ÷´Fì™æf15¨V…}3€Î¦!@;û+Tû|4ÇàÍÖ )Ÿ hÊ3f$x¨øÏÃX\ßËÂvW7øôÜeïàr +Tò§îN+Y­_øAþºþ sƒ.tÓ#j…€yÃ#À§I„´žÂ+pxí„kLj•Ðu Þ ïý j½<êRÈ·_4>ЊUI6ëE=Áß`𢳕Õýš¾mâꙟé ;Úº¤µÓTËZ~ºµ ?‡æo°yeV"(óùùîPÅÉóù_u[b=hâ•]â,Þ¡«(À¯j à]£ ðìºîx½0~¾üç9Mÿ÷#šuMLiÚþõ`æÁµ/›øc0«sWà§„ ìmc)™›îuÓZ]šB}UÜ¡Úo°02 ›> ujì àäR0Ü×.@zÚ -ÆèHfMb²ø¾ŽÆP¨»‡®›½P-ˆ¿@ÒIKCÈWháÅWç]ª>e£÷}â±¾&vs{ú"•¬lYÄ\[¹Nké©×ÚBbm‰ÛŠü~¿ÝØõ -…zh}uLŠY©Á(ú–3U\Á„´”€¼ç` -`æd@DÙ Š©áëÐ\¬ŸçŒq~\¦üóQ\rÙ{¥•¡~ÛÈCË×ä,ÔãO8E­N-5 -ÚÈÛZÁµ µGõyšÌ)Óõs/NqiÌÿû$&À„𢻸g|ßH%Ã)Œ£m»¨ BÔ?€šÚy(ÙÕ^§ÖyñfÿþhPÓÛÄÛl¯jXbû<.Øá”ŽHOŸsƾrÊÛùî~L§ô¨þòv¹¶æÓH¬ý ÓMv«øí¸)LÌL¡2>×ßÅßàÄ -ÝÓP¨½\2³½Š»}«¶GнÎУ(èF½8¶!?øõF7•}uãÕ«î\ˆãuv6‡Êæw÷‡ý«,ÅátŠ$»“`Ûf±\ Ú|?kÐ8¯v`¤ÊÖÚîôÜåˆ|ú ¹¿ØÌ–cõ8Ý8ðw£oÿÝßÊðAvxÏG¸²;/|8˜½fœcÞ©?üœÓÈìoðÛû·Ðo•‘"D(Ô ×º€!ÔÓó޽÷´¢¤ãölŠŸ/·Wþ*µJpN&Æ’´9ûõÚŠï´ô…r[›³†K üp°œLX¿¾]ùúeøCÌKŒ²àVßÇ’Ó }nŒìü %v;œŠ4„ò_ñƒüj _–-Øû2ú6€Ò»  ï‘ô-‡zÉ™PÇtž®zxF燗>N8ØmûOfÝ¥•°Žäù²µW§í9ÒœXjǹŸ‡]Þõ¡Åæ£ÞÎYM¯ïAp앤ØàìÓï0ºŸ¯Á ‚ü- LÚÓ6Ç€¤›ßãpü7È•>` -o÷y9dyϰõ ¦×Éù\Êkba:8çð~éåL8‰ç»8æð­“©êÅÓÃl<»™iC|ž'¯“ÍëÞÄõ¨5ƒÓ=C[‡d‡´Ñ&û[À×ûßgIZô,ž@nÐýÖ7Â=«„…(vEö°2Ÿ•m¤1íµÜâÆó] üRçu¥6ǧ³OOß}·9ʘóâëå˜jÕbsX9Jõ·³j×–âcòƒÔÙFŸ:UŸêu¢1zu°f…7ÑæÕëgun?ü=šö¿ª¶tí[¼ÃVÙ—‹ ìI2ַê^H…¹Ïþ ›Ñt¦-ÑShOû·íbô¾8¡»«ôb{à7=•®ƒnëeeZl‡7G6ËÔµBÈkAÃoV»ÔÑS£a\¥ÇuZÓsU²Vse¬ž -¤t=[œüÝ9ümþ s€|è—¾E×ûÛuoG ½wáN\†»žÅß>ÓGWY˜ëÏNåFw"êͱ˜òfÀ9¹}ïƒP¿”ßó`ÃÖ»9¾Ï2õšW¦µvçZVOÛc]˜G)‰ñE¾Íf5›t•–d¼º³TÛdål Éý€07. ÆÏ5œ-/ü<:&ev¶SW?ˆ7×Å(òa…,âÏœYû…ÉKyÖ†ÍÆ«ï䯟©5Eok£åÜ÷mÛ$.ÍdÝxÔWÂ9¥u^:¡F¡¬”gBCVŽ+ù5þA$yÞ¤Wùœ—oÕ!­ØWÍXÇÔC,#¿ Óïï¡Bâö<¸<úaàµA©’¬ªS0\´ËÝÍŒ³h0™$€1ÌæÓVþpÒíË…Ð3ñýeÞŽÅx£K6{¬¯wÝXÛoI ^Î1®ð¹MQñ¹.¥³üH¬õSW!IQÔ´…(~²¥¢##WKRvö ?È_×qm|Ó¼³_.²í Ø3ÚË%šnÍŽnùÞk0gSâÓëvjàÉšm£«bý¶«I£æ¼îÌê-$h=:¸ü ª[ê&rbó¨4¯ÕJbƒhׄÀ½y‰Œ">ôžSaMÀ^)AX­§%1#•X±Z@èßPÏõøøBóú–…ÃóY.t§:cqN›ˆ#¡=láøÌ-dÖ/{lꔕ}e$ƒÁ´F›çf³Vºjû…<ª^vÉBy^üPžíXBH#%lÏVŽßÖRåœF9拲“r‰¤\2åz¥·dk•#âK<¥çÊ|»pã~‹¨7m4w_])º ̘öŠ+©†èQœ¦ † õЯ²…¤‡?ªËå0RjÉòZnlvT½Æ*ÃNµ¬s}EîECi•^-E=pCöÜ[%’½tÙÍ-˜Ò¨pSŠÉ\é•ÜgÏ}"¶ÈÍ‹ýF‰ôj¹±b¹¨_ù¿¿Ëÿ¾±G$IY%®2Ë*ù±¬Ý -nnÔ.ô'Ãß äYØ:ô¹=*þ*‡JH½Kmh» œ*¼¯‚¬a=U ÌbCè1ƒgŸÏÁ·ž<;MJ£ÆvSôÛ·ïëðïTa5àÙz_Ôò-±1ËïF† gW@_B¥°â…òo±)Õʬ»Ärcé4 cð†1Œ5#†´aìz§åÇzõmMêa;=×ln¸UG’ùúõ.† „.WËV¢1Ž—]õmW?—Š -¹9YÖ èufæ‰çaÌÒŸtÈô4陋ÖßóÆs¶nµsE²µ£¯ûkògÌÉ4FÇëá•%f©í²Ãkù©Õh¡fS‹¬¨¡ -@Ñe ]±•[?tj^…ø¤ôXÜE_ßܼ;9…N”o•ë¶+V?LOmà9»Þ-Ðnk¤R#ãhR"ËMIßžI¥Ä¢¤RfšÄçÑ´þ†ä¯ëñs\@]ºØÏZÊ1Jwú¾’n.™];ví‚ú^ -PÄqÉòì½ -ó-“u(póÎH)4ò -OÜó –N°Ã~€‘+f/Öw‡*¾w©Qç6#ýÞ- æá«Q Ã‚éIÆZÕ|ÂC$›ßÂݵ:iKéMÞ¾B¼›Yº›T¯êýAùóüˆ-šyU"Ïy•sâÅ}¶óW¡Á6ÞyâVH1Çñ;›+f&Yº‚õqJ¤ê$é›Õ1·Ë"s+—ñ/HX0©Ö1â£wЮæ Ð\öò DÀ4E@¡ûúª]¡Ú;ÊRPm½µ:ÔºƒŸÜz9 Û.Í@V5vPëö[»È–kÈÒÛ2Èær5¨±a|5ZIãd[ÏÜ#Õ½—ȶ¿ñ³ö[\ Ìöʼß,¨°Õ¼£ ºý­(¢ý¤ ЖÜ(Ù÷r;œ2L"”ao ¦ÕΚ İ7¦ødÒb|‚’Ÿ/­Z P ¸J Â{ôžû™Ø¿—¯ûƒ\¥~Z¿ ªö[{Bµƒg š2 …ƽ1Ôˆîz^ešÐ€F.@íÙ Åû w: µVY€Ô‡ò·žÌ`Ÿ'/ØçÆÙm3€’1ÈÎð4È: è]²ѵ Æ`ø"ß ï^jM­ø­#Ýh#Ö›'“Y©¿Å×by…N:+ö\˜'}ÉûïvD€Õ—&ÀÐxÐ-Tà7H›Ðu—KØæÝˆˆ{É«K€`ÞdÁ š2æqÝÝ”oïÏdÃÕèµ]üû :¹ñLÆûçÝ€k9¢¥…“íZü- ®”cm€â  §ÇwÛzÿðtxéû¾À3é>ÀVÌ`¢t(€1 :X–J¡‹Ó°#F¥ @zÆJþ>WÈËõx úA6!\'Ásþì ·Q\Å߃3M¡Ýýð„ÕvóôZ,¸·ð[´‰Ázç ZîàßåÐÔîðéfù1¥AÀ¿É3žß6:Ë¿x€îp ÞùöKÙX43…3ÂñrÈšÉ&ͷļ:™©öd¦ÛÞ=6/ã¸*§Ý“¹x˜¡¼]×·hÀ¨ú»ñÖî°åß ã|ýû)xþûd!p¶—§P#¢ñÎ}Oñh@\Æ€SÐÊa*x¡öä¥d[:¯ðˆ^ô%=}öRïà HôàZÈëî,8ôvuŠ×ÉüÜþAâLáîXïÚßÍU¯¹ ç‡êÊžqââÉ¥ù4Õ(ü]ë-;ÿ*/ãŠ3„ؾ2«æ©=Û€¬‰ßÚ8ÉtÏ€˜´> £ÈçYÝ•ßÔî/to£á\ýãgÏá&΢ó¥~’(PEêÜEWÚÑfüÞ.soêëã³_]V†Ga>%µÒ,;¾±Sl·€ëþ¯æ¿º¤Ø€}®u¡) fˇ Žëûÿ£ëM·UšpÝ+˜A'b¯(ˆ"(¢"vØ€¨ b“zÿ;­õí½Î©1Wýx2Ëro6d„I2«ï3ý£€lfµ™[=Kóºý qs*wÊ{öJh²z˜Ë rœœµÇÜ n¹Ææø Ûg ý—hõ ÝmiÔKúPc—øùA;‡å¬h4®Dæ?»ežµïYÐiºnü/ÏÈ#­€ï¶`KÙ(µy–wVy,Α’&—2WBŽ ó&|,éä=ß§w×jlËáMØ$ì\Y?©ÉtõÆçëòŽ¢%‘ŸlG çeçZ0Í‚’™ÙÍZÚxû9Òèä^øüÿÖÿÊK€T÷Ph¨Ï@¦6ñ@¶©¨QûóLÿà~®f7¼L.#Uöç¸3<—=îæ¥«Â{wæÇø–®!¹MýÖ­|O© õ¥ÈÙE:P'ë(†]Xoöóò¾G˜ÉÚ,Κ•bÖèt^驨íSÓTi…Oz ýOÀ®”¡L?Âx~÷Ým›™®}µ-(t³ª>Çû{/i}Ôѵ_ÌK4+/$î=J;œwzórwÙþë½n#íÔRZîrŽ¿¨•íâ.¬[qÞLs©f¶ë:Æ'­< ¬Â5ÌdOŽÉ 5‘‰±’pØ8gÒ¨~ªç>ÿ @2øÏGûg›uæAŽ8½A>V,²çãí#ß /âB;ͯËÙá“;u½/e»G·>ï„k4ß^úð±Ï Š˜Ó¶•5­-]1>AÄMÝÖ¶ EÓ±/«§q.¡I]3Ä(Ül4î݈2ª||ð/`dñÿÿ:¤q:¤œöÏnÛl“<Üàóy«w:Y\øúÞ=;—šÜ–Ž"™ - no™=º[óì´ð äÕÖ%öa™„ý™-¶$\Áx°ÈOä[£ªŸ)¢=Šðª¦ÝÒë­fÄk\cŒ9:|03døÈßÖ{hÇà?ñ?™ßôòŸM—Y›þólBGžÑ)ÜD*S Ãê¤|æÎSf®²«FëÙ¦Í/WKùµòìÐ[Ÿ,+óŒg>÷˜ö²sd<8ä(z{%-M¹LyÐ.e—ä]EȵŠüÄ£?ƒÖ›{ZWæÑúo@²z ½¯kÿdŲOéòÝ$ˆÜŸ4¾úØ:saê…Ì‘ÌÕ«ûPW»®½th/»FÏqÎ7y;gN£ãlµ3/S™ oã☣Zc‡mIÌ P3WSº·”Ð÷XÓê+©Ã»ïÏo%]?¿”îðñR¶.òT¶òø ?Ä¿u@NP)Eÿn\;¿ïõC’%úDEŸÕñÔ׫¸cø<¿ÁœÂpÜS³y¢“+³=©î§µîK¦v1Ì-|ÜÆû‡PS³;ÙWüUI.6œV¯Âcz¯2h?z—Ûü-n³—ȇW?;><úý}ôßäÊ•%šc-o7 ·v£û#ÃÕxT”ûÁ èÕ³8<ïûÉ`œq[Ñ–[e˜@¶ã±«›ŸÚÑ6zìs3¾4RûÛ¢CÄ›„Ê~Þå3e`=ºÁç¤û¯‹/°ŠŽ÷Œ%«×}wk¶ðü!º11xô*…Ñ£§³£ä?ÈØ‡Bƒ` -²s°{ÄqþxN””ð$æø‡Es{¸Ô‡Ñvþ!ñuw(T¥·.Z€•?Ä,àB«èÛ‹‘ì7CôŠ'5Nò…½Üº³mˆo,O ëE©Æo7†Âw¥ÍEÀ· "ùÂS|ù‡T÷«‰t¯´o¿á‡øS€tó—5Ô€)¿¿D“Õ0À^÷ü§<;”{–³«3¡¿%, ñ3o³n¥i-OÎ&«º1äeÍQüéz-³Y¯k~–¡¸¼ÖáR$It”!Yi¡!·‡“eЦåOçp;øm·üøyá.|…ø?m¨¨Â©Éš‰C_gÁ›=Ï/Gƒ=ínµbÛˆã„Ñîò4$cûÞ5IssókÓ¡ñºYy÷‡ŒÒÕl¡5“'ÒÌéÎëÙ­ÈŸáˆr⽓ï3x+Jqåæ-³ê6ärlšˆµt·üns…×ÑÉv²ÎaJÜþðNÔ€lwd]è!.ø½ô@ÜÓdµë¶ IwMQouYË~0Ý£…×ßÀÈWøœ~ß X ›o`ôª*û»"_?´ÞµkKéìŠWÊ;¯}Ѧq«Èhã96JF’êro·zäÚ‡ Ýà"k2dûÝŒKågK—²÷ÿÄ÷^‰î´xs=õäÜnîÃÔ³í¶ô^sÕß[N¬4{óvI™Ûd³6 -Â0³Ü ÓºZR9¥.ß_ì¶¶yE”ÌŘWg §=^¡»Ã=£†³‰¡·äV‡\‰Ã³v·îÞÏAL÷©º8-d8´íáÜ¢=E\_xý'à½ÕÎñeÛò²›+ç:Í³Ê Ú©¥´ºÅ 2Œ_ŒÁÇQ&q¡¸ÒíÓ뢥êÆG-6J6ºÛi†-1ýæ{|áõÐÚSvm5Ÿ«‘ÛXv''æl´Þ+]ˬ·ìˬ¢›½äIf°E6SPl+¥:± ÿÄ•"óm;IØ­¹KÑ+yÒ*ÚIåQ4Åë¢2•f¬þt„—¬õÀñ'…îÞž=' ‰¬ø9þônÑ?DûF"­–u¼ÉM¤²Ö9÷±ø0{8²Çx2çr9S‹:³Vm’—ç5ÚV‘êò‹ÕXås5=÷ kg­‹ü'‚×#ÍÀˆ¯|kVש“·o«YÆLaij2^{ÙQ;ö*Ãþ¢e¨e_ÊóëüÐìUÌêÉ›/Ï ²Íºt¡¹Ì€Zƒ°ÏíºÇoûlÐ=L½v\Õ¦ÜãT5•ôüôó4)ÒÍF¾WYúµse©ÆÐ ¿oV°p±/K4¸—ö¦)Q‹þv¨ÚÙ£Pê-mØ–?ÅoØ,]>ç„~›0…¡ŽãÆû3Üö•w?F¨O¯ÓŸ¶E_îtùqùÞo×6h®DnÂíÕ‡YWËÇZ“]Íhwƒ¢j‰í+ÝÊ”Ÿ¾Pľ'™f¿›W«Eµ! -­cêùÐM6y:ݾåb?]ʃü4Ǭ=ÄÊûO,4ª›ëáèW°•Æ ¥ ÷è×òû§$¹Ò‹G­rÛªG•&O4'çQ†=M‚&Syéª%Ô%ºEQý -vŠåTt—¨dos¯Õ¢PB7O«SÎè…¬5ÀÉïU«QŸØéQ«eQ<ëEöÚTÒü'¬V0zOô†÷J³O"'s-ezµÍÊâÞäñà¨Sa‹·ô0µÚGFi§›'*XC¹ýx-ªì P(~ØRžÆ Õ“&Ø,—}62­â•§Vã§œvgoÜ;ÄœÌ m/¥ -ëG*Ÿ*dˆÓ~Þ N‡ü€Ðúñþ~ˆëÿbri,ïCwÇrÒà®4¶ >pröCyµ z_:^åsàŽåÔå'38Šoö”‹]"ÌZreZ…é•â+½ä‡HKLýAÊ"HùËšÊctšÐ$®€‡©†ÓY‹Ç˜â~ˆrô}޼’Ž8æ2A«’þOÜÌ2Oe[ƒSý›¹*NT€×S€sä -B»¼‘O3u€yZ`ÓÓ`LÊX¾sn¡}œ2}æj½ˆ€î– -@÷pyƒ:ó  ü ÑMt2 àU¾@£u`¥[áå’Ò“ÚL -\ØÈý€o -ÿWè‹.ΡLóaÜ¢×wÝÀç$ðF‹ƒZo¼y± 0£r¯< d~–V¨Á¶CJF}5V ”TEze!æ…¯ÂÂWaÆI™ê“ -æå»&W‹ÉXMrðCü[ÿ€Uöb¡{±+Á¿€À23iˆ\|Îd¾Õšï¤–‹›'€­L‡i€  Ä„N`¬-ÛRH&+v·P\’cd~F®‹gú«± -°y‰}Ê툽—èråÆŒ6…˜[*¹ßð„…2ëe(s3ë"{û>¿O(œù¿<øŠzÂPó€àý Lø®¾xONí>G08~·z `Ça"P¶Éš30²´2/ˆYâñG\ó~ˆçÞÛID7Øxž¶+ÑZÊÂ.íg Pèñaj@×Pè2¯â­™ U¾óàÃ7HUüH!)ëæÂôû|%AxpNì>8Àzà2 NñÖI…m©²6À³sà9æ q„_É—ãÞsÿ. É$Ón^ŠÎ„]ù^>šL.˜œŽéñCü[‡](@¡f» ˆÉ­åñ#šûsºÔ|@â?ÛÖk µïÁ•mØ‚ÌÕD~bØ„ÿA]§‘Úu¾YpàQá¡¿úZå!:|ɽ_¤»(=Žý5w?›‘”LZñŠæôæ9(XÕ£IγÞRÌ’‡M/À ªÝ:ìÊ”ù&eÚ#@²”óÝŸ<³O€Lê$ ³Hó¢²øv@**Ÿ@ªß •Õá$ûê¥<áéÑ;åÙ?^&€HÁ¾àÝ/ˆpK&»MæÆW&nÌßB$¸ -Ò©\]xK“-ìwþ†Úï¾-¶ïèoDÒ€Xw@ªK÷Éø: o½H·‰¤Kê»ÍºÒªÈ{c H­yúIü­ù!ï!ÿ*'q9ä’Zç$ÞL+ÄÏ(6âfö³º.¥âñŠF'"ìÕãò%{ [A-Ÿá¢þ.mOåzf‹.±¶h]½Ö4ò`$þ-àpÏ Ð(âAê¡öyÄÆ =ö\í+ ºD¢ßTy1~Zcq¿›]2S‡×w­}b¤øH_qä\Ž¤Â‘ {Ÿµpñ™­z>¥Óâ\ìœ7AœÎߎu«Ky+T¨î{ñµêŽk©ÂšËHéåjþÀ’b¼ý]¿‹y㟟?þì¯O·êS@‘èPÖâö]óãíÈäü³”ë“ûÀ3âO¾²„«»Ó1ô2R|ôÈÏ)’ìTÀ˜õü‘“‹5¿Åb-/z)÷ûnÇÝùÓ÷{[|§r¦è—–« ™YÊ:õCØ0Áæ#俥õñùqX7¡åä@ú~¤Ã© (Wð`$ -î ão ÏrzÖHæ}´Cç4Œäþɺ¥M`Zìá‡ðß³ ô£ó8oÛg‚Gv{n­ËnÄ..ÝÛö6Œú0Öœ>¾¬Zk$µ®Ç¬= «„­¯¨yÛÍÞ&[é¿fs£úø _m¡Ð'€4uW`tçY “›@¡1÷| ücîΞªt,<¿/Ä¥ºç‰ÌŽ-«lzRaºÞ+îÝÛ^-t§™Ì}ÃίèºÑÙP+$––x0l|3ð{»ïô³…•=¨G`^Î$„9ÛMÑYè¾¶õ|¨ô|L7äåþþ‘I2²g%¥jó¶Afy<‚loüzL»ñT癿IŸcÔmÛ׉|8¬íÑN»Ç–k éÕú¥n«Î´.á üáx‚‹ØAŢ棳V±&žÕ1“<5=ËAd¼Õ2Ýp˜‡ÝkÒËNa½¡xû"þ Ež¡PU…ýÖ‡°+Ë Mƒï W¯ä¥Ÿ«,L‰Ëx›Ê?k¤æQ\VØéTEuÙ¶:]#ÆÚþ¾oZvßÙùvÑ®]¬8=Lsr@gÍâ"kt2vºóꔈRÇ …ö^ceØ|èA˜Kôb+u]>ŸëèߣßRÜíû{%é; ŠÊd”ï3¾z>xê>»ñõù-<÷ʯӼ:ƽý{TÜo»¦kžîòµÁp™n3'.9mç\ÓrÝ㬕•"sJ¯É¾@cÅGË?„~ÂQ/ê­íHÿÄQeuJ48éoš»W­¶uBˆÕ¯) Àþü¸PæÐý“¼Ë^˜à~·ç뮞.ÕÎ.ü!ŽÂ8õ>äïlnËè"»^õÇÂ’âת}1¯º•°5ÓlÔöKC@éÝ”ìÝNcu>»é¥\ÑâC/7¼oìÆ+ÍC®¡&ƒ×[Н«t8ÓV8pîüCÀ¢uþ ð&DB?ÞŒ{߬ØŽùbý=™ñ”8‡…ò¼U,Нÿ0’ÝdÿLo|ª²Ü¿2MGkt{óÚØÎÞ×F|±hÍ'}û¼ÖÏç7¢ïéËpž¹<­Ý‰Tù#Ũø)Ûú#Rq ‡ðPÅ9ä¬âE¨x 9ýhêy(S}ŸËdŸSí7·Ï,q.t²ŸžÃÐ<¦g½¿xtè>g6¶&¸Sq‘#vnÜ Þl±Í¾‘² m’cS½‚wmͬîàÝÚª ÷nJ:/aýlÚ‚£/ q(fƒ³|<..ýÌmqê÷3dô­èô ±smhC9NÍì ®¾Pë_R°X#êQdKº—3Jö®fÞ`\¹Á¨2Xf‰²'çyÕaÐ2¤ëKœäÛœ¢W> ]«_ æ ã,—ÊîÒ9ôs8õ.“Ï»{µ¸B· -î½nõ“ö»×žg¾.½ 1;õΗÁQ.ß·þý)þ4v¾ m¨…zÕY”k“|ø ïïÒ£®—ëÌ”=Mfn+Óß­d,I}Ÿ',{Ú.͈™^Ÿï~GŸˆYãÎý!œc¼y(ò g˧åö*Qx’¬”sÌ##|†† |4o/ÚÂYª3å“”¬A·ê7üîä#4õ1¼};ò#z™ð*gƒêrþVA›‡@#ø]Ýl 6»„/Úó´÷@ºâò ƒNûQö8gT½l}Çí¦k½á¶šæº›×åe›Îm6•ós]?§GÄKé{ŸÉ1 šÕcàý@%¢t©PYÚËŒ©Â–½ZùÕ¾ŠdœñgFYËt; ‹vy¡ý–þÈ 4må——Ð^2¨}ì‡ëâ½Ça3Tê$ٌЋ7åΩÜk´+ZNlÞ«”Þ°ÃÔ‚ëÌŠ§ºh+ŸzªUdê©ö`ÍR©õí†ý¨Ž‡Õs}¢ÇúçùöÿÅño=ÆÃuíÈ£Ân²Ýç×⡜0ÅâMŠl§ÆLSÉjë³N»ð>­ÚúXN - þÞñYÜ.¤;tP´Sƃ\kÖѫͩt~ˆ²õ­Ü›³‡í³™WúÃ@¯Ï0ÇefU;K̵VƒkíÔšŸ™Òð™þ-{ø —ò‡¨ò«0·éxzQô«˜µ¨~ßdddÕ~_~°a7kJÈQtß@kBÏÉPCÉMŸ,A‰Ôm'¬·¢vmÍW%«Ü³Â‰ÑµY§Ì‹Ìæœ÷ŒÑÐÆ¡6..^µ -èÒµÊ{åT ìïÈ´ ümö刾oÇ*½Þz¿ÁO½R9ׯjq¢ª¨ª³·‘ /½î*Á¡žzö§Ìé- -1+m—ŽMáK‘üçýb¯üª¹à^÷¶GÞoö¸ØÌy³+×¢=œq·ÐWÙ¦òüTY$¢Òv3b-c» -r­]ÊBöu.ó›Õ©¼ÚÁ_ø!þ»jYË,½K›[“0újòÐëZ3È»},›iæ*Á?!ILçê2v=KöšKý¦5RøaVW¦³%[ä{¦ÒŸDµÚpô¬Ö“ ›k»ðCT:»Wã·RÚ ‚]¢vÇKQ¥»ébuôÂIÊí §RÑ/ák÷Åþ7¬vƒ6¿OOã`\ïz+ƪK—Þ³zƒk‘ØcgZ,c-§5/7±]—áä|µÅž¦¨ÈŒë×~AvýÚM'•¯Ï+Øy¸.§®Ê¡ƒ„K1ôW¡„®¾ûW?L.>ˆJŽÉ‹ìý4ºg­ÑÎä,Wa¦™WÌÌþöLž3Òðnº)宪ï·Î½y° „\÷yhqýšÛÒ}P÷µ:Æ„³«Í=E¿¶Z¾²RZЕ¥R¾Z’«d½˜{ÞZ…âç,äiü ç˜ôv˜µ”ó,ãhŽLÉ…ÝHZâ¢R:õ–$r¿J9¤,PÄ…!e1Ýù ?ÄÌý\nz¢QÑ€Š¹S¯!N|þTNÍ–.ëY?Ü2•ÇÝ«Z÷TùäKY*’a‰Š·Âi¾½çÇdãå²4Ó*RÅÓX&-1¯)7îô‘òWX#•Çk2¡uu½dÕî§óÏ<_úmÌçMˆÓé7Œ''*ÈT *¬¼"…Ó’µýrKÐ'WøÌÂ.É•ô¬Îy~Ü6í“ê8™×1·¢V£x–jÖš”9ÕM©íÖŽ8mZ>QJµ.øX–Ìðå* i1B"j -VÖVÛ@˜ñ´¸\ -B)þ€õ”@ý Ð]X3i¬•î,Sôص:Û*※Y€¼ €HB6²X}O7EŸ@–õ;@¦c "ÎÄhÂo×!@f¬ 1ßCœOÿ@Ö·@ÑLÐ*s{C2þ Óõâ7Ó+4Ô¸ƒB¢QílÜŒžÍ†Ü|€ô  æ,%?Z-1kP2Ü4] òŸn - ¬¡´¿u€ÜºËo[n ~ý*‹¿€µl ÖúÒóElèçCiž“ß0{Z„]9„2í+”ù*tžêʰ6ƒ2÷g`‹?{H°þX5* £€žÆ:@Ýp Ð9›1êÀëìÄh¯X˜v*&‹-Tƒ‡üë« b¢‚/ÞÏÉ|îA|xþIe$ÜÀv6¼roY…òàC¸9ïÃÚQ¸ö̓KÖ7]ÿHìM×¶T%€)þ` ¶/ËÛÏo[ð‘ƒþˆÐW -}E@sƒ˜£ßôrêµ9lSÏž)÷¢á!ñ}ZxFŸp™ü€]–PæåRûæ-‡e(4ñ€Èû€¨• c ˆÜ=øƒ¡¾5àWº'àÊØG<,¶¯ó¯€­) -B¬lf7!Þp\Ìîì›^>½\N}}óÉí ñP‹Ýô#·¯¦“ÊcI\óù ¥YçqV¯aüöÜ•ξk€@»ßTãÊô •¤ÒÎ÷—ˆOç»m}Uøf{Ô¸¶2ø³·øn\O—‹°¥-±±d_ÒlÙ~‘l¢<÷aaöðo#ï®í¶÷„Vwè7+OÅ–×KGüþ„Ÿé8²(yíæèôCü)þÀ —Ê??€üɉ¦ÒˆR#U©ógR†¦— -Rú§R­ºRˆ>yísõJëËÓ3ËO“Ç`î`÷³g¤ˆ{9·*'‘ëׄÂí¶,Žn,6\^/ŠÖæ ·ã˜ºZêx2ðÐÊ$Þ§GÄe;DMÿ7À®¼–á87`‚CãÏÉ-%ùoÞv&®!v@š9 b~ɲ(Ú »‡®¦jûL’7·çf´r£üö°¹¥ç—õý>z¬¹’‰¯ZÙea¹ÒÒüÂ]ÎÞÍ\~ÛðÈŠˆA`qö0{tÞ»çz›ßˆ6 …Z È•Úé}mÃúë7k;:ÜoìÓ‹×NÇñítã˨ÿyu²°Çöe§º "²µ-?½M £õÜCg«vÝ[.…Œ}XHOýâÈñði«g#5?—_̼dl¦½+{&W/p¯¾Dö~*T{îdkVVÿâ‡ø·ˆE—©RäÇê|³ŒÚ÷ ßù+ï -î |ôMÔâíyò”‚#š<}ÜÇw%)_pkϳnºïö -CMy±wt§ÿÄL» øð*ó2·ò¬jÕ¹šlqõ™52QÞX ­¾€úz*ô§Á¤k6c/Uܳ½×F¨pùàÔÐÐ|2uî…âÿ²ŒªýC<ªôÅŽ ëc^`ôf—îÖ=x2íÊŠ„lëµFøteI¢CÎQ3?ËVßšZÜÈ4ûykÖx>6jSbtN&]?K޽ü¶=ÎÚ†­rêøCŒ.ê~7ÒוÖZ»Ždç7€9BU†éÝGT°ÓŸ¥Ð3’Æ®3rUU ^£íÈÛWË]ˆ/·žú<×hOHý‹ÃÂÌÙg9UÓÝqô´Œ8k!gÕÀz£ñd÷hØceZÞéC©Žô¾ˆh×àYÓ¦sÄÔj]ÃÞÓênh.¹õ°Þ)-¿pþÂñ§)n…®Z RÅÞKÚÚu‘üùê·›'+ßå8Æ*{èq¬míÝóÖ+dŸ,)ìØ±™±|U2X‰5Ö™m{J6ÛݱNkºV fß=½áf=¼ ÓãàÕóžƒæ±YR?ö®~¬Ì^]qˆ«®ŠÕKu•ŠìßmèXd,A™õJ7á<©s¾v:hN–ð¾ÄQó2òNÜÑ+ÓØ8'j»J]äÈ‚-˜×Z)Òl¼å¼!ì#zr¸t¹qþþäG“¬Ù×G@¡/åçöŠ»­ß2}Êö÷þeÐßßncÔ¢¿_Ï–Jê6w”Ôq2ÿ uºÕ CÒ P³Ù k!x”¢ïŠ -TجíKÏfÛj£5îøË…ê—Nó8èÞM‡ñQï‹IMaTÔ/*ÁŒª‹ek8ßפAÞ1•­²Ÿõûäj#ÐSØ;K­ToTVú½Q¥ºê{ÂZÎëí…¬î[Ví†õ¾¿&` -Õ-îAçµÆ¥Ü±sG¬ðÉ|?.ìKçSiË*1·Þ 'u™e%ËžHǃÅí ×^Ößè§SzØkä´{iW6ÅN]ݨ÷Ž’^Z²pÚä‡èiÙìµ@šo¨Ä_¢4»?R‚§WÝjžpº>=ïF*aþ@ò¤/.w%4<'EÊë}\j_š[¹-Û‘r?Ħ ÔÚ*ª]G¯ä ëÙž¸3A½ž§™{í¡GÂÕy4=DP½ J™Tµï7ܦ\Ôx©;Ýd4©ÞÂçb“-yÂÒvÞB§µç…NÛœ ïÃu)ÚäÚ‹ø,],‰•®æo€6İ€*ÄÍ0]fèsŸÚŸÎ2¹)­Ì¦‚ôŠ*ÜŠN˜¹y:h&–b—So´9ŽËÎëª=)®-‡p• ›Ù¾ú˽(§²]¦Oó¢c?gn3^ªàûŽÌHõT›¹Ø3;ûº½àEÓ¶4±M¡ƒšÆo€îƒörG%æ\•š°3c#µ£—yØ‹ã-¾¢ˆ>æŒólzn)}Ö䳬lPþÓë÷º;ªöç!†çïj:Y"ýÂ1—êU—NNšG-¢h endstream endobj 46 0 obj <>stream -òÛU$wúóô‡hçïѦ5j´’Véû¸t«­¢/Úêxj·ý±bvz¡büèà¥j´\²Çá{­Kãn£á«ôŽþ8ú=z[ ºD~_JFƾòã¨1¸ñÇâ”zPüEt‘ñpïÞ»1"5]#-lºÈSPoçHléÊYoÖÈÛªa*|ܨ3r­Q'ê“ûœ.·gÇjNØôc­Ò >ù €"N˨£gÈUœtB‹f[É‚¢A”(‚KW‹6Hft+vjU:%Ö§4ºq¦ÌʲQß”7NPÚip–ŽütQéT¸BàšƒB±\Þæ/–Ê_Ž%xç,0£üè™æË6¢ýÁñoýAgû°"¹NóËÜY[8O)FÛ.bx+¾àæœfÓægõÃÊuÙ½{1cÂÅjµØÍTë·\ùx«z;Y­Ò~¢ŠEß”û?D1÷ %d<ÏÓ„±É1”{ÎZÉr£’qô”iÑK‡ú$IšZYóĹGñܶûæõÁû:U=ꤽ…Ö? -ˆ.²º¬;:—r?3eNAcX«Mù ýr­seÕ—£²Tä’’L3 ˜{¦ÑBñƒ¦r±¤³÷ƒWÈrـδŠ^âé@HKìU%å:#3ÈÈOùüýC¤TqQIåÉ–Jœ<%"N~- 1­þ#S.žFï)C0ÙJk~·êTHÌáܘԢTvPyÐQY*´ÍìÂÉz/¿gD´w«œ!.·Y.£ì©Ï¥æQXLéÔ I -Üã”Úþ¼ˆ“KbD‰¤³8åªSTyÔÇ“ï®Õ=â˜É iqÃ"Òj4¤Õ”Nÿâ›ý¿u8/4_9•fÛÿõ¢U_qR™Öh‡”…NN*æîæêJ)Ùûn,eUêS«£¦¥*€ýT?Sjk¡§õ|Œ‡»Á§©ï;†0&/8ÈëZ^#­ZÃXÄH‹Ic³2ÒbKD8GZõáªM¡Kú7´“¹‡Y€òT j½kâ÷Œ;ä!62v’¶G¯Ïõ½|}üîùõ1Ÿ×gV.@L8ˆ¨ûú˜yøS²^Ÿ¡µ†¸¯V¿C„Ù×gÔ¬C,¥×GGá÷ìòô ¸êûS¼>;Öøböú<»s€ûoüŠÖ>9€®jE€ú"º²Pí´µ.e€lß@ô† æ"MàÉtKß¶dŽm€ x¢50ˆp,w¾ ^Y€àßÿ *ð¢ ã—/LþÅ ì|õZ›nÖÿ¶å[T¹C¡…~š€Uô -À( -½6@ר -P5Z˜ïšBî Ûp öë*@ˆÑBX&@&§5@¦)¢s…p‘o[ŒÜ7îñ™•5ˆ™ýM|oÿvßdøî…_»G6_ØÜstý:}A™q1°Þ°0{Cl—Ô6&¾oÿÁäî7¾] Å€<¨¹†­Òp /”³&ñ e -¶¥Ü9C؈ЊX(v!FÄmó6™àÙݽgº9÷~ˆoq¼æÙý­šØn\O‰ë¿Gú›emv²»Ø€®4Àëé:ÀÅQÖÎS€“™5À"9Øâ€LÁaƒj|`¹Õwç0†ÝM€á5 å±¹ôœ|z©e ìÚK¬$â³Ç²ÆCé 6÷"3:&úˆ=&•túx›ö¯þµ9îìCqë¯/½ëxù¾¿&|ŠPèeš¸p)<ÈÒ€@ûM@T* rŒ ðûxp÷|”O¼!WžvE€]_úËUsÎ+u®ížûnçü„ëíäáwôÏ#ç:äý$Åe¸/zÅvR9™“ØjÛË+\Ïù\ã4ƒPÂ?ÞI“Jÿ ɃA†ùàpw2g7ßmÖhí”yøž8ò憫ïn[»{€Ps øqæ•.kµÇÑåùûy©÷Ò†›&㿼ÝZòáÆÌäsü`­{Ìé äêÄGêÚš•Ø:çaHª'ç|LûP×IõZ;ßz~íå€[Á|×ÏvŒíq˜›ü8{À×—ÔX€BÁ¬R#Z©­£C|ÓËvóø¢ÔúýQ¬FhrÝ -7“×ã†ß¯íyŽ~´¾vgáöÔ^†i²½¿ôûÝóù¸é'çükŠžF­ TSÑŸÏÚS¯]i¬“u÷].XoAÇÙ\©Ð\³si¼z68ý/À i~χ!2o(4J ¥@æµ ÕÏä›^^=Õ¸x¸ÓýÕùÆÑÜãʧߩpÏíJe£°ç"]‚ëSŽf'ûO+¶=4o¼vp:6©8>ý Œ÷÷Ý@«ìú€RÝ‹˜›o®ÍÏrSõÃÅÊnHÖ SgçÌFN¯5ÐþÀ› úé,Hq“TXj|Kít¦6}Ÿ;IH Îä¸-gvù^_Îç~ù±Cø¯7WðVR;ˆ·Ckß»¬å²šŒ¶Ácbn‹åÙÚ­ä æ©vHÖÉ5M®ž÷ugù~#ã%–Ë/âðaÛ>20æÃKVÿ!,ý}Z•¥7ø ?PhÔȃ”»-Ò¾yÛ» ÒNorŸvV“¸SÇPiôç§ëø¹óß|íâèþµïgb—· E7Ê% Œ÷càuÖ÷Àì­^{}´üÌÄÙ²sQÖ ·bŸœ}Þ{9”Þ£í¤yñR[¡ž5M£ãMf–¦ gRQ¿Pþñ„©R7ÿÍä@¤)§Òû\ÿ9œaƒø#ƒ^è«]ådÝïøEç@e-oW¸åc·º\¼×O²C®&U„‰_[HUÛ‘½qÏ>b†>?Eªe…ÉxkÑEÆ/¦1ᨙµùˆ06ï†õŒé-ê“-&dÏéÿ8æß³&´Yve¾£å}ç94|é^ó$1Úï3Ý·›þꙓ¡UfJîl;ne6ó=;­–¿‹u<†‡QIºTšLhîùØ1g¶?›Wæcc‘Ÿ;2º§|, éI6'än¯úi¢ m¨Iç%x•oÑû €Xàätç@Æó2ø7³þ’ÞÆG ˆéæä&;צÇJ{ØÒ‡E±7*ë#mJ —C“ -Âbð,Kƒ§9élñ }14^3Y};ƒîÒ_)²dm›{õc†•$ùVü¾ÅêóöÒ@÷Ã’qMáÌ®;øìû~ä´–žÚç§¾²nÖË›Qºü LSï%2öµ^Z×Ð\qTÑïŒVScaðJŸê‹-µÓ­•ŸË*›[º Ñ*⬡(â ÚSD¡ }!þŠR -UÙ$Ù4²—RÊû‘åëyðH÷µ/$c°:³ÔúÓœT—»++8Cù¢Ï«qÝ1“ÕÖdú49Œ·qÞj¼FQÃͰýìc,ZýxRSq¥–Üß·e£O™û½¬’VJVÓS^V+¨*û=¢/ûwOöˉø…ð   L®©r =1ƒÎë‡ð¶'ÿ±VÇÏítO?6N¡€®¤|¹´Èïöæ`!6¨Õ]ldNœ8ï+}ñ±ÊsP$Ɉzü¿ø!þ­C+šg#0ÏÿsŸÀüûî²ánnÝ®k<=ŠG®}·£¡˜²æl½:ãO±0͈Òp\b€5b°îzؼ%Õ¥ô‹B5r‰!ì±^™RbwKâSÂ2-ôT9ؼp毼p1X~S¬ÁÏꕮЋ¢°æ:Øÿ ;ëLŒ>zXð(Ôž‡~4N¶µ1ÿëÕv}]f¬ìÙž 6gkîÇÈl}‹ÓþÄhŽË™Lļ×Óa+*Ão·ÙöýG3KÙ8êÆŽó’êO>j9Ï»é,ÛÙcÕ^‡’æV[ñè°­¬øê‹Üöü—ÔéQ/'ÆX‡'z¯ö_ø39£žWDýµþÓ•Gâå¡Öíè¸<šo#w˜׿cªp25Öë 3b´ÊaûÔ×Ô´põ†²ìE^j×eSû³fêMàã*“£4Õ>¢ïjëLÄf„ -“æø”?7Ç[®ÒÏï½æxJ- …Ö ÷Ûísúÿ¶å[< ÒúŸ1‡&n$î½…À)ùEÎH¾ž,›ª{Foáî&EÇ õxðÀµ—z(1eÐPü,!÷‹ÍÞ«•+¦dãµØÉ8¿ëñQ§ÿÈ<áU -S4Õ¼ßåFR-tìp0ææ äæ×V‰›/ï]n> %î1Ý Ã[µñÕiý…xiØÄè¹çîðÉÁñbÀe/sÖîùÐ^D)ÞÃq~­òîñü}@*eRQ-w»v×ÚŠŠ„`©± µösžzŽÝöÉàƒÖ¸\º5k§4ʽúD¾þ²­z'\ŒêØ„÷X+Zw8.¬¨Å"»Ñ|¾ÞvÝv}qZµþBè+E zôÕݵhü¶¨b‘@’¹ÈÎjå›ñ­ù «ª`—%R,ó¹Ål%¬0Y¦©÷Â}Á*-T¢ÖD•ÿ–=ëÕ©*Þ­uKªº65¿â9Æ©’-žåBç•ÊÝ3Qœ…ŸRañÁÈü³[br±Ürt¢©Y‡}ùY$½3ƒ(gð>$|Çܾæ‰vW%ÁŸ½Á{«ê/–+‡>(•²èI½±æŒîØÑì”XeoTn{JiDÖÔ.WM#«SÉl -|¹ð|ƒŽQr^ëð~ShâÞ>OåVá–cJËk–«Í¢ÌF]~ßP1"ûÙq±{obá¶<À*™Ù½™‹ŒÖKÃr¿ŒLDUø Ìý7”p­û2œ9|Ÿ‰×z‘Õ¬‰J•Y›WZãù"¹Ê&QÅÓ)iÔžÏçu^å˜âh’媔œÅ1¢ lh‰…îÃÄä>ºFç#h‡¨bÂú z@LJ¿9Øæ´ 1­\êßq« EusÓ© †föëö Ù6^a¦½­þæSõÓd°hg…ŽX× ²WÞ1 UPJ=G?¯ Ë"WÊàQ«‰槉!˜Æd.bÐzfI#ÍÏÀT¥Ý…˜F¹è+ð±!†d¿YÛ]bíd -ÄjÍA¬]ðEtšʬþmûîü"*dš@%>GHžcà39§;¹§y±Ž&þ踼ºîÇ]üéTåãëÑC°/`aIüaêZü9^üÚ/À†ˆ?ã,XsÌÓ_:ÿ4_Øï[ðjµà~"lƒ07ßÑ»O ýæ-Ÿß!²?¢uŠqjoq:F¸8%ûÓ8EÖVü9Á{€ðÑøsΗ=`wQü¹gÑŒ?ñìU"Å€8>ýæw_>kð3çÁ~áÁ{>N«‘ð‹Ði#߉"È ’ÀÝ žÀ…;ss/%дL%P•¿åã)1h£~3½©Ú ‡$NµR0©vtœ.¡o2¼)dºqª—3ŽS£]¬Áz^i;|isYl#û7`uûóJ~ ñû³øK…2ÂÛ€@Wèþ›euðl«ÝzsK6‹Xï\Z&9ñˆß]ˆÉƒS…™§V±¹€뱺ò=.ÐJ 2 VïJ@ 'g’1:<öÞÄâ-¾rúQ~¨õô)^gò—itbûÓGýüJÑaÞžH‚Ôp4Ax’øæ`iú›jöšßñàý:r ;µ×!Á'Û‘ÀåG-+u°/„(63@jƽ1²Ž±B-xoCþöÎT,è•'‘Ú³4 »[ž~°GCêÎïšÏî->žÝÞÙËìj¿dé‚Qá CÄ…<úîã ZÚ—´]¢´<á$Þ) ²#Ö "0§a8ú}i›}{þ©öÚ®ôk܇†Ï£OHÏr.·ˆÎnÝŠjµyÜ<9|,øð}_jõ¦ÅÎAåÑ¥?Tçg¾[™žöëD>ûWéXkUù =6¹?ü`_'H&þÆX†´Å# ª™ Ð(_K°¼Æ&ü4,j ª¶ïѱ¾Š E—7Œ<îûwáÑDáÆÊÀÌÍꃃT¥«3h©?ر;ÖeÃNý ~<ÜξÜÂN…hÚ:Ö~ð4«â>^ÝÆ{Yvéf1òñGÚÛ¸;þoÀÑE P€4ÀÁ VgÌþþuì»>¾S7'1Pê«4ÙQƒ6wcÕ9ÝØJéuE8z!|¤xÎ9qã$¾R&”o÷þqÂáQ¹+‡¦’Zþ,ïö %E{hœ­ø[¢Ìz£cý­xùÍ©¦6uÐs5âÅ8FÃ¥~‘ çR -Jq ÷(®UHpjûM/¹˜¿5ÇÏš6ßÍð-_×GÏ8ï]~{*Ý à^ylìü(x·çéþSŸdöìdZÙõøaÓß“®ŸA§‚' ùö°ÚZÛ -6Ó¡®dÒt[ -ïtØke¯mnãkmÊÜ£çÖ_@Ké»…ÙJ 2AaZo,ÁJôí_ßô2Ó}Î}…½3ãc÷â»ÃS¥O:Åé{§”¬w}gwô³Uåá -“t[&øÌfÖè–׋v·åFónô^[^pÌÒb±úðûõŠ=Tïvï}«X³ƒÌòŽ%9 A¿R-Zë÷¶J»~sá`óú/ô°øå *wˆx‡Ê—3Ãn=!E®Ã%hDEä `É­øÂ™¥·Ó-,¬µ¥¹p—´~°•[C|{°ñOÖHÑ#S”¦úJN¿hÊ^; í%yŸ4½hÏÔx†®Uú~‚Æ4ˆ7§•ÍLî(øjÍGM¢>Ï¡híž&¬‘‹ß0(Lx\)>sÈ-\TõëqÙ•ïWÞþXËç¶5òFºï£Ôw` ™ØÃýfiå)Ñ5NÇÊ^¿ZÐeù¯/í5Ü#jÒ]çT¨²[YpšQÒq<÷¯%cžk<ž3QWʳâàEMO•G{*_ä´¢¸uùÚЪ¿øç˜·“ô} ZøewÉ^ÂEGƒ•ÿ8ì7Ð \/~@»÷mífbnl«2Ê2v¶³LiÀ(úM{ËeÖÝhcxPÙqãºè J‰2l Ø\(ÅÅÙ$7§ÓRv _ù®"׃ÏQzhDõ…¶ÔÂYòû.“—ߨNŒQ¾ü‹›AQÊyøa43ïsɹœ´ í÷¸kï}‘îí·—S뼦äFê m¼`çŠAÛ,?™¾Nº§‰×4í;çð°j/ú$ì+ß çy׌f¥™”Ê·"—‘¢ W•ZÝ9;é |I´÷´'²"‹¬úi}!E¸y«}©ˆpñXúE‚ù·?3ç‡,vÃàñ=$3£ã~Ãìv~±Öò¶óð½]°8=hÿ²…¬Ÿ5++¦®7çIW³LEP‘Su®øldÌÇs=+÷º{ù–.R¦¯É{— âg•0¶QjŒ-+#aûŽÙ…ˆÙÏB涬©}ß–…Ìj\ü85#(! -Jæ²ÇÉÛÁ|V»&xÛ+ùY¯_¶éºP,í¾ã“ßÒÃ<£"¦k:QÑì‰ÛQQµ3TF»·< ã…:OHK&?ñvëA(¦‚õop9|hšíßZsT¨† +Ë™|¹ÝÿðåA­ k¼´é‚v2/éí2/ɵÂ/¼üø<ço-=K9ä ÖuïjoÓ"<×e%Ú^e& éY - -£qËAÚg~*ª/´aŠõfÓþr<­?Ú3pŽ™ûrÂèº#rмxó"ä f‡f‰^ÔFmpŸö¸ZM´ù@Þƒ&t© T¯ ÔzZܤ4¸3qáNÍ»=8?°pa8çïÚ»­ÒÌlÝOcé®<·¬ÒGµ ’rKZòcµ?Q²Ê˜«ÖggåÆÊC¤Îô9™À…:ÞÚ#[ÊÅí¨$!_Ù¾žƒyDú¯Ê ý^Bf˜£kJ1üg1神õª½¤–{ñÛ)ô©Ò&ÿ‹ß´ÐÖXF i}ñ<ÝmZƒ÷Öé—y×.4ï B½¨-éˆ=qu3ŸJÀ·°Ù…ŠÊò³,µ$ºWè‰ë…/Œ3×Ñltìç–ü4}8ÃÆÔÞ ZcûÞûJÚƒa³À¡ñ‘êÙ–ÜvÅ{wÈQ…îV•+z×Ë—ªÅŒs³óü/b?ˆËï>ä -¶W§å»+v¿]e™¶kÖösCKî\%jW·/ÐPD®ù/sµâ úd…`0Žd˜’¾3èÖ‹ÁòäZ}º+l{]jxæ°ˆ»™ Ÿaó‡e“‘ʘÈH•æ…‘2hŽ9¹2sìPEVØrvwbr¿x¼Äüû4SÓÛ.7Ù?ئed|§¿\{Vù•::5hê‹]­3O;-sÖ(LÙP¦¯‰;'1‘ØÆea‚8mþÊrܰ9KFý¤¸•{Î~ºäTË鎭[¸T#¦|hctí«^û=¬j‹Ý]((ìDhYÌ´ßØÑ ¨të4ßm˜¢ÒYµ¸ùQÛØ.¿ð¶MTJm+ÉÛ Aä) -O²?ØŸÅ_.{iöÜLrÜ,‹lààëÛÞœ{øz 횦rH'‹i^I"o£qaœjBÙèîx¡ÃöŽ‚ûŽæ{}çZç²›fOaðƒ13¥1¡ˆÖi×ókŠ.•Níný´úÅY±…½«ƒ¦gÕý¦' Xs Íᑬ¡| }¼2¿ÉíåæÎÛ£»úTÖ ’Àõ¢wÇY챫>#3Ø|²É‘â¨NUzCjY‘6®Ú}7{ƒsðär¢‹°§ªV`æa¯N«×Ó¡¤ÒbæÕY››”ÌÖ`Ö š#Qx‘ât#‹tô,'ˆ[7ÂciÈm®Ð(û¹\£l¡ÙF™¿Øo‰æyÓ. -ÇUßïæ'ݨ\±æµ¢ IÝx9ÂO ÷Êq¶Åu'·©üƒ±çîËanCâHGãÊ«ó–(õQ -–J66/±­ŒV7ófK#K*»iL_ö½®|"¬Þp¦T-š¨v-jKi­5šg«¯•©“Q5zè¿øÁþ¾öª³mè"7öd*¥®¿„)ÝUʵ“)¥ë›2.ÎÉÐPêCnÏõX†ì¯:”Ó÷¨î{/·7£Òª=ÈM­QË~6óˆ’¡sÌ“ô ZzæÓ­«bUª=O‚Uoî±Ê4‰´ -¡;²âx­â̽¸‚¸QÞ”æx¹ïqX§+è/Öôvr²NÌ:X~ÈçV9^P[¶Æ%кZ4è*\GB/“º}†\®) -yãMòDÍ›õG$3umyë­AÛ¨½ sW3®Ÿ{õÓ%áê -‘òUXöš•ÞõÝ/{ÝŽR΄;¿´ŸRïRþs­A8ýÁŠÇÉñVœ\p¬X¢C´pº[Ha*Šð/V9 u#À½Å~Mã‘éébî°P†ï3éeéÏQÄ´FJ’•„ªÔ:æ©Rö^ß6y„5åìÙš—Dún—Š92(†é^”µ \¸âÙBaîóíü£îñy5ªê¹Wß=æŒJÍ& ¨“µ9ÚÏZ¯Ê2Ó;’e†!’I?ü—ìÏÂмþ^åYy=}n5Sêž:|ë¡Ìµ×ˆ~!®M˜ÙVC±©jµ[Û–JAµ‘/VkD· ”š£Y¾eÌõœ™Ù¬³ŸÅýí¢ÙGf=$S°ì&f ¾D­ñøþt4ñ±9¹â…Q#…ùœ€…éIrˆcå ˜TÑ‘_h}ÐYÝÇjB\ÞKþ;o Œˆ1m ÕïS/Sõeƒ©WúŽ[.Ê$œË7/ç|ŽÞ¼¸,W­ˆ™Í¢(¾N(D6Éé ]Y\aáºîartB/¾úD빂4K¯¬Ï)ŠÁ²–ãb¨2úÍ(vª}ˆ¡ç'ˆaè¿1Ÿ×Qg2¬`:¿ËJ/3mt‡|óHÓL-΋Írö¤” õþ-—eÞ5,ƒ·Î"û~×q‘© ]˜Å*hÔEë™}iVC˜ª,ˆiÌdÀY‡²ºWˆiæ¿CWw$Ä´d bÚôî;Íø¬“é‚X›àþþh†„5"³}µ7êËSÚXãæÑ[öjt4o—Dèæ^s/—Á²8†U2‡…άŒ^¶µrßݳ°.9yˆ©¾ÌË«š€”a¶ùo‰5SÀÛÑ€ä¢5¾m9°CíòðgXðâ;,¸'ü"Nç§þ%ŠÓËó§ó¼Ê9Ïø3Qã8ykàLŽïUŽ“y³'Œ.Ä ‹h~؟⤛yº0`™ÁÊÖ°,À?Ñ#–€Í1Nú…7@¿T - -ÿ¡øóçƒâ?,/åߤV%*4Ž¿Ñ^^ñ籉A´'4þ”‰rüÁe&N.á8N®$ØèuåÅÉ ¾:ïïVnKpÎÇÉ«:m€Ö\Å8yP:àÆIÔOa.Nžxõ_|Bý?4âO¡FþD˜~ç‡8T/?H.¿ˆ@^ Ú8‰SP]Ç)<?½ºñGÎ~ç|뀳†`šŽxüá± VæpÄŸQNÈæ7Ÿ,dOñGÙÀñŠÄKï5…7Þ˜ÿ¬i¼¼Qøò¿ñO„a-J`¤ýH çw> ½b•e”ñ¦§„úæ“Wä NÇ‹Yœ’g;N›­CœpÚoPœ–¾9æÒ° -°ÛqÚJzqÚnŠ€ùðtc„͆o¼/ÃÏ ®¢°å‘ôVɨlŠäãÂ@ü]´šk>fi“üË?1’ô=Çý/ü`&€4›ÀM²–@/‰M Ã'k Të¬(kŸ(űsh`17J?Ø{sžÔ߃™Ýyùw·÷©'á¼^³§hVÌè”ùéò¹7(w3ÐzóšPIóÊìNÍ+$l¬M^\Xi\¸µð?vA\{éû7ì`PKf·oô Ró¾Ã¬ß½vr÷&æ{›·¯Ýkx|nÃdzDNÓhª›Ä㚬K»'ïÑ÷VÿÓ¿½‰’xë¬výä·Þvq“âû<\·²à®Ï6ŽçbT?V‡fõ ìùÊ¡1¦Ëf‹AKÇÿ ¼ß¤j¦`=ë£ßôr)Ab¸ ~gðæß˜øƒ=¥Ð›G5aeÞŸør{‹¡ÅñšbËèÚ¥ÍÏeíøÄGÃÒ™^ê§ýêB -HÜ¥aA:ÎÆ´yXèçkÐî"è.%ðÊÉ…¿?ÑËžw”<ÏcÀµ¿Ý߉üv¼H²IÌ„gì¯Û=A'$Akð)%è‘m¿ŠÛUçñ Èî-)=GWN fg¯ê'q ®C™Žuœ¹šP\ûÞcÑ}lv²{:íUv7iï8t;ð7ÃdæãÁÈÛÆ%ÙÈä+¿žS»¢{·•‚«²BÞmÂlÖ¡ÊbŸ?ø_d¸Á¡ïˆ0óƒ”oÆ1"L(Ÿçæ¸x7.VáÚ›Qµs¾CÐGe®ô‚×¹'ím±¡íÜaÕñ·ÝüÞmü³—³3Ï­àÍ©]Ën*s²²¾Ö¥Îº®­G®cúæ¹í}Åœ#ÜvkŸ¢ªû¼5x-20G5s«ò—9EQ‚öà;ˆ°%Ø|%Ø5C¼ Ø -¿›_øª‡3¡ƒ¦žøn½XµýÌUlý¶´935m}k7ª!¾Ûñ³clñçê“)Â+¶[ÉÚÛh"²Ö es.ÆášBúé‰çôjÿ™YÞðø‚kQN÷M;  ¿€ò· ¯ã¹ÝÌ.ü©è_üâ(<ªÐä¸w8öí -eb+çâÚúÎû´»|͇½àäUwP×ì>›[YÞ2ú/VæR¼˜ùú4¤RÑÏ|« W¡¹\w¼Í®Öz o•ƘÌâãՉŊÏa‹nB”õâ -ý%öH°\õš`ñ.¾! '÷ƒ‹•‡{Tý–}Ð]ØÞs oãÞm£@*äê)Wp *Ѱ7ë+kwdŠ¥éԳå>GYwyï°{íI¶®Z[îιëã‹4¿¬.ò±«¬¯Cõ›{Oâ:ç7.> ˆ)ú™¯xVP¸t6šŸ¿$¨[2ì="õñ霈õá5/{w¸ÔvÃB[õJÁÚ4˜òÑíðÙ÷ª7xà ÄvK»b–~¥1nù ßcíU9ÏUP‰è‹4^®\Ý<*ƒÒâÌ4 D??+lãÖTÎŽ'òEë…ò¼õ†å:#_`éî=>’Êâ?€–Ò^'è£޹o¼n¶Ù…Wáà0z\ú{„T¼Ò(œm³¹ê| Ë[aýnå×CȘ>ò9½1ý44ccÒª­Œ‹^™œ(CWæ»ný`³C5õ¦§B|–¯Äó%×».5ÍÚäE„£ 5^ïĤÙûˆÖk ‰6:M¿|ß -±Èìë/Žù{>¯DMÝŒyËÚecp\¶gë9[G7qéb]Yá…ŠcØúɘY¹d¹œî .,+‹5K¶”á9fçBpÍ&†3NêRVdÙ•4aLŒþè.ÚÌ!wWsqô`ü;Va+ÜÞ´è¥Ï—DØŽ3¯_üc.¾š^÷`Ó;nhuCNœ©K;¹ñªoï$«`m c^ºK½¹Ôt,!?Ø¢o—óJ¶•’³c°¡§³Î '/HJ”–•Š2é!SdÍÇvÜS¯'aûÎ}F»{#7Ê7mš?¼u›Ÿø|Äñcú%ùòGÙ{þåû³uÑ$º[lá^÷Ðvç Ævv-«nlΧ«|,±ÝŒ¹&hËWVó5(3¼-ƒêgh÷̬ÌËò}È´¤åe'æå1W‡ƒ<^›6è½ -|sêŒÆÕé— -J4<ËgbX½µÚƒ›Z×7-{Œ“/ïaõ±¤ç—è/ ¸ç¾o\ïy8¼Ëšë+cC>â…ãÐ{µs‚+×ì§¿42Ÿ™yw1Dò§ùy¿g•™…Éê–*HT—hˆéeG7¥'ø¼0͎‡Ֆ=¼ZûÁèÜûϼ„‚j7"{qå´èÅÕàúƒõâZ9é·gí¸ßækÏþÒ$£_ÄüܯÄ,8죵òÊúXw,\¬¯æÔ<-aùÚ.Y zš“ÅP=áC|fÕÖ:’5qêäI²ÏåD·}ªŽ­%äøËŸÐÌpxI>òà½õþóòñzñ¾r©ŸO9.·¨rȱ'qH8 -9®Æ‡<_œäR¸tœ>þòƒE‹çâ|ž4ÅýÇò«Í}GëŽ[sv¾Z™yOÐ`’¤•}£<šKåñr -Ž'·îôe²ªâ±Ø[Ÿ0aM £‰Ù©óS1ß6ˆ[oЂb/±VàªìAÚÊåЙv \s£s»Äææ˜Àæ”Ê‘í6ï/OÖOü¨KT‚Ç—û_ÁŸÃÆòüñíf¯ ˜4VCQWÍüš.Í^~ôƒ){ëÙ™ÍL¿?%ÅçLŠé‚;éêéqìEþCÏ ˜?Ÿ;ÙáÍÉ”KêÖìw¢Ûc/ˇMøi7Ãm6ÏÍfbx/zêàyºÊtµõØÑÓ¥ -Ú0ôT’#ú´ž?˜b¤Þqsaò||c³½„kۅΩnç·sMâÝT]Îü¬æÉm™B)fâ,¸±¸Ù8æ8—×ýQè˾úmß¿bæ`hߢÎùžÓX׸þœ³üï³¾=aÊ\{I×:}¿³X,TôB²T»³â¨víàQËÑèE-.ú¾1·NÃÝÿrèKøk¸›ænf¯¼Zjš³§i©6‘•`cò² (qpTêÂa´íNKtÎ_­º;l¾¡}?Y½î=—Ú}¸mc…wG¾ÀŠºÁÈgš¥ëbyÔik‹o¶Û^·­ykC„hqãÛâ¸Ó¶Å•˜¨:ª–Ó¢î-§T¿ý"\d˜À ø_cþ¢âYn§j6×F¦Hµ…/²L3+ÆdA¡õ†­‚ú…Ê|ê³äܬÏî]¯qò½v=¥Úm[¹Ë/ü"ÝÚºl´w1K.!ÎÒJ*ú"—öæÓÖz<ùŽÈñ*¤´{}Qaæw‘輦j­CÝB–JR¢XT2ÚîÐܶ¶ãUØ"Âý³™»09iÜói;&ëW»Ü¯=Ö}å«5ãÕ¦jÕWµ“ת´hT’©u©XŸÑ¥Â,šç -3®üæÏs£UÖ’¶ú‹ÔmuØ:h³Æî,Oúj:Í)¢Ûwî™&{Z–A;¹ÓÖ‰æ~¤áäQÛUH©cÈ2>Ÿ4f£Ö¨5ðM}±¬žlLGµöHý¯ƒ_ª¤¥´SéÎi±¼~¬í2Þ.]K>þ)–¼O}Qâ»Î±”yª§â~À°âžjáô×{߬cWë5rÆ|Vd´ÅÑ%dúÈœÐÝ»htÌ¢^heñ-V¿ó¸âì–HmìóåÍ3Ó,6“A™f?X™˜6W¥]G J¹Uð(Ä"RœçÕÂizç -SZÒòWçsÈ+“#‘W¹Y¾~µv¹«r†|È©G&ø…UéµÁ §2²)k/A«/í¯¦‚¼rF¶uàºñ¦:4w©4ƒò#SWù<\ávøx0?q¾±  ìûv!²ït‰LgÉfÍ6ÉÒÚPϤ­­—IûÍGÆ)»X¦›ÔÉ bãŒØŒ¥51h¶‚À: ûƒ-Ï­3îçÄ÷ÌñË$´œEÞ»²•ÿ+5?°è + KL‡>MqP,Ô^t +o•Éç+œk·W®ÝGD.¿ˆm3[$ˆm¹†ï¦cª«!vXݦØ!èXØDuôôzEèÔ2huŽvã+È"? áH&³ð2£¨Ð[¬Ÿ¡w³x†[\\•Q3ëŠ=mïKêŸÔüàÃR£®´íqTú [ä©R/U“{‡(íK8»’,}“^A ?N²æâ2¡NpiCL£Ðð"`¯A Ùðþ=tu˜ÿfG[•þw‚ØÕ¿“ 1t} ˆ“_™å aÕá[ö%®\G&Ž8rú [U¤vªËQ”ÍZgÆwƒnŒ•Ù7ñVôÝJ…þoêøÿJÞþ ù6ߨ þÉÚ’˜óá¢e³ßAÁ µA¬çˆuY˜Ž"ÆŸÜøÃøCüyPÇ8…š ¾?º‹?èu'ÓÃ1ŽWö%Ž»$޹^p¥â¸G–HW1h8î‹3`>Ÿ8 ÇR°'ãx˜çö<Žùú`])XsD@ÿ?¤õs4:Ä)ÃìA¬&÷ò¿9ØÓ5ˆ“ñˆ“Z‰ã»6ê¼Úq¬QC€6ª¥V/i°8Ž PéDZ\C|°®ÅñcÓ .çT°W|œü`ïT ?ïÔ¹§ï4H wú@‘‚rèoRK ãô¢‚@}:€B}äüøÓ#ñ§Ä=ãälƒ¨}´'óï¹3r„8põ»/ï<Ð}>·87b€„Ö9ÀüŒØ JÌâdâìÞn-¸¿/?yî†ä¹›^ѧÐ9ýûÅÏ¿q[Ç*ë{€ºoEDËWŒzÄñgfâOŸªÅŸ¶ÅÆü9Ž?­VkÀç2­`úìñø“…‹1+b5°/p±Ú~÷¦lïu¶Óç…ÖÑd_gcê»be¦·¶)ìoœq,¼Çú™8Ïô*{Ö—Êú½Ñ |ÔZlõ©k‰P;˜Z°‚«)î³~P0ª~šo“h'‡ùІ‚/û¿$ÈKú2ÜîÆ»nŸ ùÒâÐ6²ã=Òô}ïžp^¡z­WwáXåÃv…LÜ“å×ì·9>ÍP£ÜmçõUµår¡”)m)á=õ})ˆ*+/],k*kžôÌé^ç<ªC³=Tgùà{Zð}µû8!»˜xy`_®ëÅ 4L²«Ù‰6#O_ÝÍeÕë¬É¤Ä­Ò8/ÙƒIË6ƒ{coTªØmy?Fí釸jõ¬Ò"5õÆQeZÁ§ƒþœß¤Y€ˆÚ4$dwZf-°y¶¾¿¥;A$rŒ “åÓs'm>|ÙÙ}ñ¿xIPnD‘³>„fL§&ù¹ùˆß(›u üÝt Öªcêzαâí¨é -uÜ.›Ñú¤Úuéµp1RN.3PyÜ Ö4<^ùâx¹¦R{˜|ÑêUîãÔœ`cgÄqc._³ÇHd_ö_@ëâ»ð¾lÿ’ NÞßÚ|}*G¬¾ÛæãÉvÚ« Ý72aÚ7mâÞn§[­§7Ý™fD¼£²óîAÙž³·ùnõŒgj‹N§ ´-e¥<©JZ®×žX§'&4+ŠP“UÇœÎ÷ÁÂ8üçŒ-5Êñ¾Áråý”µ½/Û/›¿$”¿Üº<åžéqñƒùB;œl3tè¬*,kß˦Y½l½±¾0šÉâ’ -û¸©ðè};ϯüÓôL©OY‰z¤]ÄÄ8äK¢íÁäØ]EÌ}~Fq}ÏG¹ãÇæ'•Vøƒ O‚’ +Ïjcp5ê* ô‡•×Óû²ý²ù²þË;ß1W\$œÀ.®^‰ÑE÷½z W}¶ÊšÇ Kê$<(ht}ÐZ¬Ïú7?Æ;6;~tw:ÓÅ@&Ômò¾±øq"t¼î¬²clåÕ„Œ¾êŒòʲϗ¤¥<¬ŽLc ¸ç}?«ï~k´¯ô[ÂCé·ÆªÿeÛ×öÎæËúû.Ü¿D7 -ñÏ|]íÑ[w¾¹MDÑY…»¡•0Ö¨Ù†FÏ¡ì¢&H%0ƒ™\efÓÚ“³¥§RßMèóë,v]4Æ;J*dI´“G… -]äËùZcXÃËì€Ì‘bÙÿLZò¸sޏUO*r]\™r]¢ *àÁ@1.÷ÑG¿yDÖÓ?UXÃöwÏÂl½GpÛP[Þ ½Ow© —úb#ž²s±kTfêÊÊ*Iú•0&Péºo÷ Œ†“ÛHì²1/S$:œ7 -ùZFj}=wzó Î@i’ë.Þ‘®?륅,Ë»Õ ëÃè†õ"|Õ(ºÝ¾Òç/wóøyžÎ\+KMì‘ ñÆü~îjÌ:hÌÅUŸÎ“VQnFB{’ø68ú¢;qãaV[ ¹íÐçOÝêix¥2Ñ@}ÜÒ¾~öñžXEÎÙêÍîfïô»NÙl¯±KŸépsÁéÐÃD:4·Sx¿ÖLá|u±”°‡ýƒýYüạGÛ?¶µúeæpÍâÄ*®§#½UÜs‹a}T›*ƒ.uzÇÌ®½ëcËs¾֛Ž&§–ÁWÇ™õ *ýwî|í}0/îAé÷ rèkžï7±ÎæB‘eŠW}BWŽ££T²ÇNýê£Ôc¼ç©7±;ugëvꪽêÌ×þÅ9x7»ám¡­õ’3[SB2«meüƒiögÖKÍšÔ9œ1 hXwúÅ‘´ðh~¦Gã!ÉÞ”A»}¶{)äù=ø½ 9ì.=º™SÿÃæoÁ”B¦BWwL§ÓXFTËWµ¶™‹÷íÎzý`­ÏÀì·>žÙîL5·ÝNWmÃPì_KeåIzWs¡š:·7bª·-aáé$;½í“ª¸>{ø¨lï“¡Ú¾dº€×ûñ±ZJ=ÇKÜFãµ®oò¨ôŽ ö…¾,ʯÎ]& ê©äòíXÊ‘mÚ§ú­îAVš›ÒÝkö/³¤‰+&×Ä»¬ÑÄ ÃUGX»Ùï -²gýáû³øó”k£²m5lŠŠY’™¶:g„yX)–wU!p–Ä€²úq;gSn˜åòÝè´»¹Wk¯$ËhÃÓ›C“o…r\ ÒÆQƒ -õ³Y¡êÕT×ÜÓ®‘ØâQüUû«FBoQ]»ªY³ª­[¿ÙÜ“±¹¡n,ÂÜBCÞ=y._%^²E™JL£:è„åLwLg?e”»·g÷lá§Üôg8.ÙtJ‘sâIqRœ7ÂsÓlTjܶ>WŰvY¯Z³sÅ«‡Ö*ÖvЯ@™P+;ÁøTæ&³j™k÷feÄŸ¥M½±,mr¥ßü`.Û'-«<´Œ%ÚƒÊüLä78[Æû.Ú.=¸Æùõ8KßÃjs5Nc,¬ióèQ}EXZ5ÖãBµÓ÷š•O%Ó¯ØWVª°¶­W`ö±)÷6Äé+y—†×C¡¸'ëL1¯[³Â±ÓÞ& #[˜:R¡Ô–ùÓ¹¥åO^`æÕ¿ØûGv¥ër}©æd>ò³±èjï_iïÁ`VõØŸ»ž’Sù¼›kf'§ìóÃY]SêYª]2 ~q3Ö.€3Öâj˜ŒÅ±Ë ý*iz «¿0ÔñÜü3x~v«Dò¤/>ùQ-NØÞ¶‘Ìõœ–(˜}adéõŠ«1‘¹•|š>*—¥üºOôZÄáÃ·çæ ÎÄš¯žaê lŸÓ{Ø>L1!’LxÂ,¿YX~;dУÙÔQ‰Áh9?î¡R¡j¢Gò ‚é_Œ¿h.Kè Ínvé›Jÿ“øî÷²l‡­.ûõözÕÌ5.¥*TeˆÎ³”«Ïùú.2¶U÷p¯ùÚb…ê+@.äëŒÔ¶Ð¾ë)¬V•ô\íê.9ˆjªÓ ¢zå5`ÿ€(iQ„(]A”Am Ê:Ì?+u¼ÿ†r¬š”¶—òŸ´wßõì.[ƒW­ö&3/}ub'@ S,Þ·\K‰OÄz.°ËíÑòíºƒ^Äp ~™^ †ñ†¨Ï…:™C êÐTêÌ."Ôñ%ê\KW¨“Ü2Pçcw!:«Û]_ÄÍî»`_h‘Øäèr܈æãâ0péÔcgö‡¦WÚhÔ57[A“T˜¦çW–×î¸ÏXg¬ˆ1!¬>w{ˆJ”D™;D£ÍÛw+Eù ØÂÝ|ÝÊ z¢¶ z!:Ziÿ ]mbпÚê³ïô°áwf„ì?cUéX…¾¼þ2>`ei°‚+ P¡8ÒéÕ€¹üïBͺ€îþß…z|Æ}Þé(FßÝ#žõ/…úÓ¯g{fµH‚vgcí¬Éé2½ð/>ûKœÎ+kjÇ)}ÒãO0]ü+ÖOÑvþÉ‚›•(Nø)'µSù›OΩ8Ä! ˜Æ œ5bkÝ^ÇNïÞܨ¾6‡îõE”úÏçhÆ¢à.cQ±¨•jоÏÕîôö@=çúê|ŽW£‡WêóùÃé?Øß׈ÓKÝ Fœz-NÁM„ü²@Q½øã6ÃøÓ“_ñ§tÂã^©ÆÐ£Ø~£ï -÷"Òºð8f¶ƒeTʈ«Çt,o°Gµ wE¶Î÷F%|Þ4å]ßH1w56 …È -±|ƨ²îóäþxl¥þ±ä]¼Ã™÷½Ã,kmnÛÌ -P7¨Œj „CÚ7ŸŒ×Aƒ†]œZÎ%N™{üƳ -ü{™ÇyS-Ýo»Vó;ìõ}é ¯t½7¹¬4A¹Àï©q^›¶{ÆR×?ñÜ. ÷§äy<ÞaèXæ†åà^½‚f¯£ÅåΪ2[ß!!×G–ÇÛ´ÝÕ#–#Àš1À+BI H´Ö|æïbÎßœžâ"9ÝïGáz÷Å™‘ gq4kùS.S«ðÐ>\®Ùnpða ®iyÿ»‹=5âÍuÖ;FÛ‡¾óŽ"ej˜78ÜèmΪN7EWpÖ¯´r•ÌÛrëûó_Àõògñ‡†u'óƒe7˜E+ޏoâÞz³¦cÁ¼u°µuÞÛëpRÁ‡Féyß¿Òø³³·/bgÑ’5 ÒEz»ûÔÛ|—°Í•Mi07×ÓµYW…ûÉUÂrâ6ô ²zwIÁþÈKÓ†Þ¶zQÍ´°eÖ°0 Ñ‘À¬eƒ¸d-“À|èD¹·ûkp\nt®Ï_YáÃc¾ÎËf2ß­TÔññ l÷ÏÜc#Ùèg}K¸{Çó—dò5géV©UŒµz+ÓÆvšU»+Ú–;«?+ÞMï®f¶Çõ0=Ìêª^-k9;Œ/ú²6e–¿HàÝÆü'F«¢]k5:ŸK÷XϽZ‡ÅyÒØÓÛFÇóùÑ&<¶´uélœÖ9‚þ~œ¶+m}lnØÅ­A—Ì›þ†®›£D-õ"¼œèr­./þÍ]Î…êI»—Ö¬°•jl§ pš™ ßé_–ŠãÛê_~°?‹?1&ÈwîÔb8;çÞýQðŠj窭ª¿%ö¥í—êkwƒ[È+¶º¶lô¹ò̽ ÃröÐO–ôÑ+[*-µ¨=fj]kÞ-úS¢3X$ÓP^0·ÔTÜfm§p¯Õk¾¥»µÙŽ»g¹sCŸfyí‹:ÔàÅå/ B¥«g"´+šd¦‡%aõvýì¶½= -GP[nªóBÖÕŒ°¸²Ÿ—¶-‘9&ð…Q:â«åúZ$>ÏZkû|ªU˜.VÈ•XÀÔµ¤`앜—v¶KPa–oÁæ—œ+Ÿbç"Om.+Ý -ä蓲¹œD•™úEù2ÿE‚µõCC“åéØZKûÕšã¼ÂtØZ/âEÙ‰ÑXÁ*ceaªfH–È-ï%YÒ–wy©š'yýƒ-V71PÖ!w{»ækž…Ь€W3S‰iTä³ËRÒÍìô¥F:‘'˦mŠo#9ˆ&m ãÏaÞ¯¤¬:î–åËüËìËô/ß§ /Þ»Ù C=.Drìû ÑÝÌ»¥¦?úe{Óaf;!ú…Þ–äëE©qÎ4§(µæ£r²‰¹ëq*c»›<÷PéέQ‰4ÖùI{µ®ƒÞ«ù<²ãÕõ(ŒÓ„¾Þóÿ]÷½¨¦- ü öC b/éM¤IWQ{蘒¾ÿ‡&99ß¹÷þóƒd‡aÀaÍr9ªyêùTäðÌ(¥Žc§øî‡ïž¥Èã±ù—û>1ýsk¦÷ÒÛôª,jµ7y/<ŽHc´yÖÒÑrpZ´Ÿýû£Uì±]ëæÚ󶳪ÌM»s -<ËÜöÇ&*·–FoÜ:êƒ9Ã!üÒÞw¤ eÏÕºÚn2”²4dIÞ8]G®nwioH7©ñ”H©ñª™?`º¡ìÖS2éö¿¸Ù¶\»ÍîZ¨,•Ù²ã@9âAáo ¡è™ë?°Ç­t3 +AuØ©d8Î:,9Ít[M×èËMÿÔ‡BeÞ ¨×F›4/g-gjÁÙg•²¾,ËÅ©)™›DhŸÁªÙFe-{›ÛYÄõ¸)âÆJOyXL±·Ïô¿DÑ„Jrh7g¹<)Ò4 -û\8‰vÄHßïë^?‡{À9ˆÜbÞì°«YÖ1=ßfïÕÖc:6;/ìâi“s¨óÛh©ÞA©”H®ƒâK²d³ØvþÅh8`ZpÓiQìPx½ƒ0#XXUM™˜ á™>Æ>è¹5‡mºÖb‹²Òd¸W¹@CbŸµÞ«#nQ¤Ó™…]ï×óbóEÃì¦>²­_õ4 <Û®ZÜÉ#¥²d¦r}‹m$DÎm÷]Åþ£kÅ4Z^N -œÙQøÉyãñ †m¹ùƒ¬só¸"ó a¥¯¬«:ßÒÝ/¿\¸Ëc´ñ-Ìø¾‘ðÍÏûš="½{¦SëŠtímׯñÉÄàõMOª÷lçMc°6Ë’Œº,ð²¢í0S6– /½ÇìI6hwçï¥è…§}+¾Ÿ"áuÞ¼…Ìí\æEÀ8¹RÙµ¡¸¬Öx¯ÙÊù]a+—›ÄV6uÕJ„ΪoXc× ý/N‹Ùd¸Îqo=-?o$üÊÐoê3ËZÅÙT¢—‰YáQgöüI›©òS]z³Š²ñ¯„¼Ÿ$-é´xëíëøêŠ÷ùiÔJÂÙTxk!ôμx<9ù4,°Z4G~@f_LÃÁmÚŽf ñÆy1»mÏ^ú‡}|d´ê_ŽUW,ÕÆò× |k -C´¦QÝY/*ÛP~þê¼÷ä^+”7ëPé,’»|@©¼tn9H;’}V|t|¹õ´|«hN_ȵÛ>ß^u–œ2ölǧnŒ9ieéSl5¨ë{ÎR8þÔÉÁH™ËäÀ•yr ®;ä}uIcòìT -{ÞþÅGŽÝù±ÜëŒÃûAökz˼Ò-KAÉìEúSkŸù0rÖR³æ\Ú={‹´ö¨´‚J“ÕâYJã‹å¤³× -‡Ø:Y3–S^Ón¿v¡úvùE>®­*ñ<è$ÁÁ®íŸô?‹5 ‡OÄiŸ#•Œ0Eôä¿lKsfØî„ùsFJóSo÷&Þ°-^/éϸõR¶·üQŒ­Â²Åg¢­ Rå¿Xéy¾T™ÃœfœiÖp¢6sŒž:}‰]C>EâÑœYÏ‘†Z1>µŠy¼HØˆÉØJn®‡è¨©1Ù{S+çɦØjsvSʆÒ\²ô—¥:+Ùfuüôå§ ˆ>(u‹Ê7o]ꀽâSFòÙ³0ˤƒf§ÌÍ~@¶ëGÆ‘WOúºyé^¿Õ¤î±ÅQÓ—I<±ˆñ<èÙ×$ÄÛüv)íóµ¹ Á,ºŸ -‚rZ£âhï â´$‚ AF¼Œ 6™Î÷{ÁHù¾‘Îs¨9‚‰ÞçáuÛiÔ;¸þÎKʾp|ŠIosæË횎äÌ” Ú‹%™ÎyND ƒ_˜í–ñùìØÄ‹0Èa«K3.° -Æ¥Ã×@ ‡g†\2Ái’ç'Üà*gú,D·svãU8¥Ñó;‹6ž8Ùn5$%Wÿ2ñížío_5Û‹£V§+ß»íЮ8®-ícIêM×0‡.Ï·d˜=ΰµQòÑã6¿D‚ãB=¯´ˆ°¶,?Œš¼›­jò,k%X–jÅÙ®®»¥Vu­eÿÅ7??ìiÔ8c@Í1¾­O -¦@QYÌ”µ7WŸu"jd[;\^-(r½? Äòø°>{âéì"0÷õ‚zŠk«j¬)w¥Q+ÆduKÝŪ^$­jm= -*‡NfWÒ«|e2úNCô‘8-E^¯ZŠÚX»Ô;g[¥Þ2Iñ¯é|ÿ»ùâ7ŠJ¿ÿž¸–»Í5sh_[ZÅ[í‘fTymÄfèA“½…šzDÄXB\Ñkªyå £zÅxHÅ­.Š#é|.2¥ò³ðºê¥°öVho•ùBì)ï0?=uÏyÑmób˜Àe6‚K0WÈçÁ“ã{;/‚×ÿò-Lï®JMÓê{¤Ü)®Ó¨×›?  pÕ*ÓÏ¡ !¡¹z¾g·È/j•3”»£¾Wx[7Ÿ¿­{¹mþæjJ<Ï*õ}ÖÜI÷”˜…0œ9S.—q‹9+ƒ®Í uJ7 _ÛÔ@?2€'ý5€Ÿ0€_Ò§5¾n·ÞKù½y}èÚnvtð1å:¡[…öaú½^™–í7ÚŸ+”ÅmÕxš³Ò}q -ÂðóŸ š¹ô²‡S:ÇéÇò FF‹Ý - Nð ó!P± -í ªäð4È…wÈ¥ØÈ3¡䥶ÈM;ý1²”Ýè/é¹|6Ž5-#[®ª -ZÈñ-)ãÌI„,ò(Ö âF»ž*§çlU|:¹i^’ô ·Ãš~¦ë‡9™¦D— 2Í@Uô@qÛ@iõÌ'Ÿku­Ñ]ZêÍÊc€V+Qʱ -ÐÝ•ÐãÙ '~ g ó%@ÏÃ_ŒpÉÿN{ç®4Í6%–UEî—ðÖWõKTiÞûbF¡Vàz-²ö]Tù”KÌê¬/%oih²h 8´Å&é†Y€Ž*ÀÔ;Ày`vëÀf­ÀÖèOéj­õÉŽþ«ÐöH,)³ë_T³;¿‰oör›’dþ5†Q|¡ê«ý,{4-ˆ¾«1¾Ëàfù«]¿š4xÒ£ÐèüÍ- ÞŽ~¥q™ñ8X ö)¹5ëªm¿«$húŸem¿…¶­rÚZ´6³4ùÓÖ °´ÚùË' ÷h ~gÍ—$ÝIÎ0Rv^’ðÍEÊÁäÿ— ݧ'$æÇ)̧,Ø>¤,ÒSãgâJ¹„Y‘•ø­ÚÍx\¹´â¬)y´]>ÄKùòÈwËÑx_bÀõÿ$yõÜ´¡í¤­´S§JÚÆ<ÿ§Sc?m¦´M9Çßùš¦Øªgǃ©wÌ>×á¡Í]–{ÅÏ»ÍYÎn™¹9SRgƒžûîºÏ©éuYÉùM†³ÿ’¼GÛnò¶]#mm¥¼ÞpÚÚ+g¤íÖ»i“û~ÜÒ¸é½Ä«ÈbÖ‡K4ô¢óï¾NA»[M§rHú÷+¿‡ï¶“ÞÕAGØZò@Ùœ¯3sãöîÞ:Šó“5Á»U\:ËÔÈEë*s97ÒgkIîÌ´"£ý‹ÊQ¿$ïøœöçÑR?ë],h1Ýwµ´ñ•¼™¡ó(eÖÖõbÝÍó¨àtÓ<5Ú¯‰Úl{Ôž›ÍeX¸¬oëZ²&«Íìj4h•—¯§ --yÕÅ—ÙÊœ[ˆÞYž/Ò[i^âÊéŒo¶©˜‹YÕ8EÓFàÀ㫱ÆX’t»TC -ð• ™8òx­‡ýˆ@M÷æZÎ ©¹ü<@/(ù¹E™6¨2Tk€4,ï£4NlÍ­øžÀäa<þYTpB¹Ð›+u\TžÐ~»…Ë»ýEËmpµTZNÐ<6_Ù¥Ö¬^CÌ)LóƒISÓ¦cÂÔ6áðlŸ‚>~À€›.3þ4¿)ø­å»1ZTòø¨h4¸áº!kÊ3ê ô(žêÞäíÁ+ï7w–ÜóÈ–Ô#¢Ý¢ðž™LÍ~fš”{%É®øKݹÅpuõ†°Ì*£ž‹¢€Ó£FÕÆ·•@…£iz5xg`ûbߎñr:\¿gëaUØÆ|4dðìM¡Ð¿Ö F¿;ÈÞ½h¶z¤63Àîè‘»Ìp¹üX„YíÒvòÖT´—°×ú ØòZæþòÌpB÷vœsÓLGZÛ^tÆV9s]™oÁkfÖØ'Ѥ»Ä¡N -þÌ^¡?àH"~ر „®ç Ë~¿f½G¶¼éQTùÔõƒÚ½ dˆŒ;žª%7›X°Óf'Œ½œDª]ÎCžµY.wæþÖ«š•åŒÓ3/ŽŸð¸ð³aÿò̬üÁõ®U:‡ZxÖÀ[EçúT.O¢‡üŸ†rÆÛÂu¤¬¯amÞ¬yçn‹ì÷ÛºÔª–Ñ «çNiäæ¶êÌ‘êúæ´W²~²Ë÷aéµ0g¤EÚžõ 7ÜZEÔ#Krô>ü\v½C¡Cƒ -­=¯²Âþ£Œ@yT‘ȹ[gKp8³XKäœvÙj)|…ÈÛ—&…Ópêû é÷~z´D»ýƹ +N±[¶zˆû–~¹øæ‹&lÞ¶†{Nƒû[5óÔ ³QìÐ.kÁNfµLcªÿ€êäÜ« V”EöL(Ò<Ç}`?0hE<©/÷Foëœ -“®ºi4Öh^àq‚'%‘ìŸÃúkrðÜä¶èê·s7{¦Q<(¦Ñ¸S*¤µ;h-óxÑ4ÁlÇÀ ÑÓ=W;ñN^j¯“xÐø²|S§z/«‚ÇeM‘KwJ^ëˆ*WŽ£‘´sÜD28 “™€ýÀHõ[Ÿú@þ€ßÍ—š[¹Ýf”ø^QÓ«¸€Ã±u-ªÛ^âaißKç3wº±÷NIÅv5|—ÍSûW-Ïèw3/u7«w˜íÛÕÆ¼¯¦¡èùÎ;}r-²YÛä_Ò>‡–¤†¤`m{½”Äk­Ú»ÇL$bö1‡f>Ð(±»äÈė뺻æìÔ^ljCj2p<8h‡Ër:Ž9#6îùj}çÎfjo¤ÆÆ‚²Xd¢™3pnÖÐ)qCvXeÓÒ„ÖXQÛ²o)Ëñ 'oæŽ/W_Î\2yÿÐv¨Á]¼‡¼ˆÝ¸5 XAH‚™›ÎÅ|Þ8lÎh¤Ä´à·@ê)øø ÿË…½ÓöfäĹIëÔ8ºˆ_ž9%—ÝqÛÚ›Y26Ï™ýÒè®'šf_ö„§ç¢ ‹ªíX`åͶå-NiÒÆ -©aÛ¹SS±wF7­GSˆZ”aeÖ êüx±¼`´-nö,m¸Ù¥QM™R|Þ}!ø\q}ù¿›3X¨ÛkÞk·f(OáD·ÑáaÅ{y¾;+y½5ÏÍA ?aÒ ZÊV›Œ–±:ß< Êꘃäíµ@J‡ý““`8§´›Í§)ög¿õ8çÆi=·Ï•Àåsçtž\{sóWá -‹€bÕrNgËÛÍ’-ïîe¶¼·Ó?ËvÉ8«Ü Œ]aÞ/Žjt³Vy¦ø¾Ió£ŒÜ]¹Ðm¹Áí“OÆíþ8±~'“;Õù»°RÖ5ê*ïh+#µIM‚÷[¬Ý\ÍÑ«lÛ­Q>Є—2w…L«?à[‹ÉŒ›¿·v;Çl…)–},ô1ÈhkáÌ~@ÚZΊ´ä‚>œìš_öÇ‘g,6Ûœ0~>‹”¯•øf?¶· [ôÔÈìÉu'#<‡j1Ô{òn0™IÇUtl_îågkKéÌB$U jv)„K‰²e]áó gp²,õÙõX3»¹¾aê¯áF²;êÒ J5_®DzB- ½–’ñÆÉ›WJ1²Mª¹«¦Lóè—ðûщ94Üò㌠-Ò#£Ç`½p€¶¡ÀW=¾gWj©¸Ê6ÑkwoÍP4ÔMËgƒ›ðž\²‚ÏTù™RirK½Hs¥c]d;¥ŠÆ˜µ¢MŸ,8‰ÓÈšXPØ^ºƒ‚—!5† fωøÛÊùø¸TÎàoæŠãodß$ûž"ŸÐ¿|ߘ™F—¹,&/fp•òÍnÞ’ræ/~¹Yþ€²™íÅ~t[@iê 9ü8åÛ‹ú‘[!ê“+÷F¶óX4˜}$(¼p´s¿HTïyÔÉ÷IjN 6¨ðñ­õÄs䢎µƒ -‡¸ƒ×\e“÷ˆjc+äÆM,ÿ XŸ¡X»æ#_–ÊÉQ&ô}ØòµøÆzTs:¾ž5(x}Q—“Á²ô‘ \m‡SéªÅ֫؈±öŠÛ·ˆv×¥t¾OÝ0¦LõïBÆT—"i׈àjø$ d&õ_›¦ê×ïè°K¨>ý,¿›ºÈá¹Oú#‡ÐD‘CWÑZúã_æ0~¾qú´Ä$¿„ž°ìÀ–“\2IÕ»(µ…¹éJËçÔ¹èÒciPdyÖ#bï<’"Žv'²è,‹O£{Ï‹¥&¶Ì“ Vb™vS›Èº½ÁˆœJÓ9‚è™+Ü=Ê“/äE–y»ÙòmòÚmò(†<‡þ2‰×OÙÿ¬H;è÷bÑU[d<¢iF+BÆUjv¨•ÐJšÁÈ/|L”±…ç¯@§¹9ÔúÍLÑýËß¡fx½¡PÍ"çšRAœƒÂ½gàÞ;¯@s jnŒþkqhB¨OrS´ž“‰N=§8—ÚìÕ~ÀÚlŠÂµ™Wƒj3£ÐøË8»j#8/¥¡ùkÔ²íâÒ¹ØËÊG¯qi½‹å5§Eõ1uß“>M= ˜U£ñB¥n#ˆ‚°ÁMži¤ÔȈèµ>m¸@½uZ•ê  µDz59ãkµâôíUµ¿®VV—gŨòJý‘ËG:”,P-Ëe¸|rpù°‰ñ·îY÷â~]I#Xy&·Ì{õ «›–iØ|Ä+F´ù&¾‰I€šõžàÂdf¨×§›—\­vÃN¥1›:åSû6*;psYF.ê±tíNâROÈäKxކŠ÷ù)ìB))Ä -IT:üþ²T`EC(}–¯õBy‘³žC¿ ƒÆ—a—\™½Yñ¦8¦Š´tAíÃ2–r­Üá~gaåq ùl¼Àªd#üá»Äöë3ßµ*‡âN-áç†XŽ{)–œoK½mJút JFñI Ì­’œS˜ÏU¸ÀÎî²è"«ÏNÏl]rÐ̱$ÚÙº²}fõ ŸŽ–Ùíyˆf·+É•Ÿý_ôÇuZw;ƒP1‡L:[Ô}C*[àØ>¡˜ãáŒmVñÁ'ùi}n~¾ËñHÛÅôe­å§bVK¥”mHƒp­¶èÞý>€ Nc›cÛ”ÑÀŽÕBʰ¤©¤œ}{ú€½”:€½IÀþÀ3}Ànú?t•1¨[}ùUKF}¤õBøÍä^¦‡…(ƒWr“!”î¦.ÝÍq¯È˜7;/Þ #×aóZµ2€¯Ó†âë“ à×ÒÀßò,e±Ž‚ ‹áÒd:¿:鹡ùĶ -§¼t€¸;€x´ €ˆKfJÃþ‹íºžÖYX¦ Ýu‰”• чPÄÕ¨ö„ãååX/ñ×e¹W…ñîÿ€ ²[¸Y(BÒÿL:¥ˆ ë| oz<HÞäκTæñ(L­T j<6j+ÎjÆu‹›u?Ø[îýݲo£VÒâwÞ֌Ҟ˜çŽ¿S·ßÚ`¡s˜žŸ6d›¶Ü©ÿ^Ûö×òÓî'Ÿüý&2¼˜ùó%d{`Éé`)£ö{­à_ $¸fò4c+y<õOZ±!¦ðŸC7IøÊ4exN¡˜ù¦ìR¢Ïê¦ÿ{&”\~+XÿgýªVý³í,vƒÿ¹<û›Oq1¿RïqggîòÉ.þ€7-²ëÑžðšQc4¢®ö;ä¯Èx-]z¹]ç‚ÍoöyPŒû§D{ù'æPœÇp g»îó >êè^~I­})ÜÚ»Nvhíª3ËÜîÛ’ñ—4Nþl’W¯¨&/l!¤ÝÊ“é-€¥PLÚä ñ¹aE-a¦£þ]>VGÑÏϯö+»»ô²ÙËyPÈÆ'¿Ç·Q(ùS¡r˜¡eèï×°½CéuÙUÇ·5s¹öæ$Õô ²¦zë^µ7[Ý­ÒcE^µeÒ}ÓK&}.Þ³+»·7̯½H^׊œ¼ºS&íÔÆgÉc‘Hž÷.—v¯'¦W¨0ò­Sâ›Xí“_R#ÿÐû‡üZwˆ˜mMŽÞl‘8®¯§Öm£Òsuº¹Õ -ËK±€–@i‡/„Þ‹_ä¢3o,o¶Bk‡YyrH¯Ë´–àäÄR j’ÆœÔøåÈÿ¼‘Š˜¼. *y­C2y…O >ÞnˆKn¦O£N+vyô ®-—ïKkEsçt¦ -µ_Ô#¼ú—x.ÞM¸z ‘ßïOt56»…y‡1’I¯ÂîÃQ.ØMF ò¸V¢×Ã;žB¿;!¡Þ½nPÝ1„.ÓëªnøðL7KzŽN}'Ï_¶<{í­M±xû­ªFfLcoÔŒ´ ûÒÔõ«PØêØ|vŨö r½­ü&œ—¿Ü·º|æûynó-x¡f˜Â$"I0~Ÿ·£¥º †Ú’Igâž#ó>vÕ.ÝdÞ}»ï×¾âæ²WÔ)®´­¨IËÚìÞªUƒ²¦iº¥¾qº5B%±…~MŒ“޳á³3èï‹Z’”šËj’Tè©*˜E@ÍAz:KRfçVåCù7+ºôåfÃEå¨Ð'n5Ò‰„ågóapÛ?Fªän¯ùýëcÓí>³ãÐ?O[§}Ï?lµ@å-½Ù®›ÙÆŒ³ßa>5½€ÓÖû„©u#ÇîPïÁ@ „ÙT†›­*¼’›2 ¬’^2H^I÷–\ÞØCi«ë±¤“`UªÅqYª/Å(|‰0-£îΔŽÉË"7&·Þ)€ËÞ}`ÝáU/«£.{Ç-g®âC»’gVí]L¸µ|M;*éý€;ÃÇ“Ð^ Àjܸ ©Ó\YWAºì*Räu–]ȪsN£q=½TícæYkÃm–/…wWt7™»Øì…±ÙñJŠ -bSHÃòŸ\+›Ç çFeZæ³ü¨J QßSŽ‹n¦»8’ó4-}±íý€&<ŽÆúÍÎítâ-Ħf[UuÚí£*x(E¦&‚¼æŠ\¹ÍMÉ ·^ûèœ'â¥ÿ܉Í[k•Z$𤄑èØSè^¦H–R˜Ò(Ð/¬ð!ÿ—s®lËkΞà3Û á'¸ö˜þtáÈæ``õ‰atÉÖIÚumµÖøÁ3RÅuP–ÕfIÞ@͆\u ,í1cפۧ-¶‡ëˆÝmËmÝëVÐ"íÉFmÎ7þ]¯çyÞvqntnzÃNœè] )I›Þó)»7ø—ãŠå8vñ)ækõ0hóNÁëñÛ‹;cÄ™µçÏ莹Ngô|}ÖB+öUñIÏ¥bå—²{Jp©´Ó¯‹)âå ß´2²,*6ÿÞT¼#ÜL².? —_s¬’« li¾R™¼ß1e¦ÌòlÉ_‚ÿûr¨½óíÅzÎaª,7ü2…ƒ}ªÝ?;ŠÐ˜šŽXëw|è¥jÙÝHV6t~@¹Ó¿…Ò!ßܵÏe:j£};#öb¯Øz0a£EõBLðSFèu›ºë×ìzìò•™²e >2A:+Ú?/m†–LCBoCC­vŽ6û½üð¤MÝÌÑûíàÛ4æ{˜ÆÇl_„†ðÈöµu²kVvª?¨§å*;U)³˜(™1c¶Ý’6£Þb!`æÜÊõDxnHPàj|•ŸX"üÉZ_4’“0C`UHV˜­Ûu˜Ú. h«勉K<)ô6ª‘Ñ€É>-¬É>RNç^%4GF2›ÂàÙ™/&#ü*¢s 4Áy&}ŠÍúÑ<»ûI'V=¥ìk²=’–ØïÞµÖpžu…ÌO.íùiv}ãÁöà¤UR`×Õl­˜ù&£Ÿ« }L§E4l垌Ùßz+"®ßî? 1<‰eü…Þ8ü…•ø«z}ã¯\.÷!KÐx!ó—•èì73xPD¸—,À© ǽAˆ½±ÂVW‚‰†Ôб/„ÙæÅŒc|Öˆ0öC®ø,-ÙõJ>³ÕŠ3†áçèãi\¢t QÝþž oñ] Iä­£~£¿bmóÈ"¦ýj‰ƒÉvŒçA‚«Ck=Ï™/? Ö:o/‹Mçð«Œ~´ëT‘÷Àß–óveCEË®è1y™Ÿ8ÇÉé¼­:Ƙ@»KŸZƒ1žÔ†º:ù+…Ø‹¼O”Ifú|!|¹×Äßム¹¥ŒÍ¤ÄÅòlÚTrÆ ÝH×ZÝÈZݺ#´:UhÕ›dѪ1Pm¤x½÷—Ù¹è2ak²&†u…ÝÅx· ¿ý5±…ÒñYt[Ì8¯r¥i§O›!Mõæ“69¸¬tâ)äD£f{ð <»áÂ%½~¹^¹„µP£¹¢)¢YÕ³¾ÚwÖÌv³£w:GçdŠlºE,zƒ¢² -@ ¼¡kD½`m¿¿Lw‰õ×Çå‘;§aïÉ Ñ™}c«l lcÑÍågƒ«Ê@ ¡Îà-1¦0É-¶Àæ:C›MuÚ6+F´@wPù€êÏö­Ã,bg&%ž>P¸[(0ÐM¥ˆØ ¼Æ¨‘¬ôÙxÖC, ×¹ûͪsóÇ¥ÎÙè»ÎɵWc -Ï/?àw3άnÔxO™^®\«™×¨k³å{+CØ!l±£©Ë•W§Õ?E|Zm2Í*"w9ëytÇäÅÅ!D.¤E#‘‡‡†DU²éQÆQ©ó±‹Õ³£s«Ö §ºlõfU%_½WKËM½²q§zeÃoÏím½*šyxVÖûÇëKPšÂ ê·¥£|ÖÍ¿÷ .°uÊ(Êór’ÍgËLœF;=ÎÅAa æ©y6'wY*·¸®—`ð2 ˆ†YP,öø’Æ–±^”»%¨Á™$eÕÔ*´KD?ç¸ÚšQƒ½8Ä Çž‹\Ò ¶ñr@©º^“\q|²ŒÊÍ|.{ÀrÊjËd;Ó!—­‰%s(äìŒy|àÜt>ù1çvºèͤ,aŠ€ŽŠ~Ê9PÒÐàÔÐîå =2 $  B5ó¥'† É2Y¾3óšµ_µÔ…°s¦£¸µ"À@ÿ€¨Ýëy³ÛÚµ2˜ÑʨIŠ…€ðYp>F©\¥€›¨Ðô-h.·2Ð\, yîŒRöK ù"¯V܃Vâ›)±`å °Šr°*•Î+¬†ÙV¾,ß„Sä/nÇôd“zûœZGëP:Œ§Ûtîd¶'í›i, µ -tK·êN/d¼¼ß~+s|åÛméÏQnÀ³”š¢^õφ.ÈïÝî¾5Š~+’)AÀïì rÈ Àüç›Wn.@¨7@)À_Žü‹ÈgÅt´ü®MÀ˜¯³"4ý7V‡Õ ‚euç–ý)ÔÝü¬b¹òV¨¨´|­HÏ4€ 7€®@ØH vñn$qHcþúÔôî¹@eŸ@Õ@ü ÄF Ú PVÎOy'åðRÊkPnsú¥#YNÔiœßfª5êùʘQ{&P8ñöß"å¢ß³†?`^ºY e¬ß:Vm€Ô»@nןòâ£ÿ»îö›Áu[K€ZÍu~ÞV³-Eu€^ôx€É3]€À-ÀPÇÀH+`ìeðYWÁ € FÀ„“!ÀŒÑí‹ú” <»=Ê9l¨YèT¢*Dí×EöM@µÛeºìÔûùäoÖ¶0\Jøä“ÿ“¼ýUrë§Gž - ˜ÐŸB[ÃX¬2M9<€%ó®Œ0´¿$ÉŠÿÉ(¦›$á²\ -&¥t­$á3aŠuøó!þo&ô»2Âÿ– ý~’ÿ?õ«¿—µMñ ÏQ~Úλ‰_¢Ö ³™=âqÍOþpâ»|‹.ÿúÿ³ ¶’D9’ ûÏÞ›ý•²K¸Kzx¾µIÙÞ¿«74ó¿×F¢ÏÒ»Pzš­Žþ§,ø>J˜Å}¿•ì"æ·ùmœÕ§Ç¬Î?â±—½/‘eõ.G úׂZB,þ³ÖÀ™±ûò¯âÕ§i§ }æÉ´yøŸÖþêÔo¼>û“ÞI¿•ãq‘†â¬ÖÂâ^eïKHï%WW~ÀÛ¦Ù7nUoèF{r>ˆþ*¼žØhqE&ÏÝ¥—¯Ý.ØÒÍŸ•sJŒžyòIp|;ÿˉ9]ƒÿwEÂSéÒË^ëç{ûŽžÉÕ8ù•;sböyñ8†Jê1ëV̓xÃú{9!Ó'ò®ˆë­ .’ÍIá‘ ²¶ÖW}-ÿ‹^=”þ’¼²Þ'ëYm&‰Ï¦×”eK)NãÛ©|‡¢oÕnY¼ž¨šq¹r¸÷“Fµyt|b¶åÅ‘?–wñš;íKƒóuW ¶­™Ù¾6'ñ˜Û ËKiÝ+ßê«»þj®ÈH/}n/Þ}ÉZðñj<Ÿ±džŸ*³¥XhÎð‰~ùÿîI^U™Hžûl#yŠËJòLgtI²7³â¹|¹2#ð”HFíÈïel¿l2ìvÏæÅ“{kkl}µVƒê½·ô¡ëhñv¯ã»/~ÀEvßÍÅgé<[rÕûLɰÀt#*ÅiuÙƒ&fyCOFµ3FN»Ex ¤8Ä+‹bp¿M‹Á`þ—ä¥iد¤òó^‚nÛn?}î_щu¹\GÍë1kä÷»ê°w^_Ív²¢2R~ñ>©õù¬/a³åBgged(M;½ 39äVÎ’¶ÞøŒã1ÚË-ÂH !>oß‚!Üügw^ò¡†ÿ€>07åQ !gÃù&N†E) ÔáTèkî?üÎ'ób3:nr¥3Óz¼2´>n­qg²qžÏñŠòÝÙ"7R÷3wãÉamç~ÀñùÖÂè9m†„{cƒáíÕö_TFó¹UÅö3*áZ»tºÖr(%“ã`Í­罡Â^ý´úÇ`6îuÈ­{{¶3]o%dº¤Š§!C¥šÆün’É¿¾$ïâ…¹kÑý³þþª¼3/p¼zâÉn!&ÃѼø8º³ -o÷ÇU›„d¾»÷_QøMÓÓå™wmXÞè@;–(oÏ`-Ïx‹jÿÔ1¬°ïÔB¯wµa¯Ç€›î} _ºTM}»ÉaWuY¤N»xN®î_ìr¬åuö¶”Þàõ‹2n>¿üN{¿è¡ÂÒ¥ÕËÙÅóŠÙÞ|V¢xgá`•׃¤µ±üìxì z¿Ô°÷Ùƒ¤ê³vñ|¯ŸUêÝGÛlv©ó€vŸî²år™“êLÄ»å´ -Õ=W±©-5½ƒµòÖ«üÌæM›? Y¯V †[Ý ©—Þä¡g'ºñ¸ož6sÎÐ,ºñÂi~Q²åÇø^VAfåy£"3è Ëó‚â´²ÓoаgÚ[÷Õp¢ôºdÒ°ÇimE{QêvÑÏa–h«2AEÓ)Í8Ê‚mØÈØ×/½ÓJoÆÙsÇcÈw‡x/Ú¨E¶Õ× ^¨¡³MÔL4Œ³u_~Àïægdö°ÞÐUvEæ¦ç\㸾VýjÏCàšÚ»*ß•¡áò`à9àû0·KuàhU™òÃ46\:Ù­Ie±Ô†Þ½è˜Žu‡Lg]¥3>-¡Vmôzꛇ&*?kï”4‹•|·P‘—ؘ—KÃîDÚ0ðMÒÞðãÃ]ªŽ‹7©ÚËD_®Äa·W]o,”]›³hTîgý(·ïþ€Ýd©.?)rNžkƒ×Ó,áðÖF¿îWQç~^=;w5úªjl»‚¨ãUPs˜SÚ-+…†eËŠ2J›íy%ÕåkÛtô|âjit!:³| ¢íé%åro]ŸÓè®_Îá$Ë®Ÿ%¸>;,Þ`˜Éw®8¿˜tŸ¡ã:ór_±ËÅ5kš¦'(çutÜ8:T™kA¥yøÔô’Ü]ÍùÒ[™³Ý‚R˜Œj² -†¨´•§¤TûT!Yµ›Ö†Žp_tuÞŠzós Á¬ð¸@½@gÄ3ÿœ7þéÉ×ÿð—ð˜^ ÷¸6u¯'З"ôÒ¿¡ÞÄm™”cmGMÉ„f£÷+ ¶C%ˆ¬[ÆQ[­p¬Hòb-¯®ñE®™XÒG¥¬Tg°RÛ“ ñ’ã1±)ÉìØê¯mYˆ« G Í8äƒu}ÇsÚðÅMu„k .×ò³D«×ß”óg®¥4._µ¬B-‹…]mBæfàp7ŒN=:&C{ÍålîÒ¨Í*¥‹%¡¶z×¶R”^ÓÞÈñs©~m^ÜÄKw÷›÷Øò¨kEˆƒF|•â_3²ÍsOÑä¦ÜhÄÓÛ†• DÂWdzÖöÈh5—’DŒ–»^>œõ±?1kü~Þ¡n˜ïšamÌHp`w‡‡næ,£u°tZˆZï e³¨¬N2x_kÛæ¾'^Îﱈ¥±DÚcÞŒ9·HÓN„ÑyåßM¿Äó†_禧ÆåÑÏÊ^¶Ã¬Òc4Ç[Ò»(¾Ó R®PÇg«C_Â&ev¥Žÿü…nǧ/*Òqlf“Z5ÌjíÜ·šÚYfO¾‰ÈC³Ã¨ž¨,z.í.9¤mÏ -´Ø=CíÖëØ-r7ó¿qZðï {à…L)â³Sìŵó,È.¡Â–¶”é€&õÒ{y&ÓMÒ¥œ:9§ãâFöP¤Lb7P&±}n™Ò¹Ø¬}"{YóLvïÊñË:S²šÓîR®ùKÊö’ÞÙ‚?ÒïÉв†Ø’öE(=—62Ý­:Ä_ßòü;)è¼Àâ}n¶íL¸¶>Þ°Ëxf•Ñ;a¶@.ÃÔZåmŽj êôÆ -‘ìÍgq%‚˜þÌ]ó¸\·pyHcÜÐ3#žœñž¬ Ã—åo5'dDÖF5ô tÇ“×Ö‚*ʰã CQت ^§Ú¢X €: s³w‹æ -¡Ì*³Ãlå‚Ïè%*= -S»:ÚÂuŽ_”K@2}ÐÖHü`Ćxü*Îe—6‘ªG¬UœXncóÍyËšb9/wÂrúóøá€åZѦÀaQ-VÐ1Vk?ààLøo·½XoŒ¨‘>µs\Ü‘k·Ð¢ü9Ê q³Á®´u…ÑÛ1N.u†yI§.Ù O¹ûÇ„¼Á¹5Ù¿ '"ƨ;1¼ëÏê~ÞÕýle[OøæîËHÇòáÑÜ7\93L畱;ÍUéÕï·/º¬ a[áYÍî, |¬”*MÅê¥XÕ©Ãgª×„Ý•‡ÑuI…¢®èB}Ìó!ü¾ž7ƒ×¾Aá{ý¹Ö²õ@[×ëÀLŸ•µ¬µÜå9©J¸r­b´Q-œä~µ€ûjÁyíªå–·նï¾K’{ÞN©Ù†ÏÝ:ár<—ww©/z ¾ÃçUV O=$XÛ+<©þ€ˆ¥™¥FŒ>òuîø¨×¦l®YŠ«S©.Zc«*דaµxB敵#ï+»Ž+ÕL©PÞMm¬ÜÈßäÒq•>Âl­p*^öórñ2OœâÅmJðb‘ΑKð(Ü•`ÛÛ~W;­6²Ù#ÀHËO̹ä@œ'x©ÃÖD\$‡0Ma’b£È‘NY`˵i¼ÊW:9©P:…\­äð:ZB³¦xÝ?å°Ø³ «ˆÃíAá~ñg…Á8{*PÿÊ?s›ZÞ_\ž•/ÝøìËX,ølu3hß>njÅ…¯!œ¿}fí¤ëÞ(”óH¯¢â$92B1¨N (x~Tó)÷±É‚ây–Oð` -R‚ò÷šw&»¨OwøÀhÐ)FÇ’ÿÔ—6`J­ä†þŒÉ €)w§€‘ëP‚%þÍCç 8&òÊ–9¡kÕµïodš´ÉKêÓl¹ØÎOµ†»ív9#®W* -¿éGOn£…`ÉXÁb dÀ÷oPÎlPõ@2‡îvG.¯€Ë ™'pBç;Û–•äOR19’Qp ->œjá€ÓÒÉ[-«}àŠg_©6œS®TZ;@n·ŠgÞ„ùM±Xe'd‰Púxc¦zéX·‘Yÿ`Bd¹¨þêÉ›N>Ç.–×—;6à^gð4Ý|¥9|ÿ±ÚyÁäI O½  „Y儹@aÁ|ç8P œ -£þÏôÛº³ZRêveD0v_LÁæäXzÏpš_÷ÒQzßD°X¨¥Še¯üO=y†6~]Àÿ~ûæð­ßâùéwzƒÚØqp¹þ á•á"úó -æv¤#sÒ 'Œå\ gÑ q2‘OÞÇY‚˼,ÌpFsÆ!–aö½Ýƒ?*¼˜Ç_È'ö^×gíú“?åùå_à¿âíÿ+݆/ w²/óI´M°€Â¶ößnÛŸMÈ>Ùä(bê`ÉEµ¬;q¬œÅ_ýóÛ`ùy«bz‚Šó¯EüŸ0ÛÿGý¸mÿ¥„þÛÁú“Àú' ¡‚ÇazÌÆò2_yDÄ:zh;ñøŸ(ØO¸ÀG=˳ùÇ*úQ?BmHÿâ[[TÂo6²ó»ˆpúu2b?FÛOŽƒX‰å™è<Þ•ª÷ÐVÕÎr¼ð>ÉE“»±ûN ºÍ ‡ý­r†n×W„®ö= .[Y°/õ÷fp>”Õ?ð>ð1¬þ¨£ÚÅïýzU?Š¢›ÿÅ„ù­Ôvã“ÞðÑÁkÛ8„÷G”Þ¤î|“¹ÍÉeî†yúj_öüeËïåKýñ2ÎùU=ÓÑ«v:—ðÖ‰åƒã­R…Uc}wƒëþÝdð½v>» _l}!;ãÁuvh?ç°ËpûÇËꦥZüDßßõ©gpéß#Ÿúܹ™÷ImÜæyº]ÑÂôBö°Õ™b‡;A“ÇpþŽ¿CˆÃ©9XÓäíd™-Ö‡… 1|*kN—W^&][žm±»d·ábLd!œnÜ<ä õã_ý9Š}Bû‹øiÄ…O²q6¹  - Pß7C•¸U¶ÌeËXÔ™KÂ1@xýbª½‡…ævÎf:"Ü÷Öäh­èÙx²ô±ÙrqsfÛ…°›çaay›Ë§ë{±oô[í¡ÄÔxÑÌd®êxevkc'SZŒ¶öå5"w 1rsjîçè߈ŸÝs.Žç -òSÌþûö¾£6u?µßÈyÿ¶Ú×í¼(¼¾59¼À˳yÄ7ë^œ‡äQœEÅ6ð sŠö×ö¤òÞ5Æ+}×;ð³?ÚVÀhD®àyäå²ÛˆÞ)ס_¨Á­=È„Û CÉ´û÷#½›Œ-û:ÜM›¿ˆŸã8Ÿ\}/s«.Ý׹رŽÉ”“ìЦ>Xy©V¸Ö…ÑL;§–S´—:N°a&mMŠ„}/c ‡>I$3¾ÁÍ#„p!Õ0îf(?4§ÿî×½¾öîöz}<î™ðcÌ+ø5ÀÎu´ëÔ2b7'À½Ž{ð·*œŸýcurö[ÙàôQù^®)Ørêâ๛™›ýŠiÀýù y­Ït‰u&•8´GÛZØŽŠØ$ÜáÃ:|âsìÕ×wÒ›J"ÞK/ÍB`U.ÈÞBµ[#³›{j† ù!D_ˆßríUûòœ]Û!¶P_hIVÏbÙ—rÀ¾9&.» ´]oc4¦.´öÄöèyôZ~¼žVÔí·P5j](9*>²Fø¼Ý二–p§Wrã·¨u·vÕÏF¬Çþ©Zƒ’¡K#Ó¾v¦d›,˜Ö£‘Zr6mz¯µX÷4Þí6ÇýhÜ4hߘ­wËŸi×7­ú/-ëd·´ùBj_ÿYX}p[^¶…£b °UÇØß¦‹‰²ˆ:İFûj¹5¡7kå…À&­³_çk~{&uÛݳ5n…pkí½Ó³§½vqs¢R_Hݾ2yÏ7°Ϻ«¡¥¸9¢W©o·¯N²ƒš74Wµ¢¶}8þTÌÙwšum1ÚϬ§þX[x¿²”Þpñ—Ã,¹Á­*ŸY¤FíäNµùÅ4,mH/¨¹w£s¬ã‚~× íÛ´.µ­nzº;n6Ó֦ߨÞR3w-â[7·à/õSzÖŽÝ -Tk m̹Ìǔөžxû¾~iv¯«Ö¬gÜê~!–ª¬gÕÑ8{®–êS¬2=V%ÃÒ£òâf,ËV ,þÀ¥çœz\•^]‡]xº¢åÛà­«ã`óÞ6ýëf`´^úóFƒ8ßœÖolÃR¾sܼ€ÙõBUðk­K=r®|wáð³ÙÞîÓû«-_/kÈ£ˆ•š‘xu\Ñè*²ªË•™3«T2»W«lÓLdnüÖά—ò°I¢é’Ѭ®C‹Òi}Kf¥Óè0û‹ÃèU¤oӀLJt Ñ2êøRÕmEY^oVl–ià‡ çæGN¡vj:‚ÓÕ=Ãî÷V ëy÷,ÍÁÆÕñ]UQI>Tæ™æý ©`vû]^ót™p·y³~¼±&Ù¦u£y«ÔJgqÜ/±ËÌZ¿Ù½·äj’7Å^‚ÉB é`¦…˜7ýAÿíLvËî½07ŠîjtîåêãáÒ~T½9Ÿ¾4×¥ ¦væEÂé.Y[îEK;QZÕ„»2w}¿‚£AÙQ·³r.õÞš®‹MŠbcÃk)pé|5³%¿Ðú-^ɺ¨B¶Ž…΢)Æl¡F ü¡êÖƒUaâÞQ&;eöSnò=j¼¡k¥Ü4™ØCCÕÝVÑ[z|¯¹;¶¯Ôüõ¼hÇg€[:UFª&Ó¡*Yrð«à„÷²¹Ã©¦IuõžáÅÕ‘QœG«R';Ù—¸õù¦÷ò··öôª ŠÅ¼:¦ ê°¬Lå[KI؉¼(-¯²…¶ rvÑiKkžJkõ9ùà ‘³m0þ`È81Ùõop8]ÃûÏ“ýæbÔ³ë-³®ØÏ¾K[z0Â* œI -Ky“ZóŒÑâ[zé2[8%»´õ^'=Ð%¾8Õ^ˆ³ÕÔŠûíP­üX-9 X™îޏ’¡aV¶|±$ã¹ )ÕñHܽ+'±1"sbÁ`šbAXOþ€œŽÅ†±øÁ'|ÜŠ&PßÚíví—÷µYÙúÖ“-A¶F‘X¨X¸š6wýd´œ=Vêô½·ÊHÚ‹“ÊÚî5µTy¨ãÂs¬">X(3ŽÚ+™^ñ*/%ó-ãÃVFªCSZÜ›wE,,¹ºp²'É=&0;âÀ_‡,ߥ.ßͶÆ?¸*ÅÆèƒùTœá£n.‚{ùekÓkóÀmb’e¿ê¨TÍ4`ÊÜÅÔ`œ~JïglD²íœ–šlø¤ÆŒBª¤ÌÚyG©h–//'P¶ÍÑT&¨ËFª·ž'‰dsO±H¨pzZ¤À2=‘ïvï6/ˆfŸë¶[NÖ¼4û†ûûzH£89¯&@ø¤û¼Ì,«‹Gý†wO[uíUD§Sgs™ª5nÄò¦µ#KWgërFZꆼ•Ù‚ÀŒÑhÙ·e™¨ŸªRýðjHä€ ÄƒZ‰^ª³ÎFo—Ô˜îîü­úHñÂǹ,ñìÛUXí -wY¨ë¯™‰("ŒñªXŒqÎDŒ±B"füÎüâœNó]BšuºG|Õ$TÎwbêT©Vü0y^ÌæxDêrrÑTc’”óƱLŒÎi‰Üßr¢§¼XÌÖ¤5í:—\þ§B> Sî ø5ެ΋fû g3 cÖ,³8߯ÛÅl±˜´–ôêrôêFVèÕö4¤W“SD¯‚MräÎEl»ŽOs«iÚïÎfá ¸mG̰\!¦®`°‡©šÉ޲|IT2¹.˜ñwÎÅøà>§“ëò,Ç27ÀÈ2§œ+.ün‡Õïà O“ž~ZÁ÷LzÅß‹ §³»M®Ñ¸VØù ¯Ðàáuì‡/êèm êhµCê¨v‡Ô‘i -T«Ÿ 9—ô•`‰Þv½Z¦\ŸÕ9zݲÐX3ÌÃRäõ'(“JV¤a‰êŒßw¦ÜÀcv4L#,Bp9fºuX&#†jqñqß_Hk–j\jÒ‹ªÖi×ÂÙÐSÊ]ëPRD3¬ÑSRôÖˆbÊïoye8:é…uÃñTd×ãq:›G—9l7±XÃÊ+X±ª™ãuäeZÝçà É0¢¸J_žá-ÝYqôAD -ÚÛ -Tj<ÖÈ‹~åQIM7ÑžeöÑžªôÐ{KÿA÷ºQðf]ÁOŽ–Õ£J>~5K}{kª2ÄöqIq‘e¿ø6S—jôΫ4|'2fç‚íëÈ!Ýõ÷W´?Ý?Ñþ CP¹©å 9dx^ªÈð‚'Ín§ì#¿˜Àã8w„°èJqPe9& -ãÔòE€/ÂèrªT³!4 s!Œ¨ø¿Î¸lov°*³ôÐ<¯ž6¡S:D-‘7cš±àQ`Äšæ;)ƒÁ;0­{:PRgÀ› l.—Ïä[²nûr’*Ø1Ü빨€P5é:€š"½­ fÙg‚¨°ZÔ€ŽÕNÅ€òKe@ÕàP¥Üè/>û2Ö­yøBª9,=*…³¡§8W·"ô …-Ïg\á4<ù·Êávî ˜·L¥Òâ¡öL†ùœ)_ï ™®Ç °R ViP8adƒ…×Ò…7î ¿5Øln—`+E@X ÐÐf -hx@áq©‚Âa;…#qH> ÝÐ`ÒbOÝúɼL.ã/5sðe%ÚË÷Åš°H…úB°Öt€£¯ “F e‰@D…M¾o»y‚"ŒB ä,(æš$(rKKˆŠ5ËÅή Šce’`{ʼnŽ$xó 85@±ßZƒbXBAqP­$¿¥8\ÿ8‚Qï3©tµŽËç’¦îfòAê÷øh4‹ j`‘/z_"l’°öZgÑ÷p]@Ò”EþÑ“ß] 0•S0ÍG0^ŠúÖ“C‰̼¥æ°.æ™®–òz€ã9`í`›D6ÁRlØò;0€*yÀF¬ØÑèØn¹ Çà ýMÆ <ú¦®ÉKÒ&‹’¶3›æ* bæøcRÆü'QBGMG†—9J§®õ+%w`¯Gp©»8¨D\M©®5·Œ;€{ËàéÍðžõþNoXà…‹2àOÞXý„¬Í/ù@`¸‰ -žý–ZåVµñRÓÞ2«Rg.™-®’/2T¶—eVxY &œÀø”æ[OÖr:à†º øLhý‘H·úß-"´€Pªw~œ®Â&5"nmho@\Zi Ñ”$ùìIéÏdÕS@j×T ùÕFö·sXÛ2&j 76åöTݦŸFuçUé ÿowÔ ÈÉã äZòe+…é6Âòóß ÓîÉ9þÖа¿ÿ3ÌVþŽ&øÑA?êçsö¯Ûöï"~~þgúÿQBÿÄ#ÌýqÇ9IPY'èãÝU­•>Âèoárð¯ú9róß º%—¹+S¾a ³|Ù²¦{>ˆN÷ä§ÌáQ˜‹Óƒ¼æV{mOívÆ9}Üιìmc?/õV½¼×äø¬<ô•]ž­¹d78·HFÇžeÏåË"©±Y$8ç4Ù©™¹ cŠýàÇЊ͎ôoñþ‰;jëïKý²'6²áƒ¼d³{Èeé휡ÅõV޵•Ê_È’]FÖB؆î<¤ÂÖìÝ -»3í†Ó‰§F¼ZLæÊa7ÁF·ËØAÞÏ11çÒ#r­‘—äáÙCC¿øØ Ùk‚ÿàÖ­#|ý¼Ä±GA?e”£íëº"‡SûÕíµu¼Xo¥ìlé£ëõBX/N³wsñœAþ™Ì¥qv¼Ò†ä˜˜Œ™‘›ž‰ÑÁ𩽙CŸ\ZCöpr“o Š \ñA -̬ÿK»¾Z÷Þ¤´A{è¼Às+ëØÆ_vWõÕñ!y.œô#‚ƒøâV;?ZíÆ¾uKv6s¾¯K2U˜Ì…[sLŒ¶At¨lÇósX nóq œâ{(ßnï~$Þá>4xb½‰–Î÷ÐIšæfQ°§vW–Sí¿ÑqÉIà¼ÛÔ§¯Ä¾}î†ÐÒfŸ†Òº ¿håIK„¤ñ^<)Œ>Hn»æ¡êû úÈaŘÏÞ¬ÄÕ­IÖPÄÑV_ó]–ÂQ2úï.ïô¡Pm÷ÐdÔ`³Òä é:˜µêlÚ¾CÞ[ß“:±_Ì,¡¶oo2mŽC¨VÐù–X¶J^¸ìÕ<À^‚¦ÞË;7T¿5Ì·Ç4Òk­áZü,rqdü_|!÷L¾˜9>6àµnÆìfš Vü¨]õJiÙ`Cψ¾>jziZfºÎNS;Ô²nûÅ[·Õî˜Ë°u?>f-‰y­½Á$sôRé̵9Z0¯&‚ pczm_H##ô‹î">).®f¬zmSòëùînVk<‹×Za˜cf»jØ] êÛüóÖû.ûá3³«ê­ç£\›ïF¬÷ªÎµd¢~¶›Hòì4*Y´ç.;ôØÅì¢^¯h»:‰ÙçZÓï>k47‡vï€Û7 cíîªR²bnÖ°ä~jP}W:‡j”-%¿¥2rvÅDƒ^»A «Ý¿8‰é¶ºŒ÷IÍ{N©ÖèîÕç"»ÐRT/íiÛÛƒ° Ú˜Ý/9×>Ã|Ý…©RÍ+ˆŽs:í/Äá ¯oß¶ó‰-Ró…?ã½¥¨ï‹ òUÕ]=]…©°P17¥¼€H»\t{f2Û˜Îi‰;nP6H10’¡t× úpçOÿ׭̲P×Íktí·Æ=‹ê>?O¦‡¥Ö›lXJu»÷ªoO*¨yŸvücƒ¶{©l ÈEÙáÝ­–À³]…7ÅA¥Lr“J:vÖeKqeDws½~Â_ˆ™'³„ÑðtÞ((I½¶Æ˜¯_Ñú\ï,òooK†Ö»-:Zo;òÿ‰I¯½­uRèl¶¯ÜÃ]fØmö§¥é'©ÇQ¾^è-`çJPÉ̾wë¹ÞÃÕQÒ V‘bž«dJ¼V¶ö–UÆ›½¦Y{O»f>:fî93 -ûܶtj*—óvßzWdt~µ~…ÓÕ×ÊoªÃ:Q5~óTÆáRýBC-ûŠÁ”Ú€K­V×všl⢃ýÀð§Õ¬œšBî˜9G`k5¬®Õ1¯ž+åwé]¶svÚÜôÆc¿Üˆ;ŒÒé^¨}!%¶,µô[ÆêéÝÓ`¬Åìt©õo—ƒú–À]2*¢Bv‡RŒíQUІèÊ•Sɘ¯Ý¤_‘$;δä}É>dÚ0Oµ–²«Aã"µ=!=Çhö[ïxZu·w‚Jް´c!U™#{yyFNf}ŠÄFóDÀ%rúÍ­sºpìªZ<8Yš’ººê{‹wÔÈ•†*tT§ÊdÐÙ*éÔè"ÏǯԷ›.ä$Ǫ‰R¿;¢ë*¡HQñI8œQ^ðüeSðœMëô™'¢}k h( -®î±{ÌÒ7JÝ[ œ­Øw¼šN!©2±*\ƒ»<&5æ«Ã«ÔW@ Ã3¦TP£,©°P7•IÜw”tuÙ’ÙkO®n2c9[çW’['q§tžb’'nQ¹q²•%4iݪ_ˆTã+®”{t;ân¶Šì4Žk|/0yþÎ_öý ÏÓW†ëux““øu—„Ê–UU³Àª[cUÖðX•›ìÀ®xì@ÓšSgH…Sÿ²õ¥Âª“´Éy,cÚÏ}ÈTƒN֤η~Wû'M‰Ù½ -¯Î%ýÀî2Žå€Tóõ¬”ç|ZlLç’X(ß áD“5¡u–[ã×û|çÞó|o¹æúXêÊI+-]°ÃÜfSÍT‰)­6ƒtÒëâLÈ‘ÅÛÅYšh§O´Á "Ùd -kL¨÷$Õ·z£M;E¸íú Ú”ìí1åÚ¸„•®°õÖ’QóQ™®Þ;ÙšN6IåÛ“³Ø„OáT¨Àœ¨<m›ßÕB m•ë›g—“ X‡}䈶›K6uŸC¼¾䥑Ŋ6VéåoÒvy±H¦ó;‚&@TM7¿¿œ“0{ûÁ˜q"¨—ÖÖÞŒ[µnº«ŒÔfå1½Ÿa^Jy¤My²›¬¾Ú¦¾ç»­ê‹S½'××YöMš>9‰ÕU#©1H ÆØL<­ßúÅ9EL‹XKÞÑ+¦ÓD7ÎêOM*ƒ“KyPyJÑS+CÑ}½LÑÍÀ¥š³ð}¿=zÔm‹™EÓfÒž#㆖ôb¶”¥ 6‹d4ͼdŽ"­y[¾{L/9y}Y±Z N̤š‹3[B’§¢•+Vù)SÄ™^ÍseÚ©*u:—õ:· ÿ^S^ÈÞ¨¢%ý éQ>ŸÌì|°ÀGyÑÎ@yQÏy‘3ëùÀ©ºù@ד#N¬ 5[Lùqv?wÈ®iC$©–IõRÐãÜ>ý‘½%²žù›knÙ÷ú²dÒt-™W³zgGçsaWX¿ -îI¨ˆ'©£nqTq×QÉKcQ!;…gƒäÎÅ^þy³¼¯ŽI7P„r*‰Q «¥ü8$àCïIÀ‹©FÀÃtÐOÙ¤M&ô%ìÑ+í„X¸}µ¡feRgW ·Zyßã\_´H§ìp} çåíÌ… eǘûõŠ®å¤Õj# ²3Á÷ùG:É÷Žåw^juÓ¹³!sCùÞaJ0Z.eZQZt<i݇øŒ7x&ŸÙ¥”ËgñaXÂ6z2XÍÏR¬K2¶A¶ŽGvÖN'§vpØë/ ££é»’Ë­”Rö -_ˆ:Y3Ò,ßü]‹¯lÒïéõª·¢N…Â<߆cbŒ½ Ù3->;i9,оŒ€bd  ¸²Ó x€É¦À0JMLu—Üè± ˜ãy ˜Så˜3šÌ{c ¢o¥÷ê>s« -€¹w‡€y,“sœú ²7 ƒ>î¶Æ2=¬t¹ÉL¬Ó-µ\ÉsmícRNÇM¸ˆ˜n‹JíJ¯¤ ô ÿ#%Ç ý‘n¿ÓŒØz‡ÿ#ãnv2`oYpYµç“àÎË=àGXÀ3»:àÙ`x=@Ï7 À ­)àÍ; ø²Ÿ¼-oSÌB—¤µÌy£¥0˜ô#&ÿ»ä»>jàÛ׬š‘²ï2Ã\Ê?ï⟂lÖÒù[µåNnåNú7Ç¡÷vÿ(¦|½ „ÚiüíéÝH 2ÇÛ-ˆ§qõîñ²?)µ!D@Êmw@Ê’·Â5y+–#}–g_Ò-•>qU)³NšHl’³ŽÙ>&p™“´ò$ýÛÑýãJþ.Ô§(ÿÿgzÃïÖi?Ò­¹ÛiZ¼9·†Ü1 @¾eÌ@!7 p£ïg_áC(B÷Ñ&€"1&PäìèW ýèŸ|Þþ]Ä?nÿ†Ù¶ö îÏúk´ýìì•Õ?ñ·­ßH°êülG¢Ä4–çìêeåÃC[×ï÷I>‚ïÆ-ÜQoãÜæLcú @øûqÖ~\«ÛÊëÞú 3ý#ÓV‰žü‘C«ÄŸÒþäï~Âl?Ž`uù››IýF0ùdóÅ»±•„Ûœ’”[åX6®+¦\½]ë{߬NûB†½Þù ì’^ì'ràlˆ÷;_Ç÷¬£°EÃCìbÑ_)ù#Ó~Öï/„3«ÿ*´h:õ{ô)üǽ,WcyÜl="t<øBîÆz8»aÍîæ²åêÇóAª\ÏôÐ~žØ‰™:ÞL7}.~q—Ü¿ë³×¾¸›m‡v&ÕÍJÝ mƒ59C+; +z]—~rØý÷^œŸ—õ#Œþ,â•,ô_9T³wcy$®ö±W¸Ýžx¦ÃÑwôê¢v±Ž·‡ª¿³…ÞÆ~¦k\G«C隌úVôìºZúØc¿¸9óBؽóFS³·Ob3í.PÓ‰T—¦èpiMª°4¯*ÙרÁzä˜Xµ~0Ú:V>ò -©ìÐgÆéÿ”ð[ç/›÷KýR>hyÜkã²­\ˆx]]R+JÆdìâBÍãúš½½…4ƒ:#mj4ܽ‰övzè”hÕŒ0°e{ÙuˆÍ¥³mh‡<4ßãZ=Ÿ¾ß—|!¾Çë¿ø[Æ»lÏ6DPs&—¦Æ•ÇĨ‘—Y”†ìvj‡qkäõßÝi¯¯Å»qÏëE0/w6;žºv¼u¶Îñù…tÜÜ òÍTÆ÷è|¾}ö¦ís–ÒºõzÕ–ðZ{^¨ÃÃæ{ªo›Q Mhu0“šÑI`ÿÁ†wàté´Î›<æ g#§™´0£óC_Ý`aìÙx_{¹aÅJ­M˜þÁ%Ÿ>Ò^Ûg„ uëÊÖË/Tä…§€Jòõ%ûÔÔ‘î£1©D©†‰³î¼†Ýjþ{.V_5ûµ:q~ôk.oíkÚ Ã1[q¼Xw¼…îþŵ¦µÀÞ€Sǹ†–üèä¹z¨NðB°Ø£hwmÛ¯Îv‘¾—6{dZ"¼NÆüžR¸ˆM]{–“]Új¤¥bÓ]dÌ®›µÍ0¹{ƒi=ËÚÎ<kÔâvwŽ6;Å[5owÄH´ù fY=ÛªÏ{jWMû…jŠUìj•Ñyê$óÊï—N]5÷^=r3v3Ãf¨.J·¾¯çÛ×£žjIàæ½0îì t.éëOxª‘$ÂÅ©2SÏ3e©Ö· ç„öl§5O&¯×d"gwSðð ±FzjõÑâºúª*§ªzñ^ÕT'©²RœeË3Å4Ê•ìÍ7—u{mÚb›46C¼l$óÐêh™Êû‰¼x}Dåað¤ÝÀYXR;˜BIËßœôï õ²—`ÛúÆP÷µ½"]SÍNÙ×Ù°«!mÉÜ^ª§RU«d¬Êø\hT _í”g÷RX®L;Ss•mLû nÆ–c`ƒìÕÈÒ¡üL®~‰^) ýìÄs}ÄYí6ŸZP]V´@—ÿâ1Y/À4„Ç¿R‚_#ÜÍMÝW­XœìÛ%µ¶ƒÜliÅÒæXˆå½21ßHr]Ð/WY1W“Šl:fÛ0¶Ç¡m¸­M³tL]»%o’‰JE“_èþ±vÔîÌ襷 ¡>Íš¤Ò÷š -ìêHq~ZѲ¦ÀýJY›šùÁòyYÞãåmÔz´F=ܯڭ—3ãÝMQÄ>B­õ¼¨Ç꨼\W¦;­¤Ey[ÎZÆÅÌ}çs4Ð V:VWd©Å@¼~“šÞ1äŠvŸk5­çµÛ_ˆ&Ç}uÐÙLTÕÀwÊh®>$ÛÏÈÓ+ÎË¡aI‹gv(YÃ$áÚM70fþ`}‡ çs¡^º·OJq—Τ«ò2Ø:Àë5¿Ä_Hi"‡²51–fÍ»MŒÝ:^•ŽûÛ^¿ô¡ó5Öz*¡Iý ¨±‰¨ª«ƒ®¦:˜­”DCAúrWžÉõHάfkiYCn¾×P±.Ï‘LæŠ_ˆÐ,zN^Ž$þ´8›üi°5øSk^ -ǃ1Gû6€¨Sèw7‹rÞ E§{Z§«ãË".Ûöé`ì[Ä¢Ô·c½‹U'ZŸó·êk:¹¨ZážôbÊØ‡3Š¡‹”<›*¬\iøªŒ&eiy:Ô$B{·Å-LD²¬Í…Cfxh‚ø3W§y¶w2¸î[ípˆ»rÂXáLLNè†Éªî—>øB>ÞïþœöÛò¡eºäü@Ûò­ƒV°¾• ÂÄþ¾Ä–Îsíñ6Gêp -U¨ ®TSreëÅ2æ.É~^‰¦‹¢KP¢Hn]8¸®%xâÜûBøóàÐã} ›r·‰¼çsüfãA²a»¯3ï;î3Qïybt°Œ^ÔKLT5¡(ú„¼½/="Ö‚tQÚy,èYBÊÄ[½—|µ²K9Ÿ«Ú.)è`ÊØæ>—1½·ê*Rx ‡M+#…ÉŸãËû‹´ÊsV±ÌÝq³Î‰u×gŸÂaÌ*tËÕ~2úxK§iV)š‹Mò[Ši{°§¹i‘^ Y½˜æs¿È§µ‘ß -ŽÝ# ºÍù/9÷¼W¨,RyØ8ÂøMëÂNÕuw&Ï—¡äÔ[}Ñ ;cÁ[Vü…_ùú<ÉÝ«y˜ëeUœ/"ûìLEvÀ_J,˜Ã63ªr-¦”uÅim²,¦Ð^x.Fãà-Öãv£OóëBîU¦ -¹ÓQ+Ô -O½PË\´ÂZºkç î“Ù¿ü†sÚkö€V¨òÚ@àR'}½&W—Ü)é†>“rÝéP8VK}¾ÃÜ}VšsÝØ±¿weUýØ‚d˜RÏ3­²Å鹡Ëþ¨\Ì”À·kÍr=¯8óBm5?Sû|¥ûˆ'OE²F¶.ÙºH²µiä šjäñ6P?èïçÖ_aÛ¥n}Õ*åà¼Ù—)½gm®*|Ž·ÒzXœ}» ³¥{ ™û*œ6¥§&Li‡¯Š³s,–_µ¸˜aÚ†fYß> -…M -õ‚Z*äO-‡jj Ÿ¢áô˜l—ÍÉf.p¾ë(Lî±ÛZ¹~³;ýBr}oOäúVYÍ=V‚VIÉó‹²Ò_LëÖh¢xµË³¢T3Ô.g´¢aJSëÝ‹’á[±±MϸGI°êëÚcm»C/ÍMàýi¡Þ]n -¤ð8!Ô!]xQÍ…R´ÕΑg|Æl÷$ço"^ÉwŸ¥f.V;a.„À†x›æ‹ˆÒÏÙNŸäØnì >Aq…€„|2#Z½ {‚Yx 4¾GcRy£Žr%~×L©¥¸}ýßB[„çì°™0(±ìÑ„%øÔÁ º¤_Ć$äy>(Û¼˜ï_sñ~óÎ…ÍÍ)E2ŸÀæ‰h4Ô Ù'£ |2Ïwñt¶6ÏÎ×Ï{6KZ¶:Â:–c&},ǾÌq62¶Šö ü¥”ÅV©s O³Fþ1¨LßfÓ¨Úë$ÚEÜŸ¢/T/sÆXe’Ñí¤»=ê0¦;ù;¶jå”é¤Gè§IDÀíßêØ/àOO+ﬕñÒÙìiKak±§•3»^»™iH¯Qú•Žé–þʤ¤§ *×A;–ŸúBÐK[ÒÅeEü÷JÆ)Ã}e\gÉ©bz ¬§ê"LÔä«àCÞŽ}wóbNEÊê†ù{2ÉÈßÂ3,×ÂjGª—Ù«Åa¦™2ç_H¦0öwé“1¿¦ÛéäîÅÐkË¡Ñ.³RQþFÔGÐì#} Ù!ÒkˆÀ¯ˆ`õ¾nÀjŒÞá!ùfà—ÉJˆ?ðªPÒ—‘/éüîpûþŽø5 k1œƒ& -ÇC&Ê_„ŽÞíT5f`U==1Öò€˜ä«€è×€ès°÷¬¹ÂJÑ=¦œ¾ev‡UãBP¼íB^=1HàÚï+[Jq{ºæ4–ä•9MrZVN®>ŽV=Œ ôNšÃA ÷kÖÏ»0µPƒø!ÈMʳ£ÈíÒšàHƒÜ^Ñl› w¨LAî˜&¸An9¨\l@®³úN7ÍueäêléƒZOI•ZѨ”z"Ì«ÓJ&/GgˆSÝÛ½hÅÔ‘j—­un/g8¶§G™#4 wKé!ª é{Ñ©Õùe'J0ý^Á?ƒ È¿•K‚à È,Œ'¨r Îe@â• ‰Ô*Á,È\K$"¶@>& ´E?áäÃ2?°KÓE×<åéŠ>8&#·Úvù8ýèo¦Òº=©s >ç”4•ÕËf•9žå9Ú{®ÆHjã L¡†€œ.ú€|€Q‚âPˆ³T><Š¿Å€28PõT2ILÐv@rG‡Éo¡z£= úå4 BN”÷îªu½Ê‘L@•àAòé胪m·ºÆu/Ûj¥v‘D¶5$Y3¥ Gòòƒfî[Gëœi7÷{TžxÒ&Ößzò€Bë{§ÓAý -Ç - -/ˆtÆ`- @˪èÊ} -èªw´Åå­¾+€Ö®É9nVMA«o¥7-œ>¨¸Ä ¯GáÈ”|TyÐ]2ôî5'Hi|ÅñÉ ·ÛŠQå’¿ÃåÆé–<;ôñžìvï?Vàýz¼A1PÔ ìÛÓëôIPŒ(® -`ÀÀ C ’ŸÆ°Úû7ÇáTIÿ¶Œ±ÈFœ'ÿG#À`}ìãÞ²BÏ ®ÐÏÝMÆö®%æ æ !ª1•Ï\3†jè…±RþS‡…T䈷a*øÎ3Ô•Š€<`ëG°“¢ Ø›ßGŒ§O€«™oݲ‹+ .À«äloE®¿Iþs}%xò€&C\C°F’Ñ>—¦sµB§ž7s³²¡bç¢Ç£ÑðR„³ƒròõò†ø)ÈOªD÷{UÚÝöc¹ýD8|ÜO„Ã_÷cwí“m ä—c tù-S/D¢G‘3m –è ëDr.`t öŠ# 6©=ãÛk-†¯@Ù; Au»Qq¿ -º¤Žö85œYiµv.ÁKßÑ~4íWò§4Õö¯ÛöÜð‘nÿ7üJ·ßßò74ác¹í) ÃÇ4k¡ä®Ñò„Yy‡'ç¹2PPv”4'x(@É<‡ÿZ¿ÿÉ1ý¼ý³ˆÿ³[Ö_%ô“»¸Æ±ŽC Êx‚Q1Á]ŠãR®ôk4cyøweÒh«ùè9·åÝØC‡Ûœ&·ÊÙËܰ&]íÇwÑ•­í߀Ø&ºÅ†;ïÇzkÚÆ¯LûY¿ÿA?Ñuõ¯ø“ŒP˜þÈË!´~<¢ô< kŸþBîh}‰ß*‡%yüsµ/'á²å¯ê¥§ÌóAAì3=›'a~"±SÛÂÜó½7×ò>*V[Sퟄku¦=ñã ýèÉ}«|tÛOÏÙXÅúC›CöÝXc­ÖDû—-ûŸâ{yòSÛÍñfއ¸:¿äõü±òó÷nB ê2Û¤ Ê}ï–5ìÖ.4àW^z¤-ÏÖÅY²[*XÔê0Ûù±ÒÎÞÁVþàcXý¬éÿÈ¡ÿÓºj,kàJ´R©sóñ]c'6ÚäÂôÄäåCÛM¨eycÇreMŽHgEÏPwÉ.ß­E@ÄyìÆý¹|Œ£Yļg3(€Ö¿ù«ôc‚uxì ^n´­nŹê‘GáÛ/$¢Kdxnm2öcX CýHŸ=ÿQÆï «}hí¿3ꦻI~5_o¥ôty6v«Å­z9ÍãÚ=ž½›/x:áNؤòã•6+|!£­ÙgFä¢/DÞW~¬³êàæ-ëƒäñj‡!ÿûï03ëGJéЇF½WÏDSù`^ÝÕ‚*žÔ.¶9Ÿ>^ÕŸ#‡Œ­çì~õä·>y^êf³Wçáê "ö\Þ‘•)ÚTÇpk£ä;[‘‡ƒ¡Ÿ ¦ƒ ¬Ãõýw\ûÐÀ}ôÌ”ûî¡ãTÑN&Àæ£\×ÉNéζ¶¾ÕQ”üƒ—s|ú¢wÚçÎdÚfcôÜ -” ÛSýÚW543Ñ?ð}î§„Û¢ºÝû¤úÖj'sNêfÊç·ú©Ê—nñ éCý¦ØC£v)¨&—®ëdÆ^Ç%†]ÿàŽ>}ÛÉ›·ÙëfÓ -„å¾%Ä׫*רSRÜŒ oBsƒi$¿]o ëGÝ×K;ìu‡Y$#¥:q3­Ú6pÝä(hüÅ­ -˘Æ|þ”‡Öh‡]ØHÚh_»wã  -ÑÏζúþ¡vGÛçæ3׺ùÏ¢÷ž¢'¿Ó¥f¤g«Mhúíîh˜™œ×@—ÅŽ[%Š}Û*£ºCéó:qôv5— ¯Î¡»K9ž˜ËÙç)ؾºµ¬Û„í[A™yUãåž©Æ+FÿN—Qï²jMÓý/d\ÏK¡ Tw}šC~KY]Û\lZÂm°k¾Ö¡ ­[׋­›™º“ïækÛæ©‘ç¡àüæÐ÷o-±wµmöuu­[„ø–ãa5Þ•“1U)t•¤«{Wt'Ê“ÝH.›MÊ5çï÷Ƭ^Þ„i¥Ú²™ÛÒ_ìÇrœCÉ<i¥»™Éx[ÀÑWsÌC#c©K×*]§n¶²\&×%—«¯œc¦}²/ÚìesÛ3jõ¨˜¨>‡X±:Ðh¡ -¶‚Z¹•r~Öòt0l•3©YÏ\lSS§˜½±>v^F^}¥ÆD’Jsçê§Ì|§3¾O|!Ú•;óÿÁ†ìÚ»¨Ü›» ±} I3¹zýÚØ9íh8µ»ÚÙ¼†Îª/ -ÚTS zNmöUž‘&ZÎxa.‹Ar™øh"›ÒV5ò³ƒYÚ{h­T¸mýÔ1:óè.µnåzÕ„¬–Vûµ1«¼bÆV†ƒôBÑìlVˆ*ûO@:³xÂÛÕ'ü·ënðd–äéãjºî.Ó7ûÿÐužYª+[¶nA6"÷Þ[„áÞ{ïmÿŸR{Ÿ:·î¨÷çË$!Q€ÒZsLÍxì.ÿ=[Ópªza¶nÄ!²±9üéÍšˆvÙrÜ¿ù‡Çô㋹ê]@{JLŠî]•4·ç]ç•«„NUìoóÒ±G*—Ôü6Ô-ž¬?Bº¶D3Ϭr&qhŒuŒ‘ƒÉ°ðбnò|¦x‹þØ_G±úÊ©÷7je7·T¦Í-õï$ˆÆ^7»›=šYnµiÿ ~¾Ž¼Øx/ÝûŠ9º†b<œ×½”qì …;›±öèôPlX~›ÖôÎd-,0"s‰Õc“,LGƆ&öÓ)õÊûÂéü0ôµ“' ´ë#štØ'µ¥&Í—’Ö@N²Vn¤i)v——|¡1؇RùÀ\‘ÂGj\C\©­³ûëtì7,,iÝènÃ}¹†k4J=ÇžT'ö˜ol¸Ÿ´$3~[9JCÌeÙ$³ W“| 4£dσ%ßy½Z&kºÀ}­Q­5ÙßjkÒa”'9Jw¹í*fñ3jHеþ`|f[ãÛ%Ã:2äÄ’|nAɬÌ"æ%(cúÊ—çÑÈ}*׎ó~ûu\?e~ám+—†æòê/MJ+Œ-2¾l¸üêñƒéÕ+ÅèÂØ‘µ&’359™Gê-ï~&$üœezå>/öÈq±AÛö·TÍÂk“’YøžãÄ(Çž~¨~¦zÍÒvÚyä\´fÔxk -3ÕvuN¨ºù”ç—Õ•ÞÈñä/\*ÊÃý©-C"3—²qå*¡ÀŰ"a‹Ma1ŸÂÂE‘P¨dV¦?D‚F„Á°óìÇz¶•­” ¾0|³ ØÑç“w×îKgø ‡6*WÛæªw®å¨VÒkÕ°ª?펦'#µ½Û.U½‹•Þ¿)ÖÊò·XNjyD÷Ù½Ÿij|l)û‘óâÂ-4Ä<´ ëªzèóáw±¤ñÜãXã¹þà¹ã–ç+èYøƒòkË÷³ƒy¯ñµM¯,Ž#6?uðןœêÉöÔCs­›-ƒ‹zíbê‘z/Ò%åÕQbůܾü]Öf²'·2t;\¤iûó’‚@„% ËRb¾PD’ÂúöÉ ŒÁVùÊ 0àyçqøA¸úÔ‡8)x(l¼ª—Y5^Øx\c´yöríñœDùîZwÛÉkq7ºÏU&ÌkËY¶Î÷Ò_¤z²±[hm-6³eÕPˆH±³Ç‚ìÕ¿Dš^¿m ËÒ#q‰ZK1l…Ô¾ Ñuþ˜ñã÷YŽå«hVåNùšË5¤[™½¶…«¾æ1 cfZ"ýž=ÈÏôÀiú¼–1æYîItxŵ|îìûk˜Î1Vãíu¶Ùƒ=}äçf4ôúi©FÓªÈÞbQ‚k% l§"ljý¦À|=~?ºOy…ÖÜé ž¸FÍ}p²Õù]/éú]“lkô™'ÂYL÷P+ÐqÛ¢–°¤3ÚòMù}™£û‘'gwhO!Ê—¢e)ß#XjÓl+žM•=Ý´ -ÞnOü¨ðvœ~°–¦=OæKm ´ól)`ÊU$ŒŠÂæäùêšÉs‡Zco9#fÛx4`µUÎ<›ÇÓS¡+ýéozhíâ1KgVGÊ2h@!ûbƒ …í”XÅÚí!Šê™Æ·½¤a.Û5¾mÄ8A-× Qxïþ ùnFõ²tRŒvª~B´U¼E¨Ædj ´Ò­:£Ýb -ûü¾`oxû8ì¨c)i;Þä[Q#3Ý)¾“~¹ %°dj`9”~½`'*“ïTfºÜŒ„Bž€ÅoÇ7ÌAÑùÓä„õA»½á¥‰ìP,žŠmîÒÛÕ™þÌ(S3êT$6õBˆž™,ª ·Á¦ç4iL*rÖ3óö¶“ õϬø -ç èL÷ h¡4L@ÃL%Án¨gù¨—%ê$ÔuŽ’‡kÀ$ y@`.²óÜ’ã†Éõ뛢iЉ!GÕºž–R^EF|}bu˜w4jP³¡T%J‹q„5Y-ê"Â_åBè`^ôÑýE¹–`Òô;3N`&[ÄÆ×_ Ç¡ 0ì&èÔCêKÀPh0™ƒèû¸è' ú0Õ=_) -Ù¸|’\è3Á- ßZ§å`>[ üé"*t§‹©ÓBì5†f£Flñ_õ÷œì+öcþ®YÏáà„ÍpX®fì׋o€›?2€;zÌ_[ð;[Üé<ܹqÜÅOž¸ª…üÀ. ¸®_ý×4æPà9ùkR'[¬Q#ÕOä~%õ®Ü=78ÐÓÅsMT e†iÁq‚8·Ê"H¸Ÿ¼Å¸ xø=¼ÏOß²g±éo½œ@qO ÈBaA¡Iÿz…aÏÂZn&Ø,€0^@˜H2:DÝ̵ „zÐBþ¸‚½ÞdÛ!wðç@ÓÛÛ,-/ªÂÇ×ü‹A ñ•8øŸúbŠ[Zqÿ¥'§Rò’øßfआü#ãv&/ îQHp–ÓTÂm [&X/Áhÿ7ÇaÆ%Oô2] õÁo°4€ 5³ Eø7]ÔÍó WÁ¬Ç-Sõ^MQŠŠÍ<0Yáß_Ìè;OA2 ; /2¿þ -È© ø¸†™’¿žÞTÆM3R÷h*@AP J'jeÎ ï@y9 ¼„¨3*Î @%è,PIg ”[rD(·½io—’ö©Î|iVFRÃØÆ:¹òHÉ$íÑ2Ždë7,Ã}uèï Ò\‰}À©x23þà?"R÷_³kžÒ1ÐæÍ_²žuN@ïdP O—&Ð÷èÏZò·WMÖmïœAÜ-`€rÕT¦DSY|É’ éTÀàËÁï­¼†Ì蓜Ùß!ùFXáïn¿ióG‘jÇÿÓèÝÔw›*¸ipïùö?|·¿Ò­‰möÀ¬`! ,öKŸ ´žÀª¶4`õ¦¿iÀV˜ü¡Q–Õ¬ÆÿxUÿ½ÿÖŸ²_Ö?Ëz¥ÉiFlëøÑ6M´­²×!óÞÖŸå½úˆQøU®B£üù抢î#»¯ï ¡¿ºÍõé¶VpÝ™åFzårônó46E*‡¦ZíÑñWýG¦ýW`”ÐTM•Ðßü]G ´kÿdÄæ6/kZ=?¡‚d·…ï=½j¾•Ïô9@Mù´G;7v~ោ_ãoj]CƒŠþGýu‡þ_JèµÙçù'zê"ïîûÑnÆõËѨt~sœñ'}!MþY2 ^ï§"ØíÊõ¸-¾ÇçM%Ó½¯ÙÁguÉ÷à•²™SKý°}ñiÍ¿--šC½ñïjYcè?Â~1:V)êïýûýWŸËüןî¬ò¼­¹ýí"öÌû©#¯Ã·p‡PùEî³'ZÚ쬎¼g¾¼%kñª0ÞüÛ€ƒ¹{ùÙT¥Úÿõ]LóÄ“µ÷&ôœœŒ+„´£à2÷ñw 'zp {ÐÑôQÿÕ;ßS…¶o}ŸßÔ¿ÚY‰kª‰þ/=™¿¥1û)k·?æÕ{>ÓZXûCîžÏ³Yö~ÛNÃ÷í<¡'Ûǘ[Ì?#q3‚þøWïµõµ/ö¾Ý¾úõtèUê,rDÜ!VÖ¸ÑÍÝÒÚUžßVE˜h-î2îÆÇ³ùcXýu°6cs¶h܇ÕÙ?q Øõ¤O¹ñ¦üÖJ kw±§Dhcn66‡1³úÉ ú}¹Tî}Û­¸õɬì¢ã긓G‹ö:llÚôºqhU˜ê9>V«÷X¿ÿAbñ€íeª[ïÁµ6r–Ÿê4»J6¿8P•ÙjñÒÓâ¢òuú…Ž—› x¼Ï÷s×gÞ&L"E]õ =–,„Ðá8ÍMë,ÈYí¿párî'}ñà±2…öfØM ñNu8óê´ÕìUa8*GŒ2-íªú*:6õC$ÞÝ[16ÂÏR¸*HÁÈTˆükÒaó6ÚÃïbª…9µBhsÌæ¦¥o1‡‰z—”^@\Û«l¤oYúeaþ®¿Õ|>“«zÇiþìg$‘@¢ÿE2_Î0ßW·‹·ŠTSÛŸÕ9ßÝ–Ù°7ŽN´[¼6*bܶ[…Ž Zùט†ß99=QÝ妱}Éapð û.BË®?& ¯¢¿g€þƒø•=ïy'Á(xõO§é^ÝãØm1ÖÑyTG§+r¢ýŽç{PzìA{†Û޾¤þ`Ð>Ñ‹a¶Ôôºo«%ºw¼–í[éðÂW…çä]ÔNjËÐ_oÊ! *!<4rØ*© -Éû`”-Ë¥¿_^~µIÞ½ÓùxŽ{rP ÝֲĹZ¡«8ÝíØrÌÈÛƒ7_·A}dM2ǽ…âלc á1ñ¢[0V^ëh¬| KàÿbZÜêiüoã5¨£ú®E.ÂivÑÖ껑Ý~ -¥,eÂ,ÛÉD~õ”©yÍ ÒvoYväjGsùƒ8ÏzéàX u³?ãñÛúØÎÌÏ„5©ã¼…\YÕœ·ržI ƒ’Q ¯ƒ&í•^.ÚáÅÓZ­ 4ÑÍl4±xFܰäáM‘´€ÙÊÒÞÝpµ6}1ptå§¼7ÌÎS¡Ö¯7“9TävÈvàê%×wžÉ´µ¿D¹f»Q»cCêabegŸµ…†ÔÑ ×ìÍ$bÿk¬Õ2bг­ïrÛ¤NÖ¹ÕÓÒŽ‘œ×ÄG#Vcó4S•¯wS:Þ†P $rå×q¸”ûlF~Ú¨üZÖC£•â«•tiähŸ)šÀ<æ–zæŸjdÇ}Tr5gÅÄÍ­¬ _Ž5¸‚…îÅŠI|”ØXÃÁе™¾ÛM¶z¥s>kÇ÷í¡ÕWtF“Š¡Æ»,§ª•ž®tÞç@1†r]@­‰ ‚÷YòV9T‚cÆþAÄ™¦ÏÄ™Nƒ:"ÁeëúcmZlv½¢X®V[ AðCÀJ»‰×¾;Žã°U>´ £¾™ß­#:M³ú^ÞE:ß>Ö´S@t5))~õÚÈ®Tõ–Ý)Iûªt³«b¢wD4FvdK‘Æí²+ùú¹"ΖÚPÄ©ñQXîdD øÁoÎ÷1¿¹¬ÞEBðlîaß ßÈà4ÿƒ”ä¦÷ çøyŸÆë©kN—mÛŸ Usù-ŒRåâëûAÝÖNÛ¼«^ùPÕìzYy‚i¬˜¥]_þ°ß™<8ÒÙé»g9c—Ÿ’?Ž3RÚ‘bîðúÕ`ñºb …k¹ÈoµC—/£…=wÈß NØTö\©÷9aß|qÂlqÕÇú:[˜ksêΟì+ÒÃwëUÆ©žláóCÅ(3VA ÿÑä¶g+Oë¡+æ†ä!ËäLÏ®I“0îJYb:çÑb!†êk/âOú&¬zúG(ZŒß®§<_޾&w8(yNÛm¶ÙʬYEk}™vŸ’Ý‘’zŒÑ]îδ›• s;w 涪gÚÂ`“­¹)[øßï \iîR²-4Ú&MÌ+º(yõ¶íøJ/OZ²«.ui2{øâ|u -Å0†Ë"¡J±PœÙI¿/йhÊïðΚ/Ÿgž‹¡W{00'v†ñ¾Æ*«oŽé0nL¿ª«%m]äõo"5Ò15â.Wê[±3´5õt ß&-¿ÂI&5}ÿ ÙÚ2»u¾ž1¶ˆ²óWOVß¼Ò{E¾4y, qW5a­”,žv¾Â -> .Ÿ·¨2åû~롤: góñœÄ‰å–X^áQh™|ó¬Òx©w5°P‹˜àOèµÎhcñ"Q¹p. É—²Gq僞./&ýoøL¬"€–Djúvx\'W±Mf3Ô¥èÿ¾û£¼˜?>ÏïªÛ*P:ýAæ -5þ6U2÷h;Ä*×ËÔª—Ç·ÅU/ÓÏÎî©!vèúK¬fvO˜ð9½Ñ¦‡ã¨—”¹Í!Ò+=D'‰#üÜ,pØâŠIŒWð³|mŸÿƒ -èz>X°ÔômÏÔþÊØgÇCMÝbž‹)Gö óyûì +ØŒ9A’½OM¬¡Jâ]Â9—Ð0±Ã9èÅP²hóíPeP«!wgÒA:™óѧÔÑw­ÜçW0lYú6»>äîÞÌT¨¬2h }ÁB=´LMü®[™BØ‚òSR¯w5WÒqÄ}߆W««¬ôÓq0R»ÕCKö®*RZ¥À•k–é9¾CMzg#©¾š^þp2z™¼¤³øZpÿúò`» å Ñ ~Èë+ ¶Ã^f†ó æo÷ ¿È<™‰O…nEoHý&x€Ô…i‚kò:)ÒÈ“ì™8Àέ]ÍÒ*Xi7©1úA”ÁgÛ–ðTåk¢]`ucÐPVsIb}°pw54ÞA -b/* ®w;“ û ïqPê¥(k'(Oœ÷€ÒÍw‚(Ãü]Ɉ2Þ5@™Ó Ä”äÚ€’gk@Ñ%PP—H®rÉÁ¬ÃÃäcKÙkt¢>“ÞçìÍi»*›L¯ˆö¨AŽ$%ÊÃùÙÊA[Å¢•cfÀЄ꫊Á‘: îðBVqìÞô ”ß*nÏ‹ô²ÜáâÚ±A>d‰,pƒ.hT€j«•vMBeÚô|múðJ6úCÀ Å2`¸^0Ù[çWO®ÈÀtZ[ÀLßwÀÌr(`º{ 0½ ˜>ÔM°=f0áÓlD€‰FGÀø;%y ÎŒN¸þ}Ñâ€vasW -êk)m¥|¬MùäbØûMQ1YØaUì<®F¨Þs"Ø£?…L¾ÑL¶ÒvÀfzÀÊ«ßPˆs°E¡Øf®ØÕx -Øóu8Â8N}g3t‚¾ 8G,N^ýº9¥ñœZRÇÚ À¡Ù`ßs?ùo¶–𽍠-ö*czüåò¬LîÁdçÌÐv‡äê³èb—#ýç²Ãëj”V™d4+ª8!ÿn¥Úíüuï÷}÷#À³êð~¼|i}ü„xþVÁ€@Cr‚Zo%˜m€€`À¿ ðo}ø³•üz\üØ›¥øA¼ ˜)fµ¡`ª3C_b9ÏœÙ>-o(R+ϱkg=B†™h…6öžÌ£?*²:«¿Vàüe„úkÿGÄÞʈ|ùÄ`—ùõôviˆû²$øi‰-”€Ä%ï#q£ øˆ®çAÒ‹¬Lˆ·~ Ä{û’F8Ø«ZIÑõ²N¥Â+÷Фó…3Þ W{d$(›ä “oßוŽ'EP»ÿNÅÛ4Ç!5ߦ9ÿ(¸=¯}Aò±å… Tâ6PªÜ&Á1”°¦þæ*äs ðç†7€â5;@Ñ0P8S4%µd(3Ûå¹bà *– B¢ÆoÔò*œW÷¿zr:$ƒþº€ÿµÜ¢æï¬üã¼MåÒ?é ÿ“œðâm\šH´€6ŸÌ@Ò^Ý€öF’¾;å€.,Æ@·vÐídÚë…ëèO½„浡Ðõ~‰+õt` ‘¬F{ýìd΃3þGOþ£i§ƒJÓ€Sù2wÿH·ÿïþGpÃJ·¿nÛT¼MóÞ×30«-˜=CK:®Ì7²ŽÐÀ"È2°$n,™H*lù×jj •ÐôÛÔºš.ê•&#ôêéjYÈ(AaûÑ ⟀„ú'ÖÖšÿ9"@ø„"¸ôÈî±ú?A±êøFwìuš{©gNØù¬ógee N÷b8ýAÒlØTM×úsÿÿgšÞÓŸŽöW^þ“ŒfÄ:©¼œTÒòö1¥ðã=<<¯·âåð¸–ó÷r9š‹ÌE.Ðs -ÈÓ=Xs'=éŽ}êbFÜ=»ŸJ¯h·ÐÑxqº© «÷ºN֌ե´ -ÓÈ4f`²—B*‡þ×Mü?ÿ[`V±'”{÷½aïDå­ÝŠgÆ¿r¬ðOä@£v1¥xŸ=›ÑÁ{[ºÿn¸ña¼®#‡ù*&Vëå=Ú–úþ|[Xçóç™»÷6›šœ0]¸®=%¦‡ú$Â÷· ½.1©Šœ:kÓðÕÔµÚ pëþ_ßçŸ;ùkçú¤OÛûƒ»»…ø½lvæô‘®–u‚V16Æ—ú¶AÏ¿õ?›j‚8]X¸2%F¸1‰`Äïrˆ?æVH8ª'%çðRaCå¤õ¹0ï÷õÆ¡÷íï?=/#³=h*U»öÚ¦CúÕ-Wâ-u°¶*’·û÷&þÔ^›C#²ßûc]}En~5¾ÅiøZ”'X4Ç;ÿ2sóï|3‹Õ #L÷ýWszþã_uߤéNøÛ 2ã¤Êމ±˜±íˆ\Jmzó¶ZŽ ãcÝ«Çâµ?jÆÚ÷и÷;x£ãLJÿ•ºZû.΃šG{©uõrÔ2ëäkÍ$áNÖÖL^ -²ÿª7ÙÞHsùîÔrÕÎÂsíN®åÚë\¯ÜÚ›õøX)·bñTî5c¹óº´¸Mti^jƽq¥wC8ÈRï›9ªn}Zb̓Z5ÀJVeQ(y•<Õ -Ëër£XޏAµ´«w¥Š´ìFÇÖf‰ÏÓ¢xé3§¢ -¼O¡ã¨‚bF¾Ÿ¯”òýB¸ÎÛt ¿»›ýƒ¤?Räm&ã­Åq0ÿVÖÖ0Nz…T­ë,X7:Š?­ìí°ŠÎÛÓ -±./ÊSÛ–vÕÞ¥Ä'¯èØa³}¯³JzŠúÐ -®¥Cm;+X™~;7÷få€j"¹li2ñüý"ëø{ÉÍú¼Ïžd¡7^‹5WÃŽÓ…k#ǘõæö@˜llû|8ý É$zÞ­±Ç ž‰¨9«–9;÷4cÙüä -všz)7žé,!ݵýzÏhÕÒº¬ úd« ðúö†Y-‘^£Õ° ÎÅö£< Q[MöK¶§Æ^US¥B7+¿Î›H¶ÝSM0hK£½4’¼ª·§çüN š«°„’’??ÃH~ƒÛ -ÏûWyÐí„ã»Ã [ÿŠV#6+MÔY‚é™­ßgö$/-)쉕ÜùÀì¼ÍVýhàš|®nÍzÍ‚Q2®vj3ºz­-eUN1ºZî(I.«'¬·“ ®äB1xWË"6è6„eaß -Ô{";fÍ—xõÄ3ïú›«:3Œ=MÞ"ÛŸ¹.wMF£õ%Ý­¬Þ´)ôtÚ÷#º[NôcQùƒ¤Ùe,¾Ììw8[ 7~cî œŒ,ÔÌÂÈÍëÕAßU¯/XWºqC’U–“Æ…%ÎÚsNLÊEX.>–@QŸßÜä"_jû5žÕ«m®ºè9!¿Yü ì™|ïY¹LÞ™›^B­?ãé^vhsדK 4§2…拜ÐY•œ0Ýaò~Ò‘rÆö‰\½SÃ.ºAä+·‚Êk¯õåû6RÎTÒƒ µf¦î&³òWJ–YN’çÆŠx¶M T½Hó¥kSâ~[ãjÈÜá„ù9Ç6ÅO‰•¯Tƒ¹µ´.Óö«SFGf[ºž®´E²jØ*±T湱Ȭ-UH¬“:™³Ö_-! _%½b^ÃŽ)êˆ|a‹=ôúLMß®½{~¸Uõš‰†j;›wåÏh¯KYÜ”„UyÅòeÉ%¹šOàìù` lrHz1¦=r F÷Êýb&º·?Ti«úiQÃ/5¤Ü‘³¤ ¯u"³³å›X‚/ _«ïN÷¢1V¾\.ךò×þt°ò}yÀvôæ€Ónò^…×ï}tïÙZ[9®±éš´¸­jÍÁ+Tz}Õ‘!x¦‹aQøíqȲ–#™»yÇéWñBÓÖý+R#ã¥PÎÙ´²rZ,Fd  Ä¢3ïyãùûa‡GÅÒÛíæV©² -z-ªÁ6UÌ'/)^@¹{Úë æ×ZùÐQ?`®ö¡/åÞb ¯ÈôN³€Bû¸ƒïfM½*¢ö«'£/oú2X‰’¨‹$š@‹ÐÖÑt -ÝOö½h6ýzƒ9¿¶àÙ Ð -I0Nþãk|G€¾Þ€¾­t@ï6¿j=ç @÷Fz -¢©¯¹ÝOºÂcKy²úô…òn²l”¨ Ý ñ} ñÑÖ°íÀŸŠkC(ø&£ c Ї£u“Ó ô,`&Í⯞¼ÝÖ ámÀ*ΰnwØ>v}$û€WlÀ¶¡j‚Õ°!ØjËlOë@âß\`jX° -LnN_U‡’ÊfÿSOv%À‘dS­8ÜiÎ,µ—ëv×}LwP×€GêGÀû¯àÛy<Áå7©•ï„!à»HðÕÙ ðµx;Šï”·€W36àáM'Ác`ç;’FtÈØ|¿áeç0¡'>Ô#¸Š£Ö©{—R l>Hå?ùw ¿zrª"§âmI~ÌÿšoÉÑú¯÷‚«\ðnh|q V³g7ˆóRˆ rÄþâ ÄF¬±Øl1j=€¨-Ý_¥—û®ÒôSiT™`LJP‹öfÄÝ–Äž ƒžã!<)çGÿ©'ÿŽæ”œº€SðïïðùW,MsT"“¦7üŸðÇójã2ûU(VJÐWN@þÜ“g/È›ÁÈí1 -äÎ"rO¾%8SZoþÅ¥ˆz}Ùwî}£¨‚vÂ’Šb›\ŦCq ªÒoÞñïógP©8U‘SÝ6ÕŽSËmŠÔäš7ü+ݦ맥«¦ýQmG% Yzh½óæw­´~ m ©@kÂ5 ÅÐh-BZo­b¿€VÍÛ -6¦Yþf¨8Žœ oÇÄ/ ¯ õþ‘¸SÕ6Ò¯ ù†<™ÿ“Þ࿌¿©x›únY·ùŸ–Ût²Au LœySØ0À´â˜ùò˜Â˜Íª ÌQgÌMçw¿˜ÛIö)w©55Ú¦Ù©u•ð”Ë ÎÝ– K¶§çëå±p?ù¯+¼¬YWyŽð®ñ„Š]ç1¥—¹ß,‚Ê&ºeíV¼]»×òë=½Ô3äþ£Á÷t/ØîñU>¶ÓdØ}ö©¶þµ®N"bÀ§rèÿǺúwQ¯×JÓÃÃÆËšO(?ŸÞl{y[‹Íݵüp—ÚG9Ÿ/®r?ÝúuÒ— 8ö)9Œ8Œ8@užß-4LÙ¿°½áfX~-®Œx¥ìv»dÙvB*%§jm*%§ÃKåÐÔ"ú ô9‚GŸZl ·âIF¯\[`.G½ ž/vÉ9¾BÖ?@•CnÞšùd³³•ÂúèsåÕ%„ª+e 5—úÛ^ôù{þm¼&s÷­fÙ7t˜æ3Äc²Îzèx—¯ cn åFâñ¹^st¨ÜÚè@éP¸ÚÉSßK›>ƇÿÔ“ÿ,Cõ{'êÝOÙÎx³3£u=³™­”Ål³ì$ûkÑg÷Ï9w¿Ó…Y„&k7‹wN޹¥ÂŒÄ-ÍcŽ÷:¡ ô+ôûý¾&½oÏ*õÜo¹Õ àö¸³ÈÝbmeÚ‹XÉx˜Iš,ŽìuUùtç©&ú¯uõ¬ ˳d\~yѧ¿Ît¡3Ú„½“­ŒêøÀ^ŠËp¨ìN•Á½ŠµûÖmß빯娀ñ¬³ðÇ«1lÛÉAulí¢î¥UafÏøX}cñ¼E›±rc÷.¢4ôé×ûn«ZûNß㚇×ï?¿¹« 7ÍX­A›f7õ­V5:.¯›»ú(/OÇW€ŽV1ÒËMÃçCJ¥ÆÞH¼; ×ý¶×·v oq[›‹åŠ‹ÇØn*×z¶ÑÑêù_uÔÊGµï(_­¼J³Í*j€GýÊ¢X™TòtU^W¦‡2}º÷X˜^ÝLŽÛËpsc÷^rUª;Ôr œîÛý"=¶mZYXß»µF•¤_€Nå»9mŒÀbò4B2òFÙ×׃C]\®3Ðã©ífKEÛÍ¿­’/îµÝ¢yH10iœkÞÀú^¡Ê™y¡—©Ç¹Ü¾Tô\Óõä襻šY‘œÇPý]¤³Ã$ûµXжM.UkTÚX–Ç>²t„ f Áe½2 c1¶»FÞÏôõ¡¾Ð£Úh§3âñªUÆÐx? Õú¼¯©Rž.*ñ¦ÖSÔ2~Ü™úAäήÉ]|±»Â}#?jÐ6©akT}(Ú—è—ÇaVÂ꩟6•ií¡ÈÖļ…Ø-ÊœmªŒ9+53x–$ci•4ƒ\w]½ÄtyüÞ‘ªí»×šVµà¶zZ“µ©3Ufò[¥Õ/Šfî¾rw“²éå5i°¿¥ŒhõÅñå|­FŠˆ>«ŠHPY&ˆ×¢ßé¬Ûeü‹UCcs(¼p:©“ƒ5¼¯yJ}ØÃÏÎ4çoR2V›«o{¡—ñ'ª—VRg=„Óª³Œ¨žqDSgÍUåf.TZ÷\¤hF]~Ë®ÜîcéÓ„WÒPQOR¦¼~Ñpaî2tž]Šj±´Ò V¸üj„pZàI_F$·Nd뜘[²ˆ–j–jÞ"µaž_[ç*L‹ÛùÔ«°¹{¬5Nl]¼æÒFyvºccuê5ŽÉ ÄÎP6V ©~ÎS¯YiJ'×™YÝCH“”ƒ ˆznçÍ–+X¾¾—U]½Š áFk7z¸ä%¯Í½,€°ˆ_„@<î4å¾Ï´ÆíÖŠÍUâbŽ=>Ê[ﬔ»õ™xÌ52tgßxÿ Ô[8”k&Ô}•ôzöŒ˜~_b–QrÄ Ê IohNÉÑÙ™Ö0bý-ôÛu¶òä;Î@ú”ÌýõS=YyÄA¶OJ‚Û!&buò£arã£YöÅí…áøfL°'¥Ï° è(3×àb0-ìë2êYÌÓf¶J÷r›zOWjCvXiÒ§Û81«ft;—JxAœ|L_ئwñqòµý]½À=Ƕc§• \yç½sm™¦sØãÖ¹d”‚§&mµ&¿Ž ùòœ±—…ñ›x—áöÕðÍž†Ü“•! 0­ŠÑ“AÒÝAÄÓ¦ÓQ©Of‘Ô0Ô`ûÊQN….“ã“Ø$ýFqHÌ3“5goW|…¹(NV*VÚizà—#´z®<Ñêzê¡Õät…±ùó+õo“2¿âŸaN —^«ym[³Ã$Ò«ËirŒ©ZxÕäŒÀòb®Ý£J¢Qnÿ˜fØÆP3Zùþ »¯û›ú0„r¶0NeÚ<¿­)®¦A+:m悤݋X Ñ/~½Vaü zìæP´n–È3•ð‡þƒ —5é!qd•‰›ð}Lªòš¸ƒ^—ò ²Õ õ²núÞbh¹ ’+˜vúÕ ´çF™Q”2­ÂÕoúë™füX¯UÅ‚ò|y®”ÍUÕä ³Ú~ž#³{Þ¿0úEGåö¿bá^xdŒWù*U5€¨C†®†Ã]Èg`cÚ¡w0³ v Ð$+™±t3p§3Áû~Ø0%}%6¢-€Å‡&ÀZÝÀÚc`•E`U}š"Ífq_D41×(ÛÔšg¢ mÕýcõ¦³7«m²4Óþì0êÛÉÃd Ã_úo¬rÉ'×TÚK/Dõ ôXÊð–A¤f7„¸ŽË rN‚Yà'ˆg7ÑK‚p+kÜÚ—G€+@8ÃuLÖ)’keù~89îC)ÓT;.Zü+| _Ao"«<š¶•NNO‚oØÀøKýƒÊŒ÷F´ªñ†4•É ‹[ò6“×d˜DFà´_ 63óa¥Qç×µ¾S¼þ’€@æÉ³h£ð·}ø'P~é’÷N)ü¸P>ØŽõz|k*ýÉ«øÇ﹓¤•øƒÐo‘c(¸³ ò¦¢˜`í!TÍ øÙ-h8”¾`~2€hdÐT2ÆÉbÙñ¾Z€øð¹•j‚K_w“àð$(²€Ì°Á¯Ò  î» ö;G!ù,‹å+…;@ŽÝö#M%mȾ"q‰0~*|3]m(Q¾GsUèP˜päqävycpï±D!hX€ñzü?ºþsQU¥ëÚF`D‘s%˜Å,f1g1‹¢žÿW2æZsíû}öŸ6G˜b·À’ºF«Ö¿=ëSûBå* ˜úTLÿ¬f%óÈd‹MÊ€ˆ”ò °øþX"  <À’£`Þî0Ÿ ˜[:˜ýú˜yˆÅYLB¸Þ7eb:1…MGËQõ×ñ(i;Sz:]¶™Tr÷U“ÜÉ"dy@ÁC”HÀ¢#°úŽlH€í¨:`·u°×yptäα›_뎇€+!+(ÕàÊ e\ƨ.Ko—øP€ßð1T廀áq$Ùf“}Ró3ý-{ʆ9ÕD-¼å¹tNpè­ÑK’*÷M7ÅûÑVE§UWqê;!aQSœ]7×ô“lÁÏм¨Ÿ,W_[z€Ÿ#}  Õ¸ó¼ó¶€€ç3€Ð.”ãéKzÏ{ ð›u~Ö©|4—ŠÁ–H͉ûê¶ÔtÖ—šËI·_7—a9Kª—¦[uÝBñÝ)ù§¤¿<ù‹nù{Û­Àߌˆ˜à¶Þ<¸Â;Õ¢µ±‰­€xt@®D‡†(‘¹ -ßȈ—õˆÛEˆÓùˆ½bA«Á”:æI¾žãdj8[£æ@0'ó³žÝªq0n—/dÕ*ÿÓÙYùO!1J.´z¿.à߇ÿ‹ßÆØ´D\€¼n~âÝJ¡¬Ayæb7z@Qù(Ì -{-9<²Ó²ŸÙ ƒD?˜_T÷ÓZ‹ÝÙpÂ.Û>¥¬™vlRFÖ ­ñÇŸ KúR븞¿>àØrû÷ï¿÷KLÿ·‰ÚuÈMe  ùƒ,ÐÖ¼eµÚ8­Ã©@˼¿W²–$o@3y¨ïæ ->0‡éÙõ*”.‡AÀ£þfEÓчçkò¢ü;D»Êê·¤ß@‰/EþOzÃ×öûKŽí=÷}–ÿ ÞþµÜÆ ³ï°Åæ $\ê -Î’ ­‘ Ô™ã‘Ç€1„çÏXµ¶ÀX%èoÁ¹nLõÎÂ+™ðáZ]£ôþOeñÅÜö‹³¹íÿ¸mÿf6Ä5üÛýKmc·í7åÖ÷Ñ×m[’gÀ|=®À¢àìf~ÈX¼.ß^iX<Ö–€Þ€%ŠÉÿ7pÕ¤þɈ“>Î?±q 2j»m ;(Ó+”×û7 Á^h|¥7–ò˜s©Äƒ¨§ìß_å{¡| ’ ïzrºÃË5»XC OQõœ8NÔ¼G!ÄÖÕ?y°Sñ q¿Á¦_úÿÙÆá2b£—ñòsBÛÜ.= é¦2¹±¸žÌh}é «í¹OøÁ)rýó~üTë×#Út¢“yï·–†îê¨@o=R“76k¯“§FyeßÁt™Ž^äbžÊ'bV7üLŽï¸õT¼“ÿ¿]¼&Á;ÎøÁãèwÎ[?®§Äà}ijèEŸ,¹s»hÇtP6öÛÄÕØ‰ßÜ\ ®½ËNz œüÊ>KÅåDƒ«“y’¬/ŸiÏ] g\šMƒb>˜œ*Þã_krv˜Ä(ùT&¾o?_qy1¨íŠ7cÿw'ÿ/«ývñºoÅÑ)Æ¡q°éNYuX¼x«¨²ï.?ýh™¾Ü—qàÀ>§G›Ó4È o““Û|Œ¯Õâk60J^MÌ÷ “~;L¿MiG ½¿Êgœ>½öÜ^…ݶ»uñ3ëœZ¹KG 9±}¤z¿á_ÿjsânüü·U¼‰ÿ†›Wã>^¿¹«_:9(v¬ïï’y3Å·oËÄðÓ}f†“$[“KµO/Ž^Û,;Ý:·tNõåøoþêÞÖï›]«ŸÜŸ¼È?Þ= ¶qîêߘÕÊi$U*^Š,—¯Ó³{¹Z³U@·wÞmU’ãkiCÅ«?x¯Bø§Ž'HÇvGáo-x†­äC^4ÈRžoµÄæ$]U›è¼‘läɆóë_¥÷¥|­"–JÕÀ+ÕªuµÕªœºÝ~EyŽ&厽۔Â)¸–úÙþõŽ.šë0N©øÙë›â¤^S ìí_AÏ=³0oÕ’¢Ét¹»aŒ’'Ym[¿t—ÍÕWEºSÛVh¿Ô?“j]¦—Uñ¢ï+§vñúƒW”°ü,ë¯ô»ÔOeÐ’‰T(×Ϲ¬ëžTœ¸uµ˜aúÉÂ¼ê§ -y^ž«fÔÈÓ7q˜ÛvÛÛ÷|FÙºS²"¸™Ó¼~ËJ˜Ãÿ•<ÞÆGJŒúb‹sbZ[§7W8-¾RÖŸƒ‚ëg…âgaUŠ*Q/Ìˉ”ưçF³üª>]çéóh—Û¶Ö§w_ß²AïðÌÖ“Oð}ÿ‰g¼Ŧ¯3ANwrY3®º…Tß}µhï,¿v}8ŽÔ‘ìÏeÓ·?×ÝÊãã8*b™Ö1‹ïÓ³Ó%®[¥^êÛ…\1CŽ-xÏ¿ªqznÛ<©Ù $³âckgNƒS&ãYQ1£|èZú:QZiSz©paR&•œ:Ñ&·rürmï8\ïlOêÃÐÎH°Ñ J[y]Q,"ìeL×|ÂÏJ¸<¬.’Û %+YÌLVré”ÍùW¶³Ë%†ÊíÎÛ×vJrP2çJíÓÙƒêµ`i‘©~nÀ8Ñj$:¾;UìÏn¤Û“ê¾;#ܲ6z®5÷ȪEÜȦ¹êòÓMC“~ægÉíÐÛ$¹Oÿ˜&Á#QÏ2¸qZf$Ã+ýºK¶õNõ¸Ñ;µ ©Ò«®ò|¥wÁ:–¼ï^šïfV*¬Ë·<‹CÖÍÊŽb¥žTEuRÉgg™i-ø;bå {Ü".KÒtõcÒáFHnûw-Y1ßÉD0z;‰zšÊ%$TpÓái¨Lí«’±7¶’qZ(kN®çQç|FŸu6q™»cBõr5äžO'§¤=¢Eu ÌžÜSG,þãjâ0O}‡|KH%5Nw7Ô$Áè×HõÎÊÖ´c'µþ­–ÖÌn¯ ¾˪:ŸmÕY #eÊ$–Jf×:É‹ÄȤŸ¥µ³ËþàR Ét%¦âÎ"qWx×Ūb Ä]³0l žõ[Už”'…eeWÏ´ÝZÆIë®nÑ`*$…÷›4.ý¢ßKH¤wo›Pïîîo½[ í±Ü¢šEoáJ\}]#^M‘¬‚'a(Ó¥î(Ùb.¯àt­,*í¦LÇ¡T¼k'ŽbuÒz GìÎ e5Ã_èG›oíú{î.ö%în6«¼Ö÷ùÖƒÄqÊ?x9I¡~ž“«Õt²šLÛDÞÔ“µe–3ÚÁ’Г)á£Ù‰ÍS‹75õI^ÕÔ6÷T‘Lî­Ì° -ªà… -)/©>/Så¾,mô )•Â0%± Ñk”^ÿÁEaSî M®7å/õýž×.Ô“ëé5–K<ð;´Z-æ=a¶ðÀ˜q«0ïY¹ÍÚðԠ΃Àý°Á0{ÒçÓ$R±í7¡lVïKCBýÔx7®¢ÅáS! ñ*/çáI.JÁY¦ÎÑCڴѷĆk&FŠ"©òÂqQ…f©nò—½—æÛ½m‰×“ëDŸÜšµkË;ó‘tšI·f= åÆNæzE-ß7žZ~‚2M¨´Gç<¥UÛß ûâ\ìb7µ=oŸœd0½©†Áã¬æhE\™7÷oyU)>¤m½JÜhuÄÝ‹b­sNDÿ!(žÿþÁù«ºÆøx±\˜}‰\^¹\reØläyEÖ¾ùuf’úL:â–ôÜ­]ibw%(WL'È­w¬’¶º ‚z‰"¹C½HVøÐ#·™È«*>µ-¸j¾ÿƒ§ûþ³l­Â•“8ã;U7ëOVÍÔtL^]goi{°b½\„ÓÌ; -JdìùŽ£¹pjܸ~5±Ñ1óaýfƒ`{Ê1ŸÏAa&S`2˜Àgéù1_…#F*^‡r;ÛÅ$ÄYYúq¨FÔ§ã -!åRsÜ['üLå‹„dm=¢>€<úç½ÍóÓMù·MÉÜIÛ¸ÙEsÖFÉŸ8Lf*÷xx?&xûù…ïœòG®¿;öe<ö¬3fJc!“Ù2/«h(½0J MJµ¶:å¾)rW.YåÍqhÔÇDC„tu¼e\U\}ÖJØÍg'?8ÖMÖÕÉ vkÏ[¸ºYµJÑc¶ÎIMÐu¦BÏ5ùuÝŽÁ·:MÍyí˜Ä/Ê/á\FCþV›_¹jYçqÞ endstream endobj 47 0 obj <>stream -1Yj°¡íÅ‘¦ÐÝ*åáN±äáMî.8AVÛ2G -zJ!aË"ós‘ ¼‡·6™®•W{ø~WÞX¢1‘Ч’, Ve3DFÇFˆ¼ÅµZy·‰æVœÌ’¹•m{Ѭ¹p¶”Ù¼­[FIQ½&-í&WTlÊoAC¢{¬¼ ûÚ©G&[8ìiJ¯n(¶^Ú“ÕWëBKÝh²ƒˆ÷”„ÁÛ.âm³dâÚk”Ãz©C ŽXåè0ﮑ÷z"iÎ`Á¬¾M|“ì|«]¾+™ ‡50K™^‘œ‹‹ô#´­RE)$´œji©FBV¨d“‘#@…ó;÷滽kÈYNåòåÉs¤—Ô! ÊhkG -¯ÔŽP´ì oÏ -7\ÏUŸXH´>Xo5#°¤{áÑáÑP»§9È8*¸ê Z 7½M‘Ëœ‘Ç((AV»»Ï Ô©Íñ ¸¸%c*Õ²<³z  úÓæ/øf½4-6&hmûÍ=“×›Ú¹W’'ª¬OR” -°Â’µÇ;HጙDöŽ:ŒüM ˜TÙ'‚EæM›Ä5Çâ³Iy(]9ƒâ_‰fX@<ˆ,”‹ˆã.Ä™ÉCÙ±Þ³—Dêà3ÁKʯ¨ ½·7óÿß_³·pi¬0¾§ÛöíoL.‘¿Q›+8“µ`q$”u)À;©Â3s…+ -TùØóÈÆóÈ¡ ”)(K >nB)¡œ¿Yä(³€Þ9îs€œ”᯺Ú= d½^†ÇQà·e–IÒ™‰µY>›Fˆ¬òÊïÌ{‹Ç]’tj‚qþòaf·ù“¦›Î¬sË—'K‹3n”Z'ÌœeÈÔÕ.€|<¯€ÂÙ ë ¨DTvϪ&hPª)(‡2 êFÊj ¨†ýp5"ª¸)Ê®•€Ò£/!¡Œå/[N¥`dŠYCr~^Ív–ÌÕÞÊ/øNŽç›>ó€vóYï¨!¡À7^·;úº®Hַ΀ê²@MS°ÂÛìñ/OfÞ4 S Ð¥nÊ- è²Qƒ2÷]QöP6@gs - sü…Éì-c  éZÀºÀƒ%.öªu%®‹vCûœúeÕØÙRP§48[~Á7;é{½F”<'Ÿ˜ycO™ÐÔýðK¯t´ZºÃ×Ñ}Ú¿À×8¬ð•bC6¾áµ˜ãfPqáW+0Mx:™æìK/‰ƽ€)-à¯Òý#`Œ| ˆOCÜ^P^‹Ïu}£)6´Œ×/*Œ¼sDÕ½k܈V†­ÇˆÃ̈Ëí ˆ+“¢8_l«è1Mm¨¿) - „ˆ€p¦á!z¢ç,æê²ßìŠWºRå2H7O°C$¸´…!-Ôü3DqI±5y_+þI•ذÍ8 økŽámœãð×|'ñÆv×›¦ˆ+×™þvM«%Bã _»c¯¸ðOðnžë¹ =€L/ào·‚ë—ô±:—%ÄîñÎ"ß w§Q™è©"†ÖÇùßÊ~G'æ¶±!9.$®!F¤½¿#ôøÿ Û`ÿMÏþ§±åöº€æ$L ù;h-³´Ò5ZÚ'fÔ²@ÃKs žª$PG~ ¨í{¨ì2©¥ð$Š#f3öº¤-ljZ¿–éxr<:1Ðþ›ÞW³ã8*áo×´ÿd6|ãvÿæÝÆÔ6vÛŽ½ò£­ gÈ\'|ݶ,eã~ö€1.Àh0Š—0X™t¹°ãòÌpI©áu')ƒ:ÿý9a± 8NoˆKú›‘ð×m[ë~ÿúöxû?y qÔmœ’w'‹¶q¬íz°¥¾€ùÞ‰ÀüôKÀ¼•—Àœå`Ö ðSÜÊoiwÄÿkÿ~l´ýŸl„8#¶Ý€rþ“hÛ:BÙ…q<Âó“bÏôúI=æì}µ§OOí¾Õë$¸®¼‰¾P¹*S­}‹ÖäU§ãD‘¹Cîñªí·éE3Æ¡å|§[O}¡ñVùÿé?ïA‡r‘œ¯×Wâà]øZì]x¢.Z ‹‡“wçÚóîõdv†—kª2>‡¹Ììä3üâ8Àê0—oÛ N÷{nÔ8nO¹ÂmsuKïuXmS«¨9S–ã˜^ÌíTw¾Êf>1«ýÁcZƒÚ_cè7Ý´}ÖCaÃâþ|‰÷ ÇLôª ÞÁE¿¯'ŸœÝOö:'8e±AÞzoO’Ýèë¿NîòÊ> ´oâÁ­‘XäžYs¾²³©9=Éæf¬PšÖ©l}r*W»ãk½;…­ÅÞzçð3Jñà šo ò”x‰£`ãºâ®S1 ¢1«c¾ýÅÆµÅoÚ€ž¥5œ;3K´ä¹ûé9°­ÏiÿÓ›Uz29OóñµlFacŸ´Ý??ýúe˜~Uîƒ<â<ú«œóîÓ«,Ö«0ºTÒÞS³£\ÏÅv'!uZá°¿n™ÀÁõ›ŸÉiÔÌ`§Yc^7 bó9Ö]^|Ö¶H_h6¨:’ -·Éï&þé.Þµÿ+ÿî䟕?)ŽBh%ÃÄ,fµåk÷àºúNÑuÛ̯j¢‘Ûzœ«¨ -|–wK²A7W2leÅç°—9 —£Œò™MÓÌl™6°Í6..Ç”I]®N´EàäQQ0ûs°Y{Ò6z¹ä¬y'Û±òÆkgÏf®ükÕ\ñóÿ‘ï%¾ã“`Lk y6Èq×¾”9 $.}éT8[°)“XKN´Ú©ŽïžŽ_"SŽÃJy{R“JvF4j6zr¼¯CUuºqs}sÕõf¦›˜l’Ûá꘬ØÑ#ÉOÔ³u)!៬qZµ»†W’CåXÞPyo`¨º5¼*·‹%Fóñy.wºrÔšÖž•29õ@Ö´ã¨bgøéËš7Z/‹¸ôPsÕ¦«éHèQIVÌg"ɽ;ŒÙlBB©‚qšËÃË[MC%K=ýºLtƒ]¬µ0—¯CµžEÔè¼Tž6ÕÑáòyÌîÊ'òP^cÕ1ÉI,íëìj|Þ~L3…b¶áÐÉ”¥3¢Ý,Ò,QƒO² -D-/‰º3¼§iïñƒ^vø2Txã­_—+JÄê -Z¸CU­_¥jt¤mÕo*YÕQÒ%åsm6•Ig9P2 d)χ‰³œ·—˜L‚„.­¦·ªäæúK‰!O¢ÄP{¸Þ—iKn‘íx _]—»­N;_j=séîaeØYÊ™lá‰'ä¼öÒï`y×Bî¬=†ÊIë_ѳfê׋ÁUõëׇêHÑ[ù\HLÉè© !Ïýàò¼¯JrÞÌ2ñª;Òj4)H ÔÅíœè‹•|q%ëÍ]¨—mF¸{Š?º]Þkz!ïys›÷Zb%–…à wt"Ùcr˜qƺ£š›l–ù5MDï1÷§f-‹W55AÊ4\í•lÞ”,韬åå[¿Êd.—Ö¢÷’ÜÓ•oM‰;ç!ˆUS…C®P„™¤S¿Æ·ÔmWïôâçºÉò•3¢Å>F=›5C¼Éšpd^MLe^ù˼–A¹&ØíIï¬kí¦;q>CÌ“õûZû¨ïc/Tfú,/óó@¦FîV*Á‚¤ÒLÝ}©uÍ8H,aœÅª+]EAužB£ë|9QÂù >`ùÖr-óZñ‘äº;,Ã%ªf…<½.k ¯ f ¯ÉÎ):»R,wµDZ{ª0HTá`æpj‰¹n¥é£ü¦Q­¤žOÔ¶hõ %”ô…Ô†{ä­¢tñ.ëדT.Õ÷b-Ǭ…ãð°šôt!4>» wv{þb޼6Ø]¸{D\f.±ÕÈœYµ-[#‡I½o.=Ë0-_ä§T‘ZÈMEÅÉ22‰}3× jª5-+û6–‡BʉW4ÌÕÊrÉùÈ'Ëäª#ÑÐçR÷7¼ÆV¼r“ÊëùQkæNPŠ·ßž æ¼nTgÜ}‘šqI*»fŸ›ÂŽ–«GÖæº7fÜELÚÜ£ òzÒt.ˆ4ÁÁé´X¯æ(ZÚ5Èr›ûÐ…w\‰¢„hù ¼9k¸’ynp%Ç -x³wÈA9çÝ÷}ÒÏ*µ·kÏußLŠÇ–¨'_=B™—/¸JánbíeÅly}²Xr}¬1g#K›°vë=úÁ™qˆÌáò›ZÓs’ÝÓ¹µ|¢‰’u§VZãCѽ6Nn“ž,GoJZ†¨³­!î>îIæW΀]Û®†éaTFÃÆx†Í#ƒ†^#…é«<ûÅYTéeôÕ¾h­ê•dB¥³Â/ø&>ã—ÄSèMðrî×? gbƒ%ë[©3I#cí}:•wYQ ¸lÉíôy +9üJò„ Wާ"G¬îõXBRîuß®æ²ìF,ú˜QÂÖèƒí¿P³ÎHˆ¿îÀéc¸pnRˆ“•-ÄŸ¶ò…âMê¤MfV4·k3a™W'û4.3F-ë£èö%ðQæÀõ'-ëZKf2Öf †"xq}rj>ÉW{sâXni8ÞA¹œqïsñ£#¸º+ÒØ­:”1ã¸1чGQó–j!£Äh†ŸùvÿÉ‚ °Y&°>7Ø L¬‰[P¬\œÍ’x1Ÿ<°RBï?X^Yˆ:.UÅHðîÛ+oxæ}ç- äê’ÁtaF“¹û„b¼ñˆ8TËðìÒ³6Ã[ÍÞ»]›[¬Ûé°„±¼¡Ç1BCCÞ´Â!©JZC~œÙƽp%;¸Š]¡x€³À¹ò àØŒ8^ÊìÙu³§÷ºõƒ;XÑÎ'Î9`h# ácð- -éèófÔ;âo;fJ5Vt>æT‰¸OIþÖ²Pã­e{u?Í5:äsäÝHï‘ÔÅ>‚™ž¾Úû®+ñy@|!*P¾ƒ³ÄëPS€¯Ì;ÀפðÑ©ðñfð..A9Ào«Z¦‹¶4%ÇAY€dä<”i¬±$rЉNà+x4añÏê§›ˆõ¤“e'Û}×ÿ¤gkc¾«Ë›ä‹Á7ו78ÍïÌ çO4ÕžîÉšdmˆf‰]ãzö¸Ä’Ë%ÅO{æ{ H9½¤Õ ÙØ\9x†€5ÊG€j_+€ªì·€Ê÷ÉV®ê¶g¦•â8¥K5¯Í íÆ‰àì¼øaˆlçAqøZ½üú—x£#Q; Øõ¾‡%-wðPϬ†:ê¦]bžÜxÚÙ@»oÐ#ô««†¼Y€¡Ô¯-Þ¬Ñb¥wôM¡ ¼Ó€Þ߯€žßàc‡É* ½ê¥&ô‹Rù“ž=þº %Ñ‹|r³%cúJUš^Dx&ÄëóMÕZ׿EýÊÞÖu:¿a!øÃoóÖ×Ä;â¾4Àòª ØJ?åZý2ت=€²Ù6gE€M^Àš£"`åÎ0Ÿ:|ômYƒ‡àáaû)\q\U¼·L¡ÓmKì$\ÑÔ®FbD«ã}°gÁ|£ÈýÅœû;bqQ…üáÉAƒ,·`A õÿKp pöÄœOk€O^ú€—ä”Ñð”ðÍqX€»ºuÀíì=àfiáKzëpÖ‹œ’×~³©ó;· žó)“5k -]ZÏ9ââógÉëY Äfú_žWó-ä?èöëæñO -Â};²ýܘ™Æx²Œ|ss«>åÇ -ˆÅ̈pv¢jH@d‚îµ!ë„•B3¿u")NoÐSéZK®Í"ÿ,¥¦˜)ë?8©ì{1®ˆ—ÛÿOÎÍ?õüp(Á–?Þ߯ï6†¥ß|‚ÿ Û¸uÚ­ó¥¶ÜÈçÕ÷/#ò…¼“¾<Z@®ì¶@Nt ^HçÚ -H㣠-¤*æ«ÔY‡wI©ôMPÎg¹iÅ1ÏõìÆ)¦¶qI1¥Rqw¹ØüŸ*þ nø×wûëtý›w»m¿)·šŠãº µ@h©ú h÷jª@][@-î_Òëß ÈjÔüÇ4óä/Ø–Ìô?nÛÿ¥¶qfCŒnãBþôJûv3ÓoJ¯ÉõËIðv$šÃH8Ô $Ô=\5ß«`ôÍ90 - :éý6º –—ôŽËwˆ~uç¡…LÉûgÄ~ýÉñ8Å£ó˜ßzâRþfÛÆŽßÿ±ÜÆÔöë¶MÈóòo¶í¯Ñö/°}µÖßă‚ñ–5¥¥dRÀܘ»wÌ>b3ÿ‚ÿ]þüï&þç'³z??ð8òû¯u5Έ[-;±ÑJv»mçÈG×焽žhiŒ<ˆZ‹§6}ߪ=áÎu‡êMÍ«2g/úbS='7ÏþñÓ6‡\ØC÷Ô*¬Ãf9®þÚB¿¨13ýöz<6÷ïÇ[äã=è±Ào à™†óYdOûús‚¯RÜvR¸oåJù&öÕÚåš"½s˜}uN>½îÓ‡Öà»”Fû­IvupŸþàÿDÅîwë>¼¬|ùù^¦(½È½“Æœžá*f³¾ÿÖøÍƒË‹»Nżñ7Î4nàõÅ´We°_Ç04–sr®Ì¾žÞ_õ1ؽr§òg'ŽçïuX2>+_¤Ðeúz'¹Çžžß3n¶MÅ7«Ó:9NL<¶g¯µqnzãò(y»t|;Âgà °Nƒ<>ÿܒ¸½mÍbZƒÚßýû1ýƙƴ6ÞÄ›.ÿ¦ ÄLtVþŒi¹9“Sa—™œŠLeâÑfk6°®µ^ðy8IîÆƒ¹3õWÙÖ¢O/[›^…nì»A¥qꊇֽãÉÝoˬÞÖËðO·¬B¹ù™ü&º©„yåZŒ‹‹ m)\¶òn´Õ21ýŽ˜»òƒk¿åÅ8Ô·¯÷OL{brNØ9U'o(7¼ã‰$×Üê/Ý‹†Àj~Æ·T3ƒž³yþ\läÉG¥¾*=uzÿ€ïÊZEÃw–æÕº– *§^çQ¾úªl ¦YêçNM7ÚPï_L›‘fÖïþý™h…° z¼îG‡ü²Ú†fÀp[Wá³lËQ¯ÆÐq5h3(ö~;û†¯öŠ6_eýå‚R?•Ç~;Qù¹ -Wü¬*_ÿjG/¢»¾U˜W·¹q «yWûšP%‘(ÇEcù»³>Õhf% `±Wõwÿ¾ÁNÉT¸ïq§±xã ˜áÅY«ñ&~7šÈ™â$¿wŠèú’/ÌKÏR!Ï!5(Z»@ÙaÞ•²“<·me¹ŠVÝfƒ^)ÈÖ“sæä7îϼÒ×éMw²G&.ßJªïZ)'Úu«Ž_ã§Ž#®îö¤åJvFÎã]ûÖ|ÐxZy«ðˆ%6×Â÷þ©Êìℸ¼Ø·š§á×°ZÿàÐ1&¤;i^M¨š€’w ôó©p>/§LrRsüÒij?{¿cOjðî9#=…K+¯F»Ü"îÜÅ\õro“Ž:TrëGz²’*»IÅG‰`¾ õ"™MÔÝô.!1éà¯ÄöÚ?$VG¬œÌÖü¯aÕñ]±'•Ýû·ÑÃèmåå1 -åHYÄåÂZÄ—ÌUGÐM×ÐÌäv ;ÉŠeå’Ü;ë&‚qµ–Ъgœæƒ¾áåwSC¥ÀV«np]L ˜ªõ~I3•4œaÔèfcjt?·Õ(4g±h¦žŸÇWc|¦ãQÌŠ“O™D -ƒuu#ÓÕñ[²’\žß<&$P»Ä$Ôð²½Ê -@ C%Pö×;Å  ¡háŽ3´>¼ W££žQýfÎU¥ÝP>×M_™tñ…‚>­“<nP9ïd ™D°º´švÒj‘³¤ÕRëÊ$žêÉùl©e)|.\xí1‘°ÎZo4µ†w+ŸôÎâ³Ó‹¿ÒL®²R£ÀÛ©~½„2¿¨Ž´ºC _ʤuJF{á -b´<ïó‚œ7 U&^ySrS•Ì—Á"“²¸E±’·ç"O./B°IÓB½üN ¿ü© Iz J®&Ôëµz­ÒŽ:ÅÌCËgZ™­nOý. šº¹kƒ¦xTSÜd«dI®+Ì}Måùî<—ó‰õB&ž»µ´î¶PÉuÀEbŠÛ)ÿy\@„`™ …z±À ÝÕøÓöâ½*SæUÁíræjɪýdÃ[$±ýÞ7C•í÷;¶?d (^ž Ÿ~¡Ò’&í|©tÉÆ&e .è„\·ÞÚàY¾ªˆ~ -äÂÈYK¥îc.‰Xµ±ò©ÎD¾’ ÁC› õ†°$YÛüÀêÅ -x¯lx•+ܹkÐxqúã óɳá[ÐÙþ¸”a^è¨Êø ÒgåéÉ„!èLfkÓ™ìrHgòIcäÔ‚_íìÒ g4sB®—vRsRúú“­7ód5uÏráåí$¶‡-E¡9š -r'5â[jÈ·˜o5䯾ñwk½ç\—}.ÙGí¾ar`Í—pe^#+bF©2ʤ!ÀýSûÁé¬Ä¦hì\­Rm7¤ÈÐ9ëú#K²g’ÌVi»l*"v9V…b%ÝTóUÏ´bÛ¸¿M}†iÃìò¡ä¸ÏI*k™­pl®çü¥Sšp÷?+¹ÄùØgŸ©å€ÜÇCv°_ŽY+7ž0ob6gRîxÅ ê~OOo÷3íáO굈Š4E¹C…Ü0ÏÉÖä -±CBh‘ü¨Mq¼:&.×#ïÇå¦ð€r—pÙKXE¼2­¦{|dZÔmÀízUÇÃv(Óäü ÖjÒ†¿l»3î~HŒÙçú2`m¤×eƹZAüd—A<ÎojÚ§³;{LãÝo¯ªð,®)jØ>›âæN–éç‡ØW ’¨ i™Žco*÷2.߬>véîvX;Y@лÿÐÑ^ʯ£ItsC“X“Gc"¿±R¥”-¯’&'”8½·N#q²qî.•GþAh®Àš×3QÔˆù ³>“N;tÎÛ4~#ÚTÑÛõˆzäfð’eû :'je ¥|&D~áÍÆÃ›à±Ë' WâX{2+ !Ž´Ñd±°D"{!vu €aVÀx;t¥ÁxÜÔs5/:i8Ï'Åb‚Ý1ök÷JÀ_íÇšKRSÖ##&Ýö霧·¿œÿF·)ÚˆÚdy°mµêµM0 Äöu„7ÃÓo>ˆ5® ”vµÒ7¬Cù Ë[M‘‚ø²’Bì˰&>éÇâÐfJ¨'•Ú*ÙZÖy(½DVI vnž7bÛw¼£0ß©–¸°³^±¾œ˜2öâÓs±Ý§VŒ+¬¢°Âà VhmÛ„¸™wp/½`×ùÜÇ:ùÕ×ÓkÁí·ÂjêÄñûâqÌ<&£¾06´Æ•«ã©)À°Ý`øHhT©ôµu_Ú -”‹'³XÅ—ªúfTôxÿÄØûT 8“Ÿ®˜ÏFžþânÂ/ö(¸Àê’,jAãk|¯Ú¸×Þö°º¢É÷‘Wª>BFHg†€\g2§I0ïtXKE ôy€µéo¯g¬=¬¬“˜¬K¾V=ë«m[+ào€Y÷$Àl!“ŠºåœÉ#@ÓMsO+pÿ‰Á7ßñ€£UL¼éyï8¢V^¹OnáM‹këË“åÖ¸ ºØ Þ/ ú|ˆŒR•!˜Â;€EæJ} p"8œ¤C( ÊI8e;P. €ÓÀçp„O¥{{”á‹8Z_/vlŒÍebwºæŒftìøÎÚõÆ=T)`FwÅdÊòŒÎçö#ŠQË’Op]BjDðL¿ƒ6vk]tP÷{ÈhÝ<©ö¼;¼çÏ¿Ü Ú†Lv‰c3ú—'§ðU̼Ѩ8\ ÞžÁq¢¿¶gV¶(¯`Q½  Œýþ‚+ÃoÕÐÌt -¡Ý‚Ó#Ý%ÝÆ -€.»-(ÑЕ^Ì-³Iøû¤\´$þÉšxnãcíÃ0ŽBs”"§ -°iÆiZa.ÄIj|]ÞÙ}ø,xøEšë?%u‘#ø¦cÄ<9{ûǼˆþ!¸8˜|‰L-æA& o´‹mk€Å•!`ž³=`NI˜ÕSÌèÐLy{ýÓ{Nìžb+°:7¾©€¿éÙÍ©ÍrÃÐEãŒRâ1¸~í®è¨V:*s â¢þ”ô—'ÿE·¿VàÍ·å‡ð1õ¾Y\1ÊîT¬C™õ·¦–€Á;ln,3€+ïR€ËøCÀ ½`£q°'lû§óÇ+%s¬óá1ÉÇù!±ï.ÌšFnϘsGlµ{ý)êkþ —øO)ÿÂÛ/,ýpHr™ÿ&'¤Þ_jÛ8-}»¦en@¸®q L Õ˜ µÙ -R5*~û á!àú…Ì9Qá¶ŠHU÷y¿ooš¾T±?Ü=FÜqIqTñ_ð_Ë-;û¦ÿÂÛ¿ù»1¼ýšðÍKÁ|äVîäõ–r›S\žÌ'| 7 -oÜSH:òm4¤Ãä¸w ç….7•Ðáò -ªðKºÇé_nû;0ßô†_ppûo ÿ±ýÆ–Û¿Ôöbúe°1.ýÌ8 yÊÚZ -~Þƒ üV>Þ·ùyÌ©÷Èmy-\ñ&œÇî\›Ì^›Ñ¹x³ÝÒɧ Õ#Ú jAñ¾¬í§Xßž²¶·¹•Î:¬ˆÃuò ÏVö%¹]¦ÃÜuAŒ*`¶ÍMÄiP “SÝ:Â^ ïUÄÉ).îÏþý:} [1½s^épî£ÉY¼‰?Æ¡qÚÀa.8½ xå‡;q4úÊm¾«½ÃF_ÝN+ûغ,æ†uŸ¯,ù9Û¦ÙÏ4È}©¸ú?øDÙ]éqG¸ò£°y•GÉk”ðíÇ'=œØJe0ÏäzýUa¼îm+xÔ ïTW¼ñ›xìâ˜Õxÿ~ŒCK&Êž|"ǘ66]Æm²~ð8m f¢ã;£Æúþ ŸêtÔF}Iv T‹ÃOO+æ^ë»Ø¥Ù«P›v7(oú]1XúOZNÛ×ÖrÙÖïËí?÷Gó3ý Í ‘¿#æŽÓ bÏ~ÓÖXž™‰im j÷ï÷¤‘w“„Û~ã Øtƒ¼¸GVÌòbkc«¯w·­d¸8A¹Ý½h€½ãnYÑœ¤¦1ÏSBƒXSJÝeq½¶­áf;!Nµ®"ÙÊ©‹»åIÕÊ[î”ÂIj\2±îÎõ Èuè–TœTy·ˆžÊ÷8W &´1 ýÁ3*˜˜‰Æ´6CˆËk…íêâ?ÖÕïNþŠr{•˸T›PÁ¯ÜüÊ›—LPY»~¶²/~–•S1CWn¿¨ò¼÷ùî¬ÿö¢råÛ¶§BŽ wz6`鬸6*/ÝdTL<¦¯‹•ÁæßNtJ ñ&þ˜‰Æ™¦q´îÿnâÿf­þà1È+{ÝÈm›’«('ø,«• ºDŠ^„R®eÅgÙËxv¹›¾NŠƒt'Óœ¤ÂEmžêz'ÚôÇ/onŽÃ=ßö¤AvFÎÉÖ¼=K½£†Ö²ˆÇmg®†kÚtS¶o2ÈûïßÿgÛkã㱋‹ƒ%ÍåŒòz1©~®„;ÑJC‡Áû³H{RMpP -Ò05ìŒ02mô¸N[ye‘³ˆëÎ5]cW5éÇÍKÂÛÔ^²bóãD0Q—‰z¦r4N ÿixE’1Tºcé×ÞÒ;Õw ’•Œ÷ïk᥿Ôúíú⿎¯Áx‹“üÆŽQr\Þ/ ]µ…{rÛ;^’\4<%|…r‰uçþIÔS(šF6›7N³œl¨DZÓ¯«bRï¸ÍÔ÷Yv֯͜ŽËš)\Zªß|UGM¬”ÏmtS&=R2É£ /¯§ ïa$ϧrJk \ Œ†y¶ØÏt<Š1«µòrö×;TénP“•Ö/WšÉ¹K(ý“#”í×z½«~=ŠTGÆ€2iƒ¯1”¤4”xyÞ79oºIi5ª§%7µ*K ŠvÄí<7+ùãõyªÆŠ<*ÁNš Áþ”‚ ×ˆEä¹Y3Ê|§¯ëccØâä“ÇkŒiÕš_(ï0’çÝÆ@Î' -}™xVGP3(ß* ^Y(ç½äÚ“Äp+™×Cäqò#KêÅ+HtCáOÛ…Å{UPäU!Óæ®Ç`Éu¼bħr†Ž7ÙðQEbúɉU‘ëtO_§ý7˜#W~tgD´(“—^†zk_ÔQz¾U°3—ÜgÃ+W¶'òéK[æã–PÏϺ‚D.PŽ#(Ï)Z¿¼WFÖ¼ÊQ{îÈg®S·Âœ3¤Ò‡ ÏC’í·.kê‚ÅDaÕeüþ­Ç8VeO>AO&QšžÌØ%=™wU(7‡H¹ÀÐr™﫱I9Ѩˑ6ê''ók©T=ÃO1á°M ùóüÒån£šÇuµœÇ=JNl±aD·ÙðÄuÙ¾ÇõYS}&ºéSÆïÚKÆIwôçٻГá&¢3ö§1©ùthSù^¥H¢;ùÁÉÕÚ|« £nõH·ÚÆH·&èäjŸ0 -ù×=“NÈ®b®nhîý¡¦ÚÑAÚ ÌRŠÝ ß:à.Qj·ÙÁ>í1o‚n0#Ÿð˜zŒsýT¶-d‚=ÅN}+l‡P>3jA+*¿ÕY)ÞÉ5ïÒ=„,ɃØÑÃ"QÙ ‚Ï.ŽŸl0xp6òxp™]ðàÚ ^YÊyùÎcΛã¥du³DµgvxWðúd/îÉX#®ûÚ÷Xk–k1©Ñ §Ÿ Fã™yZ<üµ8øMª`Õ¿ÒiQä»ß%×c@–Òó1É¢Á‚ØÍ¿K"v ýJT µ!P[?l9oTÚE\¨v^ŽŽXËÍÒ˜†`L õ ¦8S91{ˆŠŽ=v|¢‰¶õ=,ßdj]ßÅ<9FÉìPvሥ"Å£³× F- ¿JQJ•ÜÈ•:YÚdê$[v$›všÄuZDužéBÞBñgø‘ ¶xãB\p¹m¿±‹î3X+Œtôn%óh÷½é"ÏŒµÿÁ‘!Žˆ5ÙàíôG`Œô…¨iíÓZªÎhKÖGE¶ -¼ÐïÆ†¿LS.I¾‡Ì8—kÓ3—hRËʪFn -n…,SnØ—ÍQC¥!Ì™1¡ÏÔñFôªãMRjãòÚèc—–;2ùÿh{ÓE‘­mû<Dœœç Q&ET‘ÉY4ôÏwô_hÕ~«wïª~2;«úÇ­ ÙWÜ‹˜V.C;R¸Z{ô:{è¬ZÅÂ7U$Ë潅<Œ®ˆ´bW ;K kƒž -K$<“á¿Ê(ÑšE¼JÕ᯿XÍ©G®ÛNŸ¿%¾‹¥žZŒódø^—˜çãñ@æbL¶ÏE·R2«B4³#!‰ðXöøÈY „ˆ4ˆŒ#ÅTlŠ^·øñÕUøvï»/ÇÍCžÈñ‰,Wù4‚FÅ*‚ÆÊ ‚>œ~ªM$|Ù™HøêÅ‘°_Ì5k¹TáéL¢°Ðo KË÷SæM:éÙº¦%Ÿ)gžèö‰qœfCØ+™žÀÅRh&ßjQ­±ã±\pI®Aé;HDÜy¯ì·³F ©ØF"ÌN¸ìt‡D:\ -‰tY‰TÏÉ^«H$nVÝ|{W§g­ {’–ÙÔ˜¸– ¢³yœ¾¤_YùÝeeý(±ìCw$™Tc,*‹Vô5ƒh·9‹Dà‰˜§1¹'e‹VU(¬ÅÚ"™ö¡ˆ”gÁâLÁñ)”ËÁM‰\-þÕ–}öÿ?5ØŒE‡ª˜ž-4š|”tƒîí]l1-=JóxbCL¢»¡5ŠyˆåÓm>2‘ó*Ÿ"l¸±±HS¡7›G° _ùänþ•Á—vu×P|Á|¢øü -ì XÐ*B9ÓvÌì˜?#˜gl™lÂ=d–K—Gæ#Ý -ö>è/)íW}rc®ÚÉH:®Ç-k¶ˆ±1’µÝMŒLQa„VA^#—€ƒd6 ÿgȸÂá/¸¶§H4%+Htp_#ÑeÎE¢}€²¸Bñ$êVR¯|²kVè¦Ì Q}¯"Ñ• ïËb‰öø ­oÃH´PúV³\¬â]ô9I‰àÛ'¯´wbÂ\²?S¢Â-=ÃÆÙû4RNkRøA²p®DV)r„`[RD¢á†„D›â ‰Žµ9=y*‹§ $Vë:Hln‘Ø!ì#±[áeô* >d‘ر܄²"1» x‰Þ˜ä—û˜#±J}U“»‹=—yÝ~dHnâG2…bâ–®%Ç2·]8/Åè^}Ïy—5t^R¤IQ°¹Yþ¯¤2±‚H]ó{ž4²t_ùäöí€Ä§…+¿LŸH"‹!‰B6ƒ$R\Ê¥‡$Ðö‰ûð±ˆïáì7ù:W†K$>P£H¼ÔR߇Läz×Cæ[-õt‡ÞÞ$ÖgÎÈïboL*/W‘JÑí5ª~7ëTþV©ü‚úïŠàRóþ«øöòD’d-þýÞ5R†Òè ɹ&¼No¥—H’^d£(•‚·P|$öù’XÜVïoœË2Ý-Ad?ú®¦N®:m/žFÕ]47(¸‘Êíl‡#8½þ–ç~ùôéòÉï<黸]äú³“žBIÓÏ’+<’î/æHzr‘tazGÒh–DR>`ÔÚß )ÚÏ ©ÂðõWž:O»è–$råt"õ$ÓÏDbAùQQØŸß§† kíü=Óý—|òûh‰7Íû\àwðÏR·¦ûÊÂ}«t}—ܾӓ-lŒdΪlŒÖ »I"™r»dˆô Á+‚«2‚·qþ´¹£Ây¼&“±ŽFO“>ˆÌÏ‘b*Ãw>ù•wÿ–O~C½Ýyó¼Jx§Hÿ÷èÝï©Ûwf2Û›üïW‚ÝOHV$;ö^'µf‹•’EÏ#„œsBŽšQ„L–h„¸„ÍÌ¥q+¦:é';TJq¬ÒÞGKÎü‡ëoYÛwNûMóy”_}ÿÛ™ œ¿Õ·þ(¹ý‘·}Ÿ’ðã\ÙwžTÌ¿ŸhÛ#¤f×HÞH$OZE$÷XM‘œ«H®sê¼¾ñ°-Ç·h“ûýÌ÷/|¢ü­>ù°o½sÚocÞ4o÷Y?*~ÿ–µ}'K¿ŸmûãHÙwžôõõ_ïãc+eGGʇ«‹”ûµ;ìeç,RšY¤k¸HÑ©¦‘¢Ö’‰ç£ëU´‹xßt~Ÿ¾ñ-Ï ­zå“ßyÛ÷IÄïTòÛ˜ÇÚ¾ixçmÚþøF²÷ÉCŸÿkÂVm®‘ZÕÞ µ€¿ˆŠ™a1;£Fdž®ŒYÆ:ObÓ]2„MõÝ­-CŸgs?Ý+G±[²ËZCmÚÒJ÷éͺŽ"¦ON+¶XW†.>vì­íŸ»ÎöR8ÔËQîèS©õY‘÷ ®–²WÛm ‚ε§ÝžöBPQpäSò»öš×ÿ×·ßíZû™o¿Ûµö3ß¾êZûèÓö×BØGŸ¶¯¸Â>ú´}Þ58…â±puû,‡0t:<0‘s^W¢_ÜǘŽ[Íb.‰Ä©—wÓô|à:ø_܉ì‘ÇI^j6³n™å£5 -íåùRT;fü›‹·ÏVG|E­í²©C#á=àú -š¹Ï·ôšÖk?Ѭ[6:.%ÑyÐ;¦b™~zç·ý‘#2ëÂ|ý2õÊ×”FB˜µˆêÐÜHX§%tÖ8Jvf™FNtaÖœ”iyQIÊó+xå"”œ¶}¨BwI,7ñaûåòxÕr¦±Ìaþúv¹&­¢l× Õ2ïìÃŒ¶I®k§m¯ŸÁv+8ÌîûcÿXWóÓ¤8Þž7êù²œ¼Ú‰Žob7)„ÝÂöƺ×Y% 3º×ÿÿ@9òZš¼Æ¿¾þðùg.‡°ßíóÏ\a¿Û矹Â~·Ï?\†.&1D¹‹8Ü=C3Khé1j£—ýŒ7ƒ –U.Z4³ë»pïí_^Þ"qzã§Q›.$QÿÑNjÞK©DRNÏ›š‰—k¹S¦09"Ĉ˜e^F7²Ìp9KÜ-CX!R\‹›}Y]…ƒ&Öxû¶kôûµTKïí¸ny7<Ò„Ú¯2±gó Â(Žâe=‰é~f¶LÌÅEqXñ O«¬Ö·ÂÈÄ¿™•f -޳Ö-ûhîDoJúË¢ã6ö¼!;¬2Å÷a)_>íz‡;Ò³ûU…f Ýs><,o_@’— 5)\ÅgÓçøÁË»q°*pê­»Øði}F¨Ó}áíî?|üÝ.†°Ÿùø»] a?óñw»Â~æãAæf@æÐ\Ûû€,U­ùÈÞŒøý|Œ2>šN.O#ÆÜíšLpp6‰ÖÉô8p4· -ÏŠp… ÅKÂMÍ„¯è"¿áÉß-oL¸ù*è0CØ—A?€¾ úù‡¶üžÀ¿1CØüFÿÏþkÑÿ”Ÿp5ôgÿmùc~­¿|ÔOý?øýÏûÂþpàÞ–ßø¯õ—Où £ÿgÿoÚò/ý a8ðÿÓ–?øßý_b¾¢ÿGÿɶ|ÅÏö‡ÿ×¶ü¹ÀÿÖèÿ3fûÃÿx[ ^œýš¹€l!´÷ÔA.³KÜ‹"Ê\biuÊI‡Ã.ìæ¯GÏ|s²ÍûF¯Á•’åi f¤?/Oã×i§J¿[Þ+¥è­§¢ («ì"½¹Î=u«xs¥ÛëS>W¸n{·£íÃ&¸è±Âê¤ëþv^i”iÍÙcÎ,ý]BØÏ®~JþŠÉ'ï€\æ {¨¬ïÌOö®ù/}¿m'Ï‚¿é·wsÖ‹žõõ¼¼Q÷hÎ宪OøH‘ÒÉÉbTØMåÏKûçû0Üî;茀tªôÖ2o2í&üöÞxÜnâæZ9p„ñúl´*mgÉÍ«Eé\Ufm›„0ÉPqäe^X¨YáwËó}NĶñäƒjÞÏ7Ú WK\{=cpÉÚv‡—'«Ô;z¢´^*…És*#ÇùpÒ?K¬x¼LÊ?¥x´³íþLBØ/o}@àSYà!íxˆkû²Y±uósEÃ×ÛZÂÛJ -s*yÂ\>á9k¤GëKBÊN浇(Lãå =VaP5xÍ.×Xk±¬ÿK a¿¼A³Ü TdJx‚ìÐêJ ]]ã56vÎÉlÿ¼ú:øR·»îtá+0ì]*FIÜãÐM7VEP™ Â8êÏ3¬ËÿvD§ 1ËóÕkŽeµJo“µÌ|½´Æ“å°ƒ³sËu'Ìe]%/k79ÇcÔš`|*3¶yü—Âþùþ/\¾£®i_Ð[¥5i]áÉâè7¬ç^ïF('ou‡«Dïõ}æJ™™wf1éXM®0¼ê„€æÄ4ë¨z‚ÉSñ¾™}IBØÏ®bÚƒýiºYÇ“Œú-P­Ÿ$„œî ñÝ¡z-›ˆÓï.s'´.¯ÆÙ²´? ÙQÀÖpA»ÒI®ßžÄ˜Ýàä”&Ü%ѳýý݈ÍxMܾ]% a{Ø÷­åtäœøÌÉ|lÒ„Îéâ1êg'»Ü,%κ¥Ø0Ü+c|œEX®ä…'¦ô'÷ÒåöóŸ%„}à‡þ&€DTj îÕÈËàP{dÒ.Ùqx³¹mõ]G•›¼ˆÍ’`—z$„ ÑMâÁ'˜`‰†zœ…{ÿº<zAÒ÷Þ2œ_Ø–ÿ¾Èüf|›¥&î©€®·®PÎÅÍæ0Áè„b¬”ún¾‘´y_ż7ìðO%eÁ\R¯/›ªG+“š×Ó†áKµÏp÷ú~ùH52ñÜ97ݤ'üÒTe-ñúfK‘ê.42ÅÌÒZn,·°¯wϪÉm/[›‘ÚQ{P>i.ýèæº©Ä¶½Îd³¥ÖûùžŠÇ‡HûèO¾%¨gìá–ÚÝi:¯Äâ‹'ËLTˉJN-£ËL—#6 ~àOrR¿<8#ÓèJQzÝ» QV¾¡S _u™CÄì¦iÄù=ò:%ú?ï·ÔŠÃ¬ê–lêü¨ª+õ­}…Œ­«X÷È€g£…'ã X_©RY—¯QViu]öDuÓ{›î솶#d7Bû(éöXÔÇo a?ÞY@¦Æ7A&nŽ@&—uQ)$YÄ_ÙQÒ9îùlgo—a’½¬ñëâxäªÚnÑ?¯Ý^µ±²ÙQm ÿÓîz]ÌXæêÖÆm{§#Ûùuc§–özË/A?€¾ úñ¶|ô˜wì+~¾¢ÿGÿñ¶|ÙÏö‡ÿ½-8ð¿+úÀ a8ðjËïñ3„ýáÀ¿1_Ñÿ£ÿ Ñÿ¨Ÿpügÿµå7v¤ö‡ÿÆ|EÿþkÑÿ”Ÿ!ìþÚòWÐzÙßL’©r ›@ [ó·é¼$\zå°z§”±]^Ëqb Ëžk=íÕ½.8úRv!Lël¹ó²§t‹½Ó=üùŽ™ÝŠ CלW†dÖKRjó‘8ƒìH)ÞfA¼Ä;ý˜ßÈÎÝ\'Ú®9žÄ]c&¶úÓ¥vZWÕËø½,†æ*_+ÏýÏ$„ýòÖ/dzRë;è¨ý\qÓaHXñ@ö°+ßfmmr‰_æa:}eH6Æå¶·y»½[_ÙÔn¥Î=ÂRç¥õÀ*u¢òÌÈùÓ¥®ìÿ¥„°¿]ø+èb°ÄvœäY„ &}9ì^½UÚÖÔs‘©s˜%½Ã¦—J­£-œ×¶ì­Z)é¦cJ U9©†Î§Xî+§šš¾M¢¦pø=ò³v†Ëfs¸ÄuŽ¿VJy²·‘÷¢h-Ppkæ¹âiw¨4 zÙ·Æ1+0@bóÐÁ3¼Lwð¢pÜ€Y“1o“Ø•xŒq¶zø¥„°¾ÿ7™á¢ýô0ÝÁýÙ¬%‚\fÜ|Ú«úϽ1óð>8ìïj+p™ñôWJåèëxÜS…’{œû•Í~Öª÷’ÓD÷âð©\Gãuãü a?ÞíÝ$@`:-l2 [Û‰w/cÞ‚»kUüHÏÏÃfä´o¨<°GÇðÕ׃½ºo$¬¹Ì¯_ [I‘Òym1’Z'EX8¸æ ²ÿ=òWÌŒ ‡ÌA@‚ìЇ{±{Ñ\‚Ë¥‹½”哎·1Ó¿˜•u}«9Ù‚±˜ÖØo)[‰%üÑ·„m½ç¹nn)²¦v^ü³„°üÐ[@f¾ühyA%‚šQéVMŒ|?²–ËŸ–çÇJ7â9ûy~k´ÃîZ³9aÔÙe4IJ<+ŠS–î2×匌ÓeÜH…l÷e aï—ï ÃÇÀ9ÂQ)™½Ktt ‘¢wqâ“ò9'Ëó©¹Öt65ô$‚Ï•2:œöôæ+a›~%l“ß¶ýºTY´ÞS^;„½_¾, ³6;pL Ÿ«œ‚âü­ÂÇWêt?†0(²Å£DÙ¢­å:\:Ëêñ1Ê­å„–øìã²- Íâ¥ÈÅVXŽÁ'L¾/ÚÏ=Y¹åHûÀÌ~Û…£RjÈpNA=±àkÕ‰àq«ÝáèÛÉÜY•&–¿V&zÊHð‹êñFM§d]”v«"ì/ƒ’åLr3Û(–dK£4=eP¼7ïîÈß#¯•Rp† ‡ìö¢Éõv]²…KUb޳iÕÙÏfÚÝ?RðtZ-–©ölmv+ãSyŸ*cç»Þ$É2ê&6‰™h¿YE{‹b.F-‰{ü-!ìÇû)€ˆÜ(؇2 ø-ÉœÆî…ÚEwcÃó¯óh­Ú%W×È¥S™£Â<7Éhf4wçI!²EÙ ®E²•DûqˆÐU,ŽPOi¦PF?*°-ÿx2b=@\öʽà 7§¹Ø®+cu'o:êzÕÝVi2†/TýžšâÝl\¬leTèö8„KÕ'3¼apä˜Ùƒ@ôÀÆ}P:Ýv×mü7 ìåYú5WªUýÚ½4ûVj|uD“®¯;焬‰ˆ¼S”ôþ6Kísw1æÁ0"JÇJò•ÁuPõ:‘ñ™¢ÎÙs×.¦®Ý$ó?%°-¿ºHë"œw§-åe_XwÆQOËéH~nä—¥‰j¢’Û÷«U±GΜdœXÒϦ¦Ñh…5zÑ$¾¦è6€»$*!n—©í7­¤n;|Q=üñ[t¦årw5wâX¾4ÔrÚä -;`’³£ÏQ+À"<¥SŒ7|ûJUiÑèªÝ£¬>ÎP‰MLè²Dìì†Á¤#d¹Cœ¥-O”`õ7ygGÿçêÄÙ­k—u;}H,'¨:Z bj=ÉY†14ªÜš#Ü™3¨\ïÕå:ÖOpX7½o¦ T3Û>JåB;çgË-OÎÖZ“j¶Ù*‚l»é«…^SnféÏKûÙU€«÷À[DàE^†{±÷tƒ2 üü¨rÄîT -|";¸tÃþø„?ÆÓý„«ÈÛºPUÜUy¸tzÌY³“v]‡ã»š¦02œõQ“†×k{«ÙÕÞ}Eþ†ùù¿úL¸ßÿ*è0CØ—A? ÿÓ–ßø7&\óÿÙÀ¿1_{ä?øýégûÃÿŸ¶ü‘À¿1á˜ügÿÆ|Eÿþ“ÑÿŠŸ!ìþ¯mùsc†°ßx¦ç&f7¡£y~\dº±x­úŠÖx¤ÈD¹DÓÑÙq¸ää]P®(›Ö)¦:t=«Ù)j¤[™ptm’ ÅZçÛ1–ÎzýØÙjZ£ìþRBØ?ßË@sÀ¥%5×ÐÀ•^$#¨p·ì?9©6‚jw0ôúéœr8Kcu«ŽÒºCöp®´Ó­ƒnŽRQc=^LL£Äœ• .½Ñ{ªµ†«îdva}E c¸ƒBP©ï8 -ÈÜNw@\÷u@.å% ½dJ¿å~,zÉVW?™©¾ ×k'É©+‹0ÿ°Ýìud5½ªÉ¿]òëÛSç§jÔøŠüó|Pî?Oólé ›«F^Õ¶u­ë«3}ræÒ]cïKº¹Á<Ô²¹òÜZ{€±ÊBvõG€ô°ƒøZ7ÎO³§+—“†1 6ŠþÏÂþù>ÄK¾lÁ'ôqU‘´ˆ4àŽþ¸Yº½Ÿëå/ÃùÌI3k_elÇ5Ÿé5ÙŠ5A¶ö4@Ö”ãw©¾£ýV:¢œ3ŽãîÀìÝ„—Û[¹Ìþ¸.ï³®+éë2ÉÄ<…LvÎóâùp¶Ê¢+™“äJ¢K-„½_þ¥üÅÏL. 1yöoA+QãUh›¸]Ÿ.\Áú-©ªçën§zøÉÙ$ºžé­Áj©ž½¥‘ -yŸïä ¶}2R9J-ìÆij¯C¹h¸ûÕ­ï Ý™z‚ÎÊìC%º Ìûù`%nÍ¢¯F«¡ŸŽtö´ {Õ«“É-üu•<Í"˜ƒrHýy™Ë:3Ägœ×'ð¶cœ¥ö#q°^ÄæZÿŠÀ§’(½1ßëäŒG¥&ìâN£ c^4ïÅ‘‘…¾ÆÒÕI\t.Ûh›¼Xã â­ôÒü°Ä‘ôfQä{¶ìݬI|œ0Ç™ºhÎñ4,­TcXšÍõ·„°ï?%ÿíÂÀ/j+@l;ÐÏ3Õ1ïÚ·Š7‰ûíK]’FîÅŽ%-~rÙØþ´¾©Þ^Ç›;Eܹ‘œ›+½4Æcp?Ž—vÒ*ö¶_¡KQ¥A—b –ž¼ø)yeGÿëÈd+eH;ƒ Û¤ ¢·#w¯ÔXYžG•9ú Mm\û<6ëüf¢e’}zJPÒëÕGå‹_ä;ò&˦°[f0,Ñø[¦©#Ñ«*bŽjè“…täæWdj­ -d]2°!ÈfK 8Zj±jó¼·ªóÃco;ÊíTì‹XçV‰ùµ¯”—vgjݪ8aã9¡eº8ëØvbp±x¿°æc½[1‘ Ôã.ÝípJ¶‹abîoÂ~võ—2=º -2à ͒{»Jùø%Y*‡[HÛ$9>;¢E¬; ÕR=;Z•£a¬ç—l“"‡*yMp–·‹2»e ë{b¥åÈ ¥žE¥Ð혺ŸèÐ/õy a?ÞƒÌ|T…KŒÈ“²óÑþ9N÷ƒìƇ»œpvën\ŒiÃ[˜·ÝRzB¸£øh¡àQÞ,Ÿ„kË’Gs]ŒšK¯Ü÷Kµ‹L3ù)V ¨NôÎî¨d0ð2ƒ ?[>ºNJ·^+UT7Š‚nϳžÁ~ŒvR½^øgûÕ­¿ ðCjlz±ÚukšÛ•]Zú)#¾ÕhÕïÓ¹QÀôé®ÍìÅÅÙ &\ûq™\âG˜<`Oýy&8Ñ mxîiAçJNøBÅ¿›¸Ï‚›çô…þ·ÛòzÄ4ÞåÍhÖs»$¥WF|UM¨r|Z‘­2ßšäl‘•|DúhGeeÓÌŒçš~\“[y»‡iÀ¥zÁ~Ûu²ê¾›b†ÇgtÎ!¬½… ”²ÿäqMï"ÍuÜ. ­•Ó×°†È‰ù®“¥â&ˆZçÙè¬Äx£\·—=–FW¬Ð‹Zí •Øäå.›ÁÕnú@¾•ˆ­;„‹Xm1¸íœì[uúµ„°¾ÿ–šÙûÖÅ©+&¢·ÕŠ=_BÆ…=–3mG°šÍ-+âÒ Â¡7Õ÷QÊ¢m¢ËâÓbGÈ*б£Ôk¶Åâ ÛÎùºåÉÓšT9¾Uܨ)7nú aVÑJ˜«¸™Þ+JÒlÈtÐAFd(ô‘ÙG‘Öy™J8ú²#C»»Ž­Iexkú -4åÆkÜu&EJ5”Ž„7˜DÖŸ¦”¯kô¬ÂàË¢üé+•é•Vé®ÇÁýÒÙ†‹<'ˆè¸Y¿V} ™q³LÛ4\¹ÛÜ™>÷Ä#Ÿ¤ÇûÓY›î¦$¹ØV9UÝ4reÍÕÚñµ‹ -WÇYëÁ‰¦Î¹ewÛ_Jûçûýfû2è0¡c_ýæÿåØïñóý?øÿ+ú¿'ðoÌö‡ÿÆ|EÿþCýå÷øÂþpàÿ¡-¿1ðoÌög‡ÙÔwÌÙ“eHܰ w ×8 ºë> ÒDµ‡‚̉«ú7œ¦/¥=<±Å‚tqEÈèSòlÖÊ¡ËK7‚VE.-Û*0;ÑmL׆$å·ÀýËÿ{ÿQxJLƒôÖ€ »„ðg -bà ¸Ÿ¿À•þ®á@Ñc÷cLmú vÉ…0¯§“£°KÍv~ï>ß<ìˆêvFå¥3–ºl׎ùŽ)$ŒÝî¨/ú˜Ûš†tŒÊ+;ú_¾ƒ^ÝÀÑœ2¹òD! +ýâd Èô)y«ûÎõÙ6‡g&ß—ãÊc¾U$Cu»Sui;Åí;a»2ÉÑÄ aë|©º1&½üY{6RÎ2º>jâljÿR^˜ü³ØÁG³÷úlÑm¦!^’¤dí9Iâwq÷¯Úä*Ó}YÝO·åFO˺Cß++sªëëÓM3)žtVA?é  Ô~–7»]E[TkùCBØß.ü³|ÅÃÇ:À[]:ìÁ÷p@¶Jü÷B[¯JÜ.ÃXÿÚÞÄ哟hp®¼Y•»–YÃÆÃÌ5 Ó¸šÇQöv¾®>·åZ™_d÷¾’/ o)—6Çŧ$„ýxÿWP†»esz€瘀VRCMDO ›c³Að@×È%»8‘}k½[€ŒíÆ)Ö¶†‰¦»ž¨‹Ãªî{må$ËøQ…ëdeÛé\å2¢¬¦àÀ.¦Í:+ÿKxSÁ¿cJ<ôó áFyšyeH–ýá»Ðö>öúÙ ¦‚ÁÅLö´ãIš¬—î4õCyc9nr!™æ$Ö«ð•×—©Zi¹çËŠ,ŸgË©¦<µ õ®´3û˜!MU†²™ýKøÀÈÀ‡ôý<¯^õ–û §,Èf] F?‡Íüà^êç®Ý+`Ï„™ÑvËQÛ´÷ZeeÜŸª¦ÑJb¡ŒRÁ\¾EnòT§ˆ¹d—e̺Ôjt<õpRáæÃ 1žýLBØ/oýª¥¿fâ[Úg@@d{žt/ÒÌ5¨TîÒçwôœ³²1ý‹f„»ºj¯Ë°Wª;v'ÏgÖt: /gÉAvqïUg£q/¢ËiÄâA¾6çA²9ý—òš+€fÏ8*E<؇’y] “[Å­{~»×Èz‰pÙ_ÎÝ´:œ¯YÖÍëtq‰³Òì©7Ç“UŃ)ŠÃë‘„Úv>ã›÷=Œ>×é”®“)Ìþ¥|lÁç³…6à(žÁ1 ‡˜çRá.ÕÑIÐÀsp„¹ÆœyΊ·ÓÇ­©'R“ÕêX’ÔCe%É`Gަ1:>“)”ú“Æ6"×µ†3–ÞÌ †©gUÆ©¿”öÏ÷¿ƒ2QºÈȰ³W¯ on•–+^Óqïñh){¬”õÁ&µ8ŽÌªëŒ4wŠˆ‹™^f˜àf¼÷v˜.ÃëÙÃÚÑ!Çàe8sð·ô·îÙÚò_ -ÀÕÝk´Ä¥tŸeö"Ö¿CóJ¾FŠ‚·™)Û“W%‰=Êf:ö9è³Vã9U|Ìٙђ©ñÁy4†5S.sk[-1\î\ìŸzt‰.‰Z¯ªF»ŒÄÖ ÖCã_ -Àm€ƒg®;û`HÕºÝ*h5±®Uö$åûö¡‘™ÂŸ»ÛÒÕõs¶éj¼¦çªnOðB¤<ªØL–Fð4ÃG3‰~aØ÷æ+/A5rº¹õ³^]®w1¢Åü³¼²£¿¸ðÓ„£R‚N×*ÈÆžwíÄéäî©}˱Wî>Õ|XkåVÉN¡ªTÌFeêÔé‚x17„ ]™$ëÎÛ±öþÿûC÷Ô­Gº+ñí˜Ü.ÕvF»\›Ixå)Odz‹îk®LEô[ùº <œÆ÷­V¡î -Ó몕fW£ÇŸbêe'r,6OK¹a49\¶õg”Ö”p ^âï!”æ<.E-ÑN¿@GÛ,QIµvâëáÞ/Ÿ8¥s Üh@žXãÚ½´ûÇX\¡3ªšêœ˜®˜Ên·”²?„Í;+ °-?¹ -2Ãz›Üx\=^ŒòÂðÃÅT¯¢o-a¤V–örÞÝ<œÉÞºõ>ð(OéÊa|ÈÎïï¿%Qyy:sÕ}zÝEåƒétÕ…k›[édò¸¶5$¦aê÷XLÿ¼€t¼þþ’&JY®(¸_2—p:¿CÆ•#p/*S@Psò&u3ÝksüäÎýà$ŽýÙdwuŒùFÝè ·íaK'ãWvb›³-æˆîM¢¤V3~ Ó–N[™òyi>ý$òÐOÞ¥î æb 1»ãê·z`Ÿ¼+í ;w+Κô5|.ŽO›ýEÞ_Ñ|[—ÛŠÛ9t–¶-Hº…w<Ãhwkp#³ª¬6 íÄW*ý8+ -[<È¿”öË[ßA+UèçÊ{†^5¸ÿ<=1Sk€,R -#¸çI=T£…þ¥ëÔ§§Ìü´Ø­nÚ±¼îÄ=ìõÉÎf,sÈ÷ë¼çÒÚ4ôRs6 êÊsQíN?/ =KF^˜¯Ï$¦·>2 l <±ÝÔÿSìÝo çý†˜ê_¢n燶}`-ÝåÁÊÚÆZêFÕ­Äšz³…8Z‡ØïÕäðáÌEå¾'—çìU¡ÚE'Ÿ—ï Ývvöè«Þ2®¸÷B¾4éhËûIºƒ`ÞÑÿÚAÂýó&]”Òc£m°–±°ñÅ`±ž4[‹U}"¨Z7e«ª“¯é -gbÖ"›^Ú³ªIͦj]œLOÿYàÚò'Wa¸ñÿ€¦”.ðá̆a¸#M@>\í^h=þ#5Í_zÓðàDZîdûTnS'uªNÍBú1Y5FîD‹Úìûjº‘-F=s1/øŒ&—O´=é´c3i½ÇÇRo^?/ß1ù~àEâ]o9ª­áÃYƒ  ÙÙ¬¿ºÍÐûÃo^§y‰\‡Ë†71ŒY£éedÔTi¤õ"í¡ŠW¸Ñ"·wÆrE&¤é3éͧ(v\'}2N©'qœr’б÷˧ä? ô³•{Õ[v 檃rYm¿ -mWÁÍðÁµÛæÏüp Û²¯b¼³I䄵OOy}Õ%8usFØE~ƒq3ø¼óÓvÒJV½-ÝRu1:\Ç“Ñh¦ŠPö£HûÛØ}ò°ÍXè¨ÔÀÎΙ€¸1@žûÝwýjPî‹U™O9iDm»¨Îؤï1FÏ´DîØ_œ»SZ®Õf½iD9Âý‹Ô?ô9qçƒáHT/³añ>˜ -r¾# -~:ü¼üsI3°«Ï­w†dŽ}+ -<Ñ•¯UŸÏ=N _®t\Ç®QæT(ÝgJ%ö5¯4šÝiÇ/v¤TG¤Älô‡Óæ”敱Ä7.½9d2"ßèt†o a?Þ@@úZÂè‹y8xJ, 2º ȦÙšFÝ*Ìsu5NÏÇy˜äòûÆm:Ä,ÜaƃϷ—›Ù¨µ(1pÂm¦Ä(÷¦˜K”ÚÃòIíòËh­Ïu6Ù!kÂY…=Ãc¶—È ?*!ìÇ{Z¾fâKÜɤ -â÷‚£P¾F*Êeòî§×Ëmc¦X±&ôª¾Š&NuåœÏÕä&qªM’̲&ŠœUîrPçÑn³  ­c=†11~À»Yyñ“„GŸ€‡ÐÏíbÖ¶<ìCˆ ‡#<‚›Ï¶¯±;?ïFp¨ç2„›aKÅuc])/wC®4¿Gž•iï2-³U¦4¬%„2oªl²_®„õ£ÛÏ3W–žì33ºË´ôÀ¤Âþvá—ðƒ¦*™?øh–¶÷s¼šðÑA¬vÞ‹Ié8Û°Þ–>ñ ëjïÉU<åå”)šÍÃùÅ´üøX¯å†‹‘ã±}#Ïl»ërÿ\ÎVé²tnöj–¦šƒ­@5™Î’R‹eùSÂÞ//ôcPvpÅyƒÏ9vp,B¢ž{Û—Ž•Èe¸µ¨ëˆ´…¬Q±œ\ŽD%3oÚ9á(™Í#™ õŒÄàXGéÙÚÁ¨Ç®ë®húUÕ‰íȶCß+m§Ï:¦¡jŸ€7…8ììh >šÜdc»Ãµ{™<Ó}€«qúøè;ùÑjm6ìûUÛ¹çB¡ÉØÔ-ßÒâuË&ý±¾ òrí¬¦ÚéU¸µi¬íVñ֮ʹˆ)Qjú²÷7 a?»ú7øXŽƒL¶U$bO‚ÛLx†^Ž[ó¾HØ—D½·ÖíçRç8ÄUæý³7³)÷!å'«ÈPk”Æ9~0Ù[ôËÆôÔÍýAEžì£ Êh{3¸FZû®“hžúÙ#×Êçåõ×ÛJ‚LàÀùü”—/ýˆ®ì°ÛA·½‡[wÆVW;L“¥ŠÜ{VéܥΣ&S¾ð,weN±ê¥ïæº`×:X]+Úºw¸ºjo÷'¸oñM´•CõtSªªäçàà˜ĦРê˜=Ú/“õ®í }1m´Áá³ÐbqzƸiA’®Â|¤E.&ïØa—ͺiwPþ¢Z‘}¯SRT/Þ9whæÚaïW¿½ÏoA›ä ¤•§'ÑæD’?“öË[oý¼H¼ -C{; I'ë·Œõe¢k9ÖÆ¨¸JOÔÈ+‡SehÔš]>mÆñ³¾R}*4Ê‹ZëEUƒJ”cV7ß8¾¥oÛyzlªêµuo­Â–{6gÑ.úy„{(À½ŠÙW{\ãîFOÛ·—“H̘ùòqr¤r—Ñs‚ßx·ÛBX÷⃊?ÉÑZ“ª÷¢V²K%6È ›Þ_„޽Hí£´—Û9£¶<ùd´Š`c5}õwâÍjøtjÜWÿyÙ=°æÖšDsÖÊÌ÷ª‹·ç]ÏœLr–2µü©Âíè%tlàŸ&ÝFúªŸÀ=H–B:Ä9o‹ÅxBay.7寣‘Âmø ⽆ÒM1F4;¬?­Ú´ÞN–ç¿”öË[Ö™–«X1¶Püq#Ó¾[”¼>Þ•G›=2÷N_©½^t=g;;!3lMÊæ¤)×M­q×N£9êÏõõòÊÁö÷z;~ÖQ7©l"^£ðtªÝDÕV -Õ~¶^ù¼¬¢•Ž L‡8ïϘ†èK“Ä49Œ*Ý›Û5â´V$»îÀÁÛ¹KŽlV^©þ4¬Fµ5ªÝÊLµOÈÃjâ¨M*®hÈ6o©•ôÅÕË»éÎ, å å¾+wôÂJG%zý¼€T½zÉ'‡$;Δ" @Zh­ ?^šUá»ó¤ÍøæµãË¥ ¥åÈ{¦z¯c¢Ýñ'Zпʵ1wQÐÂÈ{F’×Êoe/¬;Ësç°\Ÿ1ÚÜžâ%étȤÞ&˜_œ_JûÙÕO~3„}ô˜Ð±¯‚~óçŽ} -ô˜¯¶üÑÀÿ<ú¿;ðoLý?xM…˜GùutÚ` h‰œ]ÕÞ ÃÕ*VØ‚ô¨Ž‚”WÉ݆§]ÿXùîUôÁE‘š‚§ב‡æWÓs·ÌÌO&]_žzÏ®}tJÝÃ1Õ«_¶—KõèªÒØaï— -HÑ Øíã(¤Ý@šp9€£ àZ7ðç£ ðÍ«®d:g†\¯*$¢iËÍf¾40{úQ aï2JëôÞ¨¼ØA&QÞÌK‚·_…Ëøú~ùGp5M¯­+K§¢ˆ'–iNöFÈÛ9H.\½^X:”`vâÚµ­ô±u aëQp<êõü@[®nµ…J{Ù©Ââ™ñ§ä/˜i¢1™‘#ÞÙC³©W>yu‚ ~Ûºyüáßë«ìe¥>ÛçÁàºô&m•4;q±EA²Sähf1on\pv¹ò¯Ô¼_Ý©Vö•TFW³[Žçâx5ú!!ìo~& u˜Ü`o²ã ]a!¨$ȧ@t;8 ÑØ«pYt‚ œ|\µ‰DzôõØ9޲Sf«ÖŽ0úŽ5²//„uÁ4„Õ|éÈJ,×ùÎBµ3õ+©<Ÿç ÄD.ñ¹Ñl­Ÿ’×üò”ŸÀaÝYM@Æœ1ã h‘dïctâÕ-þ¼¬# yÚ)ýÆ~Æ?(·'T©×_±©uq‹Ò¯C6ãXu0¹ñJ&¾’Yt©¼jz·è| -hEœ6ž®0E\Ÿÿ”€ÔM¹ÃÞ´‡˜+£ðÇbÞì ¶ów=°ð: Ü¿Y‰<¼A­NE¯mÑN½ns»±žV¥¦þ|²­¥•·•­{¥Y§Û—§÷'7›_îÒ´I²´žãC©7¤x)že™¿IûÙÕAeèçbŸÐ­U™øy -ˆÌé ȰMÒ„àFöíkÇ3çÉ}0Ü”Ý~ü^1Ïæ ²RáB6„i±!RUvV¶1—­Æìõ:ÓV¦ß›t” Ñ8;·)›·­$'ò(É|JÞ˜¯è›§8ÀÃ^ö¢° NÄ{U°’ 7FA °ökó N£tß…Ñ^ÞÞùJa]™¤Š:6²Šj&<-Î/%³Õ8Ç“º«?S{û¾å´ßt~Äß§¥ãù[Ê2¹û9,%cÐ5>Ù£ïH¶ã´®Þ­]Éö¡é·ÛÄ27no³½ŸL´?ã ù­ÎD) MÎ%PäkCPFgÎÃ%ñÓ•ÚY#a«î³AŒ'.-VžíoŒu]=ÎÎå©Ífñ!!~é)Žqá5ˆé»·ÔΟ÷÷7ír‚Ç'û4w)N1®4˜—Xõ¸o±ÕqyÈÞêøò? ‚|6M¢ -J‰‰ø¤"W°ÞC/èGbæ{®‘òÊN6Óê¯n«š¼`"B׎µ¼=i僜®6¯#qjÂ]Ûý¸}öR¹aÔéÓoÀç.V¢]ØO2\Yçsì&¨i4CÎ/°¿|_~ -8À7’ð™+ Pò€t'Àµw®±4¤¯NvÌ”V5Ÿ§ »Pïh§Ò`4ÑîËϼ¬G'„Þ 'Ž{ã¼×OÌŒ[·»/Ü;xóòùê´KH7ÁUÞÕ4;k7rLh0U6ƒÖ—ÿ¹‡Ï\üì3¾>‰¡Yö¸O>ónËVǶ™×eX0£c K3íÖÕ¸b±r†Ë -£ñq>Nïôj@O\»g»­mw«¡ÇÎ0»9ó£p×Éík©´ËsµNîÅj&†0Q¢H0I”ÿ#€Â†ÀKÏbž¸M*ÊX§Þ ŽÉK=›¾è.™ §w!”u£œ:c\T XUh†Ó_³ê —qã.v,Í:Ä]ðEXmµ\W¿=nl(<¶iß³ˆ¯)†6#ì°ÆþçþÏÚUìu€‹wpboåO}Ü4s¹‘Þ.–ÓúQ\ËNk²IØv/„Ó¢×_3NØËlµ4ì/QÎ6­´½)Û„f$ÇÕÙcÃ%+²­$«0±ÝÓ³#/é·+;ôz°„a¾»ýß»£»Ûa·Ä–º‡Ï×£.ºZ¬¢8‚37Ñóžä2×¾X9Ç÷ä^fsœñg‰5Ú• àpõ¸pfu¦ö`b«©§è÷–ÎÒë~¿@§ÊB9⤺y…¦2—E§åÊ{±5(ûÊ„–Òêl!cc5Œ¦š”x “æ¥W’0«^€3rýV-ô¹÷¤Ò9¯F[­*«ÓÇ0éÔ€n£o™êæîS*sN™­Añmµ°[Æm&Ä©)Ö*^“šyžÓ)5» ²K™†gêÄ/|ïŽþŸÿ„5‘W:ƲKtg¥¹2SéÛ$‹éy èÕÙ£—q–¾x}=ÙÐP^ôÛ)¥¨.þ@[ƒÂ-ß+ÇêBžg;’,>wtÃ3ŽíFåµëÕï«£PŸq—q½‘º)µÐyLkzµV]C4Üÿp®Ä9ë °ðø˜ ŸíZ 7Ða@z«Q:øùE0 ·êLA®®ì~¶c€F1 Û½ù˜¯ûÑXÅçUkÈO©ÎBŸ.™aMDí/äñ¼öØüÆo?½·Ë®N×|+µý)~ ùYóÿ—¢?ÐüBþkÑh~!ÿµè4aýÿûà¿5aüò߉.OO] æ&™øýIƒœ¹ž@ÃÎR ãfªÉ~?}E¹éêrÄép'.¼õ¦b"XóQÑl÷ŸŠU‡þ¨aͿ̃€»¬RÚmï¯$ïúVÿµ»¡Æêt‘èsN¾sêUómm±ð‘ÅØ¾õã“'œ¾¹³Ûg?y(ow=æeº¹ÂVÿ)¾º@Ñú3‚MSÈý•hK8Mäë‘ò‡Eä×/¸¶â Ÿ¯É@Û>ÓÍn' Òᑨ—%߬&7iνý*^z¹ó}{л”Çnt`ëåÝ.[Ý®·…~Çpü6ý'|!ÿTú·h;ûÆ]#QÔyOÍ¿—±S ð²òaò£Iyk"÷œÞÜV¹O†õhäw=[¾aÑç§\ÏuÓ¸\oÌúìÛÕùæ1áÞ:GW°8gs¥±¥H®f-XIý)¾\.¢0FÆ,âûì· +s¾ù;‚BÿUµ7f¹‹n[>~6t/˜ãvë‘jìz~/D$ï¨5'×§ëçþXëòê°2 -»½=-ß¶'2µu¦„´´¢] ®`×\UWþCú) è Ö§˜ŽAŽ>æa`ç}ŠÝ¯@¡"G àj ˜±”X©nöOí]‹aYNôÜ}ƒæíÀÁ•¦B¦ú'c`õkßì37n´ÃæÄd{Æîú¶È§¶vpnYë•¶œ¯:Êd²ÄDA2‡Oaô¯øB~+¸ÿ„5:Ãa.@vŸTäý— -Šõ¸ÜÅñsÕGÚ.¤Hô@æÝÜí(óÕËÝÊ4+sCí{«-íŽj)nsÃØÆf2:°uö)[f󩯹ãfõ’S“Ø$Ã[¢QÍÂOðwÂû[óó@žø+ßËÚŸŒ`52V›ÏɳÜ %‘?¤³×‰½/×a¾´Ç”de{]u'ÀÒ;‹Z[’[uµd9_’y¸×LCe9YŸÍ;ãù³¨ÀqlÞRûýŸâoÑuéõWÆzžk - @—mX•èw>p+zdÀ$dÁ½»·käy +Çä—FàûÝÀÊm½³’wæWµhq覼ê-^Uó¸QÈ…LTc‚ûúƒ¥á%à1ÁõGÉExmdùÔy™i÷‘çÉf²V;ŸÆWX³@˜…ç¸`L‚®“u’1I D§%îêpºéU‡Ó% -ÓlÖìMv¾Ûý) fÚû[ó;x¯ˆð‰ËÛï¿&ðÉø–Ô“ÂÊ£·î¹7¹3~œ™ùì‡g&± †×¤¸šéÕ`©¥Íâ}Ÿ5êÃ6§«b~fÓéòtK<Èɾœè¨£ÓMPGgt ôF¢ ×0ß—ä*8¬Ï{ëýwbèë“Æˆ,ÝOþj*®Ú€ -Ym#݇ ~ëÕÔ¦ì%F¡ë™oØ÷º%ƒuoÝ|™r1ÿ6=:©½Ý2ë\q|²—yu,5EˬŸ=îAiò€ýåix7ò±½nÆ7µLÚ'ÛŸv²\e…¹­±È÷÷œþ˜¿6r0ë™ÓpRÈÞR½±I©µØ#ãDcAŒÒåEMìNƒŽØs]ÑYóü(uŸpßøB~Ýÿ+ hÞ¹þ$ò5¯ûPR†ýÝÇ· @Âs¬ßEÌz Y1½ºÝÎüh#éÄyYš)ŸïñÅnãªe¸òm2æ›w¥vž…ãùˆG¼8M w«zV8:tY( SVÈ¿+{~ˆlû§ø¬-ûÕÈm7)b(•øÌ™c$©À~«¾t°„˲y2öÒà±vÞµÊvÛm ’jžæiœ¦ùâëªÔýø:~³ _Ü´`˜{Õ^¥§?ªÕ7|V6MõëIÐ÷ëÉΠ<,· -SZpµ†Ü æž aÓäUPZªÇ'›¯ûa11²§]Œî)ËÓWC4ÓÎ\½õ²Òému.ùv§Ç—µSÖb/1çóItãÄE8_ä{ÿî QOgÂT×äßDçíÖ›·ˆÝEu»1šlÿÂò[Áo€&ã^NJÃ>Ô¡@ÑÒ&ñdbƒÌˆ nµ4™=ÛÂì•¡7Þ°by¶º´g¦?²k 3]Gê‚ó…H«þØ÷5u'ȇDZ¯­zp±raÇš§RüFMåøl‚#Û»c$òÝx'@º?|âÝû÷›)U8a ”˜ç,|™¨í—†»ãÙžkï½§TÈmÌ6úÖ~竦öbæó^y«OÔ¨kÊŒÀ®Gسi jQÝôu$±ï®S†×ɰlØÞÇõ$wNПwÞÇÇm‹+ÎÙ17îwjòßä"éò¬šÅFÄÅ·%>{`avI#sïÉûhkÞ¤ÚbãiŠYΪEA·ã›<½ž3ùµnÏGû ¹n¯Îªo¨®Óå+Õ=¿“—ö¨vyr¥ø”f'Ì«À/\)•æ cQß2 YÇÙ$¿lxýš‘vé™´íõìŒ%4mÎÄb|4á–…qeïíûn79¿ò‡A'lK…F‚SÞ8ÊNŸé3÷u:®7xzzsšFÝÅOíü… ]„‘øÍÔøs:)ÃðµÛ¦¶ W×­Ñ] -ͨ9M齕š›•y­¬R]Ÿ’²x¦#ÂYf$hp„ê/;³‡¤e§³Ûó'~$Õü¶,äbnÒI¥X­rÎ|ì•LëònÐË -ݧ™ÃbúS€ü„ƒK£ûµzêTw+“Á†¯ì¨õu sa/=+ÛÏéÔÚ%¾…ë^‘ñÞ£‹b…:71Úm÷¹qiØÝÞJghìt~¼ÚXm¸¸;º»quvód›MãÍ,Ôy†~=ì½EM:é!ü?á ù§RPpbäœðƒ{Ï`‡'‰Óêž*Ó‹u1;›_Cg¢º®ÜÚi$a¾/„S:ê¿—éL/³Ý:qÙàϲƶ½©Üû~_qu 1Õ–Ll/·ŒÙ9žé·{¹ÓmüèÔ©¥œ1[ú).­òtéúQ·aBÿB–þw §BÔµ3{ª±|¥A"ÅÊ…˜èx ÷2°îçå®íM´wŸk1WiVg8[«à öçÒ-K·±`@9b$QÝBvFe®µåç÷ -ë¶°»pþ)v2†Êv§I—ÕM$éÝÃq7S‘¡¼§¹Ê˜Øb!œ0~j½¤:‡aû|“«j÷XæDÆlsSzÝ£M:µçªKtTæ<öZ®4[Ømþj&n¦)Ö<¢Iï -)µRM²ꆷ¤ÿŠÏîèÿ*°yeÛ1ïÓb{¾­¾{ÓúþyW¸`FRvbM»nw÷â‹^%ÅÕŸ^–aRRžr„j¹åŽKdó ¢t“x R"ÑYŒJü)*Ÿ4*ï†^¿¯Ûëú¬-ºõFzvª…›µ_Ó{ç¨ÖÊ>?Ų’;´æ²MMêÊÊh»ÿBD5çlúkʲ;"îÛÜ}FÃ6ÆzÊH—æAÁoäy–jëê÷å5Yo$O™šÞÙãµVæZ¨šƒ}¹Êà×zå} ¨Êzôâ*í"Ò­¤¼Ü°ì¨$-ËÝ*;û)ÖJ: {Ë]@vBF“‘<ÀGàVW¹D ×ü'& ðùšx3ž T>;½Ø²ó«¦–sŠ {r ;÷X•Rm€lGÎì)½»ó°j4VAc˜°î¦kØ~›$›³ÈÙÿŠ/ä×ýÿGÑhÂßå¿ýægì¿ýæÿYc?ÍžAvÇBÑK­pñëó! ÇpÍŸ“rÐÕå>~O^jîvòa°‰mØC`I‚¶adõÔd»ÅYœ«†úó²çáMl˜Acù´î h8þúHm£Àó<}à=cävÜä–n*¿;‹s€ëýÀ[‹7¬Ùsºž«ÝÛñÞ¾ãœëÈÑ©êhQ7áhND|4ÀѺ/‹òÎOóÙ½‡×‡Ûó W§Ga½ø&å6аv…ÂˬJ¹ ÛsãÕgÕ‡Žˆ–î‹PžãÐv|¶nÚ†°*o1ƒC/«ƒáój1ê³¼-éáäž·²µÛÜ—r{Û4‚ãUtŠ;ø“«Ã2ŸÐ é¢}!ß—?`zŠ–H(ê\Þ0¢·>‰Ëꥊ iB&Ê€(w説 ëʇ®çL|lÝëp “¯¹í¨è „ge„ŽÃzù8 æõåâu èx¢ö~ú£Tñ||Ò ûÐÅ=•'în\P€Osj[ØLY`áã rØЯ޸ɰvFäÓý: 6‹ ¨½mO芪™çÍfªÏªŠ2a#ÍýHzÐÝÜô¾’Kß.\·>BûÞr=®êþ±å[Û}zW0]<Í6òl,oÊþøø5©+ìçÙ$È©(TêÄ4œoA¾]~2ˆÄ ò@‰Ïr°Gy„Á<"lSz,)Šº·;Û¶ßyp­¯¤doH8ÚutS×—rgp<òæ¸ë1§õv¤tæÎä’R¬ˆuEk‘\ ßøB~Ýÿv&¡h—ÿlßÕ>x4>Á„ òÜuò‡!ŒóÞA ä…ITÚ›PŠþã]1Rwþ·Z©À^™§ÔæEÍÜØ\¶q‡—c÷ÉÉ|¼çƒ“±ëy»‘÷¡aÏQ}º~Í[£•=À+ä‘îýða³ßó‹$@Ñ{/ ˆLû“L+ŸÔå(Tl>©e&ò ÙIø<+Nžïö/ïdb™K­ðã2ý"©°SÚ¹Õ¸æ&µ‹¿QzÒÈñ÷çŧÆX|¶NFyÙ—C“ FÝÅé%ð?À·(ìM#6Mù³þI 寓O>ð'qùŠÄW“b¾ç¼©{ãœ;ú–®ž4çÃóÜ/¾6™LìvNÙ^H5»QgxÞ—˲wrýf„ö:ÅkãeV“Íá…-Š«l_¿#^Ÿî¾ß -~à©Þ6ÓÉ ähãór¯û9Øt5…¾p…8‹Fw¼F‡ôj/?2µº}+øéÒ¸û÷ƒ£ŠÁލ÷¢mÙZÀ–ìõGÒbNåô:]¹bK×P -æa:n,ÎÒ”7¼Éj¤ßíù`zÓ® -§™Ö˜ùNP3üœ: #ú]Áç¨X?ý$Ú^@±IfŸ¡V ‚v}+ù"®®Sa·?òCâ¸7ëæ>8ÝìÅê¬í\5Zî`޲DrQšß1ý¯‹ó'â4çºW¿™½{3 Iµgé~†™n°õü%Š»;È]Ÿ|Ë7 -åhŠÈÅ‹½&…q”l>P}5†3r©â,΋˜ßì÷Íón;áäƒ áyí”To9ìoî %‹zQâ¹1ª¦4æ²3>V¦›Þ 3E÷"?ÙMMn‚‡&áþŽ/äŸJ^—÷çn؇P”àh?;EòéEw—ʆ‰^DÞsè\¼>Úª~„¡ÄjWÄ%ljJe×JOß»åÁF åI~öÇæó¹§­øÔcÆÃñvšmÒ“\MΫ#ûA+W4ÝV®%†Sä]»¥”…vóøhÂú>î€(¢Ø_ù–Ü'1ô“ÁŠß¢Æ€Ìi߯û…‰2¼23;àåùb;J+›KøöJè®ÅÞÀhzâïµuµ~žn_ëÛ„@«r'Þ²¯&1)Ðju)°6°¿ÀË“‘»DIó&Jþ°*'Pt€õÙ)á°iöùOZè÷y¡·gëQFYîT½U’ãÁÉN>áÚr'§šš¯.Æz{.Ík­½†k#ÊÖ¬¢²ºo¯žõÖY©X7_šß'Ñxñ\dF«×©]BÑ~;ùÚº -¿J °|fþôÀÊ ö¨Ÿ?¡”Ôtï½W×¶•îÆûW$Fm­h]c2HÕ±&‡œ©Wǧ)öäZ8 ;(›ÓS/à§aÜž3ÝHí4zA7Éôk-„ì×JHý_ñ…üºøjsøëåüÄ+Á) E…¼r ôàKÜåq¥&ûì!F+Z°ëCï-‚ÃJ˜ón{4-5Ii\+Êx}c&â oÍ©œ3?ßä*Úv7æû‡Î»?~tR¢™â²Rí¬ñˆë¼Q³Kç’?À÷» -Nò¯L>qj=Niã yô/;ûú¼ ‡–ÀnPµòö†-6–êrÃêkŠëÌ2ÝW÷5”’ÃX…y^ÊyyÒ'SI½»d¶V'½€Ët¦øm!í¥¸c¿^i ¤Óv·ý…ð™kÔüî_Ž€°Þ/P¹*(U5!\Ñšr«Ú/å„a¶½»×;ñ†ë³Øz…•Uì5µAˆ±_ˆzÝxi=ºöÄCë.5?êÅYYérÝòœßZ‘ÕÒ§=WLŸî¬ÒϤØJeQeu4d¯8ɳ—Gý3¾ï ÈeŸ'Ø8[ PÌ/êñ„YöïX±',vv³!¶Þ´ Òc}””Ù4ºÄ¼Ïg+poʬdÑ£fs„{= U…nzz”ùÁMžs—b Æ/ì­J™Ù ˜ÆÂÎÐÏIÒO!¯Ò¡µ0uËêý°i6. œ (ޣƅ®•ÀwÉô®QkÃÙÙJ‹µt3®fk¶ç®g¤¦Ubž“ß[§::°;rX±r\?榽®“L:íLÚãdr•awËÖ¥Á•iNT@›É[Žzw«-j]ÆæÔ*g*Ôò$I_È÷åÏ„¶öaÌI`c$_D÷uuµ)9ٴůn€Ÿ/–ëí~žË8×Éc dk¼GGƒÁ‡“殓·z|*jwv‚-ð£âTi—Cƒ«FC‡%±é…1D;¢—ò9C³t½FY‡“HñbPéÞiþ|æÊeýym¤}{ÊÖ¶ù7vánËÀôÙEò0Ÿjãæa5ifŸ[ r§Q1Ý÷…ÀŽ_ýÕh‚ö;[ì {’sív¥øpõj¤°ó9²`âL Žüô'¯4§b:Ež0ªcæH*3Ó–›CàvZDæaðpæ›Eë9µðÎÍ0ŸöÔÝÆÌ¼ŽWJ*íU ³^óá].š:Ö^i;}ðRé•hW‚+ÊÕã}Õ™kƒ‰­€¥ß[¿O·±—L§Žeƒê¨ •¹N.­Aeµ°Yh4­õ/ä×ý?áØ­ŠòÆX -âzx†‹ç+RçC$§÷þ³ªpa–ûÅ–N$®ŸZ_ºÃ¶=õ%sõh?g›ï½ý…0Lz{ ×½ýNíOOÊÆI*sIa­A+·0¿ÙjŠõß„Qâˆ<ë',¾’DíðlË\ç YÓö¢Õ‰ÛÚ…‰N<Ÿr'æ¢7S“\¦»n÷•奢’‡z™2cr‡ÝFM†êæônkPTÅæA©Mâ1›“gÍY“ÅÈuÞâ~iTÞqð…Ôïñ®Ïx&[ ·ƒbMï›Mˆó¬w›TkÑt¥æìxG[ -¸dÉ¥íÓpVœíúÜë¸ïˆ¹Üé³?›^¡^ƧSîùÙÔWS¬ŒRjôð†JQ…ú}EVêÙ¨é]’®µP¾S5~Per²\Y5­Ò.nV•”÷pËÝZâ -‘¿ÿÂgwôüÂb>(UgGB«(t§VŽ;ôÊCëeì@i{ -&³M N ªµ°‹eR]X7* æÔB; ƒÌmp¬2˜×É•µxõ+©Ë),wËû¸œñ½diP?#%, sŃ‘*EЍÎ+‚*HÝè÷­v €Zû³oiÙWtC_e€õd>/Ò&„䌾d†W=ŠÈj‡5ÈvRm6cÍ àëy€º‡@Íè æZË šãfÑañÖ`ô*5œy¨–%#¨Ÿóú=\~‚_¢?ÐüÄûÿ¥è4¿?ˆ‡@{(º'Fe+3Œ>À®8Ö€™‰À˜ °„*CWÏÙ9æì˜ƒ[SÊBWósö {-h·ª¾ûe±qŒ71-Êcžz›Ù"˜q óÑŒŸ¦ÿ¶¬Ÿd™>Ô|(Å?;W°2)€m¨ÀÓàd{ pÞ žÉsm€ œ)¬Ùp ²Añ²v7mUV/¬Êl1ä -R€î0)<ÞÓ(ß?/BÕèšA£ŽÃyÿn–+¿]è­n™wmù€ì`¼€¢cd…¤°î) ¸8¡#€[3à§Lðq»]ÕÏ™ö˜{Рí{°Z=„¶]ÚÎJÐÖiAÛ¸²%BŽw“tÜêZxk >»ßiëÎ0²å§¢šã¹‡äúrsãŸð…üV²ª²ÙÄÖè® ¢}Îq°`œ÷`dëÕÀµmÀ‹¡°[ñ06M¬&áÐuV…® Þzð…Ä9ÆU¢ÑÈ1ÂÛ¬ºËtT…Í͹fÝ«(õWgÕzÇÖZükâóÏö2{x¾ðÉNÇïO -äU9õí‚ÜNM€œp¨ƒ\2„AÇ–/Õ¯ñA¨¿£s1EÅʳü¼ÎÔ³lìøp²ßˆa­~ÑÝACßÝÐÞõp‘ÌØ:>+mý³Ÿ,'¦»^Ò’~5ç°™VWŸ]ÆÑV%…BÃrîa -”†kþÖ!r¯ rÓâ8IØú ‰äÄùüTzÎòT&˜ï^ùGtºÖ”g3÷åcÞ÷­E_òÓ‡ñâ†nö—ӣвÌygì6¬0uq/;ÞÈâUøW|!¿îAÖ]|ú“ EŠÞ'tìrP/óɰžŸqÉdapt\¬¥§?8,C’n@z„÷w0NúöÎÞ¶†^ô„°T»ŽÔŒ+/2¿ë'ôE9‘jïÛ fºx´ŸÀŸ<;5ª.XÆ)äõlØ›L†ò©ã縉M™YûûìZùÔ .¦Û\6–Œ!óœ5ÃQæ#©ïv~¯‰xÞáÉ=/^gö>O?u\pJ.¢+ù=oµÝ&®s»lu,m Z³ô<þÞ*^³ž#ñbµØdã-õ"(Ê=¡¨ò 1>w@žPŒOâò À 6÷|¬êTG¼;³„qƒ+Vç ¹Êâp2ªèå°^Q·}ÆÜî>ââíéPJo”Àš®Ú”ç¬h"©ë¤™–[ñÒ1‡½ ·Ã÷b<]RÿŠ/äûò—(†"Ÿ]ôùÉ`-âôþÎ6AþÍú‘Ÿbá"Ul=x^nGTž^+ë¾y|¹¥õ~ã³[÷ÈLÛº8³–äÛM;ÿ´h5™üä'‹/|Õé#µåö ôL¢LöE®Ü6*R–ÒëK„ü–9­`ª`Ó,EŸƒŒ¿m?ïFËï¯Ékg`ôÑ€ëfHO7ú×›SNŒp˜íûMw ž½ÚԨж[ å®×Ñî¸ê¶{ÞÛ#I8©Ä¢¸Ìà†šì·ôYqÅÏõê”ÑbkÞҘ¢þÙ…—?`Å+Õ©lœíøskä‡\ÿ“h»Ž•Šqû:òȘaýVl$:çˆÞ¨Ù\‰+ràÃBen­ZÌbµ¹j«%þÌm—š½7Twèé ²Î[•FJ3g³²–H¿™Y[Á¨©sÏ4¦ÝQ™¢ÌÿŸù¥x…þÜ;‚\_Ÿó-¿3X…Ï ¬ëhºžøÁº^Lù‡£[¹Þ»—öÑ–ë½ÝH|Š›™·[ï•«®¶§fžÄ²aø¹éR'«˜£ÃÝÏ,B…3òtƒªÑnødÇÍè îÌHõØ]×Õê–€5XŸ¡ës1{|Ò­BÖ‚B4±žzºwýBΙHÜ.äºtiÕdú€±1¿¼äž½u†k´ÄŒÍs¤Ó=GnQר¹œn†3„ôçÓIé‡"jiE^â-¥\+5ä[Шȓy»ø/ä×ý?á/Qœ˜Àú¼:ïO¾å'1ÔA‘´¬Ð”©ó}×B^Þ¤/åOm7ÛØI™½io!t¦·åÒpL6é¬8çYKµÓ)–ÊÏÕqõ¸RÊÛÉV®­µ›Dnvïq¤ð‰¼Þêã¨ÿ¨Œ£Zü ðg¬­¾ßJ[ž\Ÿ|Ë:(Ñ0®lu°~ÕŽþñ–Œ®ä¬…vâ¶²½?ZM{¿˜Õ8ð‹[´éê­Û~ ¥+oqнŠZto38, ©Ù.Û£×+{úd¨¦È§h;'b”Ê Õ×¶*#®q)~Pø3¾E>Ýœþ:c=¿'>i¡91j`Ââ±™–w7`,v^u²/PýÂÆ@к•9Ú-ó -DFf<§ñã~gŠ_̾ZÒÓ¢ÜàGòØœåæ£Ôî´º{7ÄLÿ.ˆ¹NvˆùµÚ°ÿâKÃþ^ ~á ù­à&%á*Óœ?gX§`ãl’ È³£gkÆÎïÂq¼ºæßŽÛ®J¦© æ$üce%n‡E=¨¶æœ\`¦{µg1µS^ÞA®d6úJgUˆ -¿á ù§R8*Õl‹ºÈïœ,(¶¯­'…!]Œ;—Ub::j³Å¬„³…ӕجùtn&>-¼„¼BZýÒ˜—Ī˜C.ä2¸É9ª÷ì,Ú]ÆâÞycJ{ ¿ÜáÕÛÕe§µFãw‘ôÚlÐOY‰qÛS¾x£Lî>/»lh–=&¶ -Oú½Åt+bßïï7ËTæÒ¡Zƒ²Òk¦+¹)Ö#“<…=)QÚ“,¾{u¡ñgìŬշ­¾Â.•ɛѭªÛžã:ü]ÔXz¾%—¡Q%Ô쀎v¹®Û+—x©¸­s3’£˜xñô{“Ò©}J¥2§·Ñ”2vó0ÁM±ÖôÉóœ‹ÿzu_eÌb£šðšõ»uê³NGþBêŒïü£p­¥¨u½]@ëS%õl)œ¶ÝŒÅtÿ4¬{ëK?µzx1Ç>Ú•»±Í— -k Í#°Æ2G€·°ë³Ô$î—ÚçM~ª¡R»öç%þA}ƹnÒóZ+‹¯«ñž9TB¼WÞçõ«²–ÙJ»\ª@T«ÿŠ/äû²¼Ì«%36¥i °eÉÁ‚ÊHB]sÏŽó®ÛMÎÛžÂé¬Þº/évf`µÜQ~Û$üÛ¡áéÖµ~_j÷ZhžµVfô®šƒaªò>¡Ca˜ÿBÊŽ2®”3÷i«4h,Ú%ìy‹"¨Eâ3 çuo÷诖Ÿ–˜ 'Ù³i~è/ÊLM!ƒqÄ:p„©‡ G¯ù]»5È“ò7­´ó˜Vî–b£äNâU îvQlzÛÂÙ< -¥Äù’Wù³Ÿ¯"qL̉$ÑÀKxN5J¹VAl~!ß—? yWã`€Ì6³èhuhPIƒlnUÙz¶²„¼ÙtÂè†ÊT^6ÚOôZi|¾‹bÒdÞÎdî¡2Çâ BÍŒåÕAfžaAf\éL‡Ls¨ÄXµ¢>OÏP ¯þZ +uõÿ‚ÏOùKÔÕA&*njí}èH# Ëîë ;, –0Ôoàw€>{Yh»!ª|DûÌ  ´î@W÷]ã@$ÈûÉðò¯hÛSAær_ÀgÞ¢µädo+`årB¬;ÃÄd»<ÈŽ ȪcEÔ%vŸç²¸Ã†¡bo€Xé̬•= ÉÊ8vçô,±X ½ÞlÔÒüÑÜ%tÿ­5Ÿ‹ŒÉŸñ­ùy.ï³>Ý첬tÊLæ)€mn -À®Ì`Š Vy–@öQâAVëª KÎ`E¼ @£÷ n> P³‡®*lÄÌù³SßœFb?œ!ŸÝo#¨‡ùÅݬn~™·nEнA»<ý3*Þa}>ÃÏv¨½YÀ‡3ÀÓKàT8)í>â½ææ+c˜îgÃ~²–Ÿö íûïÌ4 8ZˆŽ<ÚˆÄk©åû’zOÑ|„abå¿òúæ,Ø•‡ç“‹‹´´µsµÓ”¿c±ÿ¹ÿ ŰZÉŒ ²ƒë`ÝIð<›øìÙ¸Õžüx€#º]J¼Ù®Ì“˜zL ƒM4ÆÚÞóš¯>¿g™*¤Â —ÄÂê9[ 44G %Èyoä@ǃ8`Ÿýä¸K†Ë³´t^¹¿‘yOÑs÷¶•‰³'ئ ÚÑE.Rçê!‰Ÿ¤zlíIþèÊÓj“ÊvÄ$G›rþ4°¯)oE)Žù…ÏÙ¶ÿ«àБ€ ­:^Á@™‚¢k2ñÉ®"ã -€è6W€Ð·HMÎÐp~Ì7«úˆ¿§§ñ o¤fW)Ù6ÏÓ™e£¹ÿB«LpÞ[í}·Ùλ[ëÊÛü»)8£.ZQ.î®_M¯½²qZ!Ú¬ùg@Ml -›æjž…Ï—òrØèó :¦õRC@„S+.•ì{ø,v‘Ǻ·)û}5b¼Â,Õ??æùñ‘é••½ÓYÍw_\ºD6Ül®ÒlïøìÀ³ƒf7²Iu’±Œ A®Y·ñ×~r©mîe@™9ÿÑXŒ¹[íÏ€c{Ö§»]œÐÎ ×_~>G5û>!xòëý|S/`aõw„PüB<© 4ÏÍÃø ÙÙ Ee¸½¤CySMu§¶ž¸éÖâj­×\[pW2{]n‡£ØÜKÛœ™{ñì¢XïQF¥U«ëÓY‡µZù7|!¿À©k4>AÑáö—íå“hë@ÑáxË\d‡Æi|y £ÌûV@ ¹KcFÔNeCîˆäžÙøù;o?Rßb¬åhz×à˜¼ {Í‹ÕbìŽ]C±O¿#^R¯«óÖ¼y\54³0¯ht´-ÍÖô"ÿgü¥‰ àû§÷I ýü-)Ÿ¼7AÞßÊQMÚ®6nýaï]§‰=Z‚”߉2YÙÜ«•†½véµ°Ú+ôží™"˜[†õ™xžÍõž»Ò̺³Ÿ½õw8kgÍâ,õþDIÓn+MÑ|½8q/uâÏ€}¨9XçEßé `Èþ9/”ú|ï^~’`±xdª„{+®œà¼XLSû]üÉÝÞØ¨è´òqumÇÏÆr¯Ô¨Åµ4eZ1Õ™ 1{F™¥[è|ŠÖP{2·®ê!¾ 걤—Uñ¶.¨ù‰›S.µþOøB~݃,CCQ=k\%û‰}>|NÅW:)‡´ ÌïØÛ\ïgévtÒY°+vȆ,.r° -¥Â -;•…G\zpªPsê`r3Ë/Áþ2EãÝP=jÚD¹ôeS¾‰ÖAžª/yÒÁ‹­¼\Ë´q û3þÖ<—mS˜; ùW‡†£¥¿ºŽ.Îü<Ÿ±.:h]Ømný§–²W ]í¤n–údA—ý²ö¦]üôZÔdXÜpjAã{²$IÛ ó±±/oF/ÂÇÆûX€Ä26(6¦4ý3à¨ÔÑ`gg؇Ï©8(P -ó ‰íð›Šw3BódnûÝ9Pî›æ¾ñ²Ë;ûK®P ­åæÌ¼X˜uRe"& ÅÃZ®ÓÙÎx!íÅ—œª¢Ý9Y"²«ùb‡âó¢-9ˆ#&Ú½Kæ_ñ…|_@VíCQNØÑC6[ð¶Æ=ÿȤīö:hÇleä¸U·rµß!­†ûCb1%—éù«d£³ÑÇóŠjN*Rx-4Go}ñùZ–“ëô†p‰, C‹[ Î…ÓYS…¼0 I\8¤zè«O?žÊà+m þðùG€búÊm¡Â}!7yíõίp ïGÏÜró„³¥ÕuwÓÓ‡‘Wºo­SSÉ©af•Éü‚KOGV}[Q–n ¢™á¥ü]ìW+©yo6ëìúÕÁë«Kï{»2aEÀþ_°ÌJÿkWŒ°âöóT>ºS‹Ö=¥(¯ÞpØ£‹ß®¿;éN²§oW#¾w]4–Í`ÞnTì/øžPüW;--:²+ÉÜp*%¡Ð,’ý‰xfzdÖºTê0é,åºÛY.*©®1 -rÝçÉövù3>Kêœàªˆzì`ã„aXÁ¨äÃ8$Ê~a¡–ÎïW©²?/~w†«Ïµ-Ó§Ë'åß·Ù€PUI Åç|bäð#d8<ïÑ—ìzÁ=¬vò'Ÿ¿Ãë>ìL¥½7í>½øNåJð6vÍv’• ógÌt؇òÇÏ[ݯøz¡óþ¨üHSÍ8â*GKwF-ÛY¿G«¢ cqö]­_9_&åùÆ—©Þ>mÔ8Šïê{ Þl¤§»¬›H‡e¾[×É6fN¬öìTlV’¢˜Ë§ýwhD¹6Öˆ²_È÷å_ðzÙ98ƒBi¼„›`{Ü„§ÃþÜËÞv5j›Ú$“ýêzT6{‹'iLæ]VYM‹u'ÇÕÑyœ!æ·/d8&Aí’{ÔÉNvìå³tªÈÃ;ÉÊ“3ÏLæ±LhÇ¢µ}6bjýr•¹)å"[R -ÄŸp_þœ –OœožÒ¶—Ç Z\í®üÛÚêjeïØäeåÞqÑ ¤£ó3r4½¸õ¹bÔÖ¸»%Ýá(çA£r¸õ˜2óìt3ÇWûÀ+®x  ̽WlÐá°ÙùB¨X*”Y -ІS eÒ’Š1«N7±eåÏ9ãf Y'Î5';¹Òšmè˜Ùè°m¯¼6™Ís ŽÉz纡gEáÚWâô[o—¨1¼ÎH{@ž¸@o‡—N–íÑ2Ø[ä#Œ¶>hcsmRËý«O%óµi‹7,·…ô5¤Å›ífË2u®eáLë_ȯûß­±¹§ÙhŸÏ>FÎ+I-L%åe­ ‹DÓ|Î !7UwACaF7ÆKÕþPU–J?~å½Tl;Ꮀ×Ä«¢÷˜ OÇ {—"õ~ÜZT*zöZÝAcÚt³Ms0¯ÇÍN4šƒî´× ãîŸáÉèzxÀzVï Ù´,Ÿ_ïÛJÇlԹ΃ùD+Ðh4Ñv~Næ5¢<*´Z AÛnØ~²\èu³Í«Ì¬…Þ.÷‡ ò•¹;ýªso:Ù`"ˆ•Vïd²Í}3}!Íܲh'a{!ÇÄ# çW‹,eþÏ8òOƒß„ǽºasMjÞ+¨íiÕ[-UZЀ„5þ¾Þt[U]kÛ>‚q*5Ö -Ö -‚(Š`¢ˆŠ (ÑóãÜ»íõ}«Íñü¹Èp2Í¢'éÝ@D,smhû|kí†Ø°ÆôÏ¿Õj£~§n–ÇíÖ¥®¶º!³mÚL÷Ðp¹AÐ ãW]¤u´vÖ‚R­|šßò¶’^,™ÈN•;fuX*þƒô_ü[Þø-óPÜTÖM¥²tVy^½mëÓÁëx”ÎÃA ÄŠ2&Dzþî•ï$ÒÑ›g²erb¾i <Óp'½VÝSÚp¥TÏÞ{b­ðj+¬Òâ7lù³+”žTc'ÕIá•ÄãJ•h¶+í|V†8:ÿ7,ŽSKëyË/]¦“ûA•åò•Ÿ¸æ›ZÙð­çaÍñ8mô‚Ù‚òÈ}˸§&v’ƒº7k?j2›¬ÒDÓLdD5Þ½³Õž.W6Z/ü|¿Ü+Ô¾î%®bY?hÑ]^¼"_G"!]$‹þ¿½ÜŸQRa÷bB– -Ô˜¹:ãQfû÷ÏRvÔebeÔúì(±‰yŽü­±°?g½¤1j'½©6P߬´ÉSîå´cÉž.Î%,\ÜŠ<³ˆ ž®} -Y`by¹ãåsÁ.fsëe£C}œU…™šeéÝö_øAÿõ6H¿qE]·Pé(x(ߨå2&•^9d«ívjZjbÇW±ž½i¶œ”™ªÞ‹a=7­ræ¬ôŠî¼1(xZq”?oq¾˜JM~МÒJ6ržPñ\ÓúøiRÉ%åRí~!ÍEñAfböMØú”$00Ïÿß‘ºÈMúVV[cß·œa¯Yp4†©¤\X°ì@o6—.i€1¥€¾P `gÐÌú¬¬zï -Ð}ö ±ÉA$5€º…@7ub$À\Pe,&Ä2Ë¿èÕyðô›vÿW¬Tü¯P¬iB¡W8‹ã5ãðIø`̼î Ç«[€+Ø1|˲h–6^uÖ½Æâ €åð¦œzr¾ ­¤ú2‹M2M€åÄæçNä×Q*LžþÐâëBáâÃþ…ôŸô…E(tê­žo¾ô÷ë„Q¸ö\,€7N°¢ X8„}t`Iw `bNƒà,€õôTÜ¿e©U2ËÛðÎÙÖ &Ó×Ñ*LŸ“æYŽg]yüXÈìðÁsƒ_k±e†À.ñàÒøðûë{;QÑaÙÒàñʸ ` †UX‚ÒV5q,BàÿöXØ-LÞ˜º}CŒž“XX ¾ÍlP€÷ÕGPá=«< %{öc~öƒÞc¼(E uFëÓû6`«½¬Jl 5'@tò/@l÷ÐË?æ;€0¤ Æ—= ØJð§”øjÛøÆ“‡wØ'Ê” -p²ýý]Añ dö3zÌb)€-õZBÂåÊ+«³8(mÔ#³ê7°­„é0 33at³Ëîoø¾‰^`s·¡Ð¡¬ÀÊ,l¡z…Bod)]dóûuó´$Iáqo@ˆV¹[àWVôš]'²á$¼Ú>¿¼Kûþ›½÷+N‰ïîôì¶ÃÔð¡fSÓ»Ž-ü,íf¤yàªÕÉUäÂÑ5‡¹_°'Â6·¿QƈCÚ$ç|éÔJß5ÿÅâyE–â#+ˆÏ­’Œ‘[ÿ%+éy±çËgñ&™±òèw¬v}¨›þ󟺙{ä£MËï„ÝçUºqdzð=Z¿ÈóõÌW]ŒÿA=Àêoó”Û¿`Ž(vß§ºÃï~`Ð|±h÷û\Râ+ó¹h©çGÓH%QŸêâ7~±)^Ϥq£~÷ôš‡Þ6KGëTŸœ5¹rãÄÚŸ¸ØÛç ìîL3*mÏ‹Ÿæîþž ¬Õ¹Þ6· Û0v© üƒ}Ë-þ -€={ —†BÕÔУ èDl‚lý#¿ÊËóö±A^4XñžF™Ë¼ hè¢E/³0?èadM—~‹}ç*;¬<åÝ#YZÏcXMöæ™Ûu%1,Ó9ŸøÖvøj³›1Y©¬}µX\OX<ÿ+ŽèÒw´ün²óPh”{:@(¸­Dn“X_ëú)åÜ›HõÃËBï‚SÇ«"GÜon®·É;J—ªìb~[·Ö×f×4Tth p_Úî«ÇÅæX8?膞—u^[ãëBfήÊE©¬/¶B^‹Ó}bø;  m$Ø5ÇK@e: [Ü Ö"›ÙGÒ~U”D|¤ÔÞâ …êswU®èådöíû‘İ¿–#Ä^ŽqÂm)göš¯’ÙZmãU[íõd V35–ôÅ4^j/ëhõœñÒêËci¹Aýü²µ³ªYIˆ\ÿ¹ü ï¹@ PÝ© èµ…j\亅Nü´Ü\ù°³àZp¶çfV;ݨqu ZíakÓÁÛêl¦iÃY½áh¹¥ôqv-[IqU!uF‹W½ö2W†ê‡«LÕL§±^ üóua‹dnÁ±­ì{vÉ9߬£­ßMÝ›@ZŸI¤ ëè— `eª¹dš-´Û—ÚÇ%Jº.øåêÔßìCÎ'‡-?n–™ŽƨFïY¾éÕíc:ûé%R%ÕôOì‚ãWÝ9_›òŠØ²Ôtv¶ðƒ’ëp¸’‘êe…-[Ï „}Ôk¨;CɆ°ûì×·5îëú­z]öÊãh1ÁS9c½÷¬œ{ ²Ö]Гæ­U’Ò“rSDù:?ž¾}lLhóHDKDD +-öÃèÿ8&e§ÐØÑÕŸB¦™OrygrwesÁ­x*ø–”®.UŒs¥”òƒZX/\oóI`¯j‹ËAKÞþâ+Êù¡ß§‘•zÉõp–L¼NˆX-ÎI2`yßñz|Ì*£kôò…,×D„lc˜Ʋú†Ðÿ?øAÿõ ”² ½ºY”ƒB…îçUõ7DÄ—nèµVí Þ!œåÜ*êvvVÚ’ ±´X®'e«òÒQ‰Ùî¨\n±?½oÚ· :p®”zzç=vO ”òÙУ`%—‡ÑBí ™8žrñjᣊŒ*é|j¤Ð ÑþpLêÂú4†ÈöÕänj®ù×n#s»t•·¿km_Ǽ]&UÄ7,<è6·lKÑ“+·^bó¡5Ÿ¨³ýŒIŸO“·D]$”™†cA žüeí¦F• OpÏq£8\¯5H ê¸o1õÝ:û0Ã5‚tŠÓ“Ü›Ó -ü -@&3hC[ dO^øx³\x©¿ ÛGâýÑOéÈe*.²Û SC:ÑýÕ󖳘†¨Ôr‘«Óë™:lYr+Ð] kÞOãl±åËè5jì$ášG¤¹b¾·§‹õ1Q„5zûž‘î÷½w¦oRŸ¾eð/ü ÿ¤¡±ï€žÞì—²/mLj'NI6Gi—±Üh0öíÏŒæØ_f7LißÔÓÖ™_øøi6»{¾*w2ØFÂ¥ÞN¸d81ë_z‡ÅìÑ•Š\ªS|ôèv%à™Ö²âZµôÄh-»+h/íJT£ÚŠWÂÚÁ§ö;u¬idó^úo>'³áEržIÚ é)—Ͷšz«íÔy/…Ô'7¯¬ŠiŠªö¡½ðÝ™(KO鹢Ι´c °vÓéWo¿dŽQ{±5>-M³éæz°OºÌ7 ÿa6ŒóóÓlõV¹æºge›/qKÿ ?èŸ ló©v­ìŠœGsnÏ -·CËÆÀ(¬Œñúy|«únÕÙ/Ç\-ž/Z61í<îeIØ£MáܨY6E®_ªÌûBá´ê^$ÁêÌ˯Í^ò!Ì,èO3¥3t£fõºÓ ÇõaveׇÈ<]wúT®ôÙb©ó¿"ÅúðÔ_ ;ûˆ÷Û;D-7·×yÒ^m6ô÷5BËÊ‹`ã}d;ÝÇ*[â[êÔQææÏŠÌm7+ý -¸ºük}÷YœËÛËÏëïË6¯Šz¿8{Mj¬¼W³–TH{GA“×Q:>ÜãAôfj‚v4C§[¼Wjeèv£‰Õ9®A¶{R}lñj팪V­0óàŒÌ–Õ f¢Ze˜½X®Æ£ _­ß»jµþÈ_+ÉdØc¿ÂNJqÙÈÕî´þö)5¼¨Y³5£$ïùªÕBcýƒŽ>«“ÁñxÓ鮡׉W£K»)Ü›6ƒŸ?dêÙhEÖ -ÏyžUšK†‰Œm»ÛÛaµäÊFx®ÊŸ3µ/›2–{ÅCºd/R9¥]âØîúûü>;³~…á-ïÔªY¢15ËØtËfÑ÷M”OzM~È¥C±/çËb—‰=¹ÝNóó&GáZÝ›^¶5™ÙÚlH‡ïžÞtÿ\m`ͰҦ`ÙM©ú)g®u˜}y+òì ZðV‹n!û>Jù³Ièy¹×ßç‹Èêš öh -¢ŠýŠÕsúý%Q·'™éæ¼ÿˆ“B/5j_‹è@$ÏL7Z4jívjË41¯T«‹Eè‚)u­Ç°©Ñ°’ìË´÷̤dËÑ´èνE‘|x«B61·y¹mÚ¹À²Ùho^¿1Xòü¢u Tr-¨ÍLlPí²Í‘Ÿè3#?wlþ+Ôc¥ù™&ç5ó£ä54»Ü³wKév²É§›a õì-‹|ûØËF«zÇËŸcŽ*q… ]ä+N¡}M˹À¬Ur¤ÊfY‚hÐúmSÉ퓟à5"{•—HØZzN` ½ÆùvÍÁ<ëûƒ{ûÄÄAÅrXÿùÏåo(½ìD[òíâS€†Ê`DäŒ,Gý((@½ˆè²o‘v+€ cŒ†O€b;  xºPýÆ. õr³`…)¢·ƒˆ#€D4„NCe€Ü3µÿ?ØdÈž+¿â¿2ýíèéý·%t(t„!€µlJþ@¡÷o¨õtÝtW=t2Pò•hƒ(Cô[-Î9ˆ‹ P×!8"y”ì!fþ› ™°¥ -í?HœÖz.níE—”ê¯huÛhúÄþº€í PæU{ÂTLŒ'€µµ¯¿Q¯-ÀÒ @ýíJ~ÑÕK°ºÔ¨lÁ²p·@‡EX ¡|h3‰Úâ ¨Áµ[\2´ÈþKp¼öS:еøše«×–ÿKÿ€v,(´sãÿÅœhpø•¯ù Àëå,À’A`?ض´˜0½¬ê§–¥ Ëô—°*á:ô[c6Pê°&@W -ì"«äЙ˜…8³ ¡ž;/á ŸÒ[Ä×±Ø}„r®ñ¨OæWüW¦‘6Uçß쟸írq¸¾!Æy€k»6À9tðâÐp߀=°E­ °ñ´0f Kš½)CD{Ч$û鼎*•{úÓ_ÅîúõÜðÁàøà®)Ïn´¶¬føA%öWTp Ðwn °§ûÝÊÐÅ»^!ŒÒÇ ºÄÙþàçíw·-.?_o–(€½ûl⎥~BÚ’øòŠÓù++H«ç9½´ŸÍñãkp‰ãrµJ=˜B‡‰^÷fæ<†Æk: 3£~t3Åöü ÿú óC`Mv p6˜C[:0@DïáÇ­ˆñó”.øûuš¿©ç¥îWžÅö¾Wò‹á#Zkòƒ‰4èWÞudfDIsæFëp†Ÿl’¹íŽÙr°»í«Wœ ®Ùâ‘;“Žö¤«zñWt{éÃ^É‹·2ß`èÑþÆ“RȸHG¨AœÆ€œ¥Ö/?¾œž¥‡?•r^’|n-J5”nˆ¤£W¨L·ÓRÂkv–ó.“ˆÏå©EŸôŒSö‡j~££C¶é¥šúÁIߊ.ï -Ô¯èáÖ8¢ŠÿÝ$ˆ½ï~àPl‡TïPT_²qÑcUÜûúÞ~Ð(Þ·}!¡¿,\s^=ßàŒå«þž;é›ÅØÛ,'Êñs]¬½òñp°¥pùòˆÞ{гÝK©Z͹HtÙ¾­Âìîn,ðáý' í<5xÏ– £<øî½«¾¦=å­(@S¹ÖërEÆq­(-î©Yh†8Ñòƒ¬¢?.å2š:½æ]ÔÛ^úØï™¥ÃàYWÈ´;ÐoÐð.tEvŠ~m+®éíØ ž²ž7g=ãc59¦`v”iXwùpÈÌq7ÏPf¢i€ÊHÇïñIN“=³‰Ï÷ZñÃ8 ïÝtuz;"³õuZ?üÆØò½^ûh’¸ôI;×EØ!%äv÷W­b=£RËôê:DujX]a³u8p߇¶î¹âº9¶zÙ mæ°õ$~ÀV(ô“LYÿž”7v|@·äïéç•ü«ÜÇÙû‡“:áèú_g‹ÂÒoîqãèÌóîêŠfËw‚­Ú̦ñ´ä•2±†»Æ(»úÙÒæˆšk?UàÖùÚ@YM×WwURœ÷ª¦´V¤ÎT¥ŒöxôÞô,¾€]ÚМÛ$ª=VߓݠнýÙ—‡®É­qB­ȪÁù‰¢Êǽ-i®t5L§Úb×¹;$Ósw:†kË9_ê¾xý -8nŠ ܺ (¦fÀ^™»€ìI=ï©9r“ ¦×°àoý¸~¤Œ·¯ä“ݪ‰FÏÕº¥ÇUs=åw®~Ÿ•OZCwƒeŠã…½)¾ç®„âŠ7B‹³shÎ -ô¤‚Qã0“'0»d2™™l§ß³âàÿŠïü"ªC8 ¦°söMØæ (ÔÏ¥âUÎÉ„ÂE® ¹&½}…/»SYêÚúg2¶2¾2ÛŽÕr=¼·º†¶¬å;{vÕ~µwZà›ÔM9åœçL6fétªèZVç3f?Âá¤Ñ˜›2k&OyY™}ä%¥/¿ûʬÏf°kÎ-ØæüõO”ñÑiòàVH`O>·¦dú8îW -ûê¼ÔØ¥.…¡Á»É:0ús=®úò~6*ºå¬¹°¿ï•<»:Ãñ¡—— L÷&µ¬$Z•AOì=ˆ•” -ùHÚî³@Ú®Ëp„æñ+¹#@NÖ -  ÇÙ¾~KŠ îè¿_£š¸´pqLÛë~ȘHz›‹ -„•zÎOµv~¤BÿepvWó±–3fWskO«aí8yqé@ÚÞO±?ÛdÆCÑÌ ‡k¦-ÐåÅ|LÈøm¼g¼"~‰èøsÿ€ ƒïÙ‚¤ïÏݧ])“Zûãýzrƹ:l~B´©Ñ,«ÔF¥Kv2ºž–gÞ¿‰/!‹ob›èWbôþ£W`ÿd³÷ãutÿSÿ¬¡î–s(Šœ+íTS¦Ûä7¦4ô—1ï/ûÖNXЙx2»M*ŠüdWÚ¤=.mE[ÙÛcrß=òç^á6šÝ>ÉpqFpN»±U®>? \<ªzÃHc_£²Ï?Ge­BOü{ù ±DhCôvΜ\ñÌodå„YÊò(¡övϾùÓ®wÀÈ5³ô*¶ŠU­k÷;‹ã~6TJ‡0­m¨Ù:é §ª8¬¿W©?1ø¢šÝïÅäÂé~ð´ùÒ7wH©Ôaߌî`K‚'×P*<«4õ”vt8ZnRGõüts‚·wžÒá||NÝ*›[Ú Ø›Âh~ßTª·WÏ¢Ê D] D¦;­küpÒï’8bš3aB—£°Õ]kòg7xÓçSßrí{oØ· íw{PèÒßï -sÃî9ÏSÜGóø«ß¯á÷_mh"Á®)é×ÀÇû§þj>8œh¶ì¼ÊÛ¿#YÄIØnKúòƒ®š›ñg9[ô|Z «ÓþiNzJ£'RÄ– -€žŒÔünÎ%½º6èn?Vo˜‡îñd†ÉÌü´¯÷T¾=[ªýöl·ƒ5`lïÝl™Ýq ü,ËÿÒÿPéÝäÑÊgˆBë8fª½=›ºövÖHë™cVnË‹£ºÚ¤× Ë“ñüzè ³Z¤å'ýˈtjñ·³ÔÕÒ¨ðƒrí&÷RyÑãýϦ›ãá’ô–n­;½›Ï<•mêW»×Ôƒã®ÅÊ«¨uÏå@»ÂRñ¯Ô6”®AÿÐò†Â zâûˆZ7w¦—­'56Ì$è®RtFÒð -i,ŠYõ"Sìp}Ú\:Iú#v'výÅ@éÌŠåu»úÈî[µ 4›÷ú§aÔ6…´‘ÞýFz›·F3õh¼§½w³épɯ¸¿‡îÀoOÓì!¬k»WS‡+­›6«g®Å¦šÏó¹Ñ[ÌšœíeÈÎÃñeòxóË+-îN•?%¥S6¢Á¦lxá¹lXôã_øAÿIï2xŽÚ(bÑú“uz>»fSrï6{gmò=zogmŽÇÖã~áÊÈ]&Nívz³jÚU/g‚Fåáx‰½‰(ã—(b3F*¹YúW}ׂ•¾QÛù é§ ÐlEhFt¾1س}ˆ“ƒ©©\H3àR®«!×6È|W€¤ª/¹2»,Ä«2Zdü)2gD‡]/™ o~rÈ?€¹ÀKæ‹ôï@ -h õú°FeRP踷hWßÃ?ã HÈeÃdõd6XdttÒFoß²´ûo€TÄ5: 亂ˆÕ Y\‡Ð=ˆä›‚á7æâ¿`u"±Ÿ2ö+¾¹ü:étJFÿÝ¿|”× £#@Íl PQ$ÊžkE @^‹@®@Ü*Ìy39oŽ@”W Ð?ˆ0ìC8€Œ©Õ7—±}ˆH&ü®ïî¢ä^‡&G¿øwƒ|QFÿ°* (Ó—znPæ»=XÉܬEž &àƒý˜$@O™@—£ @% Ð:á´Ðÿì×@’k €frûVåmÉCÜç9 v¯’Nð %…•ü3§òñe²ñtp£~àÌU¸¿ ºþŸ€="<À†â7¼oö] Î)€­?9€ñ­6À*Æ`©· P¿vènù¨vý¾ -•Ñ2@¹Z  aáȉs»,ábžÚ½¹<¥¹ø‰§ÚœxÌ7óüýAÍ ÷Úr˜^µ&­Ñ­óòo€-^úîéýo8´"ìi+ï3À—ûàZ6ðÁ plÑXðšl[u¾qð(Ù?ŒtBbý·ò‹¾›ÏI~ßæ÷×q<ý\gqÉ"¶°Ö?ý ÷ågõŽš«vŽFî¶ë ·AvŸ öºF#nÿ -ØæMØœ·”éptêã9 Ršˆ* -}¨ê½¯~ý}ÿ1¸^Óª“wžçýëj˜Äe'ÀÌÂÎÝ5{S½×v+ZÏm.|ï6bØÅ¼e˜ÞbÎ .âë‘W‹o¹ôùúò²~D*¸Ï¨rŠ{½ÔzŠE>ý7´úÝÀßDúÏ)ÔXV¡\]@Üú@ŸDH°É8CŸWê:«äx{N/Ú<’(47Þçf÷—øzÃfÓ|@öšÕëxÕl]ü°Ã¯ç¦ä‡7A?=—³·Þ€×ÑxT3ÇîcIìÉ9pu)uÀ1þíºn;ù`‹¯ ÌUîûN\µÇPãDd±äR¶ž€tJT2~×jϲ²ï>ê­î$ê€@¿9AÛ -¨ËÝ¿\˜etVpŠ_ŸŒ—xgÒk#^áØ 7ÌÁfÜΓƒ±; -ËÐö÷ã³vqÎS8ÅÒ±oÄ,e+¡ìŠ!Ä»{·óø`›ÚGz°M?”™ìÖ€´… r©P=‰~V&aéGTØ“C®èaž¹Ù¥ô†UôjM¯3êŽÈ%s=àÛàáRó½Ïµ§¨#/§Y[™ •]äÍÛ;6w‘,}Æ;fòí°Mßf'µf‡Ú= k¹ˆ¾€>òŸË¿0üÔxO‚'%èݽ·€b¬3 ¼öûU"òØ£ÙÖ²á°W‚Ü®×õãwg䙕õì°?&K—¾)†s­W\{nÞü8 -L­ÈZu§/sÛeÌ”0  dÐ-o9Wën4:ßPšãm¨X|lh&xmènüXKpÆúâú7Œ{¶nê ‚–üÄ%PÆí è–òŽ5ú‘ŽùLŠfÞå–Œ‡¸»®ë…ÑÈ æ Ùff§¥õz -[ÓèŽÑ|o;´ÖÁF˜ãµ$r©ÕÅ©Uñ½aõy/ÃÃÕøÝÙYÚ#¿ ôª_Œu&ݾk]åªiÊÿ`çLA¡‘Éýg Å4M@“ÈåuÏû§°zÝÆáã}a4–ðvcºøƒè~«áÌb§_O‚iNó²1xøË-ÙoÖþµ`¯®‹ØÓE;д´/_õmF}Wœ¼ÚÑðÎÂJ\]MÛþIíö¦wÕð¬›ÚU·ç/ü?øAÿIÿÀ.x å󶹡Àªœ~_ÊX¹<îFU ¯A¼~œ>ž•:xØžvB#¨îV¼eösÍÁö@Ç›ÅLWJ3XêjFÝÀ«Ï»ek;©é¸-ÐË-™Ê©Å£Ð×ÔLk…ÜÞá|ô®sòXò¿ðþhCÅ6 F1”é_€z[ßß’èw}$óíùvBÂÓù9/\<¼É¾ÜÂ}„ÛuY)š»Ñ¶¶=hw}qGCý>tM?sÓe»ƒ/ÕÌõ´]`æÊQ¼ÒП³ýÇ4Àéi¹³.È‹smôƒN˦²››ñuV,ÿÀ›qøβP¨Kò€Ê¦—€n^œd²nœï½AÕ Šá`ç§^æñ-ÿî¨{2c™JZ%Év*ë«¶nè6éjͮɬ5º|qœ<û-÷@Oüi8 õ¾¯¯.@¡]Zôôî<¡j…|´3.K?YyC³a»¥ºsÛ…ÿ1¹IŠÜœ§¥ÒjáŽX­Elšê®˜ë-ˆš5R$±&ÍŠà=ÿîéìV“g{aK›×ø,šÑ8‘œ¹5Ó;˜ ‹VWñ¤N;qøÂý ±‹]hCuvÍš?6¯oÄ©=þAÏ+¾2ñÓKV9øòÓp´TÇ·,[K¶§–…®+0«%V¡²Ìtåú‚¤îmeR¦á0=–—þZž4ù±&¥^ecì¨ØA8LɈ?Ù /e½*²õ·œ;Ú¶ÿÅŸÔþo€ü¦mh4TØÔnGОõèÀúÓ-ïš·‰[®ÎW»÷åíšÄ¼~ß”X!­¯ê\-{«¸¸à,£Èè¶1ry(ºsI†ÒÅJÎÇÄ"Xó~ÑvFWÚ½C,xç7¹<œ¿µÙh–·l~Ríx.º_ìÿ…ôÏ» …ú ñYågò%Í['«TíLmp˜Ø}ÞÑzn c·ô“`õV€ÖQføâ°0sÊ¥ƒ—§wRªM’[Ø’zØd bË‚ Œžü}ãACՆჷ¸G úƒ×}“ ÖZ1?Xë̄Ӽ¾5\$¹Ó0ª³î0*¿³ (ÚoGþÒð“ ]÷ˆô«å^7þ÷÷JætìíLјžÜÝ®£–íë­1òTGùBzž×ejú@/Å ¸T«R?}lŒ•nWÈ “ÑHÑUiÈҽŠq+ÛþÇb½~fÏÇ={²=ŽŠ„¾¹*Y?è Ý»xBÜAçoÄ5é'§ê޳&sB:{æ ¹Z݉ÑV}gª™šÉ üæF%ôv8wÉÑü\z¿gL§‚B«|GKZB·ïâX E³­Q•qûÜjÎ ƒVQúˆ…¯{ø¾rìRfûÑ9õ.dGBÝQW ÃM—Çž3=íûˆsqþ†ï\YÇz‘e#°2e¿|¤‚ny_}‘Õ™µË&%p¥ÍÍU{«ÕLžiÈ·â]8ÏT{ýÛu<-agµk–//å¡vLj\«avú;­7ì*•éÚ¥¯Ôª“÷rûvéÒŽZáôF´eoØžÂm'/½Ž¿8:þØÙÿ °k¶A~_­xö®[pKVÚ}²jÁ¤Òxv;ë÷ùý÷ô^×Pý),üº³š=Êè^ît„GÞS¸ŒKéÑßÃÆº•˜qRíã´Úèz.7èÈýŠÔVZ¬ÞbÖiêC5j$^ -‡ø › *\7ãÃôðƒ¶Ô†å~±ÿÏ!ß:×å]ñ Q™¼ý2ԜɧµQ¢)¡oß5JõðRiQÐú½Y\ËOå®llÅc@…ÙP GZO~PΨ­3T­S½c +uó·Þ)¥Ûz ŠØ\yò²±uT»‘&‚°ÞÛx½¿ê–_Ú4ÒiëØètNn£Suœ¿!ðÑjùõ°Ëî«S;ga•;± ¢!¦o·ïïV< SØò€š¶W£êÄÉ̆¢¸cB¥XCé8$¢>ßÚ¾»çU í( ™m3IRm&Ës»ñYB#£Fp´¬cëŒ]}ºaô>$ëI/¡Fî,£Fêȱ6:ãnmd§öƒ¿>Œrî”õ²»ô-El QÑ·Õ]JSÝÔ,^$™‰Ó1¡¿?¾òjV¥¨=z‰öx˜^(Ú€¨]Oº>Ï0·º·—Çá§Õ”ëx³3¯—ý Ó¬•ú°&,G VÚ¯wlž'"fz_õËÙ©]Ü0S]v™©,ù’™rüþo8R制›ç -¹=—›™Ucn}TáDƒYÌh¯ ·+$BØéQ²—KÃ^Nnx¦Í÷ä®t¢ýmÝÖÅ“Ûj—¬ëÚÌÀe\‹LJ/²5q`0¬Ìï{LpzÉL¥^3ªêfy©²B ©<é{³¢Û¦VÑådWчØáW8Ë#OãY3ýƒ®ôù¨‡Sú5ÓìîCr_^$T§Ü}˜‰·È@Vª½K\ìt©ç¨Ýä¦u™«¡!ušëìky¡sa§þ0fæÉ8]}´tåÕ_W+Íë£_6*ùÙZî¦V»Ò®ÿ JƒÂŠ,¡7…/¡‡Ï²„Ž™DÝþÌQMʬ™Û2YâÑû9{œ[wÉÝonüòˆ\ûûÐïÍè-Õ‰ëOæ{"[ßj5žÔo+a\“3Õ[> *Ã<ˆmµž¤J›Bý²)aÉžI ‹Êh‘|¶ -±¹h²ï‡˜—{%=_D¶§ï¾ÔT.ps­œÂodoþ}²^þ…uÔØ&K. -³‡^%w­|­zò£eóÔQmãÅ(ÞÄŽ>UÏÞÆ96кe†M‘l%q’VùsŒz%[6¡£7×á¦àiŠ\È&ªš/¦äuNé«»¬:ÒOtìcº‘§0*¹ÊUj3‹T»RWÈÏ}g“¦ÖO vØß !­×CYd¨pÄ_àŒ\£Ó€¬•¼¶.d­Æ«kge1ѺW™Ï³ü9œß%{beŠ|E -ÙW“Ì>y:WAÐB–Å_eºA_j3 ›¤©ø=2söz)8ßÎoP̳„&ö/ &š–ÃV zvŸ}ô| ø¿A ³åÛ›qg¾–Þzýã‹8¶UÒª†ˆÆ¨¥Í¾²áp³œ9Ýœ"_žÂY¬}6Ý\`^V<ÎtƒZŸ©v~r%{åîàØòwWåæä»PþÙnPhï‘ -ÍÕ3êDçÒºbËi˜Õ6•<ÇÔFÛÝ!®¯?øAÿIÿÈãÈŒv È$h …Îb.Â?d\°™yû2+2YޜỠ{¾`:Dw±öAÚñCÞ£p䇗îF¤]´ ±‚·»Þ~hÀÛoxûÍ<ÿþÿpú+þ#s}d 8ʜߦÉVÈà¾H.¬2@Ž@æpCAfQ©€LWé É Ã2kÉMöÇ ÈäÓ1D=1#@=ÂïÆòmg -2ÅâB>@œ¿greŠQøÉ·dwó‚d07/È”'ðË^Š«A¡þ’Èã"äLÁ5’-"{1@šBdA&ô¾'äf¼ìdÌ‘±÷@F}ÁL—¥b ÿuù †K¯*ÈLš]ˆûdä6lV`f•‰lÄ/>½¹¿È>½û"ø?è?iØæZ õ4ê÷W ÝÔ c~SÞ  Ð|ri“Y™u€ ?<@Z]XÛåµ ñº„Ê? F)€¤4"üž•†¤ó ˆ)œcO5±uÎ}¹æì+q|¸W\¬ï[*= ¿¸ý s=V²+ÕþÁC·7îOxù»?™pM€~°3@½î ¢ž(7Š2°[¼´ÿÅÁó!@6}Eç²Ï%Üɯ$¸ôn½ø€¾(E˜?s’e?n­Ãå~‡SÆu.Ð^"¸ZŽ£F¸„}À˜‡á¶+@òa é&z8tV¿o ØÌvÍÚÛo›Ùi€‘A`©z §Å4Ù×öp„yIãð¢{»à)­ö¯g¾tNÇÓ±GÄ¥lR|̹„}T‰Bï~7F“èi­p+\ý›¥§sH¿‚¡òޝÇÔ5º -Ö>¸Òýõå_øAÿ\"½¡PAª¬x‡B/}ûdù}3tö$?xC–0¥„§ý繌‰q€d´Gx‹w÷Çî|º×/ŒÖ”›„ïþ ;k‡¼Y‘[ÿÖïÀ¹R|@”•Õååž§GïêWístÒŠfྲྀŠï­ùäµhîð7@£ÉC™·”¹BÚï™c€ßëߣýüãŸwwÀžG¤GØKªd ±òv÷'Σͤ8 »Ñsu8';—§ë˜“‹¿•’óU¤ýP©§Ç²U<Õ®£†·.ïÇG£–7»Ñðx@?NàŽÔíùuÉÞÊÛ{¹åa?¾HÎßûe­°ÆéûNðFàQüÝÄú=5íFŸ¦ÏW>)g¡NDÛU¾9k«P«Úè"ïBèYøÑi¢ôcx›¶?šxr>f:Éý€Õω;RnÈþx¹}¶À6‰rVìÙ®`í¢úÌÛ©ÃÛ±ØÎµâÀp¾Øý ~MV¦ß¡PUìN;€Ä¾g(ÉCÿuîô£G,_Q†ÃÓÁñºÌ^fø£zzQÙ¦×)¹ÜÁiô$÷¦æ{/I­³wÙÙãír¸ vÌyÿ –^Ü#f"< -fëÜéfhF&äm!…2ŸHೈd<û¾‹ÓTfåÇê>ô¡Uö &¼»ùì½²Ko@Ë™î绚+wì;=³þU7ÛÃ0zËÔpÕ®[œ\Ç^Œ°µ·/TWgo?Y]¨ùv5¥€³šVè#¯¦ -~ 74Ÿ--§Øøoø®®)PcÕx]ýÃ}+Nö7ap>\ªd1ð,,ÿ:ï^f_Ø\r6ÓªV¬5wlü &tûFÃ["—Èêí©k‰ñŒÕßìõ9m^µûàœhì!G/Wiw°\u×ËåÊlî–+¯¿]¾Z«?Xý ãæÏ¯ íæÂwäÎ_|7]ñýþ0Ã=þÙ\gÑ9Y[ž€º\í5oЯTSO.ú­&2wÌ{cî܃Ù´íÝú›Å „®ƒ&­ÿ °kž`}FröJFTú¥*œ›æ~¬Ý²ieî'ÄaùƒGõ´µŸ¾ëçÝ+¥>M‹¹¦·îƒ!6ÙWX)D£¢3“gC«·}õ}FjÚëO說͉EÞT„sÓŸI7é5Ë/ˆâ,ï¥Ù¤•è3?讕ìà{²¤’Úß°ó©B¡ƒ‘¨.ª>VûÂ4Üë®|-¯àÙÒÔ=—÷[ûg<«se#ƒ(ÎÁú2 UeÚ&5]»¾'²QóªšYW› LDûÊøã™ÿØ)Ó««­äð´se}Ü'ß%&Ú|ÈÉÌ”We‰t9’°å´|gÔ¿àHŠDKo}#$Úx³—ˆ¿ÎÈZýçª-3¬vTÊïËÏ®¶k¹öÞØ÷Ëf’®Æ«joöÑVc_¦”%½àÖDaNe}FÉ­5 ´ÑPŽøúd÷MJZ aÄÛ½ˆÆ3BÄî>슟ÝS‘6 E—’·½œ4†‘ú7@¢j€X»°>šxúêùq~0'³j°ží›°ZÇC[?f ÿbÚKÎÚfõíi^¡öZ À2]?dÃ~PN‹kv6Åg¥iEcê2Ëc]¸øŠñzŠ˜9Ÿ·cìöñž¼žµÆxe*¼¨‰½³¸-f³€eù^þ€‹E(ôznÇwDï\§Ç°xúЃ¢‡%­ÊáT©3Î]Åz»”fË¿/n×ÁA:êuϸ.»c,^Œ*òGñ[itV’5ê•éza²š}©ƒØ-1”á—yY qÅç&úa$ß2¯Qq³|nOKüiyY -B‡Z‡Ccþ7Üdk€Ì'ЩôK?èù!Ïrž]Èæ¢™÷·•W´›MÓt\YÚ\7}Åjy{ùyŽO wpŽ”sžMfåÿÇØ}n+Ê´ƒ>‚}dP1 *Š 0€(¢ ˆŠŠL áü§ìçý¦göêYkþ\ж67•î.±r„UcÐ&F¡å-m–†’Ýá³Þíç³Ä°WÂ3s‰ÍTLœ+ÖK¬´ò÷¾5YÍ{ãÏMïyµPû ¹˜Qm_ixô9yS™#•ú¹ý]šåœO®“Ûl³õªÓ½%+޵ÌXMÞ뙘ð`æÚœv‹!Õ¬Ï%0YX¨!Uêú§XíMZPKb½sO¬nS¡q‹7]›éÜ»­W¹ ðÄd |n5]4fÓ©Ta\í?Øßý?€Z$×Â2Ú¯ßÈ${öWò îTjW«Ô3[X»öwKCn:æ²US瘼\Lϲ›L»âE]T^Oe“íF2Á<¡Ái7û>A·ˆ”sÒS ÑÌŸya™n¤n{±wÛ]uðNÕo04Û.©‹èð´».Õ´nºLþ¨CZõAtaú²dqÒ=;GÙ½ô2±mYgÌ>î[°Å,j¹ç4sû ôáx8Ÿ<׺dŠGeKlü¡å߃<½{w»JÕ;D ±r.t7ÔŠë8á´ÛîÛSµu -\«E72`¤Ô¼&w²E÷±<­íæJÓ1lLþELa׆O?lP”“5~(JM|›à7tMz Èš÷ÄŒÞGÂ@3^E?–‚VF(0RR£¹i) 9Ã<>÷½grïUH=?NZ¯ïb“Ý>ÒóJëBNÛͱ3Vøq¶xvH_ïÜçY9øGf;iŽ]XkzDvòp.ÿ÷þÛd]æú.r”+Ó"¶«!(¶Þ׺å#·ÄØýÌ\Nt:eh­œäx5¶NeÇÔõ¡ìº«¾fÕöR¾¯b37ztAîÓéc!òƒµsk7Ûôs†VæÍFPß)õˆ½™uÞ,Ÿëü.CÔù%Ò©Gµã¤Á)ïIøÀãqïï.ÙÓvia{í˜A7[h -¯FÃW Æ0u¸ΉÙñ¥=*í×ø3í£j+š•rYmóf<ê½¹±!5”ìZØ/n—$†·öy˼[c>MšåðFðÕä^ªÇ¦Û¨C«×€ë£¶g§šD6±¬¶ãÛ]¼ÇªÁ®sÀÆÿâ6uz7w«£Nƒ»£vöÇ‹` …sq½NKíÊ]µçk_>uÔpx•ÎÔ€¹Þj=î2ó}±}fÕîüœºÝ´üìèÔÔÜç”ܧÁ÷¬Þ–3N2\­7¬÷«Çę̈æŠé©škD5G»ÕÄNªÙ׌ñƒýÙœ„ž‰íæ9Ü’"^êw%4PæñšŽuî®&gó*Ë4}ë…£/­ú÷Xì°œ@¼²`\úH­»y7ç¬aòõh¸m´ Þ¥ÞùðONL¿+UjTÌTR«VnÓT)Þ¨9;-MŽì”Ÿì4Óê‚â|Ä–ç« [Væã±÷²éÎ;©ÅÔ§¡±^~^Óÿ‰ôb¹ëëØ å$H{ìÒ­kA{²á­ÊpRâ£ú²ÕH[ö îàÆ÷1oƪvÊë‡ê•ѽjaf*Ì″z’–ËÌ eî¶œ2Ñ$`,þ 1VæÞþÁ~K+¥”+j¥´™ü‹íÊZÁ–ç,B#]z~p Ævù/º_ÚuäÒi͈ɩ¹ Ù„omª®øç -”˜RfºÕqV¦|Ac_§ö -Ü›ü®Ü( =feÛQi㜈RW*TŠ»‡Þ+’•·Q8¢Ókáˆ- -…†v -ƒfgT”›Ê¿øÃÔáùcZØû¡ËIÅ7^3£ss¥X#nÖ®›ÆÙY4º—Ë«NF)T»œS²êùH±¢Ï‚*ËI>_æ‡Wi›×ai?µâ!˜Ej”n -¹uþÁò*̽i_˜á4ãåÜs*H¹YëmäªÈø– nJ>,âVÖdèþ¿XpJò˜e$Þ/!íü7ÂÎeØ[óËlü`\‡Î«þ¼÷¬Ìx.dëh!eÒ#Š2ÈÊÚ.œæ.“¿.7UÚ_Ïët]trUrÞ˚Ú±ÔA¥÷С:•òtÌ)LŠ ˆ!ñDˆƒ;+T×ûÑïò还n‚7þ”ggyÜÿã¨"¬i¯9¯Ñ¬2f nc0© mKŽòr‹}fs*äÂñ5_HwºŒ´Þ?X6p³a&:'•z”ê0FŠU?CÏ~²Ÿ\îäÚØu×–±"i/Qÿ”ý®Zƨ¦`9´œ—8äé¹½1™[¸.ò ¹¤¸o;£öpÓXó£ªn[*³’z`°ã±ó‚\ÙNhÕ›æfBež5û¸‘iÒþ‚Bæ’ă¾EPqÝÆåV}‡]·US…ÎÕúõ;ò1tŒ0&FømìyÍ~~¤žçµî*ß§šÃ˜{¡ïÃ’ Ã8CÖ½C«²PÀaCÓÀŠ!½°ô.€ý;†fCë}9†6Ù6@‘®x1tŽÍ¿€£ü÷ê?€e$#T;#=ý›¼õj12IZ1ÒÌöb$3˜Åðy³aòcXèB1\ØÐ1\Ì4b˜Á¹ë1 ú&™  ç¶~ %ï0†©6xTb8Óðö,´íhó¦í¨k «ÿ¡ÄËÃk*"¾9Ø?ÉÆï"f$Ê‚@}i#׌‘tˆ¶ùŒx…Æð%.ưÓ…nš@0á³ ÷1<6ÎïÓ,¸`“øTcXŸ [ñ<Ú¶ÊëÏ^È8rÿÚ„ÇÞÙ‡ÔfõÙÓÜúE § toäc4ï11:Êÿoe0jœÕ +F‹SŒÜÍï÷+‘UHÄȰú̓›Ýo|#Uœ ÝÛlðNú}‘|>ŠL@[pSáÙz„ƒÙLJª-æaŽ_ÙA¡³Ø½';ióf¤ÆúÍŒ»ë׳T³°ïfõ P˜Bĸn1è»V‹±ìªc•Œc¹¾£Wë£fŨTo.Í@‘'¾#ÏÜ0²#G6‘HïA/á»ËóÓ'½èC4<É•\˜»õªZòo&xë¯JÛÏ:ïÖèá<š…ãæžFûõ}m-Wÿ\l=£ówWbþäǦÜwYðwý²¬ƒ½Ó6ÆÖˆc.1T£côvm|ÜáA -Ï¢£†ôêdêû´Š£§ûÖüÀ{½2Ï'è3ðäUÊÄÓ<ˆ¥GœqÚ÷µ™…¿ƒr–çbÇËLðÍMNûzk;‹ë1ŠÒÎÅŒ@WßG'4âãÎðûsyFŒ/s»ïsFî1NÍ O6©Rß§jˆDù­Cqë5÷^ýçbíŽqx6­Òysß ÝÃuÜ›/··O¬È ùìí,¸kñ²’Ï/ë<;…çpuŒ?»ÅqµßG˜R¦îæ9ÐþðƒýÝÿ(Ì tð}\61î2-0·w”ïc‚1Ñî£>I…E ß<ù}#÷@h¬âïW^Ë;ŽöýÛ¥iŒ¯^m4¿LŸœõƒßtÅ9ׯüù´8¶Ç$7JŽ-õu»:Ó>Hæp´Ü÷ÆîÒÚÎw£¬=ÝåßˉãõËè1úzƒ0ÝSÝ•c¢Óï|óc§HÉÌp¹g{LÕàøªGÛÇ?S×±[!Br—ÊÍdNQ»S?Ú÷L÷ˆÀèÀÅ>áøÐc^óýq¯÷Yrw#ŠðO®'Né¶)lÙ' F°C6Çë=éëåí0Y/ëºæŽü/bŒ…s1^ÿKå'PÉ>´–˰ôÄ­g²íØwp7îoæ¹á©÷}‚.š#W´æÔ!“o÷¹4_Û®IËaÜGo«¿/£áoõuT>Zk^ñ¶ícÉðÈJP>uë€+«½&凪EÅòeð—ìï>¨9y¨ºÊeÌ–ŸÒ÷I‚êç -õf¯¨®é÷Þi§ß&×’uæ™öÎݳÖuŠÁ®D¡íK’›\ȯ-/eíõéÉÛHxÁx 8²ÕÏ9ÓåIõWËœjŽ M_ J&“ ;f™!Gf¹WSŒÃŒßèý‹»7迺¥˜xÊ|LðÞ÷«Ý`Ì<Ǿòpæ÷¾W,å‡çf­§¹½xmí MâàTÈ…¿YÙ` ×Nðj·î–;.ÓËó0Ã.n\X_µP0ËÊ]6fû—>7×/{cÅëÌÚŸÑTÖêßìè”ëÏš±1œµ»7—Fü úté˜È½˜˜df|Lá…^ÈÜéúNÑ—Ÿ%þR÷…Ö‘Ðe_䊳m°[mÖ©˜cÇ&¾5¬æÂ¥¿ Ei4ÇM¶¯äZ­ÁÎL©1k¡˜ o¤2ݵóÆ”0»{­ŸC-sܱڠg‰Ú`ô´#8ò—®v,T„¿ü`61.N 11ŒK19YóžÐuÿô°éÛ”àéëËí•NýäyB–œêó6Ù@v¹:l–;+·Î]w}7+œø™Î$4KorëÇ¼Ž‰ûƒMÉÖ¤©e¹Qo’/&ã±bÛêÝ'}uº‚óꣶǥ,'´%Œ'U§3žtN¿‰qsÂ\£¥è8Y7îýÒˆ¸i1EžãC‚æ×7k÷Ë;vKt6«ÛT±¥Âǰè g/ÊõÃÁ0…úuΟÂç i,>Ó} ÀÚÑeÈÉÅ@ -coUÕ‡tFo)GµSa¡,iý"'“6¥,©ñƒ)ŸG\»ØÇNû_€›sêГ(újWô®A½4ò&zꮛ葼UñýDD ÛeŒM1›í[h9]Ledi,²ýí6‡˜ø¨‹Òô6¥öõ}Ø{<ökD}2Vv *£|™ýŽÚ—áØÔ‡B+ÝqÄG‡ÛgÂÉH%ÈmÿÖ‘×u¨%§f¦ý‹ok‰¸psV™‡pÓñ[ٺ§ͳ -¹Ç1 - -¬ 9ÆkžÙ ÕVu5½…eÉãUãSwŒ9Œm]¢w{í\aÏõðöÇåšñO!U¢]—S›ã113ÜŸf„5*ŽûC¶íç¦í¤’ôÊ€ú4ºƒÃfÞ·õ/b¢raNäŠKýÃÇG)Há:Ù鑃69]–pû eËVÁ²Û¦q‡s{ Nuéy_hRq&¥B⪯}-˜öSiîPî¬btHpçì Sv+}:ï¶{Eê¦HšRýt>$?È1=õƒþ¥wcØv_þŒ[_š¿uèF‡“ú­è©£>ÃT!v³ñ!Ú=¹~´µä8X‹Ã²¢Ùca1SÚ ƒüÞ ªãiŽÞaÁxL52ûݨ±ì˜¦üáÎõƒÁ€3Ó¾RžR½q~ÌHSjÜçè|(„R¸ê'õ.„Úµ(ÎÅmKb‘¤%Må<ÿ/bÂ~Ñ?س-ÛøõU^¦Ç}.Šö>¬†N=“ 6hcõ\ɱ/ˇïÚëU›9wS˜ÊMH™h¥éL5¢d©¤y}+o×wØ+·Aö²~õ¼¦IÓO çO\}aqcëÝÕ±Ñ빕ÑÙÜš^®iù® ¼ÐXm›Â’xðÂGL~i¦x?”Úè9Þs (ïV¸«øÕ`³o/;s­ùË©’€q²¹Øàļ۾–õÌ[lMîÛ@ +]mACÁœÆÈ¾¯ÂιÇh¢/ùb(DTéÚæ'×ÙN?•öÞÌŠm2è­£MœÛdZͶ÷‡\£#£æÖÙ^ìÆ¿ˆ§"ãÓ…œ°JðÙO.r¸µ6ÐÛv‡ÊÃ*5ß7³qÝçÝDƒôAÜÏk%‹àÔOi.Ž SdÂÓ§…®,¿ùäU°•f¾êŠu¥zš=úÝE‚WÚ>L/Të4òØVîN -Í»5å}#>5 !i^gQ½•{UøÖÉšü¡ñ‹ÏxÄõÕt 0ê'°Êàr/noëg}ó3žgûûó4½âA 1#”R#zW!H«%“¯÷`?õÆ=Äç⇳,aõnwR¨/ÍŽ)b›Í åæ§Ö÷'Ë®Œ™¡È_žš€ô-³sÀÄM{8ËZ£á76üð•Âkp™¤Tç ù6gkã1×f3®£ Î>k\²ÎñõFZ-ÿwSÿ…WRÄ=ЧcjÕpM«Õû¿¯öÕQ=ìZ@ÛÍrõqnñL¼gŒ~¸é„!¨/Öøž¿IÚ»è2{pÇaÚ*’Z³ú:¿´z†ü>K:­j>èôA‰Mê²Æê-Õ*¿·ë#ó9œ^ ÏÒx)y›ÕR›K%„ØoKprÁ‹[™,·Õ_ìî`î_¬wã{°Ô3‰?whî¢i¾üý–M×Þm“ùÇ׋ŠÑ¾õÅ ¿xÜÇ\o h5yHïª×kÇ«ëV\)·v[óeÓU™˜…Ú $ƒRçPÔŠ»LgU$æÖ¹ÐÿŸBæÐÏå•Ì´–yú"hôÍlÝé›”¥é[iP£Õc‹ûVi•½íYû2- -¦«Â¥»3˜lᕸÞñft°ZÃÎÍåÚpþéUØÂÎbç1áþ`åe¶è3v¡2°%!%l#eФ7(†Ì¸šu¦-ÚÛ-$š)Ý&9}-³Ú9g9iýÉDL5ŸágV‹J»í9µÆeGš²ÝR•j·ÈÿøÁþî)5ºN¯ïÃQmãÝÀ‡³¶Øsf»T–§ ø:Wª#ï(•ë\‡iov ÷\·H=7—B.Ü<òjc~¿Éçf¢dÍÁ„ÌD#Ž’úz™ê”—uR¬ÙqX¾ÇD*­ðÍâ²PBç -vu«ìzÄÀ;Å®'ù….ž'uµ‡vÃB‹]‹E{Ñ.õÍhǶÎbTÑ(Cdšƒg«p)5õ ­µ†óÜLh/²f¿²ÊXJnCuJCŠ•ë8˜×3AÅW—[Ï7vÝF)V$Õå2uêpà¯pÅ?½õ d™p5ëCj6°6 $þbܨÑûáhZ[‹ëp¸l—¬ƒQÌ•Z)SÛ^©s9µèÛîUÍͺ‡^¶N5»Tz#Ò™¾z߬µ±ëã'k:ÀiH‘1µ;PQ/iÈóÔ˜ÁÁ­dBÑ#»‚š•Ò0õa F"xÕ¡fMU¡&‡oð·uPÈ_À¹|71t5Ñ®\ñFd Ëx.†«…R í¨J 5*í"Q”>ÞË(]×NQ*Ï߀R¥•,jQÚ=·¢T@{€º -Ðf€Óê‹R1wA”Ž™ @goðöIVúÿK #kæÐÇbø„ƒ0Oæ¬RŒaš«~3½¡!ÆÐ›ÄP/¿Š!f|‰Òôö‰!ˆ%óR”¾Î\”¾ P4 pZDi€€«àÁ1ŒÒPÈÅêq1„•À?Š)JÔéÕ†à\P*?øËÇ9aý_€¢t>FXŒP2F>é}nù^'ƒî²³Æ”m yg/†tCÖ8CÚ¹ü=—i¦èô¦ -ðç1¤S6 }Ø~ Ͱ`chžmEè²Ôû'vhL 3לUoøÿÉ7Ók?Q^‘ ]³›‰ kÅH¿¥ÄHfnÆðëuˆa#sáÑŽaîLÇpæ²ÃE0þ‡Á”0†K¹%@pß×¥Ï”ØæYA>»pT ÝdËÊõØ{ïõõ`—£×ô=Q^¬Ñ“ŸoŽü‹NRæ’Ãc›PÿåQ«ßÕà›ï÷‘‘(¶bdU>ÆÈ@ b„½³ÊÑßõàr3w¤®ÇzôéµýC‚k¹ë&ÌFé1¸¬ -~P€ÛÑÛÛ:ÔKïGÜ“ãpñÁ7s£»½NG÷vç)ÿ`þöv~øÂØêÿ"Fz9ÄØ*K2FÏo:Ʋƒ:àÜ‹1$3Q§¹ŽQÕ¼ÆhÑ>‡í OÖ.\ç»jPðÜÖöÖ˜[ïõ”o£Wåvן˜=¹´}Drñüào×=ï0ß©¢U¯—dº79W•¯j^´æ¹w~næâyÖUº_:¿ˆÿþ¦0ªèXŒAc¶cG¦cǾcº³ý¸±| nÂO»hòª>ø#V÷ùGÓ½Ôîí¶é;ÑVò‰òVñ/O÷2yoy“'ŸÝU%—ìâØÏy¶ÖéS]ÚÖMý!ºä!¹¨hw»â²}ï“æ˜öø_ÄhmÊóµaöò›µeb|L´b|!Ë`ï0¯D´Éo}²[?—’t|ÀfþîKçGäeyЋÜB-w™ë¥ó\,ÖN¡[íœp¹w\[âYõ™Û…Dû°ëZ—ƒ„SÉ>s–K;úãðθçw·÷§ÓÚN×kþÛ²Yý ÷‹ëhŒ5ßDŒÅµìw¥ hõ›^^?t[¿‚Ï|üè”`ݧKûV˜0î9 ïÄsbp„ÏtêbËïóÆÒÎï×7»»?hdg¼ý ·›nÙAm½yg7þúCäÉucµªÚv6lÛ°qnØðö^_m·ºÚŽn¿Eiƒ0]’ü®NW§ù˜ Ñêwµm;Ðfï]Ë,„z}ˆž‚ ò¥j¯µ”¥—.þÜìö'I¹îFûr&.oõM„mŒ¥Ÿ]ž×Òš§¯œmK~Ç-Ÿ¼PôÈÖ^jï,òø‰—Ãꤰ¸Ü>õÅåCÖ£=V]Œ¼leQ˜Àå_ÄXù"$Jàš»ˆ°\‹IjÝ Ù{ã±%//¯¤•óâÑã±#–y öàÞÖœ{ë½Üξ ëÝÞÅNŸ¹‡Üh%Ä!lõÞ7jy*ÜŠË\ÿÎ-Fç¸kú812™~sièçÓmdrn8;7ëBm^'Úì0ã›×‹½Ò/bLûà1.Öˆ˜¨œóŸ³–_Ÿ>™»-íëxæü 鬻·6•ýˆiuvu‘7~[·7ãÀZIÈzge¦âyIª÷lQl¡©Í1ؘj“⼎bõŸÀ‚¾n×&zq©#¡©³¬¦ŽçT¦ÎçÄLA“PúRø¸æßz›5,&ì¸ðƒ=[»RêúÂçª{£=žÑQxO±ýèS*nƒâ¾¾N"^²±¯Zƒ@5ŒZæÝ»ïŒ¹¼8ÏÁ}ëÒg©<¬£­5·|Yë[?9½†ƒl"ƒøømÂñ­>ÏŽoÊLè[¹4¡Ãzá ý P}Hh áÁý‚á~6Þ×Êâù:¥Žü­”’4q˜“üë2û…úELä¦ä£ëzŸ‹Yrî®ËB×݃šžA/Öˆ“Ë„5šàî¢"Fƒîð û ²Óì f'%c«ósNPâÄÈUT‡»Au>èÂï·n»žzs¯’vÜâÌ9b`^s)tc$Ûê6Å…þƒu›ÒúÙkkB0ßTV[\F¬®ê11t³wò½ýœÒ@ôJ÷~u ºuÚ Yï°žsëÅ,·v ‹5o3§9K¦Ãgž˜LÜcñ›OÖ*U%Ù=Û²€h½!9-+ƒ¬M{·Ô]H÷pµçºrêàÙ] Fpg#Lrôà5Ú»Á@kï†ê½Óed¼³™\3]è“RÝG‘çòÝ÷ùõF–wDÕÛ=˜Åe“$äÑîEÆ~©I¾mX«¢5süòizúƒ‰$˜€ °’nrYxøÁ‰èý‘z sKze» ®~71nž‚q6j¤mRæZƒó¶Þ¼ò£æÜh­AXF[ǪIµ¥ôùòÏ„f“ë,¼Ü̺9FG¹¬ -|²T¥âü`¦¨ÖLÂ6³©Ruw“i{wWÃÕ"AGɄ䲣q=ÿiI³É]ÍÎJød w“]vði~צFÙ[+×ÏGÍ‚ÂSüd¯Wy¦ŸUÏìwµ-?9¢ï“2Õ,H3¢9Ú­óÀÜO|†rÚ}?ön·MÚh\V§ƒí.f ¾n¦å¥æÑ¬66ìJm¸µ³²-ÔÂá ÐA¾½mroºê°bèÅ a%ïºÝ.© :Ò·û1k¶hƒÜ6‹váË -6ªެ›O£Z¯Ïó2ÐsݼBp=ÌÉ™Fµ­’_ð_ü`Þè0 ]w@{Ž1[ÜÖb»t]Þ»ÂÁX™æZx7s̱—±²}Mæro¹tWtéõ™¡KÑ'Ež#òü¼W;ûñ¢ÙV©5*¶FÍÒ%«óìW¿QÛ`Çzc_ -¸ÖšCk[tÉÖºûÒ Ö=TOµ®¤µÍ½CqpQ&¿¿¸ÔÏ×~üyy›uCº­hm2ÔÚϤwy5y„Òôbˆ5Rª~Rz/Â7¥0G¹¢u/¼„Έ€:‡Ó™lËÒ¢ØR‡S®Y&Zm¾ -³ýF/MêM(¿ä:ëÖ„´ö¨::Z¥vïråÔSú9#Ÿ*Ã7žü`•SV¥¾•ÜÇq$îÍ»ÃÉ+Ïî;äuñòÚî¼›[ÍKÏËQzùèCÙˤ›¬ ©]iÞ\uKíÒÎw´w«$Íp½sJ¶ñ!ºL=ɶug»–)É5ËϪƒ^ש(òØgo· Ì–˜|±|º½òt~pËS¡”§Üš*O &Qžâþ‹}É A_¹AIÅ_Þ7ÆÉh]ýÝôŠ×jôh™rf3Òz5q6ìî{g±ùÌ‘ÔcÞˆwëF4DoõTÍ„udšG8ÜÈgk½¨ÆV‡¯|£r­Õ$Öçc–YW?X¹²Û\“‚b†;)ù’•‡„’Uȹ¥e´OJ¼%¾ïÅZc¿Ø6ÑÍ}EûŸ«Ù8\}¸·cs±°d·œõ ä© -8Nô°–F6º (³l×2'N¨žgËqU©Áv嶬+ãÞ"dï§#²ô#[ž/1–Y¬Éf)AkƒRKRæÅÍÑÝ1:ó.ìÂM¦ -›‚dö2óùä]ƒóÊü °çoìŒSòµeï4ëM­Ý¤Z½Ù -Ã`â1Ëi.ÔåV™ì -õMFç«Ê0ª—–Óbøm4(¥bE/µï“M Y=@ StÚù°ˆ_´pP º@Ý÷•üÉ&…<4TZUW6],e¼œf/°\¹S¬e_h4ÍΖgvÖçÐlªÙÊ«€ÿbñÎëç9Nœ¾ã±ç Ý(R=»è³)¯ øVU› endstream endobj 16 0 obj [/ICCBased 25 0 R] endobj 6 0 obj [5 0 R] endobj 48 0 obj <> endobj xref 0 49 0000000000 65535 f -0000000016 00000 n -0000000144 00000 n -0000046947 00000 n -0000000000 00000 f -0000534590 00000 n -0001208740 00000 n -0000046998 00000 n -0000047414 00000 n -0000053435 00000 n -0000535003 00000 n -0000101249 00000 n -0000534776 00000 n -0000534889 00000 n -0000054579 00000 n -0000053496 00000 n -0001208705 00000 n -0000054018 00000 n -0000054066 00000 n -0000054915 00000 n -0000058488 00000 n -0000491173 00000 n -0000054978 00000 n -0000058531 00000 n -0000101284 00000 n -0000101340 00000 n -0000491287 00000 n -0000491350 00000 n -0000491384 00000 n -0000491687 00000 n -0000534478 00000 n -0000491760 00000 n -0000534660 00000 n -0000534691 00000 n -0000535077 00000 n -0000535475 00000 n -0000536535 00000 n -0000542020 00000 n -0000607608 00000 n -0000673196 00000 n -0000684001 00000 n -0000749589 00000 n -0000815177 00000 n -0000880765 00000 n -0000946353 00000 n -0001011941 00000 n -0001077529 00000 n -0001143117 00000 n -0001208763 00000 n -trailer <]>> startxref 1208937 %%EOF \ No newline at end of file diff --git a/doc/deltacheck_logo_large.png b/doc/deltacheck_logo_large.png deleted file mode 100644 index e30de0350c5e3a9113d973b8d9673c1b486fb038..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15178 zcmX|obwFE7^EU2Qyf_qhm*AAPXmEEe?!jG4(c+RI#oa0HQrxXrup&WAar=1hy}$2| z*x8&pJF~O9v(L;EtqS~vjX{P12M32OFDI)G2Zx~Y`aJ>-<@H^LjUx5+1KmYV&m9g9 zli+_Zc(^Pu>FdR}9v^f)G@PwHyv*FJ;G`{`&8=wU9nC&lsau&@`nZl*iNV1EmgHrn zHNAfwL$JLmbbQV>GmQdP>KlP*o~4wIoZ*Sa_#=lW8tQV5oOq#gdCDOzQtDOa3i(Sv z)DPxn1tWhZHVQSG-U+5i)pf?x`WzSQG7hrXo?VSb4x~4FlOTluRzCxv%m0jON^%G| z+wl23wIVK>wQ{$1c7w~r2D8{d9tZPvUrL_&;KD`A(tVSo#~-Pu2Ou}S3o^*wno9e< ze~9$Ii_x;@vWdG;fF*k6Kfma_>2!I!sq_$7Uz_N@q2**DXVHgUavnY!^1>HXPA=)a z%cJ}9kGyDTnQ^{65`!ZT0?Ru&e3J%-Kp@_Sf0}wVjAW8^Ehj48DA{g}*wX(08#sI9 zF_l=^S_xni|~DUem5GSzvIuw^(%`Wy~N=rjiV6 z>bB%F4_ks_{bNf_5yPOAt)91$ARH@E9$Lbb5HR;5)k%go5YGdn^@F2rG_xq7z;V?a?+M4|Zr_n)fCAghKIr#-8+-KD-H z`Xb6;f~aEUQP_T}+d33V6B{32?ULSsCo_M0+oGSrcU7t0RXl&fHVQ*PEM=e!ezo^M zW0U`=e4F_x{)Ogko8MXZDX!Pr2JVs(C52ko+94d+f86oj-`UxjhAggp&V5gvnNZz` zJ)9Xir&v0P>hF(ciPFbXqQ7Co4A15MDF0cDqO=Um^c%oXv)Lu-vaP9b<5j&rkC?D7 z^hJ6_TKr}N3H)sA)9L8#O*%9*H2Ul_J@czo!>S|v_$CN=eU~)4{~UfzEka!++mOsN&-arfYbI@zJi3yyAAk(W>6TnJ$@ z8ZK*Ze-Mlnmnmf_IVb%lhctWm_wCBrOF4q_lvJ-H09R!AP>k(XyfNMTk-#LbBnRK^ z-vOe8pT=!#Sf5CoOSEF> zVt=_pT9!9ukzBUpld~T2d(n3iMj5cgHamRyfOFz|_Kr_U$CpJ&rrT^s19U^et|VXA znEp{jOtXL2N8fJ(WsMI9&XN1<MP6s%c_AoAGlzcZpJ6Oy3}Zr0 z@g=2*AZ0(C%yu;FhTi~9ShqYrwNp~VdS;Lw2dA67SWLS7d|WRO_O48Wg1WQd8Yuc4 zbzLyiqobqsM)n`lrp_csq`riGTXY04Ju1NfhyP~+xBNDFtvomluHE8knUA%F3jtn% z@M%Rt{llksX*)ZfUd_F8xVZg~$MaXPrK;7LxjA>rQ=WaxAON44sc@(U-M5U?r)RD) ziOsjnN^{XY9>$l>I6yjnwMv42x^JbbEUJRR4dOHHJ5dkyd?q$3!QJ#$+yJ#*b#@-{ zgN=$l|K93)|3Xsj|Fmmy>pb3|iQ^(e)31qo=y6Zdm6q0-#Qvg=2;ApCrw48xwkm(n z0Nqj}{A>NS3P!{sSA8JMLkgE`b_!2BH2i+PSrpnc(?SLdhyGpaKT|WJXKFi}mOsGl zM8%<&FicAYAqa~nglu65chXA{B-n3Tp#1w3PDYw@f#n7e zHgEnRkKZIHIqe`ufi!3$z5I4|4EPd4pdy_I0#8cUtrkw~(}yPO<7J_N|5;t_xV_c$ zY?s+=WG-}>a+Yj|n*&?CrZ{gm%979L`ARf1+~|;aka)RzZEbAOgs!!E(ZB9wPr`bV zH~1lDjIN=KNyB}bH0vH@uhPoCo)2ti#Y=M*k2Ih;h%X)H7rf*>W0|}u29L^rS0-@r zRfjJ@&F5Ag4ng0^D%Q(<1C161`j(*u_e5)=nCas=l<@rISflw?UIbr+HD_z1u+`&8 z;x~jX$RWlu9ZpT<2u@5tv)=`{(u&b4t$Up8#cyn4-i*)*PoPZ??0^Ss0XRT@8?Oe`B!rs4%(1%^ZvEXy6yzjZkG&6&VK&LNSl*7KK)zA(}?Dc`gnh zA)rqS^zPj||MF95-OT>Oj^ZyD%G<@={}Y&gA2G@L{Z20!h+Bp+O<0wR%2@V&={aK> zcfoK|gU8!kB-{Q0|0SBHs+xva#O{5Zb9hO%LY(<(N+6+&0{OoHJdQYJP z(fT>`(**@w{EAw1(tZnk{i3jdX@GJI%w z^;{Zk1J(IXiZk1L5_Dg=bry67rILHx5p1yx_)&lz_NHw*pluRC>Z0>#c7G0PaONDv zvIpj>2!Z;tpdX=w+oUCfg^jmq$>(F~t2~ahQ!N=dJGAfNRaK@5k+UN0EopF8D8j?B zOhtw;hRBP=s@YJBxtHBDN|l9%0szKS)OnTXn&>Vv`LJ-G3~pL$mZfOSbtZ7v`=4%Y zXAq(r^j>tzl6iNQ^Qz0R-Q=JTZFtR}O~TrV=x1e;Nnc%Cqskg*@Fb+zD}u88Ir)7)lnZH9NgZtooe*RhcFW5)ZV7>aY^z8`FgSume5WAb}83#nI$lGr)q#&)3XBGm9t_E zLlHVwj6UXC#^ITLEr!RS)QyFf{T6`x?H4!{ZvOecExoBI@k63~y=Txsf8YRn@oS`6 zhl(^)9L{8>WwhenXJb@y_IPsE+gtE5HV;zwk-d9a6*59ZJrz%U@nf2G&-LbH@~J{l zfIK4zzlH*Y!3f=dvQlxTB>Cbf69g%Hzm~NDFtEH3CJUewno^R4#cxB2U#ycTgM_XO z%7Lji?MNyMTDU335rm&r_x6HK6S2R@Fm{uKr2;jD?4!`Lhu1#yqP_FBbI3G$$vTJ{ z{Z?*D!J49UrYPE#kpSwON9>IWAnqy4J9~z@-p;7JP&%z{ceMSg+!0GpZd5?9ZNjCO zM`C)rljC+C{^In_`P5tS=B#&A{OE`X91KdKt?lAv>mv&#;UV3P`~JGbX# z7q{@9K|G?QY004lleKa8{3Lq=#ktcV89{|~4+D|6b+HBbEOW zmcVH0=F9on42=!8i|H1xk-r05SsIe+>*aKn>Ca`Sa&Q_@C37c~Q3SoUT6Q4JJhaWPZNqEco zheQ;}>jiex@I+Hb1EQ(fNR2smMQL}o`YDG66QmQFmdP?@;?sEnotRayrJi?%T zCOl9v8(0?a?Ct~g?OcV3NAW`QCX%uY>hx>!m|pi4@b}d6M)##?3wh$-CK9-cbC}$M zX?$|c&Y(K07D7(v3*{8VLGP(s8fyf4h{>2q2RtJ#i!?;4rt%C>e>FZnTu##f<>piy=! zQ_j@Ltt~SFT!QkO`m6XK4!ypn0g)0sNN#@c;DJLxM8+QtZxOrEuw=5QQh)L3)x(V z=wWV0^V!Y)C@J<-v)bZPjq?&RZhKQ%88s>t9wU{#fkF8K3^UceEvB^A=Cz0bF6pRHXH5(k7}X9Pc-m3 zRpkQzn+N54j@K|AN#*;Zaf4ZHQUQigHGxph6L1_G;6{2+rK5=K{yNE>t62< zNtwy~6VY-8Py-m)U_+q3!?uVeYcoAG{3U|l{(abC5Hz8;5-$8WUe7ZS1mTNR(4Z@@ z$-_9?P#YfO(^N`Oq$cNuWs2|D`H|10tTXXH1L)p}XkrT7eNXuI%n*ETp8Q zKT)H}N0c|1JaPjcAVM-;rYB!XnZksb)+g&j)=GR|dncu*7x5zoNHSt!W1}%eK(XzA0XxX6S)`n)O(ntqhX3(q zRl+!j9H+!FNV(!tif$#eA1KUxg0pQO0jhFzkq$~lLSi0sgx@K0R|2KTKlM1IEs#F zQl&KU;ZXCN82)upJ_@-M<&5p8U9neyAg|B;+_!oz9rv{EdiVe!o~x*AN=RSLB&6ZR z1T2C^O@Sj+{BLhC;kT0tI@v!R5LdNSGR_qKMW+98#0f7aU)tG-vA)!T*AjpxgFt6ABuqbx=7*VUp%nV_526@&b=? zIV!l1)6mZr*o*N$edq+Z#O_vqaqGrDO!-yU{>2OSjJqf*I%*XomQ z?<$W6ysPM`6J}39IV zsGe-=z7Yc>@v9S#Gj z9{CJbD3KF&-QB+-FDt$+dhE@AmJ!l>PilecofDaJoPf4pYj)X;5F$3v+ul zLE6fGaejCACcl6*OKWt1@9w}+5qvpt?lsi)9~sXXNTo z?!lyD@dY~LFDZGeIrHb|g|6A&=zCk?{c4Puiu=T7JmR>Dc9~)0ef9SN>^%yC6&kXM zvv7o7kgjK&>P`_?2sdr9=L?{{kHY}$L>en|`|x_cS1W8r(wYQm~r&-5^}H{Jl3X9Fvc&U02XY_~OG` z#af^~=Bo1x5`~06DKJ+8f|}p(d8)6@Vi+28^Nj7_GknE5pv2OhaEQPgb0BB+r+bW& zWx7T6OiRk5f6!U2=UsJR{~rv|D+bI$HKwUSNNi*Kxz}X`}2+PpMy6#Y-iH}eIA&?M2D)4<3D`A z>fb`}AdHHAZ{!-zaBT#wND5Y84aMh;UkuZ9(bw12t&p1goKn-hxw`5{2~eohRYGR0 zkO}T>c^6)F(|N;~Aqt6X{am6IJZecQi_STZTGRbDElsCaC zUL)6#KA`MO@6mF`=y{5|PNOT{o(K|mgG_5FLgYgz&fg#1@tybdqZ&G{gvv{d1e7c4 zWwv3?*z!87d*Bj?eUpo$>+Zc-SO7gYH*;;JKoG=#(1~4LwVpk2N1|<_N*}Lf+S;rx zm#O0N8lpI1tfRSK1`6Nm%80b#K0BOKj)N4!Y;)*;1FXbdAh#)njUTnkzpXCckZ(Ss zP&|&dU`S`w&Q+f~PY2vjr{VV903jY2DxVJ0s=k^t;y&jM5AWzbK6u27j+o-W_|=&9 zovBkkYtf#3Q0CiH#U{VnGFAvgkP-YC?o=U#O0_Y&Xo5=nxwl{gl4zA|Qe$40Lb3xgHjxrxZ z7loMNka_)gBXRpka42+y?cP=&uA}Mv_2_G+^m9*3N?Cv}{nG+OGqFWp_9yvK3Z);z z2Ydf%KQG7?D+6v`6hb^BpjUtU`)ggNIK8h2_m7`_@Z=W4nHs>dPgEX+VaLMOxXZP0 z>cUmR7)|XmV;I5_4qcyvAz^vS%Lku9bqH>>6a9i1A~)kFVPln)a|)tf+jsqyLuuYV zj*vwm^M<(ChI?%cCEsuWieocGelh4{h43x4C1_s?HLE5LMMQr&IwrT*d8?=X`0{Bo zlO!hq#R7uoEA}>!1ujCv`~Ca(P=mcFug=SL7bix{1f@q=5p&GP8PpeY|GjBrITe*m z=W^KV5iz(W=p`_5>3)7k!L9mdJ(dZ;MRLt=Kb@kV;C1r_d+Sd;G(P1>GJ2O&r}`ml zO2Q070n^$T+z2uykwVXt03@d86&aiaGb?#VA<=H2P8G_^9i>@KyOH5%^*b;*(~@pS z^EV6$sNZrY`Qo8HqoP>P9h7DKK1%LwNPG4I&-S)Bf=s~9aVP7^^@s51!HLnU4DTS_ zUpYmcB^4Dr-LGh0`|yI6M^3+1fJo3RA&_R z&pNe>AYq==L6EwnNj8Ea1VfCaC&fL-QdaJ^WLc68HQVin*?A5+sJlH5CZy`6BdHM5 zN~>3O{S{^*$8kuP^G z_{@&;aTRa9VsFWnJG3-f8rG-D>vs+m3zT#GKi@3clEnqyG)#gDW%t9cDm^#KEQpXw z)C*3fnT@xEMvfwIi?V6==PyF{pVSv8#{n&0`lHyJ%+88BhiKsB$LR?DTpdrOMp{%i zneBfpH(wQF&lJ5izX8@G{q<>*_Yrz{w2-0v(6U;|SBe?dl)?$OkSX1J*_PdOmpAx8 ziyrZT(|$!jZaIXhFa_(UI*Qr%y-?5d8b=MvcUa}uGNkV#?o!l8a{IR{BqP~44?|-R z@hSX|UDTdS)+D?Ugi>j1G&VqXQzAhBUpSJYwTJDfFNb(Y+FWfL^?O6G4MZb|(fpMF zj@Zgco_SW3EmEZl>WOun?`$4loYqVTStSj9Y-Y+X2-L?S)eMRHKZ#rC`xJIfX}s(c&B@8FONEUnk-mh_ zc`4RBqTSbY;NiUFelq>NhBfvwAjL9vJ;#Pn%FgbavV;!L`Asg^*|8TiIw;Cg@^ctc zvUWpte@!a@YbC!qG++YuGc`LEoYn;LA8z>#=6Pp1&-cAIa+W1yg;(9F`&+mxH-LjK zB7>_oNjr{0sqFd%mDdr#3-(vh6B@sCh7+^@Ba1hSfs{mQM)SjYBfn(Lh39-d`$!%@ z#SYqN64{ImNxYrGHA0MG_OHo_a}Hka7Q@=~dE`OP;vJkMsL&3{u4h&CY3;)yFjwIC z4jU*wF6_H;Q3^XG;D+64IqUD=dZWfOLU(>Ryt^q2O!@2bcctV|7J}l-)Z^`2iCf-) zt$?7d|B$T=m@DnwOkovO-r&lc;{n{Un9Wj=_9 z?!Z%i8Y+886nxA7fwH57N;pn5ShRir2iXzN0m+4*&??+u(wwKyjeJP45)PFV$YfWG zW4f&x(erfeaOFt!6)_7Ixe5~=2SZS7MH&$D{#>ZAql{KKY}?6mzMUw&sad5} zvi1$fM#6r1B%l!?=kFTR2-D}_FoNt+yH*>bpA}Dbj|5Iu+Y#rT_YV(d&*|=Vi>{_C z!>+KjzQ0D3uIfT?fwn_Uu$0ZnB1KL&$bgh9j(TfDs$%vlG+I+8l56Nl+TkSw%9T^j zr-ICU#_t$eP%jUowy(bwQ;)o17YIE?%xbG>^mdpC#NA*s=jHy|($S?xX+w3DxACdR zyD1xTxN8Q1??LlUzqCWM>km1y3ZtolQ6oehvQ&zw;Ctn`_0EaisQIao6y}KU-2FAO z_I#T*#eu-S-@~#mLDcNh`@&wdPwO5y0y;wSfXr$f8rmWBf@Ew)?{`CYQoM4c0tV^R zaRVRl9J&WPAHwy9<*{qjAi#OHT6yWiz43`8lTy^!lA$#*I15I}8#h38de+ynBpog2FxcQx1W!uYgTa-Oo(YBFBhy>C56 z>H+!MZ1%0_dk3&pw}e7!?>GCMJ@DpqWp5_3H*J?;^O|L1QgQytJ} zN!}&ncC>Bns0}-l3gVk|D5xZiZzE7p3CGs7@~R*uFxZ(Hpt*;s8*RNd9zxz&N!rm#{} zc_d*Kao9e-hR@z5xDQxl>ArK;=>;Vf$uPR&M6Nk)*8xMX3Xl9sfPD>Ik82BQ9RFrQ z4XQi4hyl(?KPcN~;*R#v2tN(BD+MymA}!G%5Ba#r2&hW3oe~xC)&6ct*%4Q~s`EU} zHAY0cZo@qjD$}7p5S{Eq2_msie6UD|7xp0=vufkvMo8(d;t*0#qhU517t9_St?AJ) z*5^D5E-LdOvg`N~w0$~PAe!x8mpdxpz)moHM#2fI|9M+fWqVEhe<4iC}%CqTa%^gHWg!}go1}~wj*K_qhKXQM8!ko z1LL_pyHUOga$sx7~Ht-mD>-l#r)>aI*7lwkDifR0Adk`|Xp>yIh}?8Dry z;xim%n3rLEW{e4WA0GF>J zip}%>(}AXzArJ0G|9F19*7;>Dtmi!ocg^s#w6lF?eONMww>s5E^eeEQF&@|RhSP1% ztCZa&AZpY3TR!NCRp5pIAnSAk-tozef5q9!q)r*dLr7t-2~y3JrDzd5Qz{re#w*&p zT(a8ym}cR#2D~QP zmJFKn9{z5TJBPf+_;_LqX)+8Sy>>B@Iy{78wdKDZk>`X8sCIu0VagFOJi8QLr)ho7 zmVq(f<0r~|dDAESy*bW)9_q@ay%EuG28w`ut^>MVJcInniCrTgmi?gZQAbjLG*1Wj zE^?GMmDl=^CZAliobpZ8swQ+8gh8Cb-eCEQ>n4}swf}g3RQy6SMX}^dqOX%Uv-u* z5-b)#+kec*h?NXzT2w9?RKz~ju`XzpVx1Ef>{XBvM-Dg@=^rEiP^GkD;|c8;g6? zav116^2nq|XfmFcdPQ(F5A!hZ7=LSqMvD(zk#`AW4;g|nH*W9!j}VqrxYAb!28lc) zd`ImP+dV->RE0fM!uc5FD59J7d*d;-3jz1Hi;k>9>V7qP-hK8v9;5Ns+!fWn$hr#k z_96&uUPhn3kRdx;FjqF(hQk8el(38RsksHVpCZFnPu)7|j-k%{iU)<;hoZf4zAtIq zhA*dHe!nF|wCZ33vjn?3%Ol9c@{6@eyT{%cH|TT3EuOi}{&*;Xup!bb5dofA*FSh* zhx0e4o#>Rs?I~_qA>XLj<9Kz4786DUh2C?vY$|%!w*~2jbH42*DBGo>H{SxM4+bZ8 z+TOCcOM*bvAqi{Vn1!pG7-gEH1UDnmujy(UOcBVg%(SSm2c(62gu#i_f3C^d)zH}^ zml4_ES?AeOBj%}1ymwH)-C#sfAh4=d8Ksl@kmqW? z!@VO4jKUGR2q67_@5AUo-qL#(i0N3yexcI56}?CPmF!$Y;&9GUC;)A;qXS9O-xJ>7 ze$!CkrmPWG811hA2DWQ>L;aXl(c?;3%+WLBDwutOqAoyQc3QO{OQ|Ie<3v$@6J}3p z*`s;bB2yuM{*fF?=7BjCYn@wNy;oM}`*u1``ofd#4YyaMctTJT<@372E1q@6jd}ZN zlRBh^>Mq|6Nz&nVK~-z5glJ`;sX93^r-$t|CwxE(lnpM{4cqjx&gC-fzp6-w zhX_*8IbL-5lD+Jr-1~JYWbv4L9sVA0z|RL_%?$UZf;DyvDF|99PY?keB2+ z6~3@k*CMX?Ek!G?*IU7yTYMARZ-6Ool5$pE5r%E>)&;a})q98~8NG5@Ol3;*=TAjK zcGdg#Jmv+;^X=nQE=?4XA;F@mCHvxLy_RU9Q5P!pKd?IY{v6rFKJwd?x?**{dk#N1xJz{THj|9hKNj0VpQOU8sX z>S3m?+7)@v1gUO9G!J1y%HQN z+5)7F50-o+GUO?w$^AUSBlfmcCFs1@(dDln%dPRY-qlHzt`g&r#TbC5 zcX{|jVXiW`6`0gPSE50cN$(5`fopoL_i^0onF)uev5t@*ss+ye262b6`%pZN-$6zK^HCFr%JEhMc7^uQa54=;c0(SJ;~4^B0D4wzA% zURWtQ9lK7SAz;mLFr*H~-JC`>d9v1wAX;lK!aq$m7IdwGS%Q_<418Z9?$^o-nB7=f zjbv8vJZAE)gci{2tuTohD(7baB67Ch zh=N(W9|nw=^(EuGd3-eAfSFo`A=%Cqb0E2h=a~_N17Qoa*XNrZlHaA|EGY8u4GY8-#3Ei} z8U+|`MAaA%lUU&jmrx!SMhI%+YVbC7rHE*5e;y&L@f)Bsgr1$0Urg0cLG|hLMGA8# zKks$wR|yL?L^;D9`F%sM>yEIW9zI<->LFOIw<0tuafU#v33?#!pIi<^H^V=h_;&rg zlfEef*Qv0k$g?lXqv4ZSK)$+*%3HZZS}+WnTMT`4f=)HW_dK@RJy1P*Y=2sPvMYMm zx8pH)#cuFqw|*tZ$6Y_m@|{mLM&s7|g^G>mDEItaxT(4{Vv_~7Vb6)`VYR9I1Az>| zKE{)xiJ_ZB!2jgp^M!e=(ev{f^fBoQz6GSn^>`rp=;D3zzP6(S3lkHQTT%#>Z*GoT zTm(6Y_YV{6fp?}Dff?53$mru=seKgX81y>S|8ist!-jDz%sOIy<7q zrY31c`-&r_EjuwdR~69&ztl<2W$z2}t(;DR#GFj8zf`Z#A{k@JCV|Y2eA&J%;d7VSH?Sc5)=2fIl^S?2_on7k2NLNA_Qn{uo^={R zaI0Hy=JHMCGKd)z6u6SJ43MJa?1Io6*8gQ}S&ZyqJ7o3g%3Q1vHz{peJRbLpIStNKO; z6IVnww&c?}H_~F`jeJr{>BZ4$!#VWYZvJ#Pgj9g^ZG4KYtjGBG2Yl3!FQD@)WbaOL$r;-2#7EG*KUScKLVh6`@0OK0U_f$b7jo#hfcjs5%u|r%o z&HU=yXL$@FBnsxA!g4LDhR@0ZUTVjtioC?n#S=&$pW$>dopS%8YlvQ^120^|`hZmit-B#e%E5N^$cRcKVI41Cd>y__gVEmKx74EH-yJ)&GwucrVS6-3?$^)}SH6#IC z3N}dSO8+lg{LYpUp3W~%a#`THz^I#;m3}VGNI!<&BZ>y^?ty%_3c(BTvH8BJ<+lD` z5x9~KRL9#r^az%N~N7li8uo)}O}SUYKHAp9SZQdtV)9f_zD z7Rw7VUBF8B3nYI$%|FYb=aY~L##`O84=8Q;O& zc~@tD-^FRInvnmLhTz#%Afy+pmuq=#hzX!T(~~#rL9H$h6VVyS6YIF46edgXKhAMZ zy|3!C4?J?xV^O5x;Od#>qKeXTpDK;!fDB2{3S(nqhXg%|N;w%NXIEb~h3+~}^rm!U z*9$u`v26~k^Bd^|orm8aLC*UBqN>R!McOrf)h<3(h|~3PwXpDNh-WkWz7rF2NdW{} z31VxGc4iXN{ai{VOX>Bl$4~bvt-1$v*)1x4?d<2Tc zbd>Bln$|(!t#ds6;S*;en9o0Q|CRIsKUnajVG;8k=Sq**4vl#GHM9*{Q~?laJ-iJ~?EEljQiV&D#LQEPhqx$e zfkky&T8MRC6fI&xuG6kS-=*hDrT z;uN|tWpkB3+fPMmY}g1Z{fb@?<$=9;!O1;^s1-lcojlo(sp^8MssL+k*fLT%Lk#pvB0rYjCJ^0VwL?51}<*O zLte!>jket+LHiw5_1r*emh7`R*w9^OT69JqBHU-SE3RpA2zH z%uWAuGaJ1>Wv9t|e1tK*zbCS{Mt>NglNh?dH#RrP`1!SCsGd{+2u+VvY0BR#r>qqF zZ*Fd*=1vVm<;~AE-!2_4W(Rs5>lnX8x;Y*Q8SgbB`$AOlF)DJ+aW*tWOEy2}7wo)C zD4ZTy@&%yQ(Ix~pt+D-z(n}#~Hx*`%4IYG9eMTCpQT+1sV4Xeu18}rooex1p-Yns^ z+7w|s1X~_z-oG4 z+vGN`?#OL+tVHS zI-&Dgij(I)2lHg96?RuG-yar0*`V+RcrO_)t6ly#8R%}GyWpp1@zC0Yy=c4d0^{OyZ zbr3Po8rWG+&AU{?*v=>YTLRKNr0fYi!V>8S=I;@dsYwd7=ax7B3CEj zE@l8t%{7T36E$$<4t}|tff|vm^?S7jpVHhv1^)Am<-gS%j+Gu4rmTObZ&vyTfZJs` zNI*T`-hEO)iLa<8U}Q1bV`~A9`yhy*D$?-jmRIf+R_D;T{KHf#bKR@>3(q$;*4jdE z4gU&I3N_u%9)~?+{tEmKH&rv{_ld`$vm?BVL+45r{~=h?ojxocc{82sD2mh#s?cl<2RdIB5(c&b{VR(aO<c1 zBotZj@{y4qae4**Ayzg1!Vr6 z_g7W5qFCmhM)%X!GQ#zb|6|F~2_)Fjc6t(H;LO*nrxB9+D*K4X8q|DwU9!YeQGDV| zzJNx1la2uL=9S6kQ5!AME z$45pfEBawWnQKz)B*5}Mo}mpWXfcqi`ZJO2-RJ*gjm6`b`7CZ}}o1mXT#S`(KiZ zcpm{Ov&3tO5?m9%nmRT@3+ho^-De|niSxAB;Y?o z^Q|Mz(Bf)~?JP}%{_d5I#Zse@#7=FRM^B8SjHaZh>XKcL@LJIG&wqUwO*k}Ve%{gl z4j4^;RrI}yJuDe3HcdA(Ds;7x296LHpko$m_%DA)4jWY^t4wrSCgu2295>5Q6@lN? zWqS@Z_Unf6UwMBNp2@0oB0~rgoDon|(3E)zi?7pqyhr2zPeRU^4ja*oYp$%7Ku;kD z1z1>YZ!`HnKBnSA`6YcHKAI{0&<87lY5Gut*8db5r8D8s`q*IRqU}u-@--}pO86V> z-MpnvI`^C{=Ap+rI3Z`!YU|2l*ET)zf-BAw~K?f*XT-|G0VR7INpZ@&^G(9L571ni)mnOPkt9n=v;Oru9+!nj0c0e1&YM;<&x{Ep49F7AcFtEKx=MUnYOc zR1rWIA|zXZO##}3in+V9bGRIdxM+Y&X7hft#S%h)ccT&^NA+)609=R=f~PD3H3qL4 z0n>Bqt+y_|>#n%Q+ zEXze(>JG@yyckf**Qd`_v!F%=v-FJ!&<(Y+QlFu0L88BpJ}aN)wUNa6w-pYy~MPqZMmUWcxAqjD`Om!p!4 z>k_~gBBaNk%_v*H@YZg3xH;GiJn{ zciy?{{rBH@#H-Wq_rtCF50wdQ(RE{d_H(^{`~h4OZyS1&tMCm z6#RFy3TnaHLWHEq*a*yabSNjK4>+Iy^;!tjcR=Hb5bUgP>j0O_rTaS` zj|bLo*zgJhb%3z*Ny64=QE7pmh`3^ki>WRlLQ?Rt5vVKBA&7ltEl^{&#-Mf=+TelW zt2OIb`R3lZpkA)_b&|Arr2Gpf%ye>)L|9eaYhpsuc|S%pCT7YfuTi203~TA8=TfWE{)jV&cr zu@;FTtSC4QhKLQ+6FJ46)gK$aq=IFqcSnkYCAD)|>d|1LqLo_eOF#bOZD zSR49`oQv5S0~@n7mUGSLprv^?TshjVurw{$_r|P!`^0FU;6T}iKW{v59%}s@K^O!JjT504e9V5-M;TsL%3Aaci9GBZDm552jg& zfTN1~9N$x2mIK;LwOs=Nuc{xQKK!?KB2*Y&s};Eu(qF3T2k79P3EHm zdj2#mY9#!`@r{ZZu8J(nu>6r{;F*_C0iCR(%BQ|XAh(==J7(Mg&6ZZM*sNg6v4P2E z3CnGf$!&+54nlf^WOg#J=;y+Kc`VL#Gl^25L9POBOFwnKJV1b5N!kj?VD%cXa-5_e zN*I8bkZ0YiVS%|XB4BH=cT&KP=VSYff|~WMDO5vuO9z}j+z3CLGfDH>m9PC5tXQ=I znoKQVwptLNtsvRZ(PFk+hT2v^`Xk?A4b#VnRf54gl??5*Q4VmHC%-47feSbCLOk}h!-eheQPYLZ>^FGrkBfM7`&kgN(*BT$<# zsPQUneDsopm~4_T8GJ0bD=y*jR7rul98wD~?uFRMaK3Wp1()CAXK>qu7wemJ2J zN=l+G(^K<{xUBut*^eS%BT$>8pr*{-@+@q>Fd4`pCWvz>&My~HeT3sOE7DNs7Kl#m z31FuJY{HD5RM{H2*3f>u87hDKTx@h?j05hO@?C8eeb@rG-*g)bYMKqRQ5rRrj`tEZ z!87svONrm<5^zlt*mxGY(mBKZ4;q;6(b#0_EgPkV33|A*gthxCBde5R)F7lkQtwH`f)qjlo7$deYNRs) zo#A#kfAk!5eA^1$t(_o8A`rmcPNUEACa-!?m8{OIw#`*mk=TY9t1!cdb!`Xj(ZPfz(XP=8&P_kDH(TLpDj zd;3+k(~+5;$#Y=qAKriy`~L=SuYC)amHi4vw&p?Cxps(jM|#0MXL_ju<5#y;$Fg-> z7d-OQMUbDb?H1ugl>4WDPxHH#uYU>;&wX?t0=tF*(S^(uvr_;I^}}z`&l2;A44tIefA25Gw6Wb@jEb5PfG_|dhYLis0sC3k07}$6dz@vNi?D-Ae36v)o@XtQJ3_6i{ zKKZ&k5Ww5um8~xl-#eDAdD|S}I+{$)uzLL(SUlUs^|gOFUponmWW!~vu#K0mGf>pk zI{{KKAdaNy6RV)68y07B)iR|XVpCJqL07ABPiM)!It^M;doOSw*vb9k`))|FZA-~@ zL8?vUIPJj-jtgyO+EuNmo_wFEvQqo8f-2S2XR*Pvyz55pUr_$AvbPvdFcZ(3d=ISJ z_#sTaeh|F&?wcf>CdXy#U@!>3|Ksyu&M`yN`DSno`WozceK#M(i$%^fkA|UnL$#{O z<{(PYfQ3;?2AqQcUn1wJIl?|jF408`)apIBnc`xsi&9D{d5CFUMa*~y`K*Bqs%-9$ z0*J1R3NihWNZZ;8NUBJ2Q0)+++N0D!9e#+e$n!GF-<3p6VlPB9LcLURlY8<39XLb9 zGW3Q~%LiXSQZBq{V%(iVT{qk!PoMW=8C@egZoWcxOd2Z>b&XQC1?EXPU8B+wYZxl&ay>y)w8laYm2G^2m z>e^}p^%pT#fU3#QQs>lbFm^S=DC&7ygR`EhOKWvpEoi)%rJtmN`rXasxhokqbyAGU z>laq)b;d*i)xKvr%FZ6Ajv}UAojT)^CzaGaEaCdtNV+AHDPqpCz+c0MxvZReOB48TlE5oJpA23MWIxB<;*7b?D?mzQ3~nC2di z@-5d=dP4UX zo__Kv=x%CPqU0T?T9t(!nk|kL#MZz38O&D1);ML<(vvodGf{B18qQkNlK=hUB#a&N zLp`u$yEIS(#?1+^={;Yj88S%yp4-54#Jk@M>2n7Roa5IC`mu^Mk!85$8g&@)ow0%+ zmO#LOboK$Dr@Qdsi1PM33~q}F{(AaLE?5!JK>*;XLIMRmo8u0 z%hr;Gu(h(dqvv~^W6oO~vVPl_N+OIZXFvIB?*IaJh%yuDj+b?cU&F-Alr$0s_Hax} zBg&Koy*rEhBM?Ucm2hGiTE(UD%rhyACf!R)t4Tj$U>8E1%{^kaMhbm;tZ~>a<3b2; z4h%kvBL`NkU!wq+2im8fd>R(~d?A$HU&>|c9rZiGYPTt%R@mC4=7RSi8?$!J++QjS zL0G}tCq3}%S?XMQwy(c+GGN%NG1z^O*j%qK!}lD9`XOa zR#xc*q0%K-;q=!g77pM~hxIqGJxqJLjkH4_V4wZutKR*1y@ zyhl4^e(EnBR!1iKxrVp}4UFO}N|doFjVr-zH_b+8*+QKn??J95 zl`ntn#;@khLQvBKFQCEJ0cOx=(=+%fQ`-Z{<$8xTfaituP1SiJngft2X=H%uNurJh z#)MAioIbl#k6*8SHGz#-8$V0&q+3d0L))^H=>y(abQ)o}Z5Q()MLVK8#^ zC>Zf~BO!0dke+#LDgHLeMTH}{-@Wz0XPC7MP(9f&pG{9igh<1OIi2F8SinVm+IUkJ zOXB$JM*YS93MQ36Y!uf;{&z!iQSu7P5(t5Q#$LKC0}u0mJcVZ`eXDM<9H8PTs&a_8 z|638nPLJ}W4x)3}YWr4cjUz6w&wPB;Gwo+JN<{kZdNj6V|kX7bMLx8algYGG0dMiu^bG}=Oqx_@IYeIP(`Fs zUBm<{i8JuLC9TvdHJMM3JtJHqsDKRkU>!m(RH<(A)4IJf`Rqf z$~peJpLwoQhGFpU)H4Ip0ex4L6+qvFn({d2b(pk=mjPXXwA)M_=A+xzMFr;aIc%2C z05zW3&w^j;UY0^^a*As5)>C~{Nydtg>MNF2S*3o2u(!I=LIwHE%k;BbqmNlBWA4#I z#!c^-l{mnzSpHI-cSk+lpLoBsZs=-ng;NK2!m8zu_F-+z){}~6fMcu+2H8izV8ufAttJV6s9Rb$MZD7qs44rEOv&|BHqla;J^bD|t9zujzMh|?unG|(bj(HI zwVJKg{pkNDR8A3A##^#PGVoq&Awq;?5ZL%yyjfWuDnp4`-v&KX2Cb-c2~Z0Wa$zK? ztdU4}x3Yz>`Y$S-1k~rru^SZ`pC?3!kp34-iq~UMcVKoV3?5O7U~3^l2AC<%nB;6d yvH-OZAr}Mf_q-xVG=vZ#LWBqrk}3Ir0R{jF>i5Xi7>82;0000 - - - -deltacheck_logo_small.png - - - - diff --git a/regression/Makefile b/regression/Makefile index 655b3a4b2..22aec995c 100644 --- a/regression/Makefile +++ b/regression/Makefile @@ -1,4 +1,4 @@ -DIRS = spurious-check-complete spurious-check-abstract spurious-check-concrete kiki-modular termination kiki preconditions interprocedural invariants +DIRS = termination kiki preconditions interprocedural invariants test: $(foreach var,$(DIRS), make -C $(var) test;) diff --git a/regression/interprocedural/contextsensitive1/main.c b/regression/interprocedural/contextsensitive1/main.c index c1b928562..743ca83a6 100644 --- a/regression/interprocedural/contextsensitive1/main.c +++ b/regression/interprocedural/contextsensitive1/main.c @@ -1,17 +1,17 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive2/main.c b/regression/interprocedural/contextsensitive2/main.c index b228a31b4..4547b1d62 100644 --- a/regression/interprocedural/contextsensitive2/main.c +++ b/regression/interprocedural/contextsensitive2/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive3/main.c b/regression/interprocedural/contextsensitive3/main.c index deab63659..e6ff9f250 100644 --- a/regression/interprocedural/contextsensitive3/main.c +++ b/regression/interprocedural/contextsensitive3/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive4/main.c b/regression/interprocedural/contextsensitive4/main.c index 9f1348396..3a9f9d42d 100644 --- a/regression/interprocedural/contextsensitive4/main.c +++ b/regression/interprocedural/contextsensitive4/main.c @@ -1,29 +1,29 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/interprocedural/contextsensitive5/main.c b/regression/interprocedural/contextsensitive5/main.c index fb0251140..25f460e52 100644 --- a/regression/interprocedural/contextsensitive5/main.c +++ b/regression/interprocedural/contextsensitive5/main.c @@ -1,15 +1,15 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} + diff --git a/regression/interprocedural/contextsensitive6/main.c b/regression/interprocedural/contextsensitive6/main.c index be4631faa..2030a7355 100644 --- a/regression/interprocedural/contextsensitive6/main.c +++ b/regression/interprocedural/contextsensitive6/main.c @@ -1,12 +1,12 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - + +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} + diff --git a/regression/interprocedural/global1/main.c b/regression/interprocedural/global1/main.c index ecb897b62..01bd988fa 100644 --- a/regression/interprocedural/global1/main.c +++ b/regression/interprocedural/global1/main.c @@ -1,21 +1,21 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} + diff --git a/regression/interprocedural/global2/main.c b/regression/interprocedural/global2/main.c index f653bce18..d2ae47492 100644 --- a/regression/interprocedural/global2/main.c +++ b/regression/interprocedural/global2/main.c @@ -1,22 +1,22 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} + diff --git a/regression/interprocedural/global5/main.c b/regression/interprocedural/global5/main.c new file mode 100644 index 000000000..f7e5c9bd0 --- /dev/null +++ b/regression/interprocedural/global5/main.c @@ -0,0 +1,19 @@ +#include + +int x = 0; + +void foo() +{ + x = 1; +} + +void bar() +{ + foo(); +} + +int main(int argc, char** argv) +{ + bar(); + assert(x == 1); +} diff --git a/regression/interprocedural/global5/test.desc b/regression/interprocedural/global5/test.desc new file mode 100644 index 000000000..4c994a780 --- /dev/null +++ b/regression/interprocedural/global5/test.desc @@ -0,0 +1,6 @@ +KNOWNBUG +main.c +--context-sensitive +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/interprocedural/sum1/main.c b/regression/interprocedural/sum1/main.c index df354feeb..b28d8b049 100644 --- a/regression/interprocedural/sum1/main.c +++ b/regression/interprocedural/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/regression/invariants/ite1/main.c b/regression/invariants/ite1/main.c index d34a2cd40..f846c9438 100644 --- a/regression/invariants/ite1/main.c +++ b/regression/invariants/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) //need to distinguish first iteration - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) //need to distinguish first iteration + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/invariants/unwind20/test.desc b/regression/invariants/unwind20/test.desc index bc9c22f1f..744e6bbe7 100644 --- a/regression/invariants/unwind20/test.desc +++ b/regression/invariants/unwind20/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --unwind 10 ^EXIT=10$ diff --git a/regression/kiki-modular/Makefile b/regression/kiki-modular/Makefile index b5f2484a2..d2ff74910 100644 --- a/regression/kiki-modular/Makefile +++ b/regression/kiki-modular/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/kiki-modular/induction4/main.c b/regression/kiki-modular/induction4/main.c index c55adf781..241e31944 100644 --- a/regression/kiki-modular/induction4/main.c +++ b/regression/kiki-modular/induction4/main.c @@ -1,16 +1,16 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3; - - int sn0 = nondet_int(); - int sn = sn0; - - while(i=10) { - x = y = z = 0; - } -// if(x>z+2) break; -// assert(x<=z+2); - } - assert(0); //this works with assertion hoisting -} +int main() { + int x,y,z; + __CPROVER_assume(x==y && y==z && -10<=x && x<0); + +// while(1) + while(x<=z+2) + { +// __CPROVER_assume(x<=z+2); + z = -y; + y = -x; + if(nondet()) x = x+1; + if(x>=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/kiki-modular/nested12/main.c b/regression/kiki-modular/nested12/main.c index 3b136bbd9..c36a9507a 100644 --- a/regression/kiki-modular/nested12/main.c +++ b/regression/kiki-modular/nested12/main.c @@ -1,18 +1,18 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3,sn=0,x,y; - - for(x=0;x<5;x++) - { - sn = nondet_int(); - - while(i=10) { - x = y = z = 0; - } -// if(x>z+2) break; -// assert(x<=z+2); - } - assert(0); //this works with assertion hoisting -} +int main() { + int x,y,z; + __CPROVER_assume(x==y && y==z && -10<=x && x<0); + +// while(1) + while(x<=z+2) + { +// __CPROVER_assume(x<=z+2); + z = -y; + y = -x; + if(nondet()) x = x+1; + if(x>=10) { + x = y = z = 0; + } +// if(x>z+2) break; +// assert(x<=z+2); + } + assert(0); //this works with assertion hoisting +} diff --git a/regression/kiki/nested12/main.c b/regression/kiki/nested12/main.c index 3b136bbd9..c36a9507a 100644 --- a/regression/kiki/nested12/main.c +++ b/regression/kiki/nested12/main.c @@ -1,18 +1,18 @@ -#define a 2 - -extern int nondet_int(); - -int main() { - int i=0, n=3,sn=0,x,y; - - for(x=0;x<5;x++) - { - sn = nondet_int(); - - while(i0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x; - int y = sign(x); - assert(y==0); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x; + int y = sign(x); + assert(y==0); +} + diff --git a/regression/preconditions/precond_contextsensitive2/main.c b/regression/preconditions/precond_contextsensitive2/main.c index d1713f3e3..f8f2291c1 100644 --- a/regression/preconditions/precond_contextsensitive2/main.c +++ b/regression/preconditions/precond_contextsensitive2/main.c @@ -1,11 +1,11 @@ - -void foo(int x) -{ - assert(x!=1); -} - -int main(int argc, char** argv) -{ - foo(argc); -} - + +void foo(int x) +{ + assert(x!=1); +} + +int main(int argc, char** argv) +{ + foo(argc); +} + diff --git a/regression/spurious-check-abstract/Makefile b/regression/spurious-check-abstract/Makefile index d53a81eae..3a92d5017 100644 --- a/regression/spurious-check-abstract/Makefile +++ b/regression/spurious-check-abstract/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check abstract test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/spurious-check-complete/Makefile b/regression/spurious-check-complete/Makefile index 8de4c2425..2a3b5cb15 100644 --- a/regression/spurious-check-complete/Makefile +++ b/regression/spurious-check-complete/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check complete test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/spurious-check-concrete/Makefile b/regression/spurious-check-concrete/Makefile index b3ed1298c..c26b769b2 100644 --- a/regression/spurious-check-concrete/Makefile +++ b/regression/spurious-check-concrete/Makefile @@ -3,10 +3,10 @@ default: tests.log FLAGS = --verbosity 10 --spurious-check concrete test: - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" tests.log: ../test.pl - @../test.pl -c "../../../src/2ls/2ls $(FLAGS)" + @../test.pl -c "../../../src/summarizer/2ls $(FLAGS)" show: @for dir in *; do \ diff --git a/regression/termination/bubble_sort2/main.c b/regression/termination/bubble_sort2/main.c new file mode 100644 index 000000000..193cf226c --- /dev/null +++ b/regression/termination/bubble_sort2/main.c @@ -0,0 +1,20 @@ +void main() +{ + int n; + __CPROVER_assume(2<=n && n<=100); + int array[n]; + int c, d, swap; + + for (c = 0 ; c < ( n - 1 ); c++) + { + for (d = 0 ; d < n - c - 1; d++) + { + if (array[d] > array[d+1]) + { + swap = array[d]; + array[d] = array[d+1]; + array[d+1] = swap; + } + } + } +} diff --git a/regression/termination/bubble_sort2/test.desc b/regression/termination/bubble_sort2/test.desc new file mode 100644 index 000000000..9ebb38e34 --- /dev/null +++ b/regression/termination/bubble_sort2/test.desc @@ -0,0 +1,6 @@ +CORE +main.c + +^EXIT=0$ +^SIGNAL=0$ +^VERIFICATION SUCCESSFUL$ diff --git a/regression/termination/contextsensitive1/main.c b/regression/termination/contextsensitive1/main.c index c1b928562..743ca83a6 100644 --- a/regression/termination/contextsensitive1/main.c +++ b/regression/termination/contextsensitive1/main.c @@ -1,17 +1,17 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -void main() -{ - int x = 1; - int y = sign(x); - x = -x; - int z = sign(x); - assert(-1<=y && y<=1 && -1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +void main() +{ + int x = 1; + int y = sign(x); + x = -x; + int z = sign(x); + assert(-1<=y && y<=1 && -1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive2/main.c b/regression/termination/contextsensitive2/main.c index b228a31b4..4547b1d62 100644 --- a/regression/termination/contextsensitive2/main.c +++ b/regression/termination/contextsensitive2/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive3/main.c b/regression/termination/contextsensitive3/main.c index deab63659..e6ff9f250 100644 --- a/regression/termination/contextsensitive3/main.c +++ b/regression/termination/contextsensitive3/main.c @@ -1,28 +1,28 @@ - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int x = 1; - int y = do2(x); - assert(y==1); - x = -x; - int z = do1(x); - assert(-1<=z && z<=1); -} - + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int x = 1; + int y = do2(x); + assert(y==1); + x = -x; + int z = do1(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive4/main.c b/regression/termination/contextsensitive4/main.c index 9f1348396..3a9f9d42d 100644 --- a/regression/termination/contextsensitive4/main.c +++ b/regression/termination/contextsensitive4/main.c @@ -1,29 +1,29 @@ - -int x = 1; - -int sign(int x) -{ - if(x>0) return 1; - else if (x==0) return 0; - return -1; -} - -int do1(int x) -{ - return sign(x); -} - -int do2(int x) -{ - return sign(x); -} - -void main() -{ - int y = do1(x); - assert(y==1); - x = -x; - int z = do2(x); - assert(-1<=z && z<=1); -} - + +int x = 1; + +int sign(int x) +{ + if(x>0) return 1; + else if (x==0) return 0; + return -1; +} + +int do1(int x) +{ + return sign(x); +} + +int do2(int x) +{ + return sign(x); +} + +void main() +{ + int y = do1(x); + assert(y==1); + x = -x; + int z = do2(x); + assert(-1<=z && z<=1); +} + diff --git a/regression/termination/contextsensitive5/main.c b/regression/termination/contextsensitive5/main.c index fb0251140..25f460e52 100644 --- a/regression/termination/contextsensitive5/main.c +++ b/regression/termination/contextsensitive5/main.c @@ -1,15 +1,15 @@ -#include - -void foo(int* x) -{ - assert(x!=NULL); - *x = 0; -} - -void main() -{ - int x; - int *y = &x; - foo(y); -} - +#include + +void foo(int* x) +{ + assert(x!=NULL); + *x = 0; +} + +void main() +{ + int x; + int *y = &x; + foo(y); +} + diff --git a/regression/termination/contextsensitive6/main.c b/regression/termination/contextsensitive6/main.c index be4631faa..2030a7355 100644 --- a/regression/termination/contextsensitive6/main.c +++ b/regression/termination/contextsensitive6/main.c @@ -1,12 +1,12 @@ - -void foo(int x) -{ - assert(x!=0); -} - -void main() -{ - int x = 1; - foo(x); -} - + +void foo(int x) +{ + assert(x!=0); +} + +void main() +{ + int x = 1; + foo(x); +} + diff --git a/regression/termination/float4/test.desc b/regression/termination/float4/test.desc index 8314a397f..c2dc66423 100644 --- a/regression/termination/float4/test.desc +++ b/regression/termination/float4/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c ^EXIT=5$ diff --git a/regression/termination/global1/main.c b/regression/termination/global1/main.c index ecb897b62..01bd988fa 100644 --- a/regression/termination/global1/main.c +++ b/regression/termination/global1/main.c @@ -1,21 +1,21 @@ -#include - -int g; - -int foo(int y) -{ - __CPROVER_assume(g=1); - assert(z==0); -} - +#include + +int g; + +int foo(int y) +{ + __CPROVER_assume(g=1); + assert(z==0); +} + diff --git a/regression/termination/global2/main.c b/regression/termination/global2/main.c index f653bce18..d2ae47492 100644 --- a/regression/termination/global2/main.c +++ b/regression/termination/global2/main.c @@ -1,22 +1,22 @@ -int g; - -void foo() -{ - g=10; -} - -int bar() -{ - return 20; -} - -void main() -{ - g = 1; - int x; - foo(); - x = bar(); - assert(g==10); - assert(x==20); -} - +int g; + +void foo() +{ + g=10; +} + +int bar() +{ + return 20; +} + +void main() +{ + g = 1; + int x; + foo(); + x = bar(); + assert(g==10); + assert(x==20); +} + diff --git a/regression/termination/ite1/main.c b/regression/termination/ite1/main.c index b4829d864..a0844f924 100644 --- a/regression/termination/ite1/main.c +++ b/regression/termination/ite1/main.c @@ -1,13 +1,13 @@ -void main() -{ - int x; - int y = 0; - - while(y==0) - { - if(x>=5) y=x; - else y=5; - } - - assert(y>=5); -} +void main() +{ + int x; + int y = 0; + + while(y==0) + { + if(x>=5) y=x; + else y=5; + } + + assert(y>=5); +} diff --git a/regression/termination/pointer5/test.desc b/regression/termination/pointer5/test.desc index 7f13074e3..8b39a0a04 100644 --- a/regression/termination/pointer5/test.desc +++ b/regression/termination/pointer5/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --equalities ^EXIT=0$ diff --git a/regression/termination/precond_term4/test.desc b/regression/termination/precond_term4/test.desc index 4589ad0c6..425ba9bf1 100644 --- a/regression/termination/precond_term4/test.desc +++ b/regression/termination/precond_term4/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --preconditions --context-sensitive ^EXIT=5$ diff --git a/regression/termination/running3/test.desc b/regression/termination/running3/test.desc index bba925826..cfa859601 100644 --- a/regression/termination/running3/test.desc +++ b/regression/termination/running3/test.desc @@ -1,4 +1,4 @@ -CORE +KNOWNBUG main.c --context-sensitive --preconditions ^EXIT=5$ diff --git a/regression/termination/sum1/main.c b/regression/termination/sum1/main.c index df354feeb..b28d8b049 100644 --- a/regression/termination/sum1/main.c +++ b/regression/termination/sum1/main.c @@ -1,27 +1,27 @@ -#include - -int max(int x, int y) -{ - if(x>y) return x; - return y; -} - -int inv(int x) -{ - __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes - return -x; -} - -void main() -{ - int x; - __CPROVER_assume(2<=x && x<=3); - - int y=inv(x); - int z=max(y,0); - - assert(y<=-2); - assert(y==-x); - assert(z>=0); - assert(z>=y); -} +#include + +int max(int x, int y) +{ + if(x>y) return x; + return y; +} + +int inv(int x) +{ + __CPROVER_assume(x>INT_MIN); //would not be needed if we did not extend the bitvector sizes + return -x; +} + +void main() +{ + int x; + __CPROVER_assume(2<=x && x<=3); + + int y=inv(x); + int z=max(y,0); + + assert(y<=-2); + assert(y==-x); + assert(z>=0); + assert(z>=y); +} diff --git a/scripts/cpplint.py b/scripts/cpplint.py new file mode 100644 index 000000000..57d9025d7 --- /dev/null +++ b/scripts/cpplint.py @@ -0,0 +1,6619 @@ +#!/usr/bin/env python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import copy +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] [--root=subdir] + [--linelength=digits] [--headers=x,y,...] + [file] ... + + The style guidelines this tries to follow are those in + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Default linted extensions are .cc, .cpp, .cu, .cuh and .h. Change the + extensions with the --extensions flag. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. + + root=subdir + The root directory used for deriving header guard CPP variable. + By default, the header guard CPP variable is calculated as the relative + path to the directory that contains .git, .hg, or .svn. When this flag + is specified, the relative path is calculated from the specified + directory. If the specified directory does not exist, this flag is + ignored. + + Examples: + Assuming that src/.git exists, the header guard CPP variables for + src/chrome/browser/ui/browser.h are: + + No flag => CHROME_BROWSER_UI_BROWSER_H_ + --root=chrome => BROWSER_UI_BROWSER_H_ + --root=chrome/browser => UI_BROWSER_H_ + + linelength=digits + This is the allowed line length for the project. The default value is + 80 characters. + + Examples: + --linelength=120 + + extensions=extension,extension,... + The allowed file extensions that cpplint will check + + Examples: + --extensions=hpp,cpp + + headers=x,y,... + The header extensions that cpplint will treat as .h in checks. Values are + automatically added to --extensions list. + + Examples: + --headers=hpp,hxx + --headers=hpp + + cpplint.py supports per-directory configurations specified in CPPLINT.cfg + files. CPPLINT.cfg file can contain a number of key=value pairs. + Currently the following options are supported: + + set noparent + filter=+filter1,-filter2,... + exclude_files=regex + linelength=80 + root=subdir + headers=x,y,... + + "set noparent" option prevents cpplint from traversing directory tree + upwards looking for more .cfg files in parent directories. This option + is usually placed in the top-level project directory. + + The "filter" option is similar in function to --filter flag. It specifies + message filters in addition to the |_DEFAULT_FILTERS| and those specified + through --filter command-line flag. + + "exclude_files" allows to specify a regular expression to be matched against + a file name. If the expression matches, the file is skipped and not run + through liner. + + "linelength" allows to specify the allowed line length for the project. + + The "root" option is similar in function to the --root flag (see example + above). + + The "headers" option is similar in function to the --headers flag + (see example above). + + CPPLINT.cfg has an effect on files in the same directory and all + sub-directories, unless overridden by a nested configuration file. + + Example file: + filter=-build/include_order,+build/include_alpha + exclude_files=.*\.cc + + The above example disables build/include_order warning and enables + build/include_alpha as well as excludes all .cc from being + processed by linter, in the current directory (where the .cfg + file is located) and all sub-directories. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +_ERROR_CATEGORIES = [ + 'build/class', + 'build/c++11', + 'build/c++14', + 'build/c++tr1', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/alt_tokens', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/inheritance', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/namespace', + 'readability/identifiers', + 'readability/nolint', + 'readability/nul', + 'readability/strings', + 'readability/todo', + 'readability/throw', + 'readability/utf8', + 'readability/function_comment' + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/indentation_namespace', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/vlog', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/empty_conditional_body', + 'whitespace/empty_if_body', + 'whitespace/empty_loop_body', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/forcolon', + 'whitespace/indent', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + ] + +# These error categories are no longer enforced by cpplint, but for backwards- +# compatibility they may still appear in NOLINT comments. +_LEGACY_ERROR_CATEGORIES = [ + 'readability/streams', + 'readability/function', + ] + +# The default state of the category filter. This is overridden by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = ['-build/include_alpha'] + +# The default list of categories suppressed for C (not C++) files. +_DEFAULT_C_SUPPRESSED_CATEGORIES = [ + 'readability/casting', + ] + +# The default list of categories suppressed for Linux Kernel files. +_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [ + 'whitespace/tab', + ] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# C++ headers +_CPP_HEADERS = frozenset([ + # Legacy + 'algobase.h', + 'algo.h', + 'alloc.h', + 'builtinbuf.h', + 'bvector.h', + 'complex.h', + 'defalloc.h', + 'deque.h', + 'editbuf.h', + 'fstream.h', + 'function.h', + 'hash_map', + 'hash_map.h', + 'hash_set', + 'hash_set.h', + 'hashtable.h', + 'heap.h', + 'indstream.h', + 'iomanip.h', + 'iostream.h', + 'istream.h', + 'iterator.h', + 'list.h', + 'map.h', + 'multimap.h', + 'multiset.h', + 'ostream.h', + 'pair.h', + 'parsestream.h', + 'pfstream.h', + 'procbuf.h', + 'pthread_alloc', + 'pthread_alloc.h', + 'rope', + 'rope.h', + 'ropeimpl.h', + 'set.h', + 'slist', + 'slist.h', + 'stack.h', + 'stdiostream.h', + 'stl_alloc.h', + 'stl_relops.h', + 'streambuf.h', + 'stream.h', + 'strfile.h', + 'strstream.h', + 'tempbuf.h', + 'tree.h', + 'type_traits.h', + 'vector.h', + # 17.6.1.2 C++ library headers + 'algorithm', + 'array', + 'atomic', + 'bitset', + 'chrono', + 'codecvt', + 'complex', + 'condition_variable', + 'deque', + 'exception', + 'forward_list', + 'fstream', + 'functional', + 'future', + 'initializer_list', + 'iomanip', + 'ios', + 'iosfwd', + 'iostream', + 'istream', + 'iterator', + 'limits', + 'list', + 'locale', + 'map', + 'memory', + 'mutex', + 'new', + 'numeric', + 'ostream', + 'queue', + 'random', + 'ratio', + 'regex', + 'scoped_allocator', + 'set', + 'sstream', + 'stack', + 'stdexcept', + 'streambuf', + 'string', + 'strstream', + 'system_error', + 'thread', + 'tuple', + 'typeindex', + 'typeinfo', + 'type_traits', + 'unordered_map', + 'unordered_set', + 'utility', + 'valarray', + 'vector', + # 17.6.1.2 C++ headers for C library facilities + 'cassert', + 'ccomplex', + 'cctype', + 'cerrno', + 'cfenv', + 'cfloat', + 'cinttypes', + 'ciso646', + 'climits', + 'clocale', + 'cmath', + 'csetjmp', + 'csignal', + 'cstdalign', + 'cstdarg', + 'cstdbool', + 'cstddef', + 'cstdint', + 'cstdio', + 'cstdlib', + 'cstring', + 'ctgmath', + 'ctime', + 'cuchar', + 'cwchar', + 'cwctype', + ]) + +# Type names +_TYPES = re.compile( + r'^(?:' + # [dcl.type.simple] + r'(char(16_t|32_t)?)|wchar_t|' + r'bool|short|int|long|signed|unsigned|float|double|' + # [support.types] + r'(ptrdiff_t|size_t|max_align_t|nullptr_t)|' + # [cstdint.syn] + r'(u?int(_fast|_least)?(8|16|32|64)_t)|' + r'(u?int(max|ptr)_t)|' + r')$') + + +# These headers are excluded from [build/include] and [build/include_order] +# checks: +# - Anything not following google file name conventions (containing an +# uppercase character, such as Python.h or nsStringAPI.h, for example). +# - Lua headers. +_THIRD_PARTY_HEADERS_PATTERN = re.compile( + r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$') + +# Pattern for matching FileInfo.BaseName() against test file name +_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$' + +# Pattern that matches only complete whitespace, possibly across multiple lines. +_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL) + +# Assertion macros. These are defined in base/logging.h and +# testing/base/public/gunit.h. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE', 'ASSERT_TRUE', + 'EXPECT_FALSE', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + +# Alternative tokens and their replacements. For full list, see section 2.5 +# Alternative tokens [lex.digraph] in the C++ standard. +# +# Digraphs (such as '%:') are not included here since it's a mess to +# match those on a word boundary. +_ALT_TOKEN_REPLACEMENT = { + 'and': '&&', + 'bitor': '|', + 'or': '||', + 'xor': '^', + 'compl': '~', + 'bitand': '&', + 'and_eq': '&=', + 'or_eq': '|=', + 'xor_eq': '^=', + 'not': '!', + 'not_eq': '!=' + } + +# Compile regular expression that matches all the above keywords. The "[ =()]" +# bit is meant to avoid matching these keywords outside of boolean expressions. +# +# False positives include C-style multi-line comments and multi-line strings +# but those have always been troublesome for cpplint. +_ALT_TOKEN_REPLACEMENT_PATTERN = re.compile( + r'[ =()](' + ('|'.join(_ALT_TOKEN_REPLACEMENT.keys())) + r')(?=[ (]|$)') + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + +# These constants define the current inline assembly state +_NO_ASM = 0 # Outside of inline assembly block +_INSIDE_ASM = 1 # Inside inline assembly block +_END_ASM = 2 # Last line of inline assembly block +_BLOCK_ASM = 3 # The whole block is an inline assembly block + +# Match start of assembly blocks +_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)' + r'(?:\s+(volatile|__volatile__))?' + r'\s*[{(]') + +# Match strings that indicate we're working on a C (not C++) file. +_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|' + r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))') + +# Match string that indicates we're working on a Linux Kernel file. +_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)') + +_regexp_compile_cache = {} + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +# The root directory used for deriving header guard CPP variable. +# This is set by --root flag. +_root = None + +# The allowed line length of files. +# This is set by --linelength flag. +_line_length = 80 + +# The allowed extensions for file names +# This is set by --extensions flag. +_valid_extensions = set(['cc', 'h', 'cpp', 'hh']) + +# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc. +# This is set by --headers flag. +_hpp_headers = set(['h']) + +# {str, bool}: a map from error categories to booleans which indicate if the +# category should be suppressed for every line. +_global_error_suppressions = {} + +def ProcessHppHeadersOption(val): + global _hpp_headers + try: + _hpp_headers = set(val.split(',')) + # Automatically append to extensions list so it does not have to be set 2 times + _valid_extensions.update(_hpp_headers) + except ValueError: + PrintUsage('Header extensions must be comma seperated list.') + +def IsHeaderExtension(file_extension): + return file_extension in _hpp_headers + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of line error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + matched = Search(r'\bNOLINT(NEXTLINE)?\b(\([^)]+\))?', raw_line) + if matched: + if matched.group(1): + suppressed_line = linenum + 1 + else: + suppressed_line = linenum + category = matched.group(2) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(suppressed_line) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(suppressed_line) + elif category not in _LEGACY_ERROR_CATEGORIES: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ProcessGlobalSuppresions(lines): + """Updates the list of global error suppressions. + + Parses any lint directives in the file that have global effect. + + Args: + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + """ + for line in lines: + if _SEARCH_C_FILE.search(line): + for category in _DEFAULT_C_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True + if _SEARCH_KERNEL_FILE.search(line): + for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES: + _global_error_suppressions[category] = True + + +def ResetNolintSuppressions(): + """Resets the set of NOLINT suppressions to empty.""" + _error_suppressions.clear() + _global_error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ProcessGlobalSuppresions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment or + global suppression. + """ + return (_global_error_suppressions.get(category, False) or + linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def ReplaceAll(pattern, rep, s): + """Replaces instances of pattern in a string with a replacement. + + The compiled regex is kept in a cache shared by Match and Search. + + Args: + pattern: regex pattern + rep: replacement text + s: search string + + Returns: + string with replacements made (or original string if no replacements) + """ + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].sub(rep, s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if pattern not in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +def _IsSourceExtension(s): + """File extension (excluding dot) matches a source file extension.""" + return s in ('c', 'cc', 'cpp') + + +class _IncludeState(object): + """Tracks line numbers for includes, and the order in which includes appear. + + include_list contains list of lists of (header, line number) pairs. + It's a lists of lists rather than just one flat list to make it + easier to update across preprocessor boundaries. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + self.include_list = [[]] + self.ResetSection('') + + def FindHeader(self, header): + """Check if a header has already been included. + + Args: + header: header to check. + Returns: + Line number of previous occurrence, or -1 if the header has not + been seen before. + """ + for section_list in self.include_list: + for f in section_list: + if f[0] == header: + return f[1] + return -1 + + def ResetSection(self, directive): + """Reset section checking for preprocessor directive. + + Args: + directive: preprocessor directive (e.g. "if", "else"). + """ + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + # Update list of includes. Note that we never pop from the + # include list. + if directive in ('if', 'ifdef', 'ifndef'): + self.include_list.append([]) + elif directive in ('else', 'elif'): + self.include_list[-1] = [] + + def SetLastHeader(self, header_path): + self._last_header = header_path + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + header_path: Canonicalized header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + # If previous section is different from current section, _last_header will + # be reset to empty string, so it's always less than current header. + # + # If previous line was a blank line, assume that the headers are + # intentionally sorted the way they are. + if (self._last_header > header_path and + Match(r'^\s*#\s*include\b', clean_lines.elided[linenum - 1])): + return False + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + # backup of filter list. Used to restore the state after each file. + self._filters_backup = self.filters[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + self.AddFilters(filters) + + def AddFilters(self, filters): + """ Adds more filters to the existing list of error-message filters. """ + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def BackupFilters(self): + """ Saves the current filter list to backup storage.""" + self._filters_backup = self.filters[:] + + def RestoreFilters(self): + """ Restores filters previously backed up.""" + self.filters = self._filters_backup[:] + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stdout.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + +def _AddFilters(filters): + """Adds more filter overrides. + + Unlike _SetFilters, this function does not reset the current list of filters + available. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.AddFilters(filters) + +def _BackupFilters(): + """ Saves the current filter list to backup storage.""" + _cpplint_state.BackupFilters() + +def _RestoreFilters(): + """ Restores filters previously backed up.""" + _cpplint_state.RestoreFilters() + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if not self.in_a_function: + return + + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo(object): + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = current_dir = os.path.dirname(fullname) + while current_dir != os.path.dirname(current_dir): + if (os.path.exists(os.path.join(current_dir, ".git")) or + os.path.exists(os.path.join(current_dir, ".hg")) or + os.path.exists(os.path.join(current_dir, ".svn"))): + root_dir = current_dir + current_dir = os.path.dirname(current_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return _IsSourceExtension(self.Extension()[1:]) + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + elif _cpplint_state.output_format == 'eclipse': + sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Match a single C style comment on the same line. +_RE_PATTERN_C_COMMENTS = r'/\*(?:[^*]|\*(?!/))*\*/' +# Matches multi-line C style comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r'(\s*' + _RE_PATTERN_C_COMMENTS + r'\s*$|' + + _RE_PATTERN_C_COMMENTS + r'\s+|' + + r'\s+' + _RE_PATTERN_C_COMMENTS + r'(?=\W)|' + + _RE_PATTERN_C_COMMENTS + r')') + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def CleanseRawStrings(raw_lines): + """Removes C++11 raw strings from lines. + + Before: + static const char kData[] = R"( + multi-line string + )"; + + After: + static const char kData[] = "" + (replaced by blank line) + ""; + + Args: + raw_lines: list of raw lines. + + Returns: + list of lines with C++11 raw strings replaced by empty strings. + """ + + delimiter = None + lines_without_raw_strings = [] + for line in raw_lines: + if delimiter: + # Inside a raw string, look for the end + end = line.find(delimiter) + if end >= 0: + # Found the end of the string, match leading space for this + # line and resume copying the original lines, and also insert + # a "" on the last line. + leading_space = Match(r'^(\s*)\S', line) + line = leading_space.group(1) + '""' + line[end + len(delimiter):] + delimiter = None + else: + # Haven't found the end yet, append a blank line. + line = '""' + + # Look for beginning of a raw string, and replace them with + # empty strings. This is done in a loop to handle multiple raw + # strings on the same line. + while delimiter is None: + # Look for beginning of a raw string. + # See 2.14.15 [lex.string] for syntax. + # + # Once we have matched a raw string, we check the prefix of the + # line to make sure that the line is not part of a single line + # comment. It's done this way because we remove raw strings + # before removing comments as opposed to removing comments + # before removing raw strings. This is because there are some + # cpplint checks that requires the comments to be preserved, but + # we don't want to check comments that are inside raw strings. + matched = Match(r'^(.*?)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line) + if (matched and + not Match(r'^([^\'"]|\'(\\.|[^\'])*\'|"(\\.|[^"])*")*//', + matched.group(1))): + delimiter = ')' + matched.group(2) + '"' + + end = matched.group(3).find(delimiter) + if end >= 0: + # Raw string ended on same line + line = (matched.group(1) + '""' + + matched.group(3)[end + len(delimiter):]) + delimiter = None + else: + # Start of a multi-line raw string + line = matched.group(1) + '""' + else: + break + + lines_without_raw_strings.append(line) + + # TODO(unknown): if delimiter is not None here, we might want to + # emit a warning for unterminated string. + return lines_without_raw_strings + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '/**/' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 4 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments. + 2) lines member contains lines without comments. + 3) raw_lines member contains all the lines without processing. + 4) lines_without_raw_strings member is same as raw_lines, but with C++11 raw + strings removed. + All these members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + self.lines_without_raw_strings = CleanseRawStrings(lines) + for linenum in range(len(self.lines_without_raw_strings)): + self.lines.append(CleanseComments( + self.lines_without_raw_strings[linenum])) + elided = self._CollapseStrings(self.lines_without_raw_strings[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if _RE_PATTERN_INCLUDE.match(elided): + return elided + + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + + # Replace quoted strings and digit separators. Both single quotes + # and double quotes are processed in the same loop, otherwise + # nested quotes wouldn't work. + collapsed = '' + while True: + # Find the first quote character + match = Match(r'^([^\'"]*)([\'"])(.*)$', elided) + if not match: + collapsed += elided + break + head, quote, tail = match.groups() + + if quote == '"': + # Collapse double quoted strings + second_quote = tail.find('"') + if second_quote >= 0: + collapsed += head + '""' + elided = tail[second_quote + 1:] + else: + # Unmatched double quote, don't bother processing the rest + # of the line since this is probably a multiline string. + collapsed += elided + break + else: + # Found single quote, check nearby text to eliminate digit separators. + # + # There is no special handling for floating point here, because + # the integer/fractional/exponent parts would all be parsed + # correctly as long as there are digits on both sides of the + # separator. So we are fine as long as we don't see something + # like "0.'3" (gcc 4.9.0 will not allow this literal). + if Search(r'\b(?:0[bBxX]?|[1-9])[0-9a-fA-F]*$', head): + match_literal = Match(r'^((?:\'?[0-9a-zA-Z_])*)(.*)$', "'" + tail) + collapsed += head + match_literal.group(1).replace("'", '') + elided = match_literal.group(2) + else: + second_quote = tail.find('\'') + if second_quote >= 0: + collapsed += head + "''" + elided = tail[second_quote + 1:] + else: + # Unmatched single quote + collapsed += elided + break + + return collapsed + +def IsTemplateArgumentList_DB(clean_lines, linenum, pos): + """if lines[linenum][pos] is >, finds out if it is closing bracket + of a template argument. + + Finds a matching < (if possible) to the > at position line[linenum][pos] + and then analyzes the string between the two to work out if it could + in fact be a template argument list or just some general code. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + True if the angled bracket pointed to is the end of a template arguement + list + """ + (start_line, start_linenum, start_pos) = OpenExpression(clean_lines, linenum, pos) + + # We didn't find a matching < so clearly not a template argument list + if start_pos == -1: + return False + + # This collects all the charatcers between the > and matching < + # It also reverses it since we don't care about the actual contents + # we are just looking to make sure no weird characters (like &&) + inbetween_string = "" + if linenum == start_linenum: + inbetween_string = clean_lines.elided[start_linenum][start_pos:pos + 1] + else: + while(start_linenum != linenum): + inbetween_string += clean_lines.elided[start_linenum][start_pos:] + start_linenum += 1 + start_pos = 0 + inbetween_string += clean_lines.elided[linenum][0:pos] + + is_simple_template_params = Match('^[<>(::),\w\s]*$', inbetween_string) + if is_simple_template_params: + return True + + # The operators & or * are accepted within the template arguments + # as we can have reference or pointer types in the list + # however, they must be followed by a > (excepting white space) + # anything else indicates they are being used as a type + + is_looking_for_closing_angle_bracket = False + for char in inbetween_string: + if char in ['&', '*']: + is_looking_for_closing_angle_bracket = True + elif is_looking_for_closing_angle_bracket: + if char == '>': + is_looking_for_closing_angle_bracket = False + elif char not in [' ', '\t']: + return False + + return True + +def ForceOpenExpression(clean_lines, linenum, pos, bracket): + """Find an opening bracket matching the specified closing bracket + + Search starting at the position for a matching closing bracket of the + same type as bracket. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + bracket: The style of bracket to match + + Returns: + A tuple (line, linenum, pos) pointer *to* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + + # Check first line + (end_pos, stack) = FindStartOfExpressionInLine(line, pos , [bracket]) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (end_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def OpenExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the closing of the expression. + + Essentially a mirror of what CloseExpression does + + Calls ForceOpenExpression with the bracket type pointed at + + TODO(tkiley): could probably be merged with CloseExpression + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *to* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + if (line[pos] not in ')}]>'): + return (line, clean_lines.NumLines(), -1) + else: + return ForceOpenExpression(clean_lines, linenum, pos-1, line[pos]) + + +def FindEndOfExpressionInLine(line, startpos, stack): + """Find the position just after the end of current parenthesized expression. + + Args: + line: a CleansedLines line. + startpos: start searching at this position. + stack: nesting stack at startpos. + + Returns: + On finding matching end: (index just after matching end, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at end of this line) + """ + for i in xrange(startpos, len(line)): + char = line[i] + if char in '([{': + # Found start of parenthesized expression, push to expression stack + stack.append(char) + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + if stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + elif i > 0 and Search(r'\boperator\s*$', line[0:i]): + # operator<, don't add to stack + continue + else: + # Tentative start of template argument list + stack.append('<') + elif char in ')]}': + # Found end of parenthesized expression. + # + # If we are currently expecting a matching '>', the pending '<' + # must have been an operator. Remove them from expression stack. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + if ((stack[-1] == '(' and char == ')') or + (stack[-1] == '[' and char == ']') or + (stack[-1] == '{' and char == '}')): + stack.pop() + if not stack: + return (i + 1, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == '>': + # Found potential end of template argument list. + + # Ignore "->" and operator functions + if (i > 0 and + (line[i - 1] == '-' or Search(r'\boperator\s*$', line[0:i - 1]))): + continue + + # Pop the stack if there is a matching '<'. Otherwise, ignore + # this '>' since it must be an operator. + if stack: + if stack[-1] == '<': + stack.pop() + if not stack: + return (i + 1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '>', the matching '<' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '<': + stack.pop() + if not stack: + return (-1, None) + + # Did not find end of expression or unbalanced parentheses on this line + return (-1, stack) + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [ or <, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the + linenum/pos that correspond to the closing of the expression. + + TODO(unknown): cpplint spends a fair bit of time matching parentheses. + Ideally we would want to index all opening and closing parentheses once + and have CloseExpression be just a simple lookup, but due to preprocessor + tricks, this is not so easy. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + if (line[pos] not in '({[<') or Match(r'<[<=]', line[pos:]): + return (line, clean_lines.NumLines(), -1) + + # Check first line + (end_pos, stack) = FindEndOfExpressionInLine(line, pos, []) + if end_pos > -1: + return (line, linenum, end_pos) + + # Continue scanning forward + while stack and linenum < clean_lines.NumLines() - 1: + linenum += 1 + line = clean_lines.elided[linenum] + (end_pos, stack) = FindEndOfExpressionInLine(line, 0, stack) + if end_pos > -1: + return (line, linenum, end_pos) + + # Did not find end of expression before end of file, give up + return (line, clean_lines.NumLines(), -1) + +def FindDoStart(clean_lines, linenum): + """If found a while (...); find the potential do to match it. + + Work our way up through the lines starting at linenum to find a do + that hasn't been matched. This might not succeed as might just be an + emtpy while statement. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The line number with the while(...); on + + Returns: + A tuple (found, linenum), found is true if an opening + do statement found, false otherwise. The linenum will + be the line the do is found on (or -1 if never found) + """ + + reverse_lines = clean_lines.lines[linenum-1:0:-1] + found_linenum = linenum - 1; + for line in reverse_lines: + if Search(r'^\s*do\s*{?\s*$', line): + return True, found_linenum + elif Search(r'^\s*}?\s*while\(.*\)\s*;\s*$', line): + return False, -1 + else: + found_linenum = found_linenum - 1 + + return False, -1 + +def FindStartOfExpressionInLine(line, endpos, stack): + """Find position at the matching start of current expression. + + This is almost the reverse of FindEndOfExpressionInLine, but note + that the input position and returned position differs by 1. + + Args: + line: a CleansedLines line. + endpos: start searching at this position. + stack: nesting stack at endpos. + + Returns: + On finding matching start: (index at matching start, None) + On finding an unclosed expression: (-1, None) + Otherwise: (-1, new stack at beginning of this line) + """ + i = endpos + while i >= 0: + char = line[i] + if char in ')]}': + # Found end of expression, push to expression stack + stack.append(char) + elif char == '>': + # Found potential end of template argument list. + # + # Ignore it if it's a "->" or ">=" or "operator>" + if (i > 0 and + (line[i - 1] == '-' or + Match(r'\s>=\s', line[i - 1:]) or + Search(r'\boperator\s*$', line[0:i]))): + i -= 1 + else: + stack.append('>') + elif char == '<': + # Found potential start of template argument list + if i > 0 and line[i - 1] == '<': + # Left shift operator + i -= 1 + else: + # If there is a matching '>', we can pop the expression stack. + # Otherwise, ignore this '<' since it must be an operator. + if stack and stack[-1] == '>': + stack.pop() + if not stack: + return (i, None) + elif char in '([{': + # Found start of expression. + # + # If there are any unmatched '>' on the stack, they must be + # operators. Remove those. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + if ((char == '(' and stack[-1] == ')') or + (char == '[' and stack[-1] == ']') or + (char == '{' and stack[-1] == '}')): + stack.pop() + if not stack: + return (i, None) + else: + # Mismatched parentheses + return (-1, None) + elif char == ';': + # Found something that look like end of statements. If we are currently + # expecting a '<', the matching '>' must have been an operator, since + # template argument list should not contain statements. + while stack and stack[-1] == '>': + stack.pop() + if not stack: + return (-1, None) + + i -= 1 + + return (-1, stack) + + +def ReverseCloseExpression(clean_lines, linenum, pos): + """If input points to ) or } or ] or >, finds the position that opens it. + + If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the + linenum/pos that correspond to the opening of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *at* the opening brace, or + (line, 0, -1) if we never find the matching opening brace. Note + we ignore strings and comments when matching; and the line we + return is the 'cleansed' line at linenum. + """ + line = clean_lines.elided[linenum] + if line[pos] not in ')}]>': + return (line, 0, -1) + + # Check last line + (start_pos, stack) = FindStartOfExpressionInLine(line, pos, []) + if start_pos > -1: + return (line, linenum, start_pos) + + # Continue scanning backward + while stack and linenum > 0: + linenum -= 1 + line = clean_lines.elided[linenum] + (start_pos, stack) = FindStartOfExpressionInLine(line, len(line) - 1, stack) + if start_pos > -1: + return (line, linenum, start_pos) + + # Did not find start of expression before beginning of file, give up + return (line, 0, -1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Author', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Author: "') + + +def GetIndentLevel(line): + """Return the number of leading spaces in line. + + Args: + line: A string to check. + + Returns: + An integer count of leading spaces, possibly zero. + """ + indent = Match(r'^( *)\S', line) + if indent: + return len(indent.group(1)) + else: + return 0 + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) + # Replace 'c++' with 'cpp'. + filename = filename.replace('C++', 'cpp').replace('c++', 'cpp') + + fileinfo = FileInfo(filename) + file_path_from_root = fileinfo.RepositoryName() + file_path_from_root = 'CPROVER_2LS_' + file_path_from_root[4:] + if _root: + suffix = os.sep + # On Windows using directory separator will leave us with + # "bogus escape error" unless we properly escape regex. + if suffix == '\\': + suffix += '\\' + file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root) + return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + + +def CheckForHeaderGuard(filename, clean_lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + clean_lines: A CleansedLines instance containing the file. + error: The function to call with any errors found. + """ + + # Don't check for header guards if there are error suppression + # comments somewhere in this file. + # + # Because this is silencing a warning for a nonexistent line, we + # only support the very specific NOLINT(build/header_guard) syntax, + # and not the general NOLINT or NOLINT(*) syntax. + raw_lines = clean_lines.lines_without_raw_strings + for i in raw_lines: + if Search(r'//\s*NOLINT\(build/header_guard\)', i): + return + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = '' + ifndef_linenum = 0 + define = '' + endif = '' + endif_linenum = 0 + for linenum, line in enumerate(raw_lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef or not define or ifndef != define: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, raw_lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + # Check for "//" comments on endif line. + #ParseNolintSuppressions(filename, raw_lines[endif_linenum], endif_linenum, + # error) + #match = Match(r'#endif\s*//\s*' + cppvar + r'(_)?\b', endif) + #if match: + # if match.group(1) == '_': + # # Issue low severity warning for deprecated double trailing underscore + # error(filename, endif_linenum, 'build/header_guard', 0, + # '#endif line should be "#endif // %s"' % cppvar) + # return + + # Didn't find the corresponding "//" comment. If this file does not + # contain any "//" comments at all, it could be that the compiler + # only wants "/**/" comments, look for those instead. + #no_single_line_comments = True + #for i in xrange(1, len(raw_lines) - 1): + # line = raw_lines[i] + # if Match(r'^(?:(?:\'(?:\.|[^\'])*\')|(?:"(?:\.|[^"])*")|[^\'"])*//', line): + # no_single_line_comments = False + # break + # + #if no_single_line_comments: + # match = Match(r'#endif\s*/\*\s*' + cppvar + r'(_)?\s*\*/', endif) + # if match: + # if match.group(1) == '_': + # # Low severity warning for double trailing underscore + # error(filename, endif_linenum, 'build/header_guard', 0, + # '#endif line should be "#endif /* %s */"' % cppvar) + # return + # + ## Didn't find anything + #error(filename, endif_linenum, 'build/header_guard', 5, + # '#endif line should be "#endif // %s"' % cppvar) + + +def CheckHeaderFileIncluded(filename, include_state, error): + """Logs an error if a .cc file does not include its header.""" + + # Do not check test files + fileinfo = FileInfo(filename) + if Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()): + return + + headerfile = filename[0:len(filename) - len(fileinfo.Extension())] + '.h' + if not os.path.exists(headerfile): + return + headername = FileInfo(headerfile).RepositoryName() + first_include = 0 + for section_list in include_state.include_list: + for f in section_list: + if headername in f[0] or f[0] in headername: + return + if not first_include: + first_include = f[1] + + error(filename, first_include, 'build/include', 5, + '%s should include its header file %s' % (fileinfo.RepositoryName(), + headername)) + + +def CheckForBadCharacters(filename, lines, error): + """Logs an error for each line containing bad characters. + + Two kinds of bad characters: + + 1. Unicode replacement characters: These indicate that either the file + contained invalid UTF-8 (likely) or Unicode replacement characters (which + it shouldn't). Note that it's possible for this to throw off line + numbering if the invalid UTF-8 occurred adjacent to a newline. + + 2. NUL bytes. These are problematic for some tools. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + if '\0' in line: + error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. ' + 'Use C++11 raw strings or concatenation instead.') + + +# (non-threadsafe name, thread-safe alternative, validation pattern) +# +# The validation pattern is used to eliminate false positives such as: +# _rand(); // false positive due to substring match. +# ->rand(); // some member function rand(). +# ACMRandom rand(seed); // some variable named rand. +# ISAACRandom rand(); // another variable named rand. +# +# Basically we require the return value of these functions to be used +# in some expression context on the same line by matching on some +# operator before the function name. This eliminates constructors and +# member function calls. +_UNSAFE_FUNC_PREFIX = r'(?:[-+*/=%^&|(<]\s*|>\s+)' +_THREADING_LIST = ( + ('asctime(', 'asctime_r(', _UNSAFE_FUNC_PREFIX + r'asctime\([^)]+\)'), + ('ctime(', 'ctime_r(', _UNSAFE_FUNC_PREFIX + r'ctime\([^)]+\)'), + ('getgrgid(', 'getgrgid_r(', _UNSAFE_FUNC_PREFIX + r'getgrgid\([^)]+\)'), + ('getgrnam(', 'getgrnam_r(', _UNSAFE_FUNC_PREFIX + r'getgrnam\([^)]+\)'), + ('getlogin(', 'getlogin_r(', _UNSAFE_FUNC_PREFIX + r'getlogin\(\)'), + ('getpwnam(', 'getpwnam_r(', _UNSAFE_FUNC_PREFIX + r'getpwnam\([^)]+\)'), + ('getpwuid(', 'getpwuid_r(', _UNSAFE_FUNC_PREFIX + r'getpwuid\([^)]+\)'), + ('gmtime(', 'gmtime_r(', _UNSAFE_FUNC_PREFIX + r'gmtime\([^)]+\)'), + ('localtime(', 'localtime_r(', _UNSAFE_FUNC_PREFIX + r'localtime\([^)]+\)'), + ('rand(', 'rand_r(', _UNSAFE_FUNC_PREFIX + r'rand\(\)'), + ('strtok(', 'strtok_r(', + _UNSAFE_FUNC_PREFIX + r'strtok\([^)]+\)'), + ('ttyname(', 'ttyname_r(', _UNSAFE_FUNC_PREFIX + r'ttyname\([^)]+\)'), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_func, multithread_safe_func, pattern in _THREADING_LIST: + # Additional pattern matching check to confirm that this is the + # function we are looking for + if Search(pattern, line): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_func + + '...) instead of ' + single_thread_func + + '...) for improved thread safety.') + + +def CheckVlogArguments(filename, clean_lines, linenum, error): + """Checks that VLOG() is only used for defining a logging level. + + For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and + VLOG(FATAL) are not. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line): + error(filename, linenum, 'runtime/vlog', 5, + 'VLOG() should be used with numeric verbosity level. ' + 'Use LOG() if you want symbolic severity levels.') + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +def IsMacroDefinition(clean_lines, linenum): + if Search(r'^#define', clean_lines[linenum]): + return True + + if linenum > 0 and Search(r'\\$', clean_lines[linenum - 1]): + return True + + return False + + +def IsForwardClassDeclaration(clean_lines, linenum): + return Match(r'^\s*(\btemplate\b)*.*class\s+\w+;\s*$', clean_lines[linenum]) + + +class _BlockInfo(object): + """Stores information about a generic block of code.""" + + def __init__(self, linenum, seen_open_brace): + self.starting_linenum = linenum + self.seen_open_brace = seen_open_brace + self.open_parentheses = 0 + self.inline_asm = _NO_ASM + self.check_namespace_indentation = False + + def CheckBegin(self, filename, clean_lines, linenum, error): + """Run checks that applies to text up to the opening brace. + + This is mostly for checking the text after the class identifier + and the "{", usually where the base class is specified. For other + blocks, there isn't much to check, so we always pass. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Run checks that applies to text after the closing brace. + + This is mostly used for checking end of namespace comments. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + pass + + def IsBlockInfo(self): + """Returns true if this block is a _BlockInfo. + + This is convenient for verifying that an object is an instance of + a _BlockInfo, but not an instance of any of the derived classes. + + Returns: + True for this class, False for derived classes. + """ + return self.__class__ == _BlockInfo + + +class _ExternCInfo(_BlockInfo): + """Stores information about an 'extern "C"' block.""" + + def __init__(self, linenum): + _BlockInfo.__init__(self, linenum, True) + + +class _ClassInfo(_BlockInfo): + """Stores information about a class.""" + + def __init__(self, name, class_or_struct, clean_lines, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name + self.is_derived = False + self.check_namespace_indentation = True + if class_or_struct == 'struct': + self.access = 'public' + self.is_struct = True + else: + self.access = 'private' + self.is_struct = False + + # Remember initial indentation level for this class. Using raw_lines here + # instead of elided to account for leading comments. + self.class_indent = GetIndentLevel(clean_lines.raw_lines[linenum]) + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.elided[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + def CheckBegin(self, filename, clean_lines, linenum, error): + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): + self.is_derived = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + # If there is a DISALLOW macro, it should appear near the end of + # the class. + seen_last_thing_in_class = False + for i in xrange(linenum - 1, self.starting_linenum, -1): + match = Search( + r'\b(DISALLOW_COPY_AND_ASSIGN|DISALLOW_IMPLICIT_CONSTRUCTORS)\(' + + self.name + r'\)', + clean_lines.elided[i]) + if match: + if seen_last_thing_in_class: + error(filename, i, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + break + + if not Match(r'^\s*$', clean_lines.elided[i]): + seen_last_thing_in_class = True + + # Check that closing brace is aligned with beginning of the class. + # Only do this if the closing brace is indented by only whitespaces. + # This means we will not check single-line class definitions. + indent = Match(r'^( *)\}', clean_lines.elided[linenum]) + if indent and len(indent.group(1)) != self.class_indent: + if self.is_struct: + parent = 'struct ' + self.name + else: + parent = 'class ' + self.name + error(filename, linenum, 'whitespace/indent', 3, + 'Closing brace should be aligned with beginning of %s' % parent) + + +class _NamespaceInfo(_BlockInfo): + """Stores information about a namespace.""" + + def __init__(self, name, linenum): + _BlockInfo.__init__(self, linenum, False) + self.name = name or '' + self.check_namespace_indentation = True + + def CheckEnd(self, filename, clean_lines, linenum, error): + """Check end of namespace comments.""" + line = clean_lines.raw_lines[linenum] + + # Check how many lines is enclosed in this namespace. Don't issue + # warning for missing namespace comments if there aren't enough + # lines. However, do apply checks if there is already an end of + # namespace comment and it's incorrect. + # + # TODO(unknown): We always want to check end of namespace comments + # if a namespace is large, but sometimes we also want to apply the + # check if a short namespace contained nontrivial things (something + # other than forward declarations). There is currently no logic on + # deciding what these nontrivial things are, so this check is + # triggered by namespace size only, which works most of the time. + if (linenum - self.starting_linenum < 10 + and not Match(r'^\s*};*\s*(//|/\*).*\bnamespace\b', line)): + return + + # Look for matching comment at end of namespace. + # + # Note that we accept C style "/* */" comments for terminating + # namespaces, so that code that terminate namespaces inside + # preprocessor macros can be cpplint clean. + # + # We also accept stuff like "// end of namespace ." with the + # period at the end. + # + # Besides these, we don't accept anything else, otherwise we might + # get false negatives when existing comment is a substring of the + # expected namespace. +# if self.name: +# # Named namespace +# if not Match((r'^\s*};*\s*(//|/\*).*\bnamespace\s+' + +# re.escape(self.name) + r'[\*/\.\\\s]*$'), +# line): +# error(filename, linenum, 'readability/namespace', 5, +# 'Namespace should be terminated with "// namespace %s"' % +# self.name) +# else: +# # Anonymous namespace +# if not Match(r'^\s*};*\s*(//|/\*).*\bnamespace[\*/\.\\\s]*$', line): +# # If "// namespace anonymous" or "// anonymous namespace (more text)", +# # mention "// anonymous namespace" as an acceptable form +# if Match(r'^\s*}.*\b(namespace anonymous|anonymous namespace)\b', line): +# error(filename, linenum, 'readability/namespace', 5, +# 'Anonymous namespace should be terminated with "// namespace"' +# ' or "// anonymous namespace"') +# else: +# error(filename, linenum, 'readability/namespace', 5, +# 'Anonymous namespace should be terminated with "// namespace"') + + +class _PreprocessorInfo(object): + """Stores checkpoints of nesting stacks when #if/#else is seen.""" + + def __init__(self, stack_before_if): + # The entire nesting stack before #if + self.stack_before_if = stack_before_if + + # The entire nesting stack up to #else + self.stack_before_else = [] + + # Whether we have already seen #else or #elif + self.seen_else = False + + +class NestingState(object): + """Holds states related to parsing braces.""" + + def __init__(self): + # Stack for tracking all braces. An object is pushed whenever we + # see a "{", and popped when we see a "}". Only 3 types of + # objects are possible: + # - _ClassInfo: a class or struct. + # - _NamespaceInfo: a namespace. + # - _BlockInfo: some other type of block. + self.stack = [] + + # Top of the previous stack before each Update(). + # + # Because the nesting_stack is updated at the end of each line, we + # had to do some convoluted checks to find out what is the current + # scope at the beginning of the line. This check is simplified by + # saving the previous top of nesting stack. + # + # We could save the full stack, but we only need the top. Copying + # the full nesting stack would slow down cpplint by ~10%. + self.previous_stack_top = [] + + # Stack of _PreprocessorInfo objects. + self.pp_stack = [] + + def SeenOpenBrace(self): + """Check if we have seen the opening brace for the innermost block. + + Returns: + True if we have seen the opening brace, False if the innermost + block is still expecting an opening brace. + """ + return (not self.stack) or self.stack[-1].seen_open_brace + + def InNamespaceBody(self): + """Check if we are currently one level inside a namespace body. + + Returns: + True if top of the stack is a namespace block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _NamespaceInfo) + + def InExternC(self): + """Check if we are currently one level inside an 'extern "C"' block. + + Returns: + True if top of the stack is an extern block, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ExternCInfo) + + def InClassDeclaration(self): + """Check if we are currently one level inside a class or struct declaration. + + Returns: + True if top of the stack is a class/struct, False otherwise. + """ + return self.stack and isinstance(self.stack[-1], _ClassInfo) + + def InAsmBlock(self): + """Check if we are currently one level inside an inline ASM block. + + Returns: + True if the top of the stack is a block containing inline ASM. + """ + return self.stack and self.stack[-1].inline_asm != _NO_ASM + + def InTemplateArgumentList(self, clean_lines, linenum, pos): + """Check if current position is inside template argument list. + + TODO (tkiley): This method seems a little generous for what it + considers to be in a template argument list, providing the line + between pos and the end contains a > (or even an =) before the + end of the line or }, {, ; then it will say yes... For now have + added method IsTemplateArgumentList_DB but should work out + what the idea with this method is a unify. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: position just after the suspected template argument. + Returns: + True if (linenum, pos) is inside template arguments. + """ + while linenum < clean_lines.NumLines(): + # Find the earliest character that might indicate a template argument + line = clean_lines.elided[linenum] + match = Match(r'^[^{};=\[\]\.<>]*(.)', line[pos:]) + if not match: + linenum += 1 + pos = 0 + continue + token = match.group(1) + pos += len(match.group(0)) + + # These things do not look like template argument list: + # class Suspect { + # class Suspect x; } + if token in ('{', '}', ';'): return False + + # These things look like template argument list: + # template + # template + # template + # template + if token in ('>', '=', '[', ']', '.'): return True + + # Check if token is an unmatched '<'. + # If not, move on to the next character. + if token != '<': + pos += 1 + if pos >= len(line): + linenum += 1 + pos = 0 + continue + + # We can't be sure if we just find a single '<', and need to + # find the matching '>'. + (_, end_line, end_pos) = CloseExpression(clean_lines, linenum, pos - 1) + if end_pos < 0: + # Not sure if template argument list or syntax error in file + return False + linenum = end_line + pos = end_pos + return False + + def UpdatePreprocessor(self, line): + """Update preprocessor stack. + + We need to handle preprocessors due to classes like this: + #ifdef SWIG + struct ResultDetailsPageElementExtensionPoint { + #else + struct ResultDetailsPageElementExtensionPoint : public Extension { + #endif + + We make the following assumptions (good enough for most files): + - Preprocessor condition evaluates to true from #if up to first + #else/#elif/#endif. + + - Preprocessor condition evaluates to false from #else/#elif up + to #endif. We still perform lint checks on these lines, but + these do not affect nesting stack. + + Args: + line: current line to check. + """ + if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line): + # Beginning of #if block, save the nesting stack here. The saved + # stack will allow us to restore the parsing state in the #else case. + self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack))) + elif Match(r'^\s*#\s*(else|elif)\b', line): + # Beginning of #else block + if self.pp_stack: + if not self.pp_stack[-1].seen_else: + # This is the first #else or #elif block. Remember the + # whole nesting stack up to this point. This is what we + # keep after the #endif. + self.pp_stack[-1].seen_else = True + self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack) + + # Restore the stack to how it was before the #if + self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if) + else: + # TODO(unknown): unexpected #else, issue warning? + pass + elif Match(r'^\s*#\s*endif\b', line): + # End of #if or #else blocks. + if self.pp_stack: + # If we saw an #else, we will need to restore the nesting + # stack to its former state before the #else, otherwise we + # will just continue from where we left off. + if self.pp_stack[-1].seen_else: + # Here we can just use a shallow copy since we are the last + # reference to it. + self.stack = self.pp_stack[-1].stack_before_else + # Drop the corresponding #if + self.pp_stack.pop() + else: + # TODO(unknown): unexpected #endif, issue warning? + pass + + # TODO(unknown): Update() is too long, but we will refactor later. + def Update(self, filename, clean_lines, linenum, error): + """Update nesting state with current line. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remember top of the previous nesting stack. + # + # The stack is always pushed/popped and not modified in place, so + # we can just do a shallow copy instead of copy.deepcopy. Using + # deepcopy would slow down cpplint by ~28%. + if self.stack: + self.previous_stack_top = self.stack[-1] + else: + self.previous_stack_top = None + + # Update pp_stack + self.UpdatePreprocessor(line) + + # Count parentheses. This is to avoid adding struct arguments to + # the nesting stack. + if self.stack: + inner_block = self.stack[-1] + depth_change = line.count('(') - line.count(')') + inner_block.open_parentheses += depth_change + + # Also check if we are starting or ending an inline assembly block. + if inner_block.inline_asm in (_NO_ASM, _END_ASM): + if (depth_change != 0 and + inner_block.open_parentheses == 1 and + _MATCH_ASM.match(line)): + # Enter assembly block + inner_block.inline_asm = _INSIDE_ASM + else: + # Not entering assembly block. If previous line was _END_ASM, + # we will now shift to _NO_ASM state. + inner_block.inline_asm = _NO_ASM + elif (inner_block.inline_asm == _INSIDE_ASM and + inner_block.open_parentheses == 0): + # Exit assembly block + inner_block.inline_asm = _END_ASM + + # Consume namespace declaration at the beginning of the line. Do + # this in a loop so that we catch same line declarations like this: + # namespace proto2 { namespace bridge { class MessageSet; } } + while True: + # Match start of namespace. The "\b\s*" below catches namespace + # declarations even if it weren't followed by a whitespace, this + # is so that we don't confuse our namespace checker. The + # missing spaces will be flagged by CheckSpacing. + namespace_decl_match = Match(r'^\s*namespace\b\s*([:\w]+)?(.*)$', line) + if not namespace_decl_match: + break + + new_namespace = _NamespaceInfo(namespace_decl_match.group(1), linenum) + self.stack.append(new_namespace) + + line = namespace_decl_match.group(2) + if line.find('{') != -1: + new_namespace.seen_open_brace = True + line = line[line.find('{') + 1:] + + # Look for a class declaration in whatever is left of the line + # after parsing namespaces. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + class_decl_match = Match( + r'^(\s*(?:template\s*<[\w\s<>,:]*>\s*)?' + r'(class|struct)\s+(?:[A-Z_]+\s+)*(\w+(?:::\w+)*))' + r'(.*)$', line) + if (class_decl_match and + (not self.stack or self.stack[-1].open_parentheses == 0)): + # We do not want to accept classes that are actually template arguments: + # template , + # template class Ignore3> + # void Function() {}; + # + # To avoid template argument cases, we scan forward and look for + # an unmatched '>'. If we see one, assume we are inside a + # template argument list. + end_declaration = len(class_decl_match.group(1)) + if not self.InTemplateArgumentList(clean_lines, linenum, end_declaration): + self.stack.append(_ClassInfo( + class_decl_match.group(3), class_decl_match.group(2), + clean_lines, linenum)) + line = class_decl_match.group(4) + + # If we have not yet seen the opening brace for the innermost block, + # run checks here. + if not self.SeenOpenBrace(): + self.stack[-1].CheckBegin(filename, clean_lines, linenum, error) + + # Update access control if we are inside a class/struct + if self.stack and isinstance(self.stack[-1], _ClassInfo): + classinfo = self.stack[-1] + access_match = Match( + r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?' + r':(?:[^:]|$)', + line) + if access_match: + classinfo.access = access_match.group(2) + +# # Check that access keywords are indented +1 space. Skip this +# # check if the keywords are not preceded by whitespaces. +# indent = access_match.group(1) +# if (len(indent) != classinfo.class_indent + 1 and +# Match(r'^\s*$', indent)): +# if classinfo.is_struct: +# parent = 'struct ' + classinfo.name +# else: +# parent = 'class ' + classinfo.name +# slots = '' +# if access_match.group(3): +# slots = access_match.group(3) +# error(filename, linenum, 'whitespace/indent', 3, +# '%s%s: should be indented +1 space inside %s' % ( +# access_match.group(2), slots, parent)) + + # Consume braces or semicolons from what's left of the line + while True: + # Match first brace, semicolon, or closed parenthesis. + matched = Match(r'^[^{;)}]*([{;)}])(.*)$', line) + if not matched: + break + + token = matched.group(1) + if token == '{': + # If namespace or class hasn't seen a opening brace yet, mark + # namespace/class head as complete. Push a new block onto the + # stack otherwise. + if not self.SeenOpenBrace(): + self.stack[-1].seen_open_brace = True + elif Match(r'^extern\s*"[^"]*"\s*\{', line): + self.stack.append(_ExternCInfo(linenum)) + else: + self.stack.append(_BlockInfo(linenum, True)) + if _MATCH_ASM.match(line): + self.stack[-1].inline_asm = _BLOCK_ASM + + elif token == ';' or token == ')': + # If we haven't seen an opening brace yet, but we already saw + # a semicolon, this is probably a forward declaration. Pop + # the stack for these. + # + # Similarly, if we haven't seen an opening brace yet, but we + # already saw a closing parenthesis, then these are probably + # function arguments with extra "class" or "struct" keywords. + # Also pop these stack for these. + if not self.SeenOpenBrace(): + self.stack.pop() + else: # token == '}' + # Perform end of block checks and pop the stack. + if self.stack: + self.stack[-1].CheckEnd(filename, clean_lines, linenum, error) + self.stack.pop() + line = matched.group(2) + + def InnermostClass(self): + """Get class info on the top of the stack. + + Returns: + A _ClassInfo object if we are inside a class, or None otherwise. + """ + for i in range(len(self.stack), 0, -1): + classinfo = self.stack[i - 1] + if isinstance(classinfo, _ClassInfo): + return classinfo + return None + + def CheckCompletedBlocks(self, filename, error): + """Checks that all classes and namespaces have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + for obj in self.stack: + if isinstance(obj, _ClassInfo): + error(filename, obj.starting_linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + obj.name) + elif isinstance(obj, _NamespaceInfo): + error(filename, obj.starting_linenum, 'build/namespaces', 5, + 'Failed to find complete declaration of namespace %s' % + obj.name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + nesting_state, error): + r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Everything else in this function operates on class declarations. + # Return early if the top of the nesting stack is not a class, or if + # the class head is not completed yet. + classinfo = nesting_state.InnermostClass() + if not classinfo or not classinfo.seen_open_brace: + return + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + explicit_constructor_match = Match( + r'\s+(?:inline\s+)?(explicit\s+)?(?:inline\s+)?%s\s*' + r'\(((?:[^()]|\([^()]*\))*)\)' + % re.escape(base_classname), + line) + + if explicit_constructor_match: + is_marked_explicit = explicit_constructor_match.group(1) + + if not explicit_constructor_match.group(2): + constructor_args = [] + else: + constructor_args = explicit_constructor_match.group(2).split(',') + + # collapse arguments so that commas in template parameter lists and function + # argument parameter lists don't split arguments in two + i = 0 + while i < len(constructor_args): + constructor_arg = constructor_args[i] + while (constructor_arg.count('<') > constructor_arg.count('>') or + constructor_arg.count('(') > constructor_arg.count(')')): + constructor_arg += ',' + constructor_args[i + 1] + del constructor_args[i + 1] + constructor_args[i] = constructor_arg + i += 1 + + defaulted_args = [arg for arg in constructor_args if '=' in arg] + noarg_constructor = (not constructor_args or # empty arg list + # 'void' arg specifier + (len(constructor_args) == 1 and + constructor_args[0].strip() == 'void')) + onearg_constructor = ((len(constructor_args) == 1 and # exactly one arg + not noarg_constructor) or + # all but at most one arg defaulted + (len(constructor_args) >= 1 and + not noarg_constructor and + len(defaulted_args) >= len(constructor_args) - 1)) + initializer_list_constructor = bool( + onearg_constructor and + Search(r'\bstd\s*::\s*initializer_list\b', constructor_args[0])) + copy_constructor = bool( + onearg_constructor and + Match(r'(const\s+)?%s(\s*<[^>]*>)?(\s+const)?\s*(?:<\w+>\s*)?&' + % re.escape(base_classname), constructor_args[0].strip())) + + if (not is_marked_explicit and + onearg_constructor and + not initializer_list_constructor and + not copy_constructor): + if defaulted_args: + error(filename, linenum, 'runtime/explicit', 5, + 'Constructors callable with one argument ' + 'should be marked explicit.') + else: + error(filename, linenum, 'runtime/explicit', 5, + 'Single-parameter constructors should be marked explicit.') + elif is_marked_explicit and not onearg_constructor: + if noarg_constructor: + error(filename, linenum, 'runtime/explicit', 5, + 'Zero-parameter constructors should not be marked explicit.') + + +def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b', + fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'_{0,2}asm_{0,2}\s+_{0,2}volatile_{0,2}\s+\(', fncall) and + not Search(r'#\s*define|typedef|using\s+\w+\s*=', fncall) and + not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall) and + not Search(r'\bcase\s+\(', fncall)): + # TODO(unknown): Space after an operator function seem to be a common + # error, silence those for now by restricting them to highest verbosity. + if Search(r'\boperator_*\b', line): + error(filename, linenum, 'whitespace/parens', 0, + 'Extra space before ( in function call') + else: + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error): + is_namespace_indent_item = ( + len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.previous_stack_top, _NamespaceInfo) and + nesting_state.previous_stack_top == nesting_state.stack[-2]) + + if ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + clean_lines.elided, line): + CheckItemIndentationInNamespace(filename, clean_lines.elided, + line, error) + +def CheckForFunctionCommentHeaders(filename, raw_lines, error): + """ Check all the lines for functions without function comment headers + + Using the raw original lines (before multi-line comments are removed) + find lines that contain functions and checks they have a function comment header + + Args: + filename - The name of the file being checked + raw_lines - The original, with comments lines + error - the function to report errors with + """ + linenum = 0 + function_state = _FunctionState() + for line in raw_lines: + joined_line = '' + starting_func = False + # Look for declaration function_name( but allowing for *, & being attached to the function name + # but not being considered part of it + regexp = r'\w(\w|::|\s|\*|\&)* (\*|\&)?(?P(\w(\w|::)*))\('# + operator_regexp = r'\w(\w|::|\s|\*|\&)* (\*|\&)?(?P(\w(\w|::)*::)?(operator\(.*\)|operator.*))\(' + operator_match = Match(operator_regexp, line) + match_result = Match(regexp, line) + function_name = "" + if operator_match: + function_name = operator_match.group('fnc_name') + elif match_result: + function_name = match_result.group('fnc_name') + + if operator_match or match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, len(raw_lines)): + start_line = raw_lines[start_linenum] + if Search(r'{', start_line): + body_found = True + break + elif Search(r';', start_line): + body_found = False + break + + # body found, i.e. not a declaration + if body_found: + CheckForFunctionCommentHeader(filename, raw_lines, linenum, function_name, error) + linenum += 1 + +def CheckForFunctionCommentHeader(filename, raw_lines, linenum, function_name, error): + """ Check each function has a comment header + + Rules for function comment header: + 1. Header demarked by /*{67}\ at the top + 2. Header demarked by \*{67}/ at the bottom + 3. Contains Function: classname::function_name + 4. Contains Inputs: + 5. Contains Outputs: + 6. Contains Purpose: + 7. new line between function and bottom of comment + + Args: + filename - the name of the file + raw_lines - all the unmodified lines (including multi-line comments) + linenum - the line number of the line to check + function_name - the name of the function that was found + error - function to report errors with + + """ + + function_name = re.escape(function_name) + + header_top_regex = r'^/\*{67}\\$' + header_bottom_regex = r'^\\\*{67}/$' + function_name_regex = r'Function: (\w+::)?' + function_name+'$' + + found_empty_space = raw_lines[linenum-1] == "" + + header_start = linenum - 2 if found_empty_space else linenum - 1 + found_header_bottom = Match(header_bottom_regex, raw_lines[header_start]) + + if not found_header_bottom: + error(filename, linenum, 'readability/function_comment', 4, + 'Could not find function header comment for ' + function_name) + return + + found_header_top = False + + found_function_name = False + found_inputs = False + found_outputs = False + found_purpose = False + + for i in xrange(header_start - 1, 0, -1): + if(Match(header_top_regex, raw_lines[i])): + found_header_top = True + break + + if(Search(r'Inputs:', raw_lines[i])): + found_inputs = True + elif(Search(r'Outputs:', raw_lines[i])): + found_outputs = True + elif(Search(r'Purpose:', raw_lines[i])): + found_purpose = True + elif(Search(r'Function:', raw_lines[i])): + found_function_name = True + if(not Search(function_name_regex, raw_lines[i])): + error(filename, i, 'readability/function_comment', 4, + 'Function: name in the comment doesn\'t match the function name') + + if found_header_top: + if not found_inputs: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Inputs:') + if not found_outputs: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Outputs:') + if not found_purpose: + error(filename, linenum, 'readability/function_comment', 4, + 'Function header for ' + function_name + ' missing Purpose:') + else: + error(filename, linenum, 'readability/function_comment', 4, + 'Could not find top of function header comment for ' + function_name) + + if not found_empty_space: + error(filename, linenum, 'readability/function_comment', 4, + 'Insert an empty line between function header comment and the function ' + function_name) + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(line, filename, linenum, next_line_start, error): + """Checks for common mistakes in comments. + + Args: + line: The line in question. + filename: The name of the current file. + linenum: The number of the line to check. + next_line_start: The first non-whitespace column of the next line. + error: The function to call with any errors found. + """ + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + if re.sub(r'\\.', '', line[0:commentpos]).count('"') % 2 == 0: + # Allow one space for new scopes, two spaces otherwise: + if (not (Match(r'^.*{ *//', line) and next_line_start == commentpos) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace))): +#or +# (commentpos >= 2 and +# line[commentpos-2] not in string.whitespace)) + error(filename, linenum, 'whitespace/comments', 2, +# 'At least two spaces is best between code and comments') + 'At least one space is best between code and comments') + + # Checks for common mistakes in TODO comments. + comment = line[commentpos:] + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + +# username = match.group(2) +# if not username: +# error(filename, linenum, 'readability/todo', 2, +# 'Missing username in TODO; it should look like ' +# '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + # If the comment contains an alphanumeric character, there + # should be a space somewhere between it and the // unless + # it's a /// or //! Doxygen comment. + if (Match(r'//[^ ]*\w', comment) and + not Match(r'(///|//\!)(\s+|$)', comment)): + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + + +def CheckAccess(filename, clean_lines, linenum, nesting_state, error): + """Checks for improper use of DISALLOW* macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] # get rid of comments and strings + + matched = Match((r'\s*(DISALLOW_COPY_AND_ASSIGN|' + r'DISALLOW_IMPLICIT_CONSTRUCTORS)'), line) + if not matched: + return + if nesting_state.stack and isinstance(nesting_state.stack[-1], _ClassInfo): + if nesting_state.stack[-1].access != 'private': + error(filename, linenum, 'readability/constructors', 3, + '%s must be in the private: section' % matched.group(1)) + + else: + # Found DISALLOW* macro outside a class declaration, or perhaps it + # was used inside a function when it should have been part of the + # class declaration. We could issue a warning here, but it + # probably resulted in a compiler error already. + pass + + +def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw = clean_lines.lines_without_raw_strings + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + # + # Skip all the blank line checks if we are immediately inside a + # namespace body. In other words, don't issue blank line warnings + # for this block: + # namespace { + # + # } + # + # A warning about missing end of namespace comments will be issued instead. + # + # Also skip blank line checks for 'extern "C"' blocks, which are formatted + # like namespaces. + if (IsBlankLine(line) and + not nesting_state.InNamespaceBody() and + not nesting_state.InExternC()): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1: + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Redundant blank line at the start of a code block ' + 'should be deleted.') + # Ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Redundant blank line at the end of a code block ' + 'should be deleted.') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, check comments + next_line_start = 0 + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + next_line_start = len(next_line) - len(next_line.lstrip()) + CheckComment(line, filename, linenum, next_line_start, error) + + # get rid of comments and strings + line = clean_lines.elided[linenum] + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'return []() {};' + if Search(r'\w\s+\[', line) and not Search(r'(?:delete|return)\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # In range-based for, we wanted spaces before and after the colon, but + # not around "::" tokens that might appear. + if (Search(r'for *\(.*[^:]:[^: ]', line) or + Search(r'for *\(.*[^: ]:[^:]', line)): + error(filename, linenum, 'whitespace/forcolon', 2, + 'Missing space around colon in range-based for loop') + + +def CheckOperatorSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around operators. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Don't try to do spacing checks for operator methods. Do this by + # replacing the troublesome characters with something else, + # preserving column position for all other characters. + # + # The replacement is done repeatedly to avoid false positives from + # operators that call operators. + while True: + match = Match(r'^(.*\boperator\b)(\S+)(\s*\(.*)$', line) + if match: + line = match.group(1) + ('_' * len(match.group(2))) + match.group(3) + else: + break + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + left_hand_space_match = Search(r'[^\s]+[\s](=|>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=|>|<|\^|\+[^\+]|\/)', line) + right_hand_space_match = Search(r'(=|>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=|>|<|!|\^|\+|\/)[\s]', line) + operator_pos, op_end = left_hand_space_match.span(1) if left_hand_space_match else (-1, -1) + operator_pos2, op_end2 = right_hand_space_match.span(1) if right_hand_space_match else (-1, -1) + + if (left_hand_space_match and + not Search(r'<<', line) and # We ignore the left shift operator since might be a stream and then formatting rules go out of the window + not Search(r'char \*', line) and # I don't know why this exception exists? + not Search(r'\#include', line) and # I suppose file names could contains operators?? + #not Search(r'<.*[^\s]>', line)): # This checks for template declarations (providing they don't run onto next line) + not IsTemplateArgumentList_DB(clean_lines, linenum, operator_pos)): +# and not Search(r'\b(if|while|for) ', line) +# # Operators taken from [lex.operators] in C++11 standard. +# and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line) +# and not Search(r'operator=', line)): + error(filename, linenum, 'whitespace/operators', 4, +# 'Missing spaces around =') + 'Remove spaces around %s' % left_hand_space_match.group(1)) + elif (right_hand_space_match and + not Search(r'<<', line) and + not IsTemplateArgumentList_DB(clean_lines, linenum, operator_pos2)): + error(filename, linenum, 'whitespace/operators', 4, + 'Remove spaces around %s' % right_hand_space_match.group(0)) + +# these would cause too many false alarms if we checked for one-sided spaces only + match = Search(r'\s(-|\*)(\s|$)', line) + if match: + error(filename, linenum, 'readability/identifiers', 4, + 'Remove spaces around %s' % match.group(0)) + +# check any inherited classes don't have a space between the type and the : + if Search(r'(struct|class)\s[\w_]*\s+:', line): + error(filename, linenum, 'readability/identifiers', 4, 'There shouldn\'t be a space between class identifier and :') + + #check type definitions end with t + # Look for class declarations and check the final character is a t + # Exclude classes in side template argument lists (why?) + class_name_match = Search(r'\b(struct|class)\s(?P[\w_]+)', line) + if class_name_match: + class_name = class_name_match.group('class_name') + if not class_name.endswith('t'): + if not Search(r'\btemplate <', line) and not Search(r'\btemplate<', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Class or struct identifier should end with t') + + if Search(r'(struct|class)\s[\w_]*_t(;$|\s|:|$)', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Class or struct identifier should end with t, not _t') + + if Search(r'typedef\s.*\s[\w_]*[^t];$', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Typedef identifier should end with t') + + if Search(r'typedef\s.*\s[\w_]*_t;$', line): + error(filename, linenum, 'readability/identifiers', 4, + 'Typedef identifier should end with t, not _t') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # + # Check <= and >= first to avoid false positives with < and >, then + # check non-include lines for spacing around < and >. + # + # If the operator is followed by a comma, assume it's be used in a + # macro context and don't do any checks. This avoids false + # positives. + # + # Note that && is not included here. This is because there are too + # many false positives due to RValue references. +# match = Search(r'[^<>=!\s](==|!=|<=|>=|\|\|)[^<>=!\s,;\)]', line) + match = Search(r'[^\s](&&|\|\|)[^\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + +# TODO Do we want this? +# match = Search(r'[^\s^:](:|\?)[^\s^:]', line) +# if match: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around %s' % match.group(1)) + +# elif not Match(r'#.*include', line): +# # Look for < that is not surrounded by spaces. This is only +# # triggered if both sides are missing spaces, even though +# # technically should should flag if at least one side is missing a +# # space. This is done to avoid some false positives with shifts. +# match = Match(r'^(.*[^\s<])<[^\s=<,]', line) +# if match: +# (_, _, end_pos) = CloseExpression( +# clean_lines, linenum, len(match.group(1))) +# if end_pos <= -1: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around <') +# +# # Look for > that is not surrounded by spaces. Similar to the +# # above, we only trigger if both sides are missing spaces to avoid +# # false positives with shifts. +# match = Match(r'^(.*[^-\s>])>[^\s=>,]', line) +# if match: +# (_, _, start_pos) = ReverseCloseExpression( +# clean_lines, linenum, len(match.group(1))) +# if start_pos <= -1: +# error(filename, linenum, 'whitespace/operators', 3, +# 'Missing spaces around >') + + # We allow no-spaces around >> for almost anything. This is because + # C++11 allows ">>" to close nested templates, which accounts for + # most cases when ">>" is not followed by a space. + # + # We still warn on ">>" followed by alpha character, because that is + # likely due to ">>" being used for right shifts, e.g.: + # value >> alpha + # + # When ">>" is used to close templates, the alphanumeric letter that + # follows would be part of an identifier, and there should still be + # a space separating the template type and the identifier. + # type> alpha + match = Search(r'>>[a-zA-Z_]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around >>') + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + +def CheckPointerReferenceSpacing(filename, clean_lines, linenum, error): + """Checks the */& are attached to variable names rather than types + + A pointer or reference type should have the */& attached to the variable + name rather than the type. I.e. a pointer to an int should be: + + int *var_name; + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Find types by looking for word_names that are at the start of the line + # If there are followed by a * or & (after an optional ' const') + # then they appear to be a reference/pointer type with it attached to + # the type rather than the variable + wrong_type_match = Search(r'^\s*([\w_])+( const)?(?P[&\*])\s*\w', line) + + if wrong_type_match: + # This still could be a false positive, we must + # check that we are not inside brackets as then could be just be + # operators (multiply and logical AND) + pos = wrong_type_match.start(1) + _, _, opening_pos = ForceOpenExpression(clean_lines, linenum, pos, ')') + + # If we don't find a matching close brace then we aren't in brackets + # so can assume this is a type with the * or & attached to it + if opening_pos == -1: + op_type = wrong_type_match.group('type') + op_word = "" + if op_type == '*': + op_word = 'Pointer' + else: + op_word = 'Reference' + error(filename, linenum, 'whitespace/operators', 4, + op_word + ' type name must have ' + op_type + ' attached to the type name') + + + +def CheckParenthesisSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing around parentheses. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # No spaces after an if, while, switch, or for + match = Search(r' (if \(|for \(|while \(|switch \()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Remove space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if len(match.group(2)) not in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + +def CheckCommaSpacing(filename, clean_lines, linenum, error): + """Checks for horizontal spacing near commas and semicolons. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.lines_without_raw_strings + line = clean_lines.elided[linenum] + + # You should always have a space after a comma (either as fn arg or operator) + # + # This does not apply when the non-space character following the + # comma is another comma, since the only time when that happens is + # for empty macro arguments. + # + # We run this check in two passes: first pass on elided lines to + # verify that lines contain missing whitespaces, second pass on raw + # lines to confirm that those missing whitespaces are not due to + # elided comments. + if (Search(r',[^,\s]', ReplaceAll(r'\boperator\s*,\s*\(', 'F(', line)) and + Search(r',[^,\s]', raw[linenum])): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + +def _IsType(clean_lines, nesting_state, expr): + """Check if expression looks like a type name, returns true if so. + + Args: + clean_lines: A CleansedLines instance containing the file. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + expr: The expression to check. + Returns: + True, if token looks like a type. + """ + # Keep only the last token in the expression + last_word = Match(r'^.*(\b\S+)$', expr) + if last_word: + token = last_word.group(1) + else: + token = expr + + # Match native types and stdint types + if _TYPES.match(token): + return True + + # Try a bit harder to match templated types. Walk up the nesting + # stack until we find something that resembles a typename + # declaration for what we are looking for. + typename_pattern = (r'\b(?:typename|class|struct)\s+' + re.escape(token) + + r'\b') + block_index = len(nesting_state.stack) - 1 + while block_index >= 0: + if isinstance(nesting_state.stack[block_index], _NamespaceInfo): + return False + + # Found where the opening brace is. We want to scan from this + # line up to the beginning of the function, minus a few lines. + # template + # class C + # : public ... { // start scanning here + last_line = nesting_state.stack[block_index].starting_linenum + + next_block_start = 0 + if block_index > 0: + next_block_start = nesting_state.stack[block_index - 1].starting_linenum + first_line = last_line + while first_line >= next_block_start: + if clean_lines.elided[first_line].find('template') >= 0: + break + first_line -= 1 + if first_line < next_block_start: + # Didn't find any "template" keyword before reaching the next block, + # there are probably no template things to check for this block + block_index -= 1 + continue + + # Look for typename in the specified range + for i in xrange(first_line, last_line + 1, 1): + if Search(typename_pattern, clean_lines.elided[i]): + return True + block_index -= 1 + + return False + + +def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error): + """Checks for horizontal spacing near commas. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces when they are delimiting blocks, classes, namespaces etc. + # And since you should never have braces at the beginning of a line, + # this is an easy test. Except that braces used for initialization don't + # follow the same rule; we often don't want spaces before those. + match = Match(r'^(.*[^ ({>]){', line) + + if match: + # Try a bit harder to check for brace initialization. This + # happens in one of the following forms: + # Constructor() : initializer_list_{} { ... } + # Constructor{}.MemberFunction() + # Type variable{}; + # FunctionCall(type{}, ...); + # LastArgument(..., type{}); + # LOG(INFO) << type{} << " ..."; + # map_of_type[{...}] = ...; + # ternary = expr ? new type{} : nullptr; + # OuterTemplate{}> + # + # We check for the character following the closing brace, and + # silence the warning if it's one of those listed above, i.e. + # "{.;,)<>]:". + # + # To account for nested initializer list, we allow any number of + # closing braces up to "{;,)<". We can't simply silence the + # warning on first sight of closing brace, because that would + # cause false negatives for things that are not initializer lists. + # Silence this: But not this: + # Outer{ if (...) { + # Inner{...} if (...){ // Missing space before { + # }; } + # + # There is a false negative with this approach if people inserted + # spurious semicolons, e.g. "if (cond){};", but we will catch the + # spurious semicolon with a separate check. + leading_text = match.group(1) + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + trailing_text = '' + if endpos > -1: + trailing_text = endline[endpos:] + for offset in xrange(endlinenum + 1, + min(endlinenum + 3, clean_lines.NumLines() - 1)): + trailing_text += clean_lines.elided[offset] + # We also suppress warnings for `uint64_t{expression}` etc., as the style + # guide recommends brace initialization for integral types to avoid + # overflow/truncation. + if (not Match(r'^[\s}]*[{.;,)<>\]:]', trailing_text) + and not _IsType(clean_lines, nesting_state, leading_text)): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + # if Search(r'}else', line): + # error(filename, linenum, 'whitespace/braces', 5, + # 'Missing space before else') + if (Search(r'^.*[^\s].*}$', line) or Search(r'^.*[^\s].*{$', line)) and not(Search(r'{[^}]*}', line)): + error(filename, linenum, 'whitespace/braces', 5, + 'Put braces on a separate next line') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use {} instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use {} instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use {} instead.') + + +def IsDecltype(clean_lines, linenum, column): + """Check if the token ending on (linenum, column) is decltype(). + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: the number of the line to check. + column: end column of the token to check. + Returns: + True if this token is decltype() expression, False otherwise. + """ + (text, _, start_col) = ReverseCloseExpression(clean_lines, linenum, column) + if start_col < 0: + return False + if Search(r'\bdecltype\s*$', text[0:start_col]): + return True + return False + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.starting_linenum <= 24 or + linenum <= class_info.starting_linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + # Also ignores cases where the previous line ends with a backslash as can be + # common when defining classes in C macros. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line) and + not Search(r'\\$', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.starting_linenum + for i in range(class_info.starting_linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Check for else if/else that are on the same line as the previous brace + if Search(r'}\s*else if', line): + error(filename, linenum, 'readability/braces', 5, + 'Else if should be on a new line after closing brace') + elif Search(r'}\s*else', line): + error(filename, linenum, 'readability/braces', 5, + 'Else should be on a new line after closing brace') + + # Check for if/else if/else don't have statements on the same line + if Search(r'^\s*if\(.*\)\s*\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after an if should be on a new line') + elif Search(r'else if\(.*\)', line): + if Search(r'else if\(.*\)\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after else if should be on a new line') + elif Search(r'else\s*{?\s*.+;', line): + error(filename, linenum, 'readability/braces', 5, + 'Statement after else should be on a new line') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Check single-line if/else bodies. The style guide says 'curly braces are not + # required for single-line statements'. We additionally allow multi-line, + # single statements, but we reject anything with more than one semicolon in + # it. This means that the first semicolon after the if should be at the end of + # its line, and the line after that should have an indent level equal to or + # lower than the if. We also check for ambiguous if/else nesting without + # braces. + if_else_match = Search(r'\b(if\s*\(|else\b)', line) + if if_else_match and not Match(r'\s*#', line): + if_indent = GetIndentLevel(line) + endline, endlinenum, endpos = line, linenum, if_else_match.end() + if_match = Search(r'\bif\s*\(', line) + if if_match: + # This could be a multiline if condition, so find the end first. + pos = if_match.end() - 1 + (endline, endlinenum, endpos) = CloseExpression(clean_lines, linenum, pos) + # Check for an opening brace, either directly after the if or on the next + # line. If found, this isn't a single-statement conditional. + if (not Match(r'\s*{', endline[endpos:]) + and not (Match(r'\s*$', endline[endpos:]) + and endlinenum < (len(clean_lines.elided) - 1) + and Match(r'\s*{', clean_lines.elided[endlinenum + 1]))): + while (endlinenum < len(clean_lines.elided) + and ';' not in clean_lines.elided[endlinenum][endpos:]): + endlinenum += 1 + endpos = 0 + if endlinenum < len(clean_lines.elided): + endline = clean_lines.elided[endlinenum] + # We allow a mix of whitespace and closing braces (e.g. for one-liner + # methods) and a single \ after the semicolon (for macros) + endpos = endline.find(';') + if not Match(r';[\s}]*(\\?)$', endline[endpos:]): + # Semicolon isn't the last character, there's something trailing. + # Output a warning if the semicolon is not contained inside + # a lambda expression. + if not Match(r'^[^{};]*\[[^\[\]]*\][^{}]*\{[^{}]*\}\s*\)*[;,]\s*$', + endline): + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + elif endlinenum < len(clean_lines.elided) - 1: + # Make sure the next line is dedented + next_line = clean_lines.elided[endlinenum + 1] + next_indent = GetIndentLevel(next_line) + # With ambiguous nested if statements, this will error out on the + # if that *doesn't* match the else, regardless of whether it's the + # inner one or outer one. + if (if_match and Match(r'\s*else\b', next_line) + and next_indent != if_indent): + error(filename, linenum, 'readability/braces', 4, + 'Else clause should be indented at the same level as if. ' + 'Ambiguous nested if/else chains require braces.') + elif next_indent > if_indent: + error(filename, linenum, 'readability/braces', 4, + 'If/else bodies with multiple statements require braces') + +def CheckDoWhile(filename, clean_lines, linenum, error): + """Looks for while of a do while on the same line as the closing brace + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if Search(r'}\s*while\s*\(', line): + do_found, num = FindDoStart(clean_lines, linenum) + if do_found: + error(filename, linenum, 'readability/braces', 4, + 'while statement of do...while loop should be on a separate line to the closing brace') + +def CheckTrailingSemicolon(filename, clean_lines, linenum, error): + """Looks for redundant trailing semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] + + # Block bodies should not be followed by a semicolon. Due to C++11 + # brace initialization, there are more places where semicolons are + # required than not, so we use a whitelist approach to check these + # rather than a blacklist. These are the places where "};" should + # be replaced by just "}": + # 1. Some flavor of block following closing parenthesis: + # for (;;) {}; + # while (...) {}; + # switch (...) {}; + # Function(...) {}; + # if (...) {}; + # if (...) else if (...) {}; + # + # 2. else block: + # if (...) else {}; + # + # 3. const member function: + # Function(...) const {}; + # + # 4. Block following some statement: + # x = 42; + # {}; + # + # 5. Block at the beginning of a function: + # Function(...) { + # {}; + # } + # + # Note that naively checking for the preceding "{" will also match + # braces inside multi-dimensional arrays, but this is fine since + # that expression will not contain semicolons. + # + # 6. Block following another block: + # while (true) {} + # {}; + # + # 7. End of namespaces: + # namespace {}; + # + # These semicolons seems far more common than other kinds of + # redundant semicolons, possibly due to people converting classes + # to namespaces. For now we do not warn for this case. + # + # Try matching case 1 first. + match = Match(r'^(.*\)\s*)\{', line) + if match: + # Matched closing parenthesis (case 1). Check the token before the + # matching opening parenthesis, and don't warn if it looks like a + # macro. This avoids these false positives: + # - macro that defines a base class + # - multi-line macro that defines a base class + # - macro that defines the whole class-head + # + # But we still issue warnings for macros that we know are safe to + # warn, specifically: + # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P + # - TYPED_TEST + # - INTERFACE_DEF + # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: + # + # We implement a whitelist of safe macros instead of a blacklist of + # unsafe macros, even though the latter appears less frequently in + # google code and would have been easier to implement. This is because + # the downside for getting the whitelist wrong means some extra + # semicolons, while the downside for getting the blacklist wrong + # would result in compile errors. + # + # In addition to macros, we also don't want to warn on + # - Compound literals + # - Lambdas + # - alignas specifier with anonymous structs + # - decltype + closing_brace_pos = match.group(1).rfind(')') + opening_parenthesis = ReverseCloseExpression( + clean_lines, linenum, closing_brace_pos) + if opening_parenthesis[2] > -1: + line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] + macro = Search(r'\b([A-Z_][A-Z0-9_]*)\s*$', line_prefix) + func = Match(r'^(.*\])\s*$', line_prefix) + if ((macro and + macro.group(1) not in ( + 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', + 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', + 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or + (func and not Search(r'\boperator\s*\[\s*\]', func.group(1))) or + Search(r'\b(?:struct|union)\s+alignas\s*$', line_prefix) or + Search(r'\bdecltype$', line_prefix) or + Search(r'\s+=\s*$', line_prefix)): + match = None + if (match and + opening_parenthesis[1] > 1 and + Search(r'\]\s*$', clean_lines.elided[opening_parenthesis[1] - 1])): + # Multi-line lambda-expression + match = None + + else: + # Try matching cases 2-3. + match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) + if not match: + # Try matching cases 4-6. These are always matched on separate lines. + # + # Note that we can't simply concatenate the previous line to the + # current line and do a single match, otherwise we may output + # duplicate warnings for the blank line case: + # if (cond) { + # // blank line + # } + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if prevline and Search(r'[;{}]\s*$', prevline): + match = Match(r'^(\s*)\{', line) + + # Check matching closing brace + if match: + (endline, endlinenum, endpos) = CloseExpression( + clean_lines, linenum, len(match.group(1))) + if endpos > -1 and Match(r'^\s*;', endline[endpos:]): + # Current {} pair is eligible for semicolon check, and we have found + # the redundant semicolon, output warning here. + # + # Note: because we are scanning forward for opening braces, and + # outputting warnings for the matching closing brace, if there are + # nested blocks with trailing semicolons, we will get the error + # messages in reversed order. + + # We need to check the line forward for NOLINT + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1, + error) + ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum, + error) + + error(filename, endlinenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def CheckEmptyBlockBody(filename, clean_lines, linenum, error): + """Look for empty loop/conditional body with only a single semicolon. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Search for loop keywords at the beginning of the line. Because only + # whitespaces are allowed before the keywords, this will also ignore most + # do-while-loops, since those lines should start with closing brace. + # + # We also check "if" blocks here, since an empty conditional block + # is likely an error. + line = clean_lines.elided[linenum] + matched = Match(r'\s*(for|while|if)\s*\(', line) + while_matched = Match(r'\s*(while)\s*\(', line) + + # if it is a do while loop, we have the while clause on a separate line + # so need to exclude that + do_found = False + if while_matched: + do_found, num = FindDoStart(clean_lines, linenum) + + is_do_while = while_matched and do_found + # either not a while or if we are, we didn't find a do + if matched and not is_do_while: + # Find the end of the conditional expression. + (end_line, end_linenum, end_pos) = CloseExpression( + clean_lines, linenum, line.find('(')) + + # Output warning if what follows the condition expression is a semicolon. + # No warning for all other cases, including whitespace or newline, since we + # have a separate check for semicolons preceded by whitespace. + if end_pos >= 0 and Match(r';', end_line[end_pos:]): + if matched.group(1) == 'if': + error(filename, end_linenum, 'whitespace/empty_conditional_body', 5, + 'Empty conditional bodies should use {}') + else: + error(filename, end_linenum, 'whitespace/empty_loop_body', 5, + 'Empty loop bodies should use {} or continue') + + # Check for if statements that have completely empty bodies (no comments) + # and no else clauses. + if end_pos >= 0 and matched.group(1) == 'if': + # Find the position of the opening { for the if statement. + # Return without logging an error if it has no brackets. + opening_linenum = end_linenum + opening_line_fragment = end_line[end_pos:] + # Loop until EOF or find anything that's not whitespace or opening {. + while not Search(r'^\s*\{', opening_line_fragment): + if Search(r'^(?!\s*$)', opening_line_fragment): + # Conditional has no brackets. + return + opening_linenum += 1 + if opening_linenum == len(clean_lines.elided): + # Couldn't find conditional's opening { or any code before EOF. + return + opening_line_fragment = clean_lines.elided[opening_linenum] + # Set opening_line (opening_line_fragment may not be entire opening line). + opening_line = clean_lines.elided[opening_linenum] + + # Find the position of the closing }. + opening_pos = opening_line_fragment.find('{') + if opening_linenum == end_linenum: + # We need to make opening_pos relative to the start of the entire line. + opening_pos += end_pos + (closing_line, closing_linenum, closing_pos) = CloseExpression( + clean_lines, opening_linenum, opening_pos) + if closing_pos < 0: + return + + # Now construct the body of the conditional. This consists of the portion + # of the opening line after the {, all lines until the closing line, + # and the portion of the closing line before the }. + if (clean_lines.raw_lines[opening_linenum] != + CleanseComments(clean_lines.raw_lines[opening_linenum])): + # Opening line ends with a comment, so conditional isn't empty. + return + if closing_linenum > opening_linenum: + # Opening line after the {. Ignore comments here since we checked above. + body = list(opening_line[opening_pos+1:]) + # All lines until closing line, excluding closing line, with comments. + body.extend(clean_lines.raw_lines[opening_linenum+1:closing_linenum]) + # Closing line before the }. Won't (and can't) have comments. + body.append(clean_lines.elided[closing_linenum][:closing_pos-1]) + body = '\n'.join(body) + else: + # If statement has brackets and fits on a single line. + body = opening_line[opening_pos+1:closing_pos-1] + + # Check if the body is empty + if not _EMPTY_CONDITIONAL_BODY_PATTERN.search(body): + return + # The body is empty. Now make sure there's not an else clause. + current_linenum = closing_linenum + current_line_fragment = closing_line[closing_pos:] + # Loop until EOF or find anything that's not whitespace or else clause. + while Search(r'^\s*$|^(?=\s*else)', current_line_fragment): + if Search(r'^(?=\s*else)', current_line_fragment): + # Found an else clause, so don't log an error. + return + current_linenum += 1 + if current_linenum == len(clean_lines.elided): + break + current_line_fragment = clean_lines.elided[current_linenum] + + # The body is empty and there's no else clause until EOF or other code. + error(filename, end_linenum, 'whitespace/empty_if_body', 4, + ('If statement had no body and no else clause')) + + +def FindCheckMacro(line): + """Find a replaceable CHECK-like macro. + + Args: + line: line to search on. + Returns: + (macro name, start position), or (None, -1) if no replaceable + macro is found. + """ + for macro in _CHECK_MACROS: + i = line.find(macro) + if i >= 0: + # Find opening parenthesis. Do a regular expression match here + # to make sure that we are matching the expected CHECK macro, as + # opposed to some other macro that happens to contain the CHECK + # substring. + matched = Match(r'^(.*\b' + macro + r'\s*)\(', line) + if not matched: + continue + return (macro, len(matched.group(1))) + return (None, -1) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + lines = clean_lines.elided + (check_macro, start_pos) = FindCheckMacro(lines[linenum]) + if not check_macro: + return + + # Find end of the boolean expression by matching parentheses + (last_line, end_line, end_pos) = CloseExpression( + clean_lines, linenum, start_pos) + if end_pos < 0: + return + + # If the check macro is followed by something other than a + # semicolon, assume users will log their own custom error messages + # and don't suggest any replacements. + if not Match(r'\s*;', last_line[end_pos:]): + return + + if linenum == end_line: + expression = lines[linenum][start_pos + 1:end_pos - 1] + else: + expression = lines[linenum][start_pos + 1:] + for i in xrange(linenum + 1, end_line): + expression += lines[i] + expression += last_line[0:end_pos - 1] + + # Parse expression so that we can take parentheses into account. + # This avoids false positives for inputs like "CHECK((a < 4) == b)", + # which is not replaceable by CHECK_LE. + lhs = '' + rhs = '' + operator = None + while expression: + matched = Match(r'^\s*(<<|<<=|>>|>>=|->\*|->|&&|\|\||' + r'==|!=|>=|>|<=|<|\()(.*)$', expression) + if matched: + token = matched.group(1) + if token == '(': + # Parenthesized operand + expression = matched.group(2) + (end, _) = FindEndOfExpressionInLine(expression, 0, ['(']) + if end < 0: + return # Unmatched parenthesis + lhs += '(' + expression[0:end] + expression = expression[end:] + elif token in ('&&', '||'): + # Logical and/or operators. This means the expression + # contains more than one term, for example: + # CHECK(42 < a && a < b); + # + # These are not replaceable with CHECK_LE, so bail out early. + return + elif token in ('<<', '<<=', '>>', '>>=', '->*', '->'): + # Non-relational operator + lhs += token + expression = matched.group(2) + else: + # Relational operator + operator = token + rhs = matched.group(2) + break + else: + # Unparenthesized operand. Instead of appending to lhs one character + # at a time, we do another regular expression match to consume several + # characters at once if possible. Trivial benchmark shows that this + # is more efficient when the operands are longer than a single + # character, which is generally the case. + matched = Match(r'^([^-=!<>()&|]+)(.*)$', expression) + if not matched: + matched = Match(r'^(\s*\S)(.*)$', expression) + if not matched: + break + lhs += matched.group(1) + expression = matched.group(2) + + # Only apply checks if we got all parts of the boolean expression + if not (lhs and operator and rhs): + return + + # Check that rhs do not contain logical operators. We already know + # that lhs is fine since the loop above parses out && and ||. + if rhs.find('&&') > -1 or rhs.find('||') > -1: + return + + # At least one of the operands must be a constant literal. This is + # to avoid suggesting replacements for unprintable things like + # CHECK(variable != iterator) + # + # The following pattern matches decimal, hex integers, strings, and + # characters (in that order). + lhs = lhs.strip() + rhs = rhs.strip() + match_constant = r'^([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')$' + if Match(match_constant, lhs) or Match(match_constant, rhs): + # Note: since we know both lhs and rhs, we can provide a more + # descriptive error message like: + # Consider using CHECK_EQ(x, 42) instead of CHECK(x == 42) + # Instead of: + # Consider using CHECK_EQ instead of CHECK(a == b) + # + # We are still keeping the less descriptive message because if lhs + # or rhs gets long, the error message might become unreadable. + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[check_macro][operator], + check_macro, operator)) + + +def CheckAltTokens(filename, clean_lines, linenum, error): + """Check alternative keywords being used in boolean expressions. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Avoid preprocessor lines + if Match(r'^\s*#', line): + return + + # Last ditch effort to avoid multi-line comments. This will not help + # if the comment started before the current line or ended after the + # current line, but it catches most of the false positives. At least, + # it provides a way to workaround this warning for people who use + # multi-line comments in preprocessor macros. + # + # TODO(unknown): remove this once cpplint has better support for + # multi-line comments. + if line.find('/*') >= 0 or line.find('*/') >= 0: + return + + for match in _ALT_TOKEN_REPLACEMENT_PATTERN.finditer(line): + error(filename, linenum, 'readability/alt_tokens', 2, + 'Use operator %s instead of %s' % ( + _ALT_TOKEN_REPLACEMENT[match.group(1)], match.group(1))) + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + + # Don't use "elided" lines here, otherwise we can't check commented lines. + # Don't want to use "raw" either, because we don't want to check inside C++11 + # raw strings, + raw_lines = clean_lines.lines_without_raw_strings + line = raw_lines[linenum] + prev = raw_lines[linenum - 1] if linenum > 0 else '' + + elided_line = clean_lines.elided[linenum] + elided_prev = clean_lines.elided[linenum - 1] if linenum > 0 else '' + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found, replace by spaces') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + scope_or_label_pattern = r'\s*\w+\s*:\s*\\?$' + classinfo = nesting_state.InnermostClass() + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + # There are certain situations we allow one space, notably for + # section labels, and also lines containing multi-line raw strings. + # We also don't check for lines that look like continuation lines + # (of lines ending in double quotes, commas, equals, or angle brackets) + # because the rules for how to indent those are non-trivial. + if (not Search(r'[",=><] *$', prev) and + (initial_spaces == 1 or initial_spaces == 3) and + not Match(scope_or_label_pattern, cleansed_line) and + not (clean_lines.raw_lines[linenum] != line and + Match(r'^\s*""', line))): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + +# ensure indentation within parenthesized expressions (only on elided lines since we don't care about comments or strings) + prev_initial_spaces = 0 + if linenum>0: + while prev_initial_spaces < len(prev) and prev[prev_initial_spaces] == ' ': + prev_initial_spaces += 1 + # here a regex isn't sufficent we need a stack to match brackets + # because even an open bracket and a , at the end might just be function call + # as a parameter. + # Instead the rule we try to apply here is: + # - if we find an opening bracket, we find the matching closing bracket + # - if the bracket is on a different line we require all of the parameters to be on a separate line + # - if there is another opening bracket Skip to the closing bracket as will be checked in a subsequent line check + # - ignore the line if it is a for/if etc since rules are different + + # Look for an opening bracket that doesn't have a semi-colon on the same line + bracket_search = Search(r'[\w_]+\s*(?P\()[^;]*$', elided_line) + + # Exclude the check if any of these keywords are present + # They could trip us up as they have different formatting rules to functions + keyword_search = Search(r'\b(if|for|while|catch|switch)\b', elided_line) + + if bracket_search and not keyword_search: + open_bracket_pos = bracket_search.start('bracket') + close_line, close_linenum, close_pos = CloseExpression(clean_lines, linenum, open_bracket_pos) + if close_pos != -1: + # If the closing line is different from the opening line we need to + # verify that each of the parameters are on separate lines + if close_linenum != linenum: + # The first line needs to have no parameters on it + if(Search(r'\(+[^\(]+', elided_line)): + error(filename, linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + # For each line afer we need to verify it consists of exactly one parameter + # Except if we find an opening bracket - in this case we ignore everything until the closing + # bracket (any errors within these brackets will be picked up when we check this line) + start_linenum = linenum + 1 + while(start_linenum < close_linenum): + arg_line = clean_lines.elided[start_linenum] + nested_bracket_search = Search('\(', arg_line) + if nested_bracket_search: + nested_open_bracket_pos = nested_bracket_search.start() + # Just because we are calling a nested function doesn't mean + # we allow multiple parameters on the line + if(Search(',', arg_line[:nested_open_bracket_pos])): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + nested_close_line, nested_close_linenum, _ = CloseExpression(clean_lines, start_linenum, nested_open_bracket_pos) + + # If anything other closing statements or commas appear there is another parameter after the nested call + if not Search(r'\)(,|\)|;)*', nested_close_line): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + # Skip to the end of the bracket + start_linenum = nested_close_linenum + else: + if(not Match('^\s*[^,]+,$', arg_line)): + error(filename, start_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + start_linenum+=1 + # For the final line we also need to check one parameter on it + # e.g. we require bracket on same line as last parameter + # foo( + # x); + if not Search(r'^\s*[^,]+\)', close_line): + # If this is true, the we failed because we just had the close bracket + if Search(r'[^,]*\)', close_line): + error(filename, close_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, the closing bracket should be on the same line as the final parameter') + else: + # In this case the problem is we had a bracket + # i.e. more than one parameter on the last line + error(filename, close_linenum, 'whitespace/indent', 4, + 'If parameters or arguments require a line break, each parameter should be put on its own line.') + + + if (Search(r'\([^\)]*$', elided_prev) and initial_spaces-2 != prev_initial_spaces) and not Search(r'for|while|if|;', elided_prev): + error(filename, linenum, 'whitespace/indent', 4, + 'Indent of wrapped parenthesized expression or parameter or argument list should be 2') + + # Check if the line is a header guard. + is_header_guard = False + if IsHeaderExtension(file_extension): + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^\s*//\s*[^\s]*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + if line_width > _line_length: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= %i characters long' % _line_length) + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 0, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckDoWhile(filename, clean_lines, linenum, error) + CheckTrailingSemicolon(filename, clean_lines, linenum, error) + CheckEmptyBlockBody(filename, clean_lines, linenum, error) + CheckAccess(filename, clean_lines, linenum, nesting_state, error) + CheckSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckOperatorSpacing(filename, clean_lines, linenum, error) + CheckPointerReferenceSpacing(filename, clean_lines, linenum, error) + CheckParenthesisSpacing(filename, clean_lines, linenum, error) + CheckCommaSpacing(filename, clean_lines, linenum, error) + CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error) + CheckSpacingForFunctionCall(filename, clean_lines, linenum, error) + CheckCheck(filename, clean_lines, linenum, error) + CheckAltTokens(filename, clean_lines, linenum, error) + classinfo = nesting_state.InnermostClass() + if classinfo: + CheckSectionSpacing(filename, clean_lines, classinfo, linenum, error) + + +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_cpp_h = include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + # Only do this check if the included header follows google naming + # conventions. If not, assume that it's a 3rd party API that + # requires special include conventions. + # + # We also make an exception for Lua headers, which follow google + # naming convention but not the include convention. +# match = Match(r'#include\s*"([^/]+\.h)"', line) +# if match and not _THIRD_PARTY_HEADERS_PATTERN.match(match.group(1)): +# error(filename, linenum, 'build/include', 4, +# 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + duplicate_line = include_state.FindHeader(include) + if duplicate_line >= 0: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, duplicate_line)) + elif (include.endswith('.cc') and + os.path.dirname(fileinfo.RepositoryName()) != os.path.dirname(include)): + error(filename, linenum, 'build/include', 4, + 'Do not include .cc files from other packages') + elif not _THIRD_PARTY_HEADERS_PATTERN.match(include): + include_state.include_list[-1].append((include, linenum)) + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. +# error_message = include_state.CheckNextIncludeOrder( +# _ClassifyInclude(fileinfo, include, is_system)) +# if error_message: +# error(filename, linenum, 'build/include_order', 4, +# '%s. Should be: %s.h, c system, c++ system, other.' % +# (error_message, fileinfo.BaseName())) +# canonical_include = include_state.CanonicalizeAlphabeticalOrder(include) +# if not include_state.IsInAlphabeticalOrder( +# clean_lines, linenum, canonical_include): +# error(filename, linenum, 'build/include_alpha', 4, +# 'Include "%s" not in alphabetical order' % include) +# include_state.SetLastHeader(canonical_include) + + + +def _GetTextInside(text, start_pattern): + r"""Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(unknown): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +# Patterns for matching call-by-reference parameters. +# +# Supports nested templates up to 2 levels deep using this messy pattern: +# < (?: < (?: < [^<>]* +# > +# | [^<>] )* +# > +# | [^<>] )* +# > +_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]* +_RE_PATTERN_TYPE = ( + r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?' + r'(?:\w|' + r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|' + r'::)+') +# A call-by-reference parameter ends with '& identifier'. +_RE_PATTERN_REF_PARAM = re.compile( + r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*' + r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]') +# A call-by-const-reference parameter either ends with 'const& identifier' +# or looks like 'const type& identifier' when 'type' is atomic. +_RE_PATTERN_CONST_REF_PARAM = ( + r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT + + r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')') +# Stream types. +_RE_PATTERN_REF_STREAM_PARAM = ( + r'(?:.*stream\s*&\s*' + _RE_PATTERN_IDENT + r')') + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, + include_state, nesting_state, error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Reset include state across preprocessor directives. This is meant + # to silence warnings for conditional includes. + match = Match(r'^\s*#\s*(if|ifdef|ifndef|elif|else|endif)\b', line) + if match: + include_state.ResetSection(match.group(1)) + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # Perform other checks now that we are sure that this is not an include line + CheckCasts(filename, clean_lines, linenum, error) + CheckGlobalStatic(filename, clean_lines, linenum, error) + CheckPrintf(filename, clean_lines, linenum, error) + + if IsHeaderExtension(file_extension): + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes declare or disable copy/assign + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. +# if Search(r'\bshort port\b', line): +# if not Search(r'\bunsigned short port\b', line): +# error(filename, linenum, 'runtime/int', 4, +# 'Use "unsigned short" for ports, not "short"') +# else: +# match = Search(r'\b(short|long(?! +double)|long long)\b', line) +# if match: +# error(filename, linenum, 'runtime/int', 4, +# 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(unknown): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match and match.group(1) != '__VA_ARGS__': + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays.') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (IsHeaderExtension(file_extension) + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + + + # Check that throw statements don't include the optional bracket + # We use raw lines as we want to check the contents of the string too + # We require the error message starts with a lower case character + raw_line = clean_lines.raw_lines[linenum] + if(Match(r'^\s*throw', raw_line)): + if(Match(r'^\s*throw\s*\(', raw_line)): + error(filename, linenum, 'readability/throw', 4, + 'Do not include brackets when throwing an error') + if(Match(r'\s*throw\s*\(?"[A-Z]', raw_line)): + error(filename, linenum, 'readability/throw', 4, + 'First character of throw error message should be lower case') + + +def CheckGlobalStatic(filename, clean_lines, linenum, error): + """Check for unsafe global or static objects. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Match two lines at a time to support multiline declarations + if linenum + 1 < clean_lines.NumLines() and not Search(r'[;({]', line): + line += clean_lines.elided[linenum + 1].strip() + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access, and + # also because globals can be destroyed when some threads are still running. + # TODO(unknown): Generalize this to also find static unique_ptr instances. + # TODO(unknown): File bugs for clang-tidy to find these. + match = Match( + r'((?:|static +)(?:|const +))(?::*std::)?string( +const)? +' + r'([a-zA-Z0-9_:]+)\b(.*)', + line) + + # Remove false positives: + # - String pointers (as opposed to values). + # string *pointer + # const string *pointer + # string const *pointer + # string *const pointer + # + # - Functions and template specializations. + # string Function(... + # string Class::Method(... + # + # - Operators. These are matched separately because operator names + # cross non-word boundaries, and trying to match both operators + # and functions at the same time would decrease accuracy of + # matching identifiers. + # string Class::operator*() + if (match and + not Search(r'\bstring\b(\s+const)?\s*[\*\&]\s*(const\s+)?\w', line) and + not Search(r'\boperator\W', line) and + not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)*\s*\(([^"]|$)', match.group(4))): + if Search(r'\bconst\b', line): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string ' + 'instead: "%schar%s %s[]".' % + (match.group(1), match.group(2) or '', match.group(3))) + else: + error(filename, linenum, 'runtime/string', 4, + 'Static/global string variables are not permitted.') + + if (Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line) or + Search(r'\b([A-Za-z0-9_]*_)\(CHECK_NOTNULL\(\1\)\)', line)): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + +def CheckPrintf(filename, clean_lines, linenum, error): + """Check for printf related issues. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\s*\(', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\s*\(', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + +def IsDerivedFunction(clean_lines, linenum): + """Check if current line contains an inherited function. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains a function with "override" + virt-specifier. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + match = Match(r'^([^()]*\w+)\(', clean_lines.elided[i]) + if match: + # Look for "override" after the matching closing parenthesis + line, _, closing_paren = CloseExpression( + clean_lines, i, len(match.group(1))) + return (closing_paren >= 0 and + Search(r'\boverride\b', line[closing_paren:])) + return False + + +def IsOutOfLineMethodDefinition(clean_lines, linenum): + """Check if current line contains an out-of-line method definition. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line contains an out-of-line method definition. + """ + # Scan back a few lines for start of current function + for i in xrange(linenum, max(-1, linenum - 10), -1): + if Match(r'^([^()]*\w+)\(', clean_lines.elided[i]): + return Match(r'^[^()]*\w+::\w+\(', clean_lines.elided[i]) is not None + return False + + +def IsInitializerList(clean_lines, linenum): + """Check if current line is inside constructor initializer list. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + Returns: + True if current line appears to be inside constructor initializer + list, False otherwise. + """ + for i in xrange(linenum, 1, -1): + line = clean_lines.elided[i] + if i == linenum: + remove_function_body = Match(r'^(.*)\{\s*$', line) + if remove_function_body: + line = remove_function_body.group(1) + + if Search(r'\s:\s*\w+[({]', line): + # A lone colon tend to indicate the start of a constructor + # initializer list. It could also be a ternary operator, which + # also tend to appear in constructor initializer lists as + # opposed to parameter lists. + return True + if Search(r'\}\s*,\s*$', line): + # A closing brace followed by a comma is probably the end of a + # brace-initialized member in constructor initializer list. + return True + if Search(r'[{};]\s*$', line): + # Found one of the following: + # - A closing brace or semicolon, probably the end of the previous + # function. + # - An opening brace, probably the start of current class or namespace. + # + # Current line is probably not inside an initializer list since + # we saw one of those things without seeing the starting colon. + return False + + # Got to the beginning of the file without seeing the start of + # constructor initializer list. + return False + + +def CheckForNonConstReference(filename, clean_lines, linenum, + nesting_state, error): + """Check for non-const references. + + Separate from CheckLanguage since it scans backwards from current + line, instead of scanning forward. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: The function to call with any errors found. + """ + # Do nothing if there is no '&' on current line. + line = clean_lines.elided[linenum] + if '&' not in line: + return + + # If a function is inherited, current function doesn't have much of + # a choice, so any non-const references should not be blamed on + # derived function. + if IsDerivedFunction(clean_lines, linenum): + return + + # Don't warn on out-of-line method definitions, as we would warn on the + # in-line declaration, if it isn't marked with 'override'. + if IsOutOfLineMethodDefinition(clean_lines, linenum): + return + + # Long type names may be broken across multiple lines, usually in one + # of these forms: + # LongType + # ::LongTypeContinued &identifier + # LongType:: + # LongTypeContinued &identifier + # LongType< + # ...>::LongTypeContinued &identifier + # + # If we detected a type split across two lines, join the previous + # line to current line so that we can match const references + # accordingly. + # + # Note that this only scans back one line, since scanning back + # arbitrary number of lines would be expensive. If you have a type + # that spans more than 2 lines, please use a typedef. + if linenum > 1: + previous = None + if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line): + # previous_line\n + ::current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$', + clean_lines.elided[linenum - 1]) + elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line): + # previous_line::\n + current_line + previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$', + clean_lines.elided[linenum - 1]) + if previous: + line = previous.group(1) + line.lstrip() + else: + # Check for templated parameter that is split across multiple lines + endpos = line.rfind('>') + if endpos > -1: + (_, startline, startpos) = ReverseCloseExpression( + clean_lines, linenum, endpos) + if startpos > -1 and startline < linenum: + # Found the matching < on an earlier line, collect all + # pieces up to current line. + line = '' + for i in xrange(startline, linenum + 1): + line += clean_lines.elided[i].strip() + + # Check for non-const references in function parameters. A single '&' may + # found in the following places: + # inside expression: binary & for bitwise AND + # inside expression: unary & for taking the address of something + # inside declarators: reference parameter + # We will exclude the first two cases by checking that we are not inside a + # function body, including one that was just introduced by a trailing '{'. + # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare]. + if (nesting_state.previous_stack_top and + not (isinstance(nesting_state.previous_stack_top, _ClassInfo) or + isinstance(nesting_state.previous_stack_top, _NamespaceInfo))): + # Not at toplevel, not within a class, and not within a namespace + return + + # Avoid initializer lists. We only need to scan back from the + # current line for something that starts with ':'. + # + # We don't need to check the current line, since the '&' would + # appear inside the second set of parentheses on the current line as + # opposed to the first set. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 10), -1): + previous_line = clean_lines.elided[i] + if not Search(r'[),]\s*$', previous_line): + break + if Match(r'^\s*:\s+\S', previous_line): + return + + # Avoid preprocessors + if Search(r'\\\s*$', line): + return + + # Avoid constructor initializer lists + if IsInitializerList(clean_lines, linenum): + return + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". Do not check + # those function parameters. + # + # We also accept & in static_assert, which looks like a function but + # it's actually a declaration expression. +# whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|' +# r'operator\s*[<>][<>]|' +# r'static_assert|COMPILE_ASSERT' +# r')\s*\(') +# if Search(whitelisted_functions, line): +# return +# elif not Search(r'\S+\([^)]*$', line): +# # Don't see a whitelisted function on this line. Actually we +# # didn't see any function name on this line, so this is likely a +# # multi-line parameter list. Try a bit harder to catch this case. +# for i in xrange(2): +# if (linenum > i and +# Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])): +# return +# +# decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body +# for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls): +# if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and +# not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)): +# error(filename, linenum, 'runtime/references', 2, +# 'Is this a non-const reference? ' +# 'If so, make const or use a pointer: ' + +# ReplaceAll(' *<', '<', parameter)) + + +def CheckCasts(filename, clean_lines, linenum, error): + """Various cast related checks. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+(?:const\s+)?|\S<\s*(?:const\s+)?)?\b' + r'(int|float|double|bool|char|int32|uint32|int64|uint64)' + r'(\([^)].*)', line) + expecting_function = ExpectingFunctionArgs(clean_lines, linenum) + if match and not expecting_function: + matched_type = match.group(2) + + # matched_new_or_template is used to silence two false positives: + # - New operators + # - Template arguments with function types + # + # For template arguments, we match on types immediately following + # an opening bracket without any spaces. This is a fast way to + # silence the common case where the function type is the first + # template argument. False negative with less-than comparison is + # avoided because those operators are usually followed by a space. + # + # function // bracket + no space = false positive + # value < double(42) // bracket + space = true positive + matched_new_or_template = match.group(1) + + # Avoid arrays by looking for brackets that come after the closing + # parenthesis. + if Match(r'\([^()]+\)\s*\[', match.group(3)): + return + + # Other things to ignore: + # - Function pointers + # - Casts to pointer types + # - Placement new + # - Alias declarations + matched_funcptr = match.group(3) + if (matched_new_or_template is None and + not (matched_funcptr and + (Match(r'\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(', + matched_funcptr) or + matched_funcptr.startswith('(*)'))) and + not Match(r'\s*using\s+\S+\s*=\s*' + matched_type, line) and + not Search(r'new\(\S+\)\s*' + matched_type, line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + matched_type) + + if not expecting_function: + CheckCStyleCast(filename, clean_lines, linenum, 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, clean_lines, linenum, 'const_cast', + r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, clean_lines, linenum, 'reinterpret_cast', + r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + # + # Some non-identifier character is required before the '&' for the + # expression to be recognized as a cast. These are casts: + # expression = &static_cast(temporary()); + # function(&(int*)(temporary())); + # + # This is not a cast: + # reference_type&(int* function_param); + match = Search( + r'(?:[^\w]&\(([^)*][^)]*)\)[\w(])|' + r'(?:[^\w]&(static|dynamic|down|reinterpret)_cast\b)', line) + if match: + # Try a better error message when the & is bound to something + # dereferenced by the casted pointer, as opposed to the casted + # pointer itself. + parenthesis_error = False + match = Match(r'^(.*&(?:static|dynamic|down|reinterpret)_cast\b)<', line) + if match: + _, y1, x1 = CloseExpression(clean_lines, linenum, len(match.group(1))) + if x1 >= 0 and clean_lines.elided[y1][x1] == '(': + _, y2, x2 = CloseExpression(clean_lines, y1, x1) + if x2 >= 0: + extended_line = clean_lines.elided[y2][x2:] + if y2 < clean_lines.NumLines() - 1: + extended_line += clean_lines.elided[y2 + 1] + if Match(r'\s*(?:->|\[)', extended_line): + parenthesis_error = True + + if parenthesis_error: + error(filename, linenum, 'readability/casting', 4, + ('Are you taking an address of something dereferenced ' + 'from a cast? Wrapping the dereferenced expression in ' + 'parentheses will make the binding more obvious')) + else: + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + +def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): + """Checks for a C-style cast by looking for the pattern. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + line = clean_lines.elided[linenum] + match = Search(pattern, line) + if not match: + return False + + # Exclude lines with keywords that tend to look like casts + context = line[0:match.start(1) - 1] + if Match(r'.*\b(?:sizeof|alignof|alignas|catch|[_A-Z][_A-Z0-9]*)\s*$', context): + return False + + # Try expanding current context to see if we one level of + # parentheses inside a macro. + if linenum > 0: + for i in xrange(linenum - 1, max(0, linenum - 5), -1): + context = clean_lines.elided[i] + context + if Match(r'.*\b[_A-Z][_A-Z0-9]*\s*\((?:\([^()]*\)|[^()])*$', context): + return False + + # operator++(int) and operator--(int) + if context.endswith(' operator++') or context.endswith(' operator--'): + return False + + # A single unnamed argument for a function tends to look like old style cast. + # If we see those, don't issue warnings for deprecated casts. + remainder = line[match.end(0):] + if Match(r'^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)', + remainder): + return False + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +def ExpectingFunctionArgs(clean_lines, linenum): + """Checks whether where function type arguments are expected. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + + Returns: + True if the line at 'linenum' is inside something that expects arguments + of function types. + """ + line = clean_lines.elided[linenum] + return (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + (linenum >= 2 and + (Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\((?:\S+,)?\s*$', + clean_lines.elided[linenum - 1]) or + Match(r'^\s*MOCK_(?:CONST_)?METHOD\d+(?:_T)?\(\s*$', + clean_lines.elided[linenum - 2]) or + Search(r'\bstd::m?function\s*\<\s*$', + clean_lines.elided[linenum - 1])))) + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', + 'unique_ptr', 'weak_ptr')), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('tuple',)), + ('', ('unordered_map', 'unordered_multimap')), + ('', ('unordered_set', 'unordered_multiset')), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_MAYBE_TEMPLATES = ( + ('', ('copy', 'max', 'min', 'min_element', 'sort', + 'transform', + )), + ('', ('forward', 'make_pair', 'move', 'swap')), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_headers_maybe_templates = [] +for _header, _templates in _HEADERS_MAYBE_TEMPLATES: + for _template in _templates: + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_headers_maybe_templates.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + _header)) + +# Other scripts may reach in and modify this pattern. +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + fileinfo = FileInfo(filename_cc) + if not fileinfo.IsSource(): + return (False, '') + filename_cc = filename_cc[:-len(fileinfo.Extension())] + matched_test_suffix = Search(_TEST_FILE_SUFFIX, fileinfo.BaseName()) + if matched_test_suffix: + filename_cc = filename_cc[:-len(matched_test_suffix.group(1))] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_dict, io=codecs): + """Fill up the include_dict with new includes found from the file. + + Args: + filename: the name of the header to read. + include_dict: a dictionary in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was successfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + include_dict.setdefault(include, linenum) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_headers_maybe_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + matched = pattern.search(line) + if matched: + # Don't warn about IWYU in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's flatten the include_state include_list and copy it into a dictionary. + include_dict = dict([item for sublist in include_state.include_list + for item in sublist]) + + # Did we find the header for this file (if any) and successfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_dict is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_dict.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_dict, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + +# # All the lines have been processed, report the errors found. +# for required_header_unstripped in required: +# template = required[required_header_unstripped][1] +# if required_header_unstripped.strip('<>"') not in include_dict: +# error(filename, required[required_header_unstripped][0], +# 'build/include_what_you_use', 4, +# 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++11 mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'For C++11-compatibility, omit template arguments from make_pair' + ' OR use pair directly OR if appropriate, construct a pair directly') + + +def CheckRedundantVirtual(filename, clean_lines, linenum, error): + """Check if line contains a redundant "virtual" function-specifier. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Look for "virtual" on current line. + line = clean_lines.elided[linenum] + virtual = Match(r'^(.*)(\bvirtual\b)(.*)$', line) + if not virtual: return + + # Ignore "virtual" keywords that are near access-specifiers. These + # are only used in class base-specifier and do not apply to member + # functions. + if (Search(r'\b(public|protected|private)\s+$', virtual.group(1)) or + Match(r'^\s+(public|protected|private)\b', virtual.group(3))): + return + + # Ignore the "virtual" keyword from virtual base classes. Usually + # there is a column on the same line in these cases (virtual base + # classes are rare in google3 because multiple inheritance is rare). + if Match(r'^.*[^:]:[^:].*$', line): return + + # Look for the next opening parenthesis. This is the start of the + # parameter list (possibly on the next line shortly after virtual). + # TODO(unknown): doesn't work if there are virtual functions with + # decltype() or other things that use parentheses, but csearch suggests + # that this is rare. + end_col = -1 + end_line = -1 + start_col = len(virtual.group(2)) + for start_line in xrange(linenum, min(linenum + 3, clean_lines.NumLines())): + line = clean_lines.elided[start_line][start_col:] + parameter_list = Match(r'^([^(]*)\(', line) + if parameter_list: + # Match parentheses to find the end of the parameter list + (_, end_line, end_col) = CloseExpression( + clean_lines, start_line, start_col + len(parameter_list.group(1))) + break + start_col = 0 + + if end_col < 0: + return # Couldn't find end of parameter list, give up + +# # Look for "override" or "final" after the parameter list +# # (possibly on the next few lines). +# for i in xrange(end_line, min(end_line + 3, clean_lines.NumLines())): +# line = clean_lines.elided[i][end_col:] +# match = Search(r'\b(override|final)\b', line) +# if match: +# error(filename, linenum, 'readability/inheritance', 4, +# ('"virtual" is redundant since function is ' +# 'already declared as "%s"' % match.group(1))) +# +# # Set end_col to check whole lines after we are done with the +# # first line. +# end_col = 0 +# if Search(r'[^\w]\s*$', line): +# break + + + +# Returns true if we are at a new block, and it is directly +# inside of a namespace. +def IsBlockInNameSpace(nesting_state, is_forward_declaration): + """Checks that the new block is directly in a namespace. + + Args: + nesting_state: The _NestingState object that contains info about our state. + is_forward_declaration: If the class is a forward declared class. + Returns: + Whether or not the new block is directly in a namespace. + """ + if is_forward_declaration: + if len(nesting_state.stack) >= 1 and ( + isinstance(nesting_state.stack[-1], _NamespaceInfo)): + return True + else: + return False + + return (len(nesting_state.stack) > 1 and + nesting_state.stack[-1].check_namespace_indentation and + isinstance(nesting_state.stack[-2], _NamespaceInfo)) + + +def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, + raw_lines_no_comments, linenum): + """This method determines if we should apply our namespace indentation check. + + Args: + nesting_state: The current nesting state. + is_namespace_indent_item: If we just put a new class on the stack, True. + If the top of the stack is not a class, or we did not recently + add the class, False. + raw_lines_no_comments: The lines without the comments. + linenum: The current line number we are processing. + + Returns: + True if we should apply our namespace indentation check. Currently, it + only works for classes and namespaces inside of a namespace. + """ + + is_forward_declaration = IsForwardClassDeclaration(raw_lines_no_comments, + linenum) + + if not (is_namespace_indent_item or is_forward_declaration): + return False + + # If we are in a macro, we do not want to check the namespace indentation. + if IsMacroDefinition(raw_lines_no_comments, linenum): + return False + + return IsBlockInNameSpace(nesting_state, is_forward_declaration) + + +# Call this method if the line is directly inside of a namespace. +# If the line above is blank (excluding comments) or the start of +# an inner namespace, it cannot be indented. +def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, + error): + line = raw_lines_no_comments[linenum] + if Match(r'^\s+', line): + error(filename, linenum, 'runtime/indentation_namespace', 4, + 'Do not indent within a namespace') + +def CheckNamespaceOrUsing(filename, clean_lines, linenum, error): + line = clean_lines.elided[linenum] + if Match(r'^namespace(\s|$)', line): + error(filename, linenum, 'readability/namespace', 4, + 'Do not use namespaces') + if Match(r'^using\s', line): + error(filename, linenum, 'readability/namespace', 4, + 'Do not use using') + +def ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + nesting_state: A NestingState instance which maintains information about + the current stack of nested blocks being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + nesting_state.Update(filename, clean_lines, line, error) + CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, + error) + if nesting_state.InAsmBlock(): return + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + nesting_state, error) + CheckForNonConstReference(filename, clean_lines, line, nesting_state, error) + CheckForNonStandardConstructs(filename, clean_lines, line, + nesting_state, error) + CheckVlogArguments(filename, clean_lines, line, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + CheckRedundantVirtual(filename, clean_lines, line, error) + CheckNamespaceOrUsing(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def FlagCxx11Features(filename, clean_lines, linenum, error): + """Flag those c++11 features that we only allow in certain places. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + +# # Flag unapproved C++ TR1 headers. +# if include and include.group(1).startswith('tr1/'): +# error(filename, linenum, 'build/c++tr1', 5, +# ('C++ TR1 headers such as <%s> are unapproved.') % include.group(1)) + + # Flag unapproved C++11 headers. + if include and include.group(1) in ('cfenv', + 'condition_variable', + 'fenv.h', + 'future', + 'mutex', + 'thread', + 'chrono', + 'ratio', + 'regex', + 'system_error', + ): + error(filename, linenum, 'build/c++11', 5, + ('<%s> is an unapproved C++11 header.') % include.group(1)) + + # The only place where we need to worry about C++11 keywords and library + # features in preprocessor directives is in macro definitions. + if Match(r'\s*#', line) and not Match(r'\s*#\s*define\b', line): return + + # These are classes and free functions. The classes are always + # mentioned as std::*, but we only catch the free functions if + # they're not found by ADL. They're alphabetical by header. + for top_name in ( + # type_traits + 'alignment_of', + 'aligned_union', + ): + if Search(r'\bstd::%s\b' % top_name, line): + error(filename, linenum, 'build/c++11', 5, + ('std::%s is an unapproved C++11 class or function. Send c-style ' + 'an example of where it would make your code more readable, and ' + 'they may let you use it.') % top_name) + + +def FlagCxx14Features(filename, clean_lines, linenum, error): + """Flag those C++14 features that we restrict. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + include = Match(r'\s*#\s*include\s+[<"]([^<"]+)[">]', line) + + # Flag unapproved C++14 headers. + if include and include.group(1) in ('scoped_allocator', 'shared_mutex'): + error(filename, linenum, 'build/c++14', 5, + ('<%s> is an unapproved C++14 header.') % include.group(1)) + + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + nesting_state = NestingState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + CheckForFunctionCommentHeaders(filename, lines, error) + ProcessGlobalSuppresions(lines) + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + + if IsHeaderExtension(file_extension): + CheckForHeaderGuard(filename, clean_lines, error) + + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, nesting_state, error, + extra_check_functions) + FlagCxx11Features(filename, clean_lines, line, error) + nesting_state.CheckCompletedBlocks(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # Check that the .cc file has included its header if it exists. + if _IsSourceExtension(file_extension): + CheckHeaderFileIncluded(filename, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForBadCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessConfigOverrides(filename): + """ Loads the configuration files and processes the config overrides. + + Args: + filename: The name of the file being processed by the linter. + + Returns: + False if the current |filename| should not be processed further. + """ + + abs_filename = os.path.abspath(filename) + cfg_filters = [] + keep_looking = True + while keep_looking: + abs_path, base_name = os.path.split(abs_filename) + if not base_name: + break # Reached the root directory. + + cfg_file = os.path.join(abs_path, "CPPLINT.cfg") + abs_filename = abs_path + if not os.path.isfile(cfg_file): + continue + + try: + with open(cfg_file) as file_handle: + for line in file_handle: + line, _, _ = line.partition('#') # Remove comments. + if not line.strip(): + continue + + name, _, val = line.partition('=') + name = name.strip() + val = val.strip() + if name == 'set noparent': + keep_looking = False + elif name == 'filter': + cfg_filters.append(val) + elif name == 'exclude_files': + # When matching exclude_files pattern, use the base_name of + # the current file name or the directory name we are processing. + # For example, if we are checking for lint errors in /foo/bar/baz.cc + # and we found the .cfg file at /foo/CPPLINT.cfg, then the config + # file's "exclude_files" filter is meant to be checked against "bar" + # and not "baz" nor "bar/baz.cc". + if base_name: + pattern = re.compile(val) + if pattern.match(base_name): + sys.stderr.write('Ignoring "%s": file excluded by "%s". ' + 'File path component "%s" matches ' + 'pattern "%s"\n' % + (filename, cfg_file, base_name, val)) + return False + elif name == 'linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + sys.stderr.write('Line length must be numeric.') + elif name == 'root': + global _root + _root = val + elif name == 'headers': + ProcessHppHeadersOption(val) + else: + sys.stderr.write( + 'Invalid configuration option (%s) in file %s\n' % + (name, cfg_file)) + + except IOError: + sys.stderr.write( + "Skipping config file '%s': Can't open for reading\n" % cfg_file) + keep_looking = False + + # Apply all the accumulated filters in reverse order (top-level directory + # config options having the least priority). + for filter in reversed(cfg_filters): + _AddFilters(filter) + + return True + + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + _BackupFilters() + +#exclude these files: + if Search(r'(\.l|\.y|\.inc|\.d|\.o|y\.tab\.cpp|\.tab\.h|\.yy\.cpp|builtin_headers)$', filename): + return + + if not ProcessConfigOverrides(filename): + _RestoreFilters() + return + + lf_lines = [] + crlf_lines = [] + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + # Remove trailing '\r'. + # The -1 accounts for the extra trailing blank line we get from split() + for linenum in range(len(lines) - 1): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + crlf_lines.append(linenum + 1) + else: + lf_lines.append(linenum + 1) + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + _RestoreFilters() + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if filename != '-' and file_extension not in _valid_extensions: + sys.stderr.write('Ignoring %s; not a valid file name ' + '(%s)\n' % (filename, ', '.join(_valid_extensions))) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + + # If end-of-line sequences are a mix of LF and CR-LF, issue + # warnings on the lines with CR. + # + # Don't issue any warnings if all lines are uniformly LF or CR-LF, + # since critique can handle these just fine, and the style guide + # doesn't dictate a particular end of line sequence. + # + # We can't depend on os.linesep to determine what the desired + # end-of-line sequence should be, since that will return the + # server-side end-of-line sequence. + if lf_lines and crlf_lines: + # Warn on every line with CR. An alternative approach might be to + # check whether the file is mostly CRLF or just LF, and warn on the + # minority, we bias toward LF here since most tools prefer LF. + for linenum in crlf_lines: + Error(filename, linenum, 'whitespace/newline', 1, + 'Unexpected \\r (^M) found; better to use only \\n') + + sys.stdout.write('Done processing %s\n' % filename) + _RestoreFilters() + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=', + 'root=', + 'linelength=', + 'extensions=', + 'headers=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if val not in ('emacs', 'vs7', 'eclipse'): + PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + elif opt == '--root': + global _root + _root = val + elif opt == '--linelength': + global _line_length + try: + _line_length = int(val) + except ValueError: + PrintUsage('Line length must be digits.') + elif opt == '--extensions': + global _valid_extensions + try: + _valid_extensions = set(val.split(',')) + except ValueError: + PrintUsage('Extensions must be comma seperated list.') + elif opt == '--headers': + ProcessHppHeadersOption(val) + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/scripts/run_lint.sh b/scripts/run_lint.sh new file mode 100755 index 000000000..f2f1a41d8 --- /dev/null +++ b/scripts/run_lint.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +set -e + +if [[ "$#" -ne 2 ]] +then + echo "Script for running the CPP linter only on modified lines." + echo "Requires two arguments the start and the end" + echo "start - a git reference that marks the first commit whose changes to consider" + echo "end - a git reference that marks the last commit whose changes to consider" + + exit 1 +fi + +if ! [[ -e scripts/cpplint.py ]] +then + echo "Lint script could not be found in the scripts directory" + echo "Ensure cpplint.py is inside the scripts directory then run again" + exit 1 +fi + +git_start=$1 +git_end=$2 + +# Get the list of files that have changed +diff_files=`git diff --name-only $git_start $git_end` + +# Build a filter that will filter the blame output +# to only include lines that come from one of the relevant_commits +# We do this by making the blame tool output the same hash for all +# lines that are too old. +blame_grep_filter=`git rev-parse "$git_start"` + +# Build a regex for finding the line number of a given line inside blame +# First matches the 40 digit hash of the commi +# Then match an arbitary length number that represents the line in the original file +# Finally matches (and groups) another arbitary length digit which is the +# line in the final file +regex="[0-9a-f]{40} [0-9]+ ([0-9]+)" + +# We only split on lines or otherwise the git blame output is nonsense +IFS=$'\n' + +are_errors=0 + +for file in $diff_files; do + # We build another grep filter the output of the linting script + lint_grep_filter="^(" + + # Include line 0 errors (e.g. copyright) + lint_grep_filter+=$file + lint_grep_filter+=":0" + + # We first filter only the lines that start with a commit hash + # Then we filter out the ones that come from the start commit + modified_lines=`git blame $git_start..$git_end --line-porcelain $file | grep -E "^[0-9a-f]{40}" | { grep -v "$blame_grep_filter" || true; }` + + # For each modified line we find the line number + for line in $modified_lines; do + + # Use the above regex to match the line number + if [[ $line =~ $regex ]] + then + # Some bash magic to get the first group from the regex (the line number) + LINENUM="${BASH_REMATCH[1]}" + + # The format from the linting script is filepath:linenum: [error type] + # So we build the first bit to filter out relevant lines + LINE_FILTER=$file:$LINENUM + + # Add the line filter on to the grep expression as we want + # lines that match any of the line filters + lint_grep_filter+="|" + lint_grep_filter+=$LINE_FILTER + fi + done + + # Add the closing bracket + lint_grep_filter+=")" + + # Run the linting script and filter by the filter we've build + # of all the modified lines + # The errors from the linter go to STDERR so must be redirected to STDOUT + result=`python scripts/cpplint.py $file 2>&1 | { grep -E "$lint_grep_filter" || true; }` + + # Providing some errors were relevant we print them out + if [ "$result" ] + then + are_errors=1 + (>&2 echo "$result") + fi +done + +unset IFS + +# Return an error code if errors are found +exit $are_errors diff --git a/src/2ls/2ls_parse_options.cpp b/src/2ls/2ls_parse_options.cpp index e2debca7c..c67380dc5 100644 --- a/src/2ls/2ls_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -60,6 +60,8 @@ Author: Daniel Kroening, Peter Schrammel #define IGNORE_THREADS 1 #define EXPLICIT_NONDET_LOCALS 0 #define FILTER_ASSERTIONS 1 +#define ASSUME_AFTER_ASSERT 0 + /*******************************************************************\ @@ -149,11 +151,6 @@ void twols_parse_optionst::get_command_line_options(optionst &options) if(cmdline.isset("inline")) options.set_option("inline", true); - if(cmdline.isset("spurious-check")) - options.set_option("spurious-check", cmdline.get_value("spurious-check")); - else - options.set_option("spurious-check", "all"); - if(cmdline.isset("slice") && cmdline.isset("inline")) options.set_option("slice", true); else @@ -845,7 +842,7 @@ void twols_parse_optionst::require_entry( if(goto_model.symbol_table.symbols.find(entry_point)== symbol_table.symbols.end()) - throw "The program has no entry point; please complete linking"; + throw "the program has no entry point; please complete linking"; } /*******************************************************************\ @@ -1099,10 +1096,12 @@ bool twols_parse_optionst::process_goto_program( inline_main(goto_model); } +#ifdef ASSUME_AFTER_ASSERT if(!cmdline.isset("independent-properties")) { add_assumptions_after_assertions(goto_model); } +#endif #ifdef FILTER_ASSERTIONS filter_assertions(goto_model); diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index 642b37082..d564b1279 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -49,10 +49,9 @@ class optionst; "(show-goto-functions)(show-guards)(show-defs)(show-ssa)(show-assignments)" \ "(show-invariants)(std-invariants)" \ "(property):(all-properties)(k-induction)(incremental-bmc)" \ - "(all-functions)" \ + "(no-spurious-check)(all-functions)" \ "(no-simplify)(no-fixed-point)" \ "(graphml-witness):(json-cex):" \ - "(spurious-check):(no-all-properties)" \ "(no-spurious-check)(stop-on-fail)" \ "(competition-mode)(slice)(no-propagation)(independent-properties)" \ "(no-unwinding-assertions)" diff --git a/src/2ls/Makefile b/src/2ls/Makefile index fcd7b93c7..b3a785bfd 100644 --- a/src/2ls/Makefile +++ b/src/2ls/Makefile @@ -10,10 +10,7 @@ SRC = 2ls_main.cpp 2ls_parse_options.cpp \ cover_goals_ext.cpp horn_encoding.cpp \ preprocessing_util.cpp \ instrument_goto.cpp dynamic_cfg.cpp \ - summarizer_bw_cex_ai.cpp summarizer_bw_cex_complete.cpp summarizer_bw_cex.cpp \ - summarizer_bw_cex_all.cpp summarizer_bw_cex_concrete.cpp summarizer_bw_cex_wp.cpp \ - graphml_witness_ext.cpp \ - + graphml_witness_ext.cpp OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ $(CBMC)/src/linking/linking$(LIBEXT) \ @@ -32,6 +29,7 @@ OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ ../domains/domains$(LIBEXT) \ ../ssa/ssa$(LIBEXT) \ ../solver/solver$(LIBEXT) \ + # empty last line CP_CXXFLAGS+= $(TWOLSFLAGS) diff --git a/src/2ls/cover_goals_ext.cpp b/src/2ls/cover_goals_ext.cpp index 7331999df..fc8c3a234 100644 --- a/src/2ls/cover_goals_ext.cpp +++ b/src/2ls/cover_goals_ext.cpp @@ -1,6 +1,5 @@ /*******************************************************************\ - Module: Cover a set of goals incrementally Author: Daniel Kroening, kroening@kroening.com @@ -175,71 +174,76 @@ Function: cover_goals_extt::assignment void cover_goals_extt::assignment() { - // check loop head choices in model - bool invariants_involved=false; - if(spurious_check) + std::list::const_iterator g_it=goals.begin(); + for(goal_mapt::const_iterator it=goal_map.begin(); + it!=goal_map.end(); it++, g_it++) { - for(exprt::operandst::const_iterator l_it=loophead_selects.begin(); - l_it!=loophead_selects.end(); l_it++) + if(property_map[it->first].result==property_checkert::UNKNOWN && + solver.l_get(g_it->condition).is_true()) { - if(solver.get(l_it->op0()).is_true()) +#if 1 + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); +#else // THE ASSERTIONS THAT FAIL COULD BE RATHER ARBITRARY SINCE THE FORMULA + // IS NOT "ROOTED" IN AN INITIAL STATE. + assert((g_it->cond_expression).id()==ID_not); + exprt conjunct_expr=(g_it->cond_expression).op0(); +#if 0 + std::cout << "FAILED EXPR: " + << from_expr(SSA.ns, "", conjunct_expr) << std::endl; +#endif + + if(conjunct_expr.id()!=ID_and) + { + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + summarizer_bw_cex.summarize(g_it->cond_expression); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); + } + else { - invariants_involved=true; - break; + // filter out assertion instances that are not violated + exprt::operandst failed_exprs; + for(exprt::operandst::const_iterator c_it= + conjunct_expr.operands().begin(); + c_it!=conjunct_expr.operands().end(); c_it++) + { + literalt conjunct_literal=solver.convert(*c_it); + if(solver.l_get(conjunct_literal).is_false()) + { + failed_exprs.push_back(*c_it); +#if 0 + std::cout << "failed_expr: " + << from_expr(SSA.ns, "", *c_it) << std::endl; +#endif + } + } + // otherwise this would interfere with necessary preconditions + solver.pop_context(); + + summarizer_bw_cex.summarize(not_exprt(conjunction(failed_exprs))); + property_map[it->first].result=summarizer_bw_cex.check(); + solver.new_context(); } +#endif } - } - if(!invariants_involved || !spurious_check) - { - std::list::const_iterator g_it=goals.begin(); - for(goal_mapt::const_iterator it=goal_map.begin(); - it!=goal_map.end(); it++, g_it++) + if(property_map[it->first].result==property_checkert::FAIL) { - if(property_map[it->first].result==property_checkert::UNKNOWN && - solver.l_get(g_it->condition).is_true()) - { - if(spurious_check) - { - assert((g_it->cond_expression).id() == ID_not); - exprt conjunct_expr = (g_it->cond_expression).op0(); - - if(conjunct_expr.id() != ID_and) - { - solver.pop_context(); //otherwise this would interfere with necessary preconditions - summarizer_bw_cex.summarize(g_it->cond_expression); - property_map[it->first].result = summarizer_bw_cex.check(); - solver.new_context(); - } - else - { - exprt::operandst failed_exprs; - for(exprt::operandst::const_iterator c_it = - conjunct_expr.operands().begin(); - c_it != conjunct_expr.operands().end(); c_it++) - { - literalt conjunct_literal = solver.convert(*c_it); - if(solver.l_get(conjunct_literal).is_false()) - failed_exprs.push_back(*c_it); - } - solver.pop_context(); //otherwise this would interfere with necessary preconditions - for(unsigned i=0; ifirst].result = summarizer_bw_cex.check(); - if(property_map[it->first].result == - property_checkert::FAIL) - break; + if(build_error_trace) + { + ssa_build_goto_tracet build_goto_trace(SSA, solver.get_solver()); + build_goto_trace(property_map[it->first].error_trace); } - solver.new_context(); } - } - else - property_map[it->first].result = property_checkert::FAIL; - } - } + if(!all_properties && + property_map[it->first].result==property_checkert::FAIL) + break; + } _iterations++; // statistics - } } - diff --git a/src/2ls/cover_goals_ext.h b/src/2ls/cover_goals_ext.h index c3a7b781c..9a40f4693 100644 --- a/src/2ls/cover_goals_ext.h +++ b/src/2ls/cover_goals_ext.h @@ -12,11 +12,10 @@ Author: Daniel Kroening, kroening@kroening.com #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/unwindable_local_ssa.h" -#include "../domains/incremental_solver.h" -#include "summarizer_bw_cex.h" -#include "summarizer_bw_cex_complete.h" +#include +#include +#include +#include /*******************************************************************\ @@ -48,42 +47,22 @@ struct goalt class cover_goals_extt:public messaget { -/* public: - explicit inline cover_goals_extt( + cover_goals_extt( unwindable_local_SSAt &_SSA, incremental_solvert &_solver, - const exprt::operandst& _loophead_selects, property_checkert::property_mapt &_property_map, - bool _spurious_check, bool _all_properties, bool _build_error_trace, + bool _all_properties, + bool _build_error_trace, summarizer_bw_cex_baset &_summarizer_bw_cex): SSA(_SSA), solver(_solver), property_map(_property_map), - spurious_check(_spurious_check), all_properties(_all_properties), build_error_trace(_build_error_trace), - loophead_selects(_loophead_selects), summarizer_bw_cex(_summarizer_bw_cex) { } -*/ - -public: - explicit inline cover_goals_extt(unwindable_local_SSAt &_SSA, - incremental_solvert &_solver, - property_checkert::property_mapt &_property_map, - bool _all_properties, - bool _build_error_trace, - summarizer_bw_cex_baset &_summarizer_bw_cex) - : - SSA(_SSA), - solver(_solver), - property_map(_property_map), - all_properties(_all_properties), - build_error_trace(_build_error_trace), - summarizer_bw_cex(_summarizer_bw_cex) -{} virtual ~cover_goals_extt(); @@ -95,7 +74,7 @@ class cover_goals_extt:public messaget { literalt condition; bool covered; - exprt cond_expression; + exprt cond_expression; cover_goalt():covered(false) { @@ -127,16 +106,10 @@ class cover_goals_extt:public messaget // managing the goals - inline void add(const literalt condition) - { - goals.push_back(cover_goalt()); - goals.back().condition=condition; - } - inline void add(const exprt cond_expression) { goals.push_back(cover_goalt()); - goals.back().condition=!solver.convert(not_exprt(cond_expression)); + goals.back().condition=!solver.convert(cond_expression); goals.back().cond_expression=cond_expression; } @@ -145,8 +118,7 @@ class cover_goals_extt:public messaget unsigned _number_covered, _iterations; incremental_solvert &solver; property_checkert::property_mapt &property_map; - bool spurious_check, all_properties, build_error_trace; - exprt::operandst loophead_selects; + bool all_properties, build_error_trace; summarizer_bw_cex_baset &summarizer_bw_cex; // this method is called for each satisfying assignment diff --git a/src/2ls/dynamic_cfg.cpp b/src/2ls/dynamic_cfg.cpp index 807ec5ed3..c747a59e2 100644 --- a/src/2ls/dynamic_cfg.cpp +++ b/src/2ls/dynamic_cfg.cpp @@ -284,7 +284,8 @@ void dynamic_cfgt::build_from_invariants( if(summary.fw_invariant.id()==ID_implies) { build_from_invariant( - ssa, summary.fw_invariant, + ssa, + summary.fw_invariant, assumptions); } else if(summary.fw_invariant.id()==ID_and) @@ -293,9 +294,9 @@ void dynamic_cfgt::build_from_invariants( { assert(summary.fw_invariant.operands()[i].id()==ID_implies); build_from_invariant( - ssa, summary.fw_invariant.operands()[i], + ssa, + summary.fw_invariant.operands()[i], assumptions); } } } - diff --git a/src/2ls/graphml_witness_ext.h b/src/2ls/graphml_witness_ext.h index df62a2b6d..0b573dba5 100644 --- a/src/2ls/graphml_witness_ext.h +++ b/src/2ls/graphml_witness_ext.h @@ -27,8 +27,10 @@ class graphml_witness_extt:public graphml_witnesst void operator()(const summary_checker_baset &summary_checker); protected: + typedef std::map loc_to_node_mapt; + graphmlt::node_indext add_node( - std::map &loc_to_node, + loc_to_node_mapt &loc_to_node, goto_programt::const_targett it); void add_edge( diff --git a/src/2ls/instrument_goto.cpp b/src/2ls/instrument_goto.cpp index 3e550c6e1..647aaab10 100644 --- a/src/2ls/instrument_goto.cpp +++ b/src/2ls/instrument_goto.cpp @@ -115,8 +115,7 @@ Function: instrument_gotot::instrument_body void instrument_gotot::instrument_body( const local_SSAt &SSA, const exprt &expr, - goto_functionst::goto_functiont &function -) + goto_functionst::goto_functiont &function) { // expected format (/\_j g_j)=> inv const exprt &impl=expr.op0(); diff --git a/src/2ls/show.h b/src/2ls/show.h index e30bb0565..2e9557eac 100644 --- a/src/2ls/show.h +++ b/src/2ls/show.h @@ -48,7 +48,6 @@ void show_assignments( void show_guards( const goto_modelt &, const irep_idt &function, - //bool simplify, std::ostream &, message_handlert &); diff --git a/src/2ls/summarizer_bw_cex.cpp b/src/2ls/summarizer_bw_cex.cpp deleted file mode 100644 index 4422d49a8..000000000 --- a/src/2ls/summarizer_bw_cex.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/*******************************************************************\ - -Module: Counterexample-based Backward Analysis - -Author: Kumar Madhukar, Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw_cex.h" -#include "../solver/summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizer_bw_cex_baset::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_baset::summarize() -{ - assert(false); //unused -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_baset::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_baset::summarize(const function_namet &function_name) -{ - exprt postcondition = true_exprt(); //initial calling context - - status() << "\nSummarizing function " << function_name << eom; - compute_summary_rec(function_name,postcondition,true); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_baset::summarize() - - Inputs: - - Outputs: - - Purpose: summarize backwards from given assertion - -\*******************************************************************/ - -void summarizer_bw_cex_baset::summarize(const exprt &_error_assertion) -{ - status() << "\nBackward error analysis..." << eom; - error_assertion = _error_assertion; - - summarize(entry_function); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_baset::check() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summarizer_bw_cex_baset::check() -{ - assert(false); -} diff --git a/src/2ls/summarizer_bw_cex.h b/src/2ls/summarizer_bw_cex.h deleted file mode 100644 index 3fe5c4edb..000000000 --- a/src/2ls/summarizer_bw_cex.h +++ /dev/null @@ -1,56 +0,0 @@ -/*******************************************************************\ - -Module: Counterexample-based Backward Analysis Base - -Author: Kumar Madhukar, Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_CEX_H -#define CPROVER_SUMMARIZER_BW_CEX_H - -#include -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/ssa_refiner_selective.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include "../solver/summarizer_bw.h" - -class summarizer_bw_cex_baset : public summarizer_bwt -{ - public: - typedef ssa_refiner_selectivet::reasont reasont; - - virtual void summarize(); - virtual void summarize(const function_namet &entry_function); - virtual void summarize(const exprt &_error_assertion); - - virtual property_checkert::resultt check(); - virtual void get_reason(reasont &_reason) { _reason.merge(reason); } - - protected: - function_namet entry_function; - function_namet error_function; - exprt error_assertion; - reasont reason; - - explicit summarizer_bw_cex_baset(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - function_namet _entry_function, - function_namet _error_function): - summarizer_bwt(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner), - entry_function(_entry_function), - error_function(_error_function) - {} - -}; - - -#endif diff --git a/src/2ls/summarizer_bw_cex_ai.cpp b/src/2ls/summarizer_bw_cex_ai.cpp deleted file mode 100644 index 5f3472321..000000000 --- a/src/2ls/summarizer_bw_cex_ai.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw_cex_ai.h" -#include "../solver/summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../domains/disjunctive_analyzer.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_ait::summarize(const function_namet &function_name) -{ - exprt postcondition = true_exprt(); //initial calling context - - status() << "\nSummarizing function " << function_name << eom; - compute_summary_rec(function_name,summaryt::entry_call_site, - postcondition,true); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::summarize() - - Inputs: - - Outputs: - - Purpose: summarize backwards from given assertion - -\*******************************************************************/ - -void summarizer_bw_cex_ait::summarize(const exprt &_error_assertion) -{ - status() << "\nBackward error analysis (abstract)..." << eom; - error_assertion = _error_assertion; - - summarize(entry_function); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::check() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summarizer_bw_cex_ait::check() -{ - property_checkert::resultt result = property_checkert::FAIL; - if(!summary_db.exists(entry_function)) - { - result = property_checkert::UNKNOWN; - } - else - { - const summaryt &summary = summary_db.get(entry_function); - if(summary.error_summaries.empty() || - summary.error_summaries.begin()->second.is_nil() || - summary.error_summaries.begin()->second.is_true()) - result = property_checkert::UNKNOWN; - } - - //we are only complete if we are in the entry function - if(result == property_checkert::UNKNOWN && - entry_function == error_function) - { - incremental_solvert &solver = ssa_db.get_solver(entry_function); - const local_SSAt &ssa = ssa_db.get(entry_function); - ssa_local_unwindert &ssa_local_unwinder = - ssa_unwinder.get(entry_function); - exprt::operandst loophead_selects; - exprt::operandst loop_continues; - get_loophead_selects(ssa, ssa_local_unwinder, - *solver.solver, loophead_selects); - get_loop_continues(ssa, ssa_local_unwinder, loop_continues); - //check whether loops have been fully unwound - bool fully_unwound = - is_fully_unwound(loop_continues,loophead_selects,solver); - status() << "Loops " << (fully_unwound ? "" : "not ") - << "fully unwound" << eom; - - if(fully_unwound) - result = property_checkert::PASS; - } - - return result; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_ait::compute_summary_rec( - const function_namet &function_name, - const summaryt::call_sitet &call_site, - const exprt &_postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - summaryt summary; - if(summary_db.exists(function_name)) - summary = summary_db.get(function_name); - else - { - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.nondets = SSA.nondets; - } - - // insert assertion - exprt end_guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - exprt postcondition = implies_exprt(end_guard,_postcondition); - if(function_name == error_function) - { - postcondition = and_exprt(postcondition,not_exprt(error_assertion)); - } - - summary.bw_postcondition = _postcondition; - -#if 0 - debug() << "Postcondition: " << - from_expr(SSA.ns, "", postcondition) << eom; -#endif - - if(_postcondition.is_false()){ - - summary.error_summaries[call_site] = false_exprt(); - - } - else{ - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,summary, - postcondition,context_sensitive, - true); - - status() << "Analyzing function " << function_name << eom; - - do_summary(function_name,call_site,SSA,summary,summary,postcondition,context_sensitive); - - if(function_name == error_function) - summary.has_assertion = true; - - } - - summary_db.set(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - debug() << out.str() << eom; - } - -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_ait::do_summary(const function_namet &function_name, - const summaryt::call_sitet &call_site, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - const exprt &postcondition, - bool context_sensitive) -{ - status() << "Computing error summary" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //TODO: maybe allow setting this separately on the command line - optionst _options = options; - _options.set_option("intervals",true); - _options.set_option("binsearch-solver",true); - - //TODO: use a template generator without invariants - template_generator_summaryt template_generator( - _options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,false); - - exprt::operandst c; - //add forward information if available - if(!old_summary.fw_precondition.is_nil()) - c.push_back(old_summary.fw_precondition); - if(!old_summary.fw_invariant.is_nil()) - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - exprt::operandst assert_postcond, noassert_postcond; - // add error summaries for function calls - bool assertion_flag; - assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false, - assert_postcond,noassert_postcond,c); //backward summaries - - assert_postcond.push_back(postcondition); //context - - //add nondet variables from callees to summary.nondets - std::set summary_vars; - find_symbols(conjunction(assert_postcond),summary_vars); - for(std::set::const_iterator it = summary_vars.begin(); - it != summary_vars.end(); ++it) - if(it->id()==ID_nondet_symbol) - summary.nondets.insert(*it); - - // assumptions must hold - for(local_SSAt::nodest::const_iterator - n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); - ++n_it) - for(local_SSAt::nodet::assumptionst::const_iterator - a_it = n_it->assumptions.begin(); - a_it != n_it->assumptions.end(); - ++a_it) - c.push_back(*a_it); - - - - -#if 0 - std::cout << from_expr(SSA.ns, "", cc) << std::endl; -#endif - - //TODO: pushing loophead_selects into the solver - - summary.error_summaries[call_site]; - if(!template_generator.empty()) - { - c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt - c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis - - //std::cout << "unsimplified constraints (if): " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; - exprt cc = simplify_expr(conjunction(c), SSA.ns); - //exprt cc = conjunction(c); - //std::cout << "simplified constraints passed (if): " << from_expr(SSA.ns, "", cc) << "\n\n\n"; - - /* - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver,SSA,cc,template_generator); - analyzer.get_result(summary.error_summaries[call_site], - template_generator.inout_vars()); - */ - /**/ - disjunctive_analyzert disjunctive_analyzer; - disjunctive_analyzer.set_message_handler(get_message_handler()); - disjunctive_analyzer(solver,SSA,cc,template_generator, - cc,summary.error_summaries[call_site], - template_generator.inout_vars()); - /**/ - -#if 0 - std::cout << "SUM: " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; -#endif - - - summary.error_summaries[call_site] = - simplify_expr(summary.error_summaries[call_site], SSA.ns); - - -#if 0 - std::cout << "SUM (post simplification): " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; -#endif - - //statistics - /* - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - */ - solver_instances += disjunctive_analyzer.get_number_of_solver_instances(); - solver_calls += disjunctive_analyzer.get_number_of_solver_calls(); - - } - else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - { - c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt - c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis - //c.push_back(not_exprt(conjunction(assert_postcond))); - //c.push_back(not_exprt(disjunction(noassert_postcond))); - - //std::cout << "unsimplified constraints (else): " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n\n"; - exprt cc = simplify_expr(conjunction(c), SSA.ns); - //exprt cc = conjunction(c); - //std::cout << "simplified constraints passed (else): " << from_expr(SSA.ns, "", cc) << "\n\n\n"; - - //std::cout << "enabling expressions (else): " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) << "\n\n\n"; - - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << cc; - exprt result = true_exprt(); - if(solver()!=decision_proceduret::D_SATISFIABLE) result = false_exprt(); - solver.pop_context(); - summary.error_summaries[call_site] = result; - -#if 0 - std::cout << "SUM: " << from_expr(SSA.ns, "", summary.error_summaries[call_site]) << std::endl; -#endif - } - - summary.error_summaries[call_site] = - simplify_expr((summary.error_summaries[call_site]), SSA.ns); //not_exprt - - summary.has_assertion = assertion_flag; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_ait::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); - n_it != SSA.nodes.begin(); ) - { - n_it--; - - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - - exprt postcondition_call = true_exprt(); - postcondition_call = compute_calling_context2( - function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,summaryt::call_sitet(n_it->location), - postcondition_call,context_sensitive); - } - } -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_ait::compute_calling_context2() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_cex_ait::compute_calling_context2( - const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Computing calling context for function " << fname << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //analyze - /* - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - */ - - disjunctive_analyzert disjunctive_analyzer; - disjunctive_analyzer.set_message_handler(get_message_handler()); - - //TODO: maybe allow setting this separately on the command line - optionst _options = options; - _options.set_option("intervals",true); - _options.set_option("binsearch-solver",true); - - //TODO: use a template generator without invariants - template_generator_callingcontextt template_generator( - _options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,n_it,f_it,false); - - // collect globals at call site - std::map - cs_globals_out; - SSA.get_globals(n_it->location,cs_globals_out[f_it],false); - - exprt::operandst c; - - //add forward information if available - if(!old_summary.fw_precondition.is_nil()) - c.push_back(old_summary.fw_precondition); - if(!old_summary.fw_invariant.is_nil()) - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - - exprt::operandst assert_postcond, noassert_postcond; - // add error summaries for function calls - ssa_inliner.get_summaries(SSA,summaryt::call_sitet(n_it->location),false, - assert_postcond,noassert_postcond,c); //backward summaries - assert_postcond.push_back(postcondition); //context - - - //TODO: pushing loophead_selects into the solver - - // set preconditions - local_SSAt &fSSA = ssa_db.get(fname); - - exprt postcondition_call; - - if(!template_generator.empty()){ - - c.push_back(conjunction(assert_postcond)); //with negative information would need: not_exprt - c.push_back(conjunction(noassert_postcond)); //with negative information would need: not_exprt dis - - /* - analyzer(solver,SSA,conjunction(c),template_generator); - analyzer.get_result(postcondition_call, - template_generator.callingcontext_vars()); - */ - - disjunctive_analyzer(solver,SSA,conjunction(c),template_generator, - conjunction(c), postcondition_call, - template_generator.callingcontext_vars()); - - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_out[f_it],fSSA.globals_out, - postcondition_call); - - } - else{ // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - - c.push_back(not_exprt(conjunction(assert_postcond))); - c.push_back(not_exprt(disjunction(noassert_postcond))); - - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << conjunction(c); - - //std::cout << "passed to solver, else branch, calling context: " << from_expr(SSA.ns, "", conjunction(c)) << "\n\n"; - - postcondition_call = false_exprt(); - if(solver()!=decision_proceduret::D_SATISFIABLE) postcondition_call = true_exprt(); - solver.pop_context(); - } - - debug() << "Backward calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", postcondition_call) << eom; - - //statistics - /* - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - */ - solver_instances += disjunctive_analyzer.get_number_of_solver_instances(); - solver_calls += disjunctive_analyzer.get_number_of_solver_calls(); - - return postcondition_call; -} - - diff --git a/src/2ls/summarizer_bw_cex_ai.h b/src/2ls/summarizer_bw_cex_ai.h deleted file mode 100644 index 6862ce2c5..000000000 --- a/src/2ls/summarizer_bw_cex_ai.h +++ /dev/null @@ -1,76 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backwards Error Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_CEX_AI_H -#define CPROVER_SUMMARIZER_BW_CEX_AI_H - -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include - -#include "summarizer_bw_cex.h" - -class summarizer_bw_cex_ait : public summarizer_bw_cex_baset -{ - public: - explicit summarizer_bw_cex_ait(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - function_namet _entry_function, - function_namet _error_function): - summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _entry_function,_error_function) - {} - - virtual void summarize(const function_namet &entry_function); - virtual void summarize(const exprt &_error_assertion); - - virtual property_checkert::resultt check(); - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const summaryt::call_sitet &call_site, - const exprt &postcondition, - bool context_sensitive); - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient); - - virtual void do_summary(const function_namet &function_name, - const summaryt::call_sitet &call_site, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - const exprt &postcondition, - bool context_sensitive); - - virtual exprt compute_calling_context2(const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient); - -}; - - -#endif diff --git a/src/2ls/summarizer_bw_cex_all.h b/src/2ls/summarizer_bw_cex_all.h deleted file mode 100644 index 1c81cf46a..000000000 --- a/src/2ls/summarizer_bw_cex_all.h +++ /dev/null @@ -1,73 +0,0 @@ -/*******************************************************************\ - -Module: Counterexample-based Backward Analysis All - -Author: Kumar Madhukar, Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_CEX_ALL_H -#define CPROVER_SUMMARIZER_BW_CEX_ALL_H - -#include -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include "summarizer_bw_cex.h" -#include "summarizer_bw_cex_concrete.h" -#include "summarizer_bw_cex_ai.h" -#include "summarizer_bw_cex_complete.h" - -class summarizer_bw_cex_allt : public summarizer_bw_cex_baset -{ - public: - explicit summarizer_bw_cex_allt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - incremental_solvert &_solver, - function_namet _entry_function, - function_namet _error_function): - summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _entry_function,_error_function), - summarizer_bw_cex_concrete(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _entry_function,_error_function), - summarizer_bw_cex_ai(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _entry_function,_error_function), - summarizer_bw_cex_complete(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _solver,_entry_function,_error_function), - result(property_checkert::UNKNOWN) - {} - - virtual void summarize(const exprt &_error_assertion); - - virtual void set_message_handler(message_handlert &handler) - { - summarizer_bw_cex_concrete.set_message_handler(handler); - summarizer_bw_cex_ai.set_message_handler(handler); - summarizer_bw_cex_complete.set_message_handler(handler); - messaget::set_message_handler(handler); - } - - - virtual property_checkert::resultt check(); - - protected: - summarizer_bw_cex_concretet summarizer_bw_cex_concrete; - summarizer_bw_cex_ait summarizer_bw_cex_ai; - summarizer_bw_cex_completet summarizer_bw_cex_complete; - property_checkert::resultt result; - -}; - - -#endif diff --git a/src/2ls/summarizer_bw_cex_complete.cpp b/src/2ls/summarizer_bw_cex_complete.cpp deleted file mode 100644 index 9ec87e5f1..000000000 --- a/src/2ls/summarizer_bw_cex_complete.cpp +++ /dev/null @@ -1,680 +0,0 @@ -/*******************************************************************\ - -Module: Simple Complete Counterexample-based Backward Analysis - -Author: Madhukar Kumar, Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "../solver/summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_dependency_graph.h" - -#include "summarizer_bw_cex_complete.h" - -#define REFINE_ALL - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_completet::summarize -( - const function_namet &entry_function) -{ - // no dependencies to begin with - find_symbols_sett dependency_set; - - status() << "\nSummarizing function " << entry_function << eom; - compute_summary_rec(entry_function,dependency_set,-1); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::summarize() - - Inputs: - - Outputs: - - Purpose: summarize backwards from given assertion - -\*******************************************************************/ - -void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) -{ - status() << "\nBackward error analysis (complete)..." << eom; - error_assertion = _error_assertion; - ssa_inliner.rename(error_assertion,-1,false); - /* - std::cout << "error assertion: " - << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) - << "\n"; - */ - summarize(entry_function); -} - - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -find_symbols_sett summarizer_bw_cex_completet::inline_summaries -( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter) -{ - local_SSAt &SSA = ssa_db.get(function_name); - ssa_local_unwindert &ssa_local_unwinder = ssa_unwinder.get(function_name); - ssa_local_unwinder.compute_loop_continuation_conditions(); - //add enabling expressions - exprt enable_exprs = SSA.get_enabling_exprs(); - ssa_inliner.rename(enable_exprs, counter); - - solver << enable_exprs; - - // assumptions must hold - for(local_SSAt::nodest::const_iterator - n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) - for(local_SSAt::nodet::assumptionst::const_iterator - a_it = n_it->assumptions.begin(); a_it != n_it->assumptions.end(); ++a_it) - { - exprt assumption = *a_it; - ssa_inliner.rename(assumption, counter); - solver << assumption; - } - -#ifdef REFINE_ALL - //TODO: let's just put all loops into the reason - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); ++n_it) - if (n_it->loophead != SSA.nodes.end()) - reason[function_name].loops.insert(n_it->loophead->location); -#endif - - ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); - - struct worknodet{ - int node_index; - find_symbols_sett dependency_set; - }; - - worknodet start_node; - start_node.node_index = 0; - start_node.dependency_set = dependency_set; - - typedef std::list worklistt; - worklistt worklist, work_waitlist; - std::vector covered_nodes; - - worklist.push_back(start_node); - - while(!worklist.empty()){ - - /* - std::cout << "worklist: "; - for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - - std::cout << "\t waitlist: "; - for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - */ - - worknodet &worknode = worklist.front(); - const ssa_dependency_grapht::depnodet &depnode = - ssa_depgraph.depnodes_map[worknode.node_index]; - - //std::cout << "working node: " << function_name << ": " << worknode.node_index << "\n"; - ///////////////////////////////////////////////////////////////////////////////////// - //std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - /* - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n\n\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - - // return if the top most node is reached - if(worknode.node_index == ssa_depgraph.top_node_index) - return worknode.dependency_set; - - // modify worknode_dependency_set if the node is an assertion - if(depnode.is_assertion == true){ - - //std::cout << "\t\t an assertion node\n"; - for(find_symbols_sett::const_iterator d_it = depnode.used_symbols.begin(); - d_it != depnode.used_symbols.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - - - } - - // if this is a function call - if(depnode.is_function_call == true){ - //std::cout << "fcall: working node: " << function_name << ": " << worknode.node_index << "\n"; - irep_idt fname = - to_symbol_expr((to_function_application_expr(depnode.node_info)).function()).get_identifier(); - - find_symbols_sett renamed_dependencies; - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt renamed_id = *d_it; - // detach the '@' symbol if there - ssa_inliner.rename(renamed_id, - depnode.rename_counter, false); - renamed_dependencies.insert(renamed_id); - } - - worknode.dependency_set = renamed_dependencies; - - if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(depnode.guard, - guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - } - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - -#ifdef REFINE_ALL - //TODO: just put all function calls into reason - reason[function_name].functions.insert(depnode.location); -#endif - - //recurse - worknode.dependency_set = - compute_summary_rec(fname,worknode.dependency_set, - depnode.rename_counter); - - - renamed_dependencies.clear(); - - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt renamed_id = *d_it; - // attach the '@' symbol if not already there - ssa_inliner.rename(renamed_id, - depnode.rename_counter, true); - renamed_dependencies.insert(renamed_id); - } - - worknode.dependency_set = renamed_dependencies; - - if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(depnode.guard, guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - } - - } - - // if the dependency set is non-empty - if(!worknode.dependency_set.empty()) - { - exprt worknode_info = depnode.node_info; - - bool is_error_assertion = false; - if(depnode.is_assertion) - { -#if 0 - std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) << std::endl; - std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) << std::endl; -#endif - assert(error_assertion.id()==ID_not); - if(error_assertion.op0().id()!=ID_and) - is_error_assertion = (worknode_info == error_assertion.op0()); - else - forall_operands(a_it, error_assertion.op0()) - if(worknode_info == *a_it) - { - is_error_assertion = true; - break; - } - } - - if(worknode.node_index != 0){ - if(!(depnode.is_function_call)){ - if(!depnode.is_assertion || is_error_assertion) - { - /* - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t original info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - */ - ssa_inliner.rename(worknode_info, counter); -#if 0 - std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t renamed info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; -#endif - - if(depnode.is_assertion) //keep for later - renamed_error_assertion.push_back(worknode_info); - else - solver << worknode_info; - - if(depnode.is_loop) - { - //loop head selects - exprt lsguard = depnode.guard; - ssa_inliner.rename(lsguard, counter); - loophead_selects.push_back(lsguard); - //solver.solver->set_frozen(solver.convert(lsguard)); - add_reason_to_check(lsguard,function_name,false,depnode.location); - - //loop continuations - exprt::operandst local_loop_continues; - get_loop_continues(SSA, ssa_local_unwinder, depnode.location, - local_loop_continues); - for(size_t i=0; inode_index == pred_node_index){ - - dependencies_merged = true; - - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ - if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ - (w_it->dependency_set).insert(*a_it); - } - } - } - break; - } - } - - if(dependencies_merged == false){ - worknodet new_worknode; - new_worknode.node_index = pred_node_index; - - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) - new_worknode.dependency_set.insert(*a_it); - } - - work_waitlist.push_back(new_worknode); - } - } - -#if 0 - std::cout << function_name << ": worklist: "; - for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - - - std::cout << "\t" << function_name << ": waitlist: "; - for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; -#endif - - covered_nodes.push_back(worknode.node_index); - worklist.pop_front(); - -#if 0 - std::cout << function_name << ": covered : "; - for(int l = 0; l < covered_nodes.size(); l++){ - std::cout << covered_nodes[l] << " "; - } - std::cout << "\n"; -#endif - - worklistt iterate_work_waitlist = work_waitlist; - work_waitlist.clear(); - - for(worklistt::const_iterator w_it = iterate_work_waitlist.begin(); w_it != iterate_work_waitlist.end(); w_it++){ - worknodet waitlisted_worknode = *w_it; - - bool uncovered_successor = false; - - std::vector &waitlisted_worknode_successors = - ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; - - for(unsigned i = 0; i < waitlisted_worknode_successors.size(); i++){ - bool check_covered = false; - for(unsigned j = 0; j < covered_nodes.size(); j++){ - if(waitlisted_worknode_successors[i] == covered_nodes[j]){ - check_covered = true; - break; - } - } - if(!check_covered){ -#if 0 - std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " - << waitlisted_worknode_successors[i] << "\n"; -#endif - uncovered_successor = true; - break; - } - } - - if(!uncovered_successor){ - worklist.push_back(waitlisted_worknode); - } - else{ - work_waitlist.push_back(waitlisted_worknode); - } - - } - } - - /* the following code is to stop a warning; this function must - return from the first if-condition inside the while loop */ - std::cout << "check graph of the function: " << function_name << "\n"; - assert(false); - return dependency_set; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -find_symbols_sett summarizer_bw_cex_completet::compute_summary_rec -( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter) -{ - // recursively compute summaries for function calls - return inline_summaries(function_name,dependency_set,counter); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::check() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summarizer_bw_cex_completet::check() -{ - assert(!renamed_error_assertion.empty()); //otherwise the error assertion was not renamed - -//add loophead selects -#ifdef REFINE_ALL - solver.new_context(); - solver << not_exprt(conjunction(renamed_error_assertion)); - solver << conjunction(loophead_selects); -#else - formula.push_back(solver.solver->convert( - not_exprt(conjunction(renamed_error_assertion)))); - solver.solver->set_assumptions(formula); -#endif - - solver_calls++; // for statistics - if(solver() == decision_proceduret::D_SATISFIABLE) - { - //pop_context() not necessary - return property_checkert::FAIL; - } -#ifndef REFINE_ALL - else - { - const namespacet &ns = ssa_db.get(entry_function).ns; - //get reasons for spuriousness - for(unsigned i=0; iis_in_conflict(formula[i])) - { - debug() << "is_in_conflict: " << from_expr(ns, "", formula_expr[i]) << eom; - const reason_to_checkt &r = reasons_to_check[i]; - if(r.is_function) - reason[r.function_name].functions.insert(r.info); - else - reason[r.function_name].loops.insert(r.info); - } - } - bvt assumptions; - solver.solver->set_assumptions(assumptions); - for(unsigned i=0; i " << function_name - << " ; dependency_set -> "; - for(find_symbols_sett::iterator d_it = dependency_set.begin(); - d_it != dependency_set.end(); d_it++){ - std::cout << *d_it << ", "; - } - std::cout << "\n"; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_completet::add_reason_to_check - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_completet::add_reason_to_check( - const exprt &expr, - const function_namet &function_name, - bool is_function, - const local_SSAt::locationt & info) -{ - literalt l = solver.solver->convert(expr); - if(l.is_false()) - { - literalt dummy = solver.solver->convert(symbol_exprt("goto_symex::\\dummy", - bool_typet())); - formula.push_back(dummy); - formula.push_back(!dummy); - } - else if(!l.is_true()) - { - formula.push_back(l); - formula_expr.push_back(expr); - reasons_to_check.push_back(reason_to_checkt()); - reason_to_checkt &r = reasons_to_check.back(); - r.function_name = function_name; - r.info = info; - r.is_function = is_function; - } -} diff --git a/src/2ls/summarizer_bw_cex_complete.h b/src/2ls/summarizer_bw_cex_complete.h deleted file mode 100644 index c1ecf12e1..000000000 --- a/src/2ls/summarizer_bw_cex_complete.h +++ /dev/null @@ -1,81 +0,0 @@ -/*******************************************************************\ - -Module: Simple Complete Counterexample-based Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_CEX_COMPLETE_H -#define CPROVER_SUMMARIZER_BW_CEX_COMPLETE_H - -#include -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include - -#include "summarizer_bw_cex.h" - -class summarizer_bw_cex_completet : public summarizer_bw_cex_baset -{ - public: - explicit summarizer_bw_cex_completet(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - incremental_solvert &_solver, - function_namet _entry_function, - function_namet _error_function): - summarizer_bw_cex_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner, - _entry_function,_error_function), - solver(_solver) - {} - - virtual void summarize(const function_namet &entry_function); - virtual void summarize(const exprt &_error_assertion); - - virtual property_checkert::resultt check(); - - protected: - incremental_solvert &solver; - bvt formula; //for UNSAT core - exprt::operandst formula_expr; //for debugging - exprt::operandst loophead_selects; - exprt::operandst loop_continues; - exprt::operandst renamed_error_assertion; - - struct reason_to_checkt { - function_namet function_name; - bool is_function; - local_SSAt::locationt info; - }; - std::vector reasons_to_check; - void add_reason_to_check( - const exprt &expr, - const function_namet &function_name, - bool is_function, - const local_SSAt::locationt & info); - - virtual find_symbols_sett inline_summaries( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter); - - virtual find_symbols_sett compute_summary_rec( - const function_namet &function_name, - find_symbols_sett &dependency_set, - int counter); - virtual void debug_print( - const function_namet &function_name, - find_symbols_sett &dependency_set); - -}; - - -#endif diff --git a/src/2ls/summarizer_bw_cex_concrete.cpp b/src/2ls/summarizer_bw_cex_concrete.cpp deleted file mode 100644 index 2fda14c0e..000000000 --- a/src/2ls/summarizer_bw_cex_concrete.cpp +++ /dev/null @@ -1,637 +0,0 @@ -/*******************************************************************\ - -Module: Simple Counterexample-based Backward Analysis - -Author: Kumar Madhukar, Peter Schrammel - -\*******************************************************************/ - -//#define OPT_11 // simplify before pushing to solver -#define OPT_12 // collect, conjunct, simplify and then push to the solver - -//#define OPT_2 // a fresh solver each time - -//TODO: a bug in the fresh solver case; does not compute -//calling contexts (see struct tests in regression) - -//#define DEBUG - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw_cex_concrete.h" -#include "../solver/summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_concretet::summarize(const function_namet &function_name) -{ - exprt postcondition = true_exprt(); //initial calling context - - status() << "\nSummarizing function " << function_name << eom; - compute_summary_rec(function_name,summaryt::entry_call_site, - postcondition,true); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::summarize() - - Inputs: - - Outputs: - - Purpose: summarize backwards from given assertion - -\*******************************************************************/ - -void summarizer_bw_cex_concretet::summarize(const exprt &_error_assertion) -{ - status() << "\nBackward error analysis (concrete)..." << eom; - error_assertion = _error_assertion; - - summarize(entry_function); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::check() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summarizer_bw_cex_concretet::check() -{ - property_checkert::resultt result = property_checkert::FAIL; - if(!summary_db.exists(entry_function)) - { - result = property_checkert::UNKNOWN; - } - else - { - const summaryt &summary = summary_db.get(entry_function); - if(summary.error_summaries.empty() || - summary.error_summaries.begin()->second.is_nil() || - summary.error_summaries.begin()->second.is_true()) - result = property_checkert::UNKNOWN; - } - - //we are only complete if everything was inlined - if(result == property_checkert::UNKNOWN && - options.get_bool_option("inline")) - { - incremental_solvert &solver = ssa_db.get_solver(entry_function); - const local_SSAt &ssa = ssa_db.get(entry_function); - ssa_local_unwindert &ssa_local_unwinder = - ssa_unwinder.get(entry_function); - exprt::operandst loophead_selects; - exprt::operandst loop_continues; - get_loophead_selects(ssa, ssa_local_unwinder, - *solver.solver, loophead_selects); - get_loop_continues(ssa, ssa_local_unwinder, loop_continues); - //check whether loops have been fully unwound - bool fully_unwound = - is_fully_unwound(loop_continues,loophead_selects,solver); - status() << "Loops " << (fully_unwound ? "" : "not ") - << "fully unwound" << eom; - - if(fully_unwound) - result = property_checkert::PASS; - } - - return result; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_concretet::compute_summary_rec( - const function_namet &function_name, - const summaryt::call_sitet &call_site, - const exprt &_postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - //TODO: let's just put all loops into the reason - for(local_SSAt::nodest::iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); ++n_it) - if (n_it->loophead != SSA.nodes.end()) - reason[function_name].loops.insert(n_it->loophead->location); - - summaryt summary; - if(summary_db.exists(function_name)) - summary = summary_db.get(function_name); - else - { - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.nondets = SSA.nondets; - } - - // insert assertion - exprt end_guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - exprt postcondition = implies_exprt(end_guard,_postcondition); - if(function_name == error_function) - { - postcondition = and_exprt(postcondition,not_exprt(error_assertion)); - } - - summary.bw_postcondition = _postcondition; - -#if 0 - debug() << "Postcondition: " << from_expr(SSA.ns, "", postcondition) << eom; -#endif - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,summary, - postcondition,context_sensitive, - true); - - status() << "Analyzing function " << function_name << eom; - - do_summary(function_name,call_site,SSA,summary,summary,postcondition,context_sensitive); - - if(function_name == error_function) - summary.has_assertion = true; - - summary_db.set(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - debug() << out.str() << eom; - } - -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_concretet::do_summary( - const function_namet &function_name, - const summaryt::call_sitet &call_site, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - const exprt &postcondition, - bool context_sensitive) -{ - status() << "Computing error summary" << eom; - - // solver - -#ifdef OPT_2 - incremental_solvert* fresh_solver = - incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); - incremental_solvert &solver = (*fresh_solver); - SSA.unmark_nodes(); - exprt::operandst store; -#else - incremental_solvert &solver = ssa_db.get_solver(function_name); -#endif - - solver.set_message_handler(get_message_handler()); - - //ENHANCE: we could reuse the solver state, but it's difficult - // (the function maybe called several times) - exprt::operandst c; - -#ifdef OPT_12 - exprt::operandst store; -#endif - - //add forward information if available - if(!old_summary.fw_precondition.is_nil()) - c.push_back(old_summary.fw_precondition); - if(!old_summary.fw_invariant.is_nil()) - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - - exprt::operandst assert_postcond, noassert_postcond; - // add error summaries for function calls - bool assertion_flag; - assertion_flag = ssa_inliner.get_summaries(SSA,call_site,false,assert_postcond,noassert_postcond,c); //backward summaries - assert_postcond.push_back(postcondition); //context - - //add nondet variables from callees to summary.nondets - std::set summary_vars; - find_symbols(conjunction(assert_postcond),summary_vars); - for(std::set::const_iterator it = summary_vars.begin(); - it != summary_vars.end(); ++it) - if(it->id()==ID_nondet_symbol) - summary.nondets.insert(*it); - -#ifdef DEBUG - std::cout << "Assert Summary: " << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; - std::cout << "Noassert Summary: " << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; -#endif - - c.push_back(not_exprt(conjunction(assert_postcond))); - c.push_back(not_exprt(disjunction(noassert_postcond))); - -#ifdef DEBUG - debug() << "Backward summaries: " << - from_expr(SSA.ns, "", simplify_expr(conjunction(c),SSA.ns)) << eom; -#endif - -#ifdef OPT_12 - store << SSA; -#else -#ifdef OPT_2 - store << SSA; -#else - solver << SSA; -#endif -#endif - -#ifndef OPT_2 - solver.new_context(); -#endif - - // assumptions must hold - for(local_SSAt::nodest::const_iterator - n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); - ++n_it) - for(local_SSAt::nodet::assumptionst::const_iterator - a_it = n_it->assumptions.begin(); - a_it != n_it->assumptions.end(); - ++a_it) - { - -#ifdef OPT_11 - solver << simplify_expr(*a_it, SSA.ns); -#else -#ifdef OPT_12 - store.push_back(*a_it); -#else -#ifdef OPT_2 - store.push_back(*a_it); -#else - solver << *a_it; -#endif -#endif -#endif - - } - -#ifdef OPT_12 - store.push_back(SSA.get_enabling_exprs()); -#else -#ifdef OPT_2 - store.push_back(SSA.get_enabling_exprs()); -#else - solver << SSA.get_enabling_exprs(); -#endif -#endif - -#ifdef OPT_11 - solver << simplify_expr(conjunction(c), SSA.ns); -#else -#ifdef OPT_12 - store.push_back(conjunction(c)); -#else -#ifdef OPT_2 - store.push_back(conjunction(c)); -#else - solver << conjunction(c); -#endif -#endif -#endif - - exprt::operandst loophead_selects; - get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); - -#ifdef OPT_11 - solver << simplify_expr(conjunction(loophead_selects), SSA.ns); -#else -#ifdef OPT_12 - store.push_back(conjunction(loophead_selects)); -#else -#ifdef OPT_2 - store.push_back(conjunction(loophead_selects)); -#else - solver << conjunction(loophead_selects); -#endif -#endif -#endif - -#ifdef OPT_12 -#ifdef DEBUG - std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; -#endif - solver << simplify_expr(conjunction(store), SSA.ns); -#endif -#ifdef OPT_2 -#ifdef DEBUG - std::cout << "\n\n\n pushing to the solver in do_summary:" << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) << "\n\n\n"; -#endif - solver << simplify_expr(conjunction(store), SSA.ns); -#endif - - //statistics - solver_calls++; - - //solve - if(solver() == decision_proceduret::D_UNSATISFIABLE) - { - summary.error_summaries[call_site] = true_exprt(); //TODO: this is likely to be incomplete - summary.has_assertion = assertion_flag; -#ifndef OPT_2 - solver.pop_context(); -#endif - - return; - } - - //build error summary and add to summary - exprt::operandst var_values; - - for(local_SSAt::var_listt::const_iterator it = SSA.params.begin(); - it != SSA.params.end(); it++){ - exprt summ_value = solver.get(*it); - if(!summ_value.is_nil()) - var_values.push_back(equal_exprt(*it, summ_value)); - } - - for(local_SSAt::var_sett::const_iterator it = SSA.globals_in.begin(); - it != SSA.globals_in.end(); it++){ - exprt summ_value = solver.get(*it); - if(!summ_value.is_nil()) - var_values.push_back(equal_exprt(*it, summ_value)); - } - - for(local_SSAt::var_sett::const_iterator it = SSA.globals_out.begin(); - it != SSA.globals_out.end(); it++){ - exprt summ_value = solver.get(*it); - if(!summ_value.is_nil()) - var_values.push_back(equal_exprt(*it, summ_value)); - } - - for(std::set::const_iterator it = summary.nondets.begin(); - it != summary.nondets.end(); it++){ - exprt summ_value = solver.get(*it); - if(!summ_value.is_nil()) - var_values.push_back(equal_exprt(*it, summ_value)); - } - - summary.error_summaries[call_site] = not_exprt(conjunction(var_values)); - summary.has_assertion = assertion_flag; - -#ifndef OPT_2 - solver.pop_context(); -#endif - -#ifdef OPT_2 - delete fresh_solver; -#endif - -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_concretet::inline_summaries( - const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); - n_it != SSA.nodes.begin(); ) - { - n_it--; - - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - - exprt postcondition_call = true_exprt(); - postcondition_call = compute_calling_context2( - function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); - - //TODO: just put all function calls into reason - reason[function_name].functions.insert(n_it->location); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,summaryt::call_sitet(n_it->location), - postcondition_call,context_sensitive); - } - } -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_concretet::compute_calling_context2() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bw_cex_concretet::compute_calling_context2( - const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Computing calling context for function " << fname << eom; - - // solver - -#ifdef OPT_2 - incremental_solvert* fresh_solver = - incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); - incremental_solvert &solver = (*fresh_solver); -#else - incremental_solvert &solver = ssa_db.get_solver(function_name); -#endif - - solver.set_message_handler(get_message_handler()); - - // collect globals at call site - std::map - cs_globals_out; - SSA.get_globals(n_it->location,cs_globals_out[f_it],false); - - exprt::operandst c; - -#ifdef OPT_12 - exprt::operandst store; -#endif - - // add forward information if available - if(!old_summary.fw_precondition.is_nil()) - c.push_back(old_summary.fw_precondition); - if(!old_summary.fw_invariant.is_nil()) - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - - exprt::operandst assert_postcond, noassert_postcond; - // add error summaries for function calls - ssa_inliner.get_summaries(SSA,summaryt::call_sitet(n_it->location),false,assert_postcond,noassert_postcond,c); //backward summaries - assert_postcond.push_back(postcondition); //context - c.push_back(not_exprt(conjunction(assert_postcond))); - c.push_back(not_exprt(disjunction(noassert_postcond))); - -#ifdef OPT_12 - store << SSA; -#else - solver << SSA; -#endif - - solver.new_context(); - -#ifdef OPT_12 - store.push_back(SSA.get_enabling_exprs()); -#else - solver << SSA.get_enabling_exprs(); -#endif - -#ifdef OPT_11 - solver << simplify_expr(conjunction(c), SSA.ns); -#else -#ifdef OPT_12 - store.push_back(conjunction(c)); -#else - solver << conjunction(c); -#endif -#endif - - exprt::operandst loophead_selects; - get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); - -#ifdef OPT_11 - solver << simplify_expr(conjunction(loophead_selects), SSA.ns); -#else -#ifdef OPT_12 - store.push_back(conjunction(loophead_selects)); -#else - solver << conjunction(loophead_selects); -#endif -#endif - -#ifdef OPT_12 -#ifdef DEBUG - std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; -#endif - solver << simplify_expr(conjunction(store), SSA.ns); -#endif - - - // build postcondition - exprt postcondition_call; - - if(solver() != decision_proceduret::D_SATISFIABLE) - { - postcondition_call = true_exprt(); //TODO: this is likely to be incomplete - solver.pop_context(); - return postcondition_call; - } - - bool result = solver()==decision_proceduret::D_SATISFIABLE; - assert(result); - - exprt::operandst postcond_values; - for(local_SSAt::var_sett::const_iterator it = cs_globals_out[f_it].begin(); - it != cs_globals_out[f_it].end(); it++) - { - exprt postc_value = solver.get(*it); - postcond_values.push_back(equal_exprt(*it, postc_value)); - } - postcondition_call = conjunction(postcond_values); - - solver.pop_context(); - - // get callee SSA and rename - local_SSAt &fSSA = ssa_db.get(fname); - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_out[f_it],fSSA.globals_out, - postcondition_call); - - debug() << "Backward calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", postcondition_call) << eom; - - //statistics - solver_calls++; - -#ifdef OPT_2 - delete fresh_solver; -#endif - - return not_exprt(postcondition_call); -} - - diff --git a/src/2ls/summarizer_bw_cex_concrete.h b/src/2ls/summarizer_bw_cex_concrete.h deleted file mode 100644 index 49ccd3070..000000000 --- a/src/2ls/summarizer_bw_cex_concrete.h +++ /dev/null @@ -1,77 +0,0 @@ -/*******************************************************************\ - -Module: Simple Counterexample-based Backward Analysis - -Author: Kumar Madhukar, Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_CEX_CONCRETE_H -#define CPROVER_SUMMARIZER_BW_CEX_CONCRETE_H - -#include -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include - -#include "summarizer_bw_cex.h" - -class summarizer_bw_cex_concretet : public summarizer_bw_cex_baset -{ - public: - explicit summarizer_bw_cex_concretet(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - function_namet _entry_function, - function_namet _error_function): - summarizer_bw_cex_baset(_options,_summary_db,_ssa_db, - _ssa_unwinder,_ssa_inliner, - _entry_function,_error_function) - {} - - virtual void summarize(const function_namet &entry_function); - virtual void summarize(const exprt &_error_assertion); - - virtual property_checkert::resultt check(); - - protected: - virtual void compute_summary_rec(const function_namet &function_name, - const summaryt::call_sitet &call_site, - const exprt &postcondition, - bool context_sensitive); - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient); - - virtual void do_summary( - const function_namet &function_name, - const summaryt::call_sitet &call_site, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - const exprt &postcondition, - bool context_sensitive); - - virtual exprt compute_calling_context2(const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient); - -}; - - -#endif diff --git a/src/2ls/summarizer_bw_cex_wp.cpp b/src/2ls/summarizer_bw_cex_wp.cpp deleted file mode 100644 index 1883ee7a1..000000000 --- a/src/2ls/summarizer_bw_cex_wp.cpp +++ /dev/null @@ -1,643 +0,0 @@ -/*******************************************************************\ - -Module: Slicing-based WP Counterexample-based Backward Analysis - -Author: Madhukar Kumar, Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "../solver/summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_dependency_graph.h" - -#include "summarizer_bw_cex_wp.h" - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::summarize() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_wpt::summarize -( - const function_namet &entry_function) -{ - // no dependencies to begin with - find_symbols_sett dependency_set; - - status() << "\nSummarizing function " << entry_function << eom; - compute_summary_rec(entry_function,dependency_set,-1, - summaryt::entry_call_site); -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::summarize() - - Inputs: - - Outputs: - - Purpose: summarize backwards from given assertion - -\*******************************************************************/ - -void summarizer_bw_cex_wpt::summarize(const exprt &_error_assertion) -{ - status() << "\nBackward error analysis (WP)..." << eom; - error_assertion = _error_assertion; - /* - std::cout << "error assertion: " - << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) - << "\n"; - */ - summarize(entry_function); -} - - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -find_symbols_sett summarizer_bw_cex_wpt::inline_summaries -( - const function_namet &function_name, - const find_symbols_sett &dependency_set, - int counter, - exprt &error_summary) -{ - exprt::operandst slice; - - local_SSAt &SSA = ssa_db.get(function_name); - //solver << SSA.get_enabling_exprs(); - - exprt::operandst loophead_selects; - get_loophead_selects(SSA,ssa_unwinder.get(function_name),*solver.solver,loophead_selects); - exprt c = conjunction(loophead_selects); - - //std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" - // << "\t original info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; - - slice.push_back(c); - ssa_inliner.rename(c, counter); - -#if 0 - std::cout << "Solver <-- " << function_name << ": (conjunction of loophead_selects):" - << "\t renamed info ~ " << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; -#endif - - solver << c; - - ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(function_name); - - struct worknodet{ - int node_index; - find_symbols_sett dependency_set; - }; - - worknodet start_node; - start_node.node_index = 0; - start_node.dependency_set = dependency_set; - - typedef std::list worklistt; - worklistt worklist, work_waitlist; - std::vector covered_nodes; - - worklist.push_back(start_node); - - while(!worklist.empty()){ - - /* - std::cout << "worklist: "; - for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - - std::cout << "\t waitlist: "; - for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - */ - - worknodet &worknode = worklist.front(); - - //std::cout << "working node: " << function_name << ": " << worknode.node_index << "\n"; - ///////////////////////////////////////////////////////////////////////////////////// - //std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - /* - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n\n\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - - // return if the top most node is reached - if(worknode.node_index == ssa_depgraph.top_node_index) - { - find_symbols_sett vars = worknode.dependency_set; - vars.insert(dependency_set.begin(), dependency_set.end()); - error_summary = simplify_summary(SSA.ns, conjunction(slice), vars); - return worknode.dependency_set; - } - - // modify worknode_dependency_set if the node is an assertion - if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true){ - - //std::cout << "\t\t an assertion node\n"; - for(find_symbols_sett::const_iterator d_it = ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); - d_it != ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - - - } - - // if this is a function call - if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == true){ - //std::cout << "fcall: working node: " << function_name << ": " << worknode.node_index << "\n"; - irep_idt fname = - to_symbol_expr((to_function_application_expr(ssa_depgraph.depnodes_map[worknode.node_index].node_info)).function()).get_identifier(); - - find_symbols_sett renamed_dependencies; - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt renamed_id = *d_it; - // detach the '@' symbol if there - ssa_inliner.rename(renamed_id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); - renamed_dependencies.insert(renamed_id); - } - - worknode.dependency_set = renamed_dependencies; - - if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, - guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - } - - ///////////////////////////////////////////////////////////////////////////////////// - /* - std::cout << "\t size of dependency set: " << worknode.dependency_set.size() << "\n"; - - std::cout << "\t dependency set: "; - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - std::cout << *d_it; - } - std::cout << "\n"; - */ - ///////////////////////////////////////////////////////////////////////////////////// - - worknode.dependency_set = - compute_summary_rec(fname,worknode.dependency_set, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter,summaryt::call_sitet(ssa_depgraph.depnodes_map[worknode.node_index].location)); - slice.push_back(ssa_depgraph.depnodes_map[worknode.node_index].node_info); - - renamed_dependencies.clear(); - - for(find_symbols_sett::iterator d_it = worknode.dependency_set.begin(); - d_it != worknode.dependency_set.end(); d_it++){ - irep_idt renamed_id = *d_it; - // detach the '@' symbol if there - ssa_inliner.rename(renamed_id, - ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, false); - renamed_dependencies.insert(renamed_id); - } - - worknode.dependency_set = renamed_dependencies; - - if(!worknode.dependency_set.empty()){ - find_symbols_sett guard_dependencies; - find_symbols(ssa_depgraph.depnodes_map[worknode.node_index].guard, guard_dependencies); - for(find_symbols_sett::const_iterator d_it = guard_dependencies.begin(); - d_it != guard_dependencies.end(); d_it++){ - worknode.dependency_set.insert(*d_it); - } - } - - } - - // if the dependency set is non-empty - if(!worknode.dependency_set.empty()){ - exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; - if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) - worknode_info = not_exprt(worknode_info); - - if(worknode.node_index != 0){ - if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)){ - if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || - (worknode_info == error_assertion)){ - /* - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t original info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - */ - slice.push_back(worknode_info); - ssa_inliner.rename(worknode_info, counter); -#if 0 - std::cout << "Solver <-- renamed assertion: " << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; - std::cout << "Solver <-- " << function_name << ": (node) node#:" - << worknode.node_index << "\t renamed info ~ " - << from_expr((ssa_db.get(function_name)).ns, "", worknode_info) << "\n"; -#endif - solver << worknode_info; - } - } - else{ - exprt guard_binding = ssa_depgraph.depnodes_map[worknode.node_index].guard; - /* - std::cout << "Solver <-- " << function_name << ": (bind) node#:" - << worknode.node_index << "\t original info ~ " - << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; - */ - ssa_inliner.rename(guard_binding, counter); -#if 0 - std::cout << "Solver <-- " << function_name << ": (bind) node#:" - << worknode.node_index << "\t renamed info ~ " - << from_expr(ssa_db.get(function_name).ns, "", guard_binding) << "\n"; -#endif - solver << guard_binding; - slice.push_back(guard_binding); - } - } - } - - // if not a function call and the dependency set is non-empty - if((ssa_depgraph.depnodes_map[worknode.node_index].is_function_call == false) && - (!worknode.dependency_set.empty())){ - - exprt worknode_info = ssa_depgraph.depnodes_map[worknode.node_index].node_info; - if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == true) - worknode_info = not_exprt(worknode_info); - - if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion == false) || - (worknode_info == error_assertion)){ - worknode.dependency_set = - ssa_depgraph.depnodes_map[worknode.node_index].used_symbols; - } - } - - for(ssa_dependency_grapht::annotated_predecessorst::const_iterator - p_it = ssa_depgraph.depnodes_map[worknode.node_index].predecessors.begin(); - p_it != ssa_depgraph.depnodes_map[worknode.node_index].predecessors.end(); - p_it++){ - - ssa_dependency_grapht::annotated_predecessort pred = *p_it; - int pred_node_index = pred.predecessor_node_index; - find_symbols_sett pred_annotation = pred.annotation; - - bool dependencies_merged = false; - for(worklistt::iterator w_it = work_waitlist.begin(); w_it != work_waitlist.end(); w_it++){ - if(w_it->node_index == pred_node_index){ - - dependencies_merged = true; - - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()){ - if((w_it->dependency_set).find(*a_it) == (w_it->dependency_set).end()){ - (w_it->dependency_set).insert(*a_it); - } - } - } - break; - } - } - - if(dependencies_merged == false){ - worknodet new_worknode; - new_worknode.node_index = pred_node_index; - - for(find_symbols_sett::const_iterator - a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) - { - if(worknode.dependency_set.find(*a_it) != worknode.dependency_set.end()) - new_worknode.dependency_set.insert(*a_it); - } - - work_waitlist.push_back(new_worknode); - } - } - -#if 0 - std::cout << function_name << ": worklist: "; - for(worklistt::const_iterator w_it=worklist.begin(); - w_it != worklist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; - - - std::cout << "\t" << function_name << ": waitlist: "; - for(worklistt::const_iterator w_it=work_waitlist.begin(); - w_it != work_waitlist.end(); w_it++){ - std::cout << w_it->node_index << " "; - } - std::cout << "\n"; -#endif - - covered_nodes.push_back(worknode.node_index); - worklist.pop_front(); - -#if 0 - std::cout << function_name << ": covered : "; - for(int l = 0; l < covered_nodes.size(); l++){ - std::cout << covered_nodes[l] << " "; - } - std::cout << "\n"; -#endif - - worklistt iterate_work_waitlist = work_waitlist; - work_waitlist.clear(); - - for(worklistt::const_iterator w_it = iterate_work_waitlist.begin(); w_it != iterate_work_waitlist.end(); w_it++){ - worknodet waitlisted_worknode = *w_it; - - bool uncovered_successor = false; - - std::vector &waitlisted_worknode_successors = - ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; - - for(unsigned i = 0; i < waitlisted_worknode_successors.size(); i++){ - bool check_covered = false; - for(unsigned j = 0; j < covered_nodes.size(); j++){ - if(waitlisted_worknode_successors[i] == covered_nodes[j]){ - check_covered = true; - break; - } - } - if(!check_covered){ -#if 0 - std::cout << function_name << ": an uncovered successor of " << waitlisted_worknode.node_index << " : " - << waitlisted_worknode_successors[i] << "\n"; -#endif - uncovered_successor = true; - break; - } - } - - if(!uncovered_successor){ - worklist.push_back(waitlisted_worknode); - } - else{ - work_waitlist.push_back(waitlisted_worknode); - } - - } - } - - /* the following code is to stop a warning; this function must - return from the first if-condition inside the while loop */ - std::cout << "check graph of the function: " << function_name << "\n"; - assert(false); - return dependency_set; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -find_symbols_sett summarizer_bw_cex_wpt::compute_summary_rec - ( - const function_namet &function_name, - const find_symbols_sett &dependency_set, - int counter, - const summaryt::call_sitet &call_site) -{ - local_SSAt &SSA = ssa_db.get(function_name); - summaryt summary; - if(summary_db.exists(function_name)) - summary = summary_db.get(function_name); - else - { - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - } - // recursively compute summaries for function calls - find_symbols_sett new_dependency_set = - inline_summaries(function_name,dependency_set,counter, - summary.error_summaries[call_site]); - - summary_db.set(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - debug() << out.str() << eom; - } - - return new_dependency_set; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::check() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -property_checkert::resultt summarizer_bw_cex_wpt::check() -{ - solver_calls++; // for statistics - if(solver() == decision_proceduret::D_SATISFIABLE){ - //std::cout << "Solver <-- renamed info ~ SAT\n"; - return property_checkert::FAIL; - } - //std::cout << "Solver <-- renamed info ~ UNSAT\n"; - return property_checkert::UNKNOWN; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::debug_print() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bw_cex_wpt::debug_print -( - const function_namet &function_name, - find_symbols_sett &dependency_set) -{ - std::cout << "DebugInfo: function -> " << function_name - << " ; dependency_set -> "; - for(find_symbols_sett::iterator d_it = dependency_set.begin(); - d_it != dependency_set.end(); d_it++){ - std::cout << *d_it << ", "; - } - std::cout << "\n"; -} - -/*******************************************************************\ - -Function: summarizer_bw_cex_wpt::simplify_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ -void summarizer_bw_cex_wpt::simplify_summary_build_map( - replace_mapt &replace_map, const exprt &expr) -{ - if(expr.id()==ID_equal) - { - replace_map[expr.op0()] = expr.op1(); - return; - } - forall_operands(it, expr) - simplify_summary_build_map(replace_map, *it); -} -bool summarizer_bw_cex_wpt::simplify_summary_replace( - const replace_mapt &replace_map, exprt &expr) -{ - if(expr.id()==ID_function_application) - { - bool result = true; - exprt::operandst &args = to_function_application_expr(expr).arguments(); - for(size_t i=0;i -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_db.h" - -#include - -#include "summarizer_bw_cex.h" - -class summarizer_bw_cex_wpt : public summarizer_bw_cex_baset -{ - public: - explicit summarizer_bw_cex_wpt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner, - incremental_solvert &_solver, - function_namet _entry_function, - function_namet _error_function): - summarizer_bw_cex_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner, - _entry_function,_error_function), - solver(_solver) - {} - - virtual void summarize(const function_namet &entry_function); - virtual void summarize(const exprt &_error_assertion); - - virtual property_checkert::resultt check(); - - protected: - incremental_solvert &solver; - - virtual find_symbols_sett inline_summaries( - const function_namet &function_name, - const find_symbols_sett &dependency_set, - int counter, - exprt &error_summary); - - virtual find_symbols_sett compute_summary_rec( - const function_namet &function_name, - const find_symbols_sett &dependency_set, - int counter, - const summaryt::call_sitet &call_site); - virtual void debug_print( - const function_namet &function_name, - find_symbols_sett &dependency_set); - - exprt simplify_summary(const namespacet &ns, - exprt summary, - const find_symbols_sett &vars); - void simplify_summary_build_map( - replace_mapt &replace_map, const exprt &expr); - bool simplify_summary_replace( - const replace_mapt &replace_map, exprt &expr); - void simplify_summary_cleanup( - const find_symbols_sett &vars, exprt &expr); - -}; - - -#endif diff --git a/src/2ls/summary_checker_ai.cpp b/src/2ls/summary_checker_ai.cpp index 6cda09787..65259c40c 100644 --- a/src/2ls/summary_checker_ai.cpp +++ b/src/2ls/summary_checker_ai.cpp @@ -77,7 +77,10 @@ property_checkert::resultt summary_checker_ait::operator()( return property_checkert::UNKNOWN; #endif - property_checkert::resultt result=check_properties(); + property_checkert::resultt result= + options.get_bool_option("all-functions")? + check_properties(): + check_properties(goto_model.goto_functions.entry_point()); report_statistics(); return result; } diff --git a/src/2ls/summary_checker_base.cpp b/src/2ls/summary_checker_base.cpp index 1418b632d..57f5e8581 100644 --- a/src/2ls/summary_checker_base.cpp +++ b/src/2ls/summary_checker_base.cpp @@ -1,6 +1,6 @@ /*******************************************************************\ -Module: Summarizer Checker Base +Module: Summary Checker Base Author: Peter Schrammel @@ -19,38 +19,35 @@ Author: Peter Schrammel #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_build_goto_trace.h" -#include "../domains/ssa_analyzer.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/ssa_const_propagator.h" -#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHOW_CALLING_CONTEXTS +#include +#endif #include "show.h" #include "instrument_goto.h" #include "summary_checker_base.h" -#include "summarizer_bw_cex.h" -#include "summarizer_bw_cex_concrete.h" -#include "summarizer_bw_cex_ai.h" -#include "summarizer_bw_cex_complete.h" -#include "summarizer_bw_cex_wp.h" -#include "summarizer_bw_cex_all.h" - -#include "../solver/summarizer_fw.h" -#include "../solver/summarizer_fw_term.h" -#include "../solver/summarizer_bw.h" -#include "../solver/summarizer_bw_term.h" - -#ifdef SHOW_CALLING_CONTEXTS -#include "summarizer_fw_contexts.h" -#endif - /*******************************************************************\ -Function: summary_checker_baset::SSA_dependency_graphs +Function: summary_checker_baset::SSA_functions Inputs: @@ -60,60 +57,22 @@ Function: summary_checker_baset::SSA_dependency_graphs \*******************************************************************/ -void summary_checker_baset::SSA_dependency_graphs( - const goto_modelt &goto_model, +void summary_checker_baset::SSA_functions( + const goto_modelt &goto_model, const namespacet &ns) { - // compute dependency graph for all the functions - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; - - status() << "Computing dependency graph of " << f_it->first << messaget::eom; - - //ssa_db.depgraph_create(f_it->first, ns, ssa_inliner); - - if(entry_function == f_it->first) - ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, true); - else - ssa_db.depgraph_create(f_it->first, ns, ssa_inliner, false); // change to true if all functions are to be treated equal - -#if 0 - ssa_dependency_grapht &ssa_depgraph = ssa_db.get_depgraph(f_it->first); - ssa_depgraph.output(debug()); debug() << eom; - std::cout << "output SSA for function: " << f_it->first << "\n"; - ssa_db.get(f_it->first).output_verbose(std::cout); -#endif - } -} - -/*******************************************************************\ - -Function: summary_checker_baset::SSA_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_checker_baset::SSA_functions(const goto_modelt &goto_model, const namespacet &ns) -{ - entry_function = goto_model.goto_functions.entry_point(); - // compute SSA for all the functions forall_goto_functions(f_it, goto_model.goto_functions) { - if(!f_it->second.body_available()) continue; - if(has_prefix(id2string(f_it->first),TEMPLATE_DECL)) continue; + if(!f_it->second.body_available()) + continue; + if(has_prefix(id2string(f_it->first), TEMPLATE_DECL)) + continue; status() << "Computing SSA of " << f_it->first << messaget::eom; - + ssa_db.create(f_it->first, f_it->second, ns); - local_SSAt &SSA = ssa_db.get(f_it->first); - + local_SSAt &SSA=ssa_db.get(f_it->first); + // simplify, if requested if(simplify) { @@ -140,33 +99,34 @@ Function: summary_checker_baset::summarize \*******************************************************************/ -void summary_checker_baset::summarize(const goto_modelt &goto_model, - bool forward, - bool termination) -{ - summarizer_baset *summarizer = NULL; +void summary_checker_baset::summarize( + const goto_modelt &goto_model, + bool forward, + bool termination) +{ + summarizer_baset *summarizer=nullptr; #ifdef SHOW_CALLING_CONTEXTS if(options.get_bool_option("show-calling-contexts")) - summarizer = new summarizer_fw_contextst( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); - else + summarizer=new summarizer_fw_contextst( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); + else // NOLINT(*) #endif { if(forward && !termination) - summarizer = new summarizer_fwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + summarizer=new summarizer_fwt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); if(forward && termination) - summarizer = new summarizer_fw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + summarizer=new summarizer_fw_termt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); if(!forward && !termination) - summarizer = new summarizer_bwt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + summarizer=new summarizer_bwt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); if(!forward && termination) - summarizer = new summarizer_bw_termt( - options,summary_db,ssa_db,ssa_unwinder,ssa_inliner); + summarizer=new summarizer_bw_termt( + options, summary_db, ssa_db, ssa_unwinder, ssa_inliner); } - assert(summarizer != NULL); + assert(summarizer!=nullptr); summarizer->set_message_handler(get_message_handler()); @@ -176,11 +136,11 @@ void summary_checker_baset::summarize(const goto_modelt &goto_model, else summarizer->summarize(goto_model.goto_functions.entry_point()); - //statistics - solver_instances += summarizer->get_number_of_solver_instances(); - solver_calls += summarizer->get_number_of_solver_calls(); - summaries_used += summarizer->get_number_of_summaries_used(); - termargs_computed += summarizer->get_number_of_termargs_computed(); + // statistics + solver_instances+=summarizer->get_number_of_solver_instances(); + solver_calls+=summarizer->get_number_of_solver_calls(); + summaries_used+=summarizer->get_number_of_summaries_used(); + termargs_computed+=summarizer->get_number_of_termargs_computed(); delete summarizer; } @@ -189,10 +149,10 @@ void summary_checker_baset::summarize(const goto_modelt &goto_model, Function: summary_checker_baset::check_properties - Inputs: function_name != nil + Inputs: function_name!=nil checks all functions in the call graph from the entry point - else - checks all functions + else + checks all functions Outputs: @@ -206,41 +166,51 @@ summary_checker_baset::resultt summary_checker_baset::check_properties() return check_properties("", "", seen_function_calls, false); } +summary_checker_baset::resultt summary_checker_baset::check_properties(irep_idt entry_function) +{ + std::set seen_function_calls; + return check_properties(entry_function, entry_function, seen_function_calls, false); +} + summary_checker_baset::resultt summary_checker_baset::check_properties( - irep_idt function_name, - irep_idt entry_function, + irep_idt function_name, + irep_idt entry_function, std::set seen_function_calls, bool is_inlined) { if(function_name!="") { - ssa_dbt::functionst::const_iterator f_it = + ssa_dbt::functionst::const_iterator f_it= ssa_db.functions().find(function_name); - assert(f_it != ssa_db.functions().end()); - local_SSAt &SSA = *f_it->second; + assert(f_it!=ssa_db.functions().end()); + local_SSAt &SSA=*f_it->second; // call recursively for all function calls first - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); ++n_it) + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); ++n_it) { - for(local_SSAt::nodet::function_callst::const_iterator ff_it = + for(local_SSAt::nodet::function_callst::const_iterator ff_it= n_it->function_calls.begin(); - ff_it != n_it->function_calls.end(); ff_it++) + ff_it!=n_it->function_calls.end(); ff_it++) { - assert(ff_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(ff_it->function()).get_identifier(); - - //ENHANCE?: can the return value be exploited? + assert(ff_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(ff_it->function()).get_identifier(); + + // ENHANCE?: can the return value be exploited? if(ssa_db.functions().find(fname)!=ssa_db.functions().end() && - (!summary_db.exists(fname) || + (!summary_db.exists(fname) || summary_db.get(fname).bw_transformer.is_nil())) { #if 0 debug() << "Checking call " << fname << messaget::eom; #endif - if(seen_function_calls.find(fname) == seen_function_calls.end()){ + if(seen_function_calls.find(fname)==seen_function_calls.end()) + { seen_function_calls.insert(fname); - check_properties(fname, entry_function, seen_function_calls, + check_properties( + fname, + entry_function, + seen_function_calls, n_it->function_calls_inlined); } } @@ -249,35 +219,36 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( if(!is_inlined) { - //now check function itself + // now check function itself status() << "Checking properties of " << f_it->first << messaget::eom; check_properties(f_it, entry_function); } } else // check all the functions { - for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) { status() << "Checking properties of " << f_it->first << messaget::eom; #if 0 - //for debugging - show_ssa_symbols(*f_it->second,std::cerr); + // for debugging + show_ssa_symbols(*f_it->second, std::cerr); #endif - check_properties(f_it); + check_properties(f_it, f_it->first); - if(options.get_bool_option("show-invariants")) + if(options.get_bool_option("show-invariants")) { - if(!summary_db.exists(f_it->first)) continue; - show_invariants(*(f_it->second),summary_db.get(f_it->first),result()); + if(!summary_db.exists(f_it->first)) + continue; + show_invariants(*(f_it->second), summary_db.get(f_it->first), result()); result() << eom; } } } - summary_checker_baset::resultt result = property_checkert::PASS; + summary_checker_baset::resultt result=property_checkert::PASS; if(function_name=="" || function_name==entry_function) { // determine overall status @@ -287,10 +258,10 @@ summary_checker_baset::resultt summary_checker_baset::check_properties( if(p_it->second.result==FAIL) return property_checkert::FAIL; if(p_it->second.result==UNKNOWN) - result = property_checkert::UNKNOWN; + result=property_checkert::UNKNOWN; } } - + return result; } @@ -302,7 +273,7 @@ Function: summary_checker_baset::check_properties Outputs: - Purpose: + Purpose: \*******************************************************************/ @@ -310,148 +281,190 @@ void summary_checker_baset::check_properties( const ssa_dbt::functionst::const_iterator f_it, irep_idt entry_function) { - unwindable_local_SSAt &SSA = *f_it->second; + unwindable_local_SSAt &SSA=*f_it->second; - //check whether function has assertions + // check whether function has assertions if(!has_assertion(f_it->first)) return; - bool all_properties = options.get_bool_option("all-properties"); - bool build_error_trace = options.get_bool_option("show-trace"); + bool all_properties=options.get_bool_option("all-properties"); + bool build_error_trace= + options.get_bool_option("show-trace") || + options.get_option("graphml-witness")!="" || + options.get_option("json-cex")!=""; + + SSA.output_verbose(debug()); debug() << eom; - SSA.output(debug()); debug() << eom; - // incremental version // solver - incremental_solvert &solver = ssa_db.get_solver(f_it->first); + incremental_solvert &solver=ssa_db.get_solver(f_it->first); solver.set_message_handler(get_message_handler()); -#if 0 - // TEST ssa_const_propagation - if(options.get_bool_option("ssa-propagation")) - { - ssa_const_propagatort ssa_const_propagator; - std::list c; - ssa_const_propagator(c,SSA); - solver << c; - debug() << "SSA const propagation: " << eom; - for(std::list::iterator it = c.begin(); - it!=c.end(); it++) - debug() << " " << from_expr(SSA.ns,"",*it) << eom; - } -#endif - // give SSA to solver solver << SSA; SSA.mark_nodes(); solver.new_context(); - exprt enabling_expr = SSA.get_enabling_exprs(); + exprt enabling_expr=SSA.get_enabling_exprs(); solver << enabling_expr; // invariant, calling contexts if(summary_db.exists(f_it->first)) { - const summaryt &summary = summary_db.get(f_it->first); - if(!summary.fw_invariant.is_nil()) - solver << summary.fw_invariant; - if(!summary.fw_precondition.is_nil()) - solver << summary.fw_precondition; + if(!summary_db.get(f_it->first).fw_invariant.is_nil()) + solver << summary_db.get(f_it->first).fw_invariant; + if(!summary_db.get(f_it->first).fw_precondition.is_nil()) + solver << summary_db.get(f_it->first).fw_precondition; } - //callee summaries and inlined functions - ssa_inlinert::assertion_mapt assertion_map; - solver << ssa_inliner.get_summaries(SSA, assertion_map); + // callee summaries + solver << ssa_inliner.get_summaries(SSA); - //spuriousness checkers - summarizer_bw_cex_baset *summarizer_bw_cex = NULL; - incremental_solvert* cex_complete_solver = - incremental_solvert::allocate(SSA.ns, - options.get_bool_option("refine")); + // spuriousness checkers + summarizer_bw_cex_baset *summarizer_bw_cex=nullptr; + incremental_solvert *cex_complete_solver= + incremental_solvert::allocate( + SSA.ns, + options.get_bool_option("refine")); #if 1 cex_complete_solver->set_message_handler(get_message_handler()); #endif - if(options.get_bool_option("inline") || - options.get_option("spurious-check") == "concrete") - { - summarizer_bw_cex = new summarizer_bw_cex_concretet( - options,summary_db,ssa_db, - ssa_unwinder,ssa_inliner, - entry_function,f_it->first); - } - else if(options.get_option("spurious-check") == "abstract") + if(options.get_option("spurious-check")=="abstract") { - summarizer_bw_cex = new summarizer_bw_cex_ait( - options,summary_db,ssa_db, - ssa_unwinder,ssa_inliner, - entry_function,f_it->first); + summarizer_bw_cex=new summarizer_bw_cex_ait( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + entry_function, + f_it->first); } - else if(options.get_option("spurious-check") == "complete") + else if(options.get_option("spurious-check")=="complete") { - summarizer_bw_cex = new summarizer_bw_cex_completet( - options,summary_db,ssa_db, - ssa_unwinder,ssa_inliner,*cex_complete_solver, - entry_function,f_it->first); + summarizer_bw_cex=new summarizer_bw_cex_completet( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); } - else if(options.get_option("spurious-check") == "wp") + else if(options.get_option("spurious-check")=="wp") { - summarizer_bw_cex = new summarizer_bw_cex_wpt( - options,summary_db,ssa_db, - ssa_unwinder,ssa_inliner,*cex_complete_solver, - entry_function,f_it->first); + summarizer_bw_cex=new summarizer_bw_cex_wpt( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); } - else if(options.get_option("spurious-check") == "all") + else if(options.get_option("spurious-check")=="all") { - summarizer_bw_cex = new summarizer_bw_cex_allt( - options,summary_db,ssa_db, - ssa_unwinder,ssa_inliner,*cex_complete_solver, - entry_function,f_it->first); + summarizer_bw_cex=new summarizer_bw_cex_allt( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + *cex_complete_solver, + entry_function, + f_it->first); } - assert(summarizer_bw_cex != NULL); + else //NOLINT(*) +#if 0 + if(options.get_bool_option("inline") || + options.get_option("spurious-check")=="concrete") +#endif + { + summarizer_bw_cex=new summarizer_bw_cex_concretet( + options, + summary_db, + ssa_db, + ssa_unwinder, + ssa_inliner, + entry_function, + f_it->first); + } + assert(summarizer_bw_cex!=nullptr); summarizer_bw_cex->set_message_handler(get_message_handler()); cover_goals_extt cover_goals( - SSA, solver, property_map, - all_properties, build_error_trace, + SSA, + solver, + property_map, + all_properties, + build_error_trace, *summarizer_bw_cex); - for(ssa_inlinert::assertion_mapt::const_iterator - aa_it=assertion_map.begin(); - aa_it!=assertion_map.end(); - aa_it++) +#if 0 + debug() << "(C) " << from_expr(SSA.ns, "", enabling_expr) << eom; +#endif + + const goto_programt &goto_program=SSA.goto_function.body; + + for(goto_programt::instructionst::const_iterator + i_it=goto_program.instructions.begin(); + i_it!=goto_program.instructions.end(); + i_it++) { - irep_idt property_id = aa_it->first->source_location.get_property_id(); + if(!i_it->is_assert()) + continue; - //do not recheck properties that have already been decided - if(property_map[property_id].result!=UNKNOWN) - continue; + const source_locationt &location=i_it->source_location; + irep_idt property_id=location.get_property_id(); -#if 0 - if(property_id=="") //TODO: some properties do not show up in initialize_property_map - continue; -#endif + if(i_it->guard.is_true()) + { + property_map[property_id].result=PASS; + continue; + } + + // do not recheck properties that have already been decided + if(property_map[property_id].result!=UNKNOWN) + continue; - for(exprt::operandst::const_iterator - a_it=aa_it->second.begin(); - a_it!=aa_it->second.end(); - a_it++) + // TODO: some properties do not show up in initialize_property_map + if(property_id=="") + continue; + + std::list assertion_nodes; + SSA.find_nodes(i_it, assertion_nodes); + + unsigned property_counter=0; + for(std::list::const_iterator + n_it=assertion_nodes.begin(); + n_it!=assertion_nodes.end(); + n_it++) { - exprt property=*a_it; + for(local_SSAt::nodet::assertionst::const_iterator + a_it=(*n_it)->assertions.begin(); + a_it!=(*n_it)->assertions.end(); + a_it++, property_counter++) + { + exprt property=*a_it; - if(simplify) - property=::simplify_expr(property, SSA.ns); + if(simplify) + property=::simplify_expr(property, SSA.ns); -#if 1 - debug() << "property: " << from_expr(SSA.ns, "", property) << eom; +#if 0 + std::cout << "property: " << from_expr(SSA.ns, "", property) + << std::endl; #endif - - property_map[property_id].location = aa_it->first; - cover_goals.goal_map[property_id].conjuncts.push_back(property); + + property_map[property_id].location=i_it; + cover_goals.goal_map[property_id].conjuncts.push_back(property); + } } } - + for(cover_goals_extt::goal_mapt::const_iterator it=cover_goals.goal_map.begin(); it!=cover_goals.goal_map.end(); @@ -459,22 +472,16 @@ void summary_checker_baset::check_properties( { // Our goal is to falsify a property. // The following is TRUE if the conjunction is empty. - //literalt p=!solver.convert(conjunction(it->second.conjuncts)); - //cover_goals.add(p); - cover_goals.add(not_exprt(conjunction(it->second.conjuncts))); + cover_goals.add(conjunction(it->second.conjuncts)); } - + status() << "Running " << solver.solver->decision_procedure_text() << eom; - cover_goals(); - /* - std::cout << "Output Verbose: " << entry_function << "\n"; - (ssa_db.get(entry_function)).output_verbose(std::cout); - assert(false); - */ - //set all non-covered goals to PASS except if we do not try + cover_goals(); + + // set all non-covered goals to PASS except if we do not try // to cover all goals and we have found a FAIL - if(all_properties || cover_goals.number_covered()==0) + if(all_properties || cover_goals.number_covered()==0) { std::list::const_iterator g_it= cover_goals.goals.begin(); @@ -483,25 +490,21 @@ void summary_checker_baset::check_properties( it!=cover_goals.goal_map.end(); it++, g_it++) { - if(!g_it->covered) property_map[it->first].result=PASS; + if(!g_it->covered) + property_map[it->first].result=PASS; } } solver.pop_context(); - summarizer_bw_cex->get_reason(reason); - debug() << "** " << cover_goals.number_covered() << " of " << cover_goals.size() << " failed (" << cover_goals.iterations() << " iterations)" << eom; - - delete summarizer_bw_cex; - delete cex_complete_solver; -} +} /*******************************************************************\ -Function: summary_checker_baset::report_statistics() +Function: summary_checker_baset::report_statistics Inputs: @@ -513,24 +516,25 @@ Function: summary_checker_baset::report_statistics() void summary_checker_baset::report_statistics() { - for(ssa_dbt::functionst::const_iterator f_it = ssa_db.functions().begin(); - f_it != ssa_db.functions().end(); f_it++) + for(ssa_dbt::functionst::const_iterator f_it=ssa_db.functions().begin(); + f_it!=ssa_db.functions().end(); f_it++) { - incremental_solvert &solver = ssa_db.get_solver(f_it->first); - unsigned calls = solver.get_number_of_solver_calls(); - if(calls>0) solver_instances++; - solver_calls += calls; + incremental_solvert &solver=ssa_db.get_solver(f_it->first); + unsigned calls=solver.get_number_of_solver_calls(); + if(calls>0) + solver_instances++; + solver_calls+=calls; } statistics() << "** statistics: " << eom; statistics() << " number of solver instances: " << solver_instances << eom; statistics() << " number of solver calls: " << solver_calls << eom; - statistics() << " number of summaries used: " + statistics() << " number of summaries used: " << summaries_used << eom; - statistics() << " number of termination arguments computed: " + statistics() << " number of termination arguments computed: " << termargs_computed << eom; statistics() << eom; } - + /*******************************************************************\ Function: summary_checker_baset::do_show_vcc @@ -550,7 +554,7 @@ void summary_checker_baset::do_show_vcc( { std::cout << i_it->source_location << "\n"; std::cout << i_it->source_location.get_comment() << "\n"; - + std::list ssa_constraints; ssa_constraints << SSA; @@ -561,9 +565,9 @@ void summary_checker_baset::do_show_vcc( std::cout << "{-" << i << "} " << from_expr(SSA.ns, "", *c_it) << "\n"; std::cout << "|--------------------------\n"; - + std::cout << "{1} " << from_expr(SSA.ns, "", *a_it) << "\n"; - + std::cout << "\n"; } @@ -582,22 +586,25 @@ Function: summary_checker_baset::instrument_and_output void summary_checker_baset::instrument_and_output(goto_modelt &goto_model) { - instrument_gotot instrument_goto(options,ssa_db,summary_db); + instrument_gotot instrument_goto(options, ssa_db, summary_db); instrument_goto(goto_model); - std::string filename = options.get_option("instrument-output"); + std::string filename=options.get_option("instrument-output"); status() << "Writing instrumented goto-binary " << filename << eom; - write_goto_binary(filename, - goto_model.symbol_table, - goto_model.goto_functions, get_message_handler()); + write_goto_binary( + filename, + goto_model.symbol_table, + goto_model.goto_functions, + get_message_handler()); } + /*******************************************************************\ Function: summary_checker_baset::has_assertion Inputs: - Outputs: + Outputs: Purpose: searches recursively for assertions in inlined functions @@ -606,17 +613,17 @@ Function: summary_checker_baset::has_assertion bool summary_checker_baset::has_assertion(irep_idt function_name) { // SSA.goto_function.body.has_assertion() has become too semantic - bool _has_assertion = false; - const local_SSAt &SSA = ssa_db.get(function_name); + bool _has_assertion=false; + const local_SSAt &SSA=ssa_db.get(function_name); - for(local_SSAt::nodest::const_iterator - n_it = SSA.nodes.begin(); n_it != SSA.nodes.end(); ++n_it) + for(local_SSAt::nodest::const_iterator + n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); ++n_it) { - for(local_SSAt::nodet::assertionst::const_iterator - a_it = n_it->assertions.begin(); a_it != n_it->assertions.end(); ++a_it) + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); a_it!=n_it->assertions.end(); ++a_it) { - irep_idt property_id = n_it->location->source_location.get_property_id(); - + irep_idt property_id=n_it->location->source_location.get_property_id(); + if(n_it->location->guard.is_true()) property_map[property_id].result=PASS; else @@ -625,16 +632,16 @@ bool summary_checker_baset::has_assertion(irep_idt function_name) if(!n_it->function_calls_inlined) continue; - for(local_SSAt::nodet::function_callst::const_iterator - f_it = n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); ++f_it) + for(local_SSAt::nodet::function_callst::const_iterator + f_it=n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); ++f_it) { - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); if(ssa_db.functions().find(fname)==ssa_db.functions().end()) continue; - bool new_has_assertion = has_assertion(fname); //recurse - _has_assertion = _has_assertion || new_has_assertion; + bool new_has_assertion=has_assertion(fname); // recurse + _has_assertion=_has_assertion || new_has_assertion; } } diff --git a/src/2ls/summary_checker_base.h b/src/2ls/summary_checker_base.h index 4daefa5b5..c758f4ed9 100644 --- a/src/2ls/summary_checker_base.h +++ b/src/2ls/summary_checker_base.h @@ -6,32 +6,33 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SUMMARY_CHECKER_BASE_H -#define CPROVER_SUMMARY_CHECKER_BASE_H +#ifndef CPROVER_2LS_2LS_SUMMARY_CHECKER_BASE_H +#define CPROVER_2LS_2LS_SUMMARY_CHECKER_BASE_H #include - #include #include +#include +#include +#include +#include +#include +#include + #include "cover_goals_ext.h" -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/ssa_inliner.h" -#include "../domains/incremental_solver.h" -#include "../ssa/ssa_db.h" -#include "../solver/summary_db.h" -#include "summarizer_bw_cex.h" + +class graphml_witness_extt; class summary_checker_baset:public property_checkert { public: - inline summary_checker_baset(optionst &_options): + explicit summary_checker_baset(optionst &_options): show_vcc(false), simplify(false), fixed_point(false), options(_options), - ssa_db(_options),summary_db(), + ssa_db(_options), summary_db(), ssa_unwinder(ssa_db), ssa_inliner(summary_db, ssa_db), solver_instances(0), @@ -41,7 +42,7 @@ class summary_checker_baset:public property_checkert { ssa_inliner.set_message_handler(get_message_handler()); } - + bool show_vcc, simplify, fixed_point; irep_idt function_to_check; @@ -53,18 +54,14 @@ class summary_checker_baset:public property_checkert absolute_timet start_time; time_periodt sat_time; +protected: optionst &options; + ssa_dbt ssa_db; summary_dbt summary_db; ssa_unwindert ssa_unwinder; ssa_inlinert ssa_inliner; -protected: - - irep_idt entry_function; - - summarizer_bw_cex_baset::reasont reason; - unsigned solver_instances; unsigned solver_calls; unsigned summaries_used; @@ -77,23 +74,26 @@ class summary_checker_baset:public property_checkert const local_SSAt::nodet::assertionst::const_iterator &); void SSA_functions(const goto_modelt &, const namespacet &ns); - void SSA_dependency_graphs(const goto_modelt &, const namespacet &ns); - void summarize(const goto_modelt &, - bool forward=true, bool termination=false); + void summarize( + const goto_modelt &, + bool forward=true, + bool termination=false); property_checkert::resultt check_properties(); + property_checkert::resultt check_properties(irep_idt entry_function); property_checkert::resultt check_properties( - irep_idt function_name, - irep_idt entry_function, - std::set seen_function_calls, - bool is_inlined); + irep_idt function_name, + irep_idt entry_function, + std::set seen_function_calls, + bool is_inlined); void check_properties( const ssa_dbt::functionst::const_iterator f_it, irep_idt entry_function=""); bool has_assertion(irep_idt function_name); + friend graphml_witness_extt; }; #endif diff --git a/src/deltacheck/Makefile b/src/deltacheck/Makefile deleted file mode 100644 index 07353ef3e..000000000 --- a/src/deltacheck/Makefile +++ /dev/null @@ -1,88 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = deltacheck_main.cpp deltacheck_parse_options.cpp \ - rename.cpp ssa_fixed_point.cpp source_diff.cpp change_impact.cpp \ - html_report.cpp analyzer.cpp properties.cpp report_source_code.cpp \ - get_source.cpp statistics.cpp \ - $(CBMC)/src/cbmc/xml_interface.cpp - -OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ - $(CBMC)/src/linking/linking$(LIBEXT) \ - $(CBMC)/src/assembler/assembler$(LIBEXT) \ - $(CBMC)/src/big-int/big-int$(LIBEXT) \ - $(CBMC)/src/goto-programs/goto-programs$(LIBEXT) \ - $(CBMC)/src/goto-symex/goto-symex$(LIBEXT) \ - $(CBMC)/src/analyses/analyses$(LIBEXT) \ - $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ - $(CBMC)/src/langapi/langapi$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - $(CBMC)/src/solvers/solvers$(LIBEXT) \ - $(CBMC)/src/util/util$(LIBEXT) \ - ../html/logo$(OBJEXT) \ - ../html/html_escape$(OBJEXT) \ - ../html/syntax_highlighting$(OBJEXT) \ - ../ssa/local_ssa$(OBJEXT) \ - ../ssa/malloc_ssa$(OBJEXT) \ - ../ssa/ssa_domain$(OBJEXT) \ - ../ssa/ssa_value_set$(OBJEXT) \ - ../ssa/assignments$(OBJEXT) \ - ../ssa/guard_map$(OBJEXT) \ - ../ssa/ssa_object$(OBJEXT) \ - ../ssa/address_canonizer$(OBJEXT) \ - ../ssa/ssa_dereference$(OBJEXT) \ - ../solver/predicate$(OBJEXT) \ - ../solver/solver$(OBJEXT) \ - ../solver/fixed_point$(OBJEXT) \ - ../functions/summary$(OBJEXT) \ - ../functions/path_util$(OBJEXT) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src -# \ -# -I $(CUDD)/cudd -I $(CUDD)/obj -I $(CUDD)/mtr -I $(CUDD)/epd - -LIBS = -#LIBS = $(CUDD)/cudd/libcudd.a $(CUDD)/mtr/libmtr.a \ -# $(CUDD)/st/libst.a $(CUDD)/epd/libepd.a $(CUDD)/util/libutil.a - -# $(CUDD)/obj/libobj.a - -CLEANFILES = deltacheck$(EXEEXT) - -all: deltacheck$(EXEEXT) - -ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) - OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) - CP_CXXFLAGS += -DHAVE_CPP -endif - -ifneq ($(wildcard $(CBMC)/src/java/Makefile),) - OBJ += $(CBMC)/src/java/java$(LIBEXT) - CXXFLAGS += -DHAVE_JAVA -endif - -ifneq ($(wildcard $(CBMC)/src/specc/Makefile),) - OBJ += $(CBMC)/src/specc/specc$(LIBEXT) - CP_CXXFLAGS += -DHAVE_SPECC -endif - -ifneq ($(wildcard $(CBMC)/src/php/Makefile),) - OBJ += $(CBMC)/src/php/php$(LIBEXT) - CXXFLAGS += -DHAVE_PHP -endif - -# HTML Header - -html_report$(OBJEXT): report_header.inc - -report_header.inc: report_header.html - ../html/to_c_string.perl < report_header.html > $@ - -############################################################################### - -deltacheck$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/deltacheck/analyzer.cpp b/src/deltacheck/analyzer.cpp deleted file mode 100644 index d5d00af92..000000000 --- a/src/deltacheck/analyzer.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include - -//#include -//#include - -#include "../html/html_escape.h" -#include "../functions/path_util.h" - -#include "html_report.h" -#include "ssa_fixed_point.h" -#include "statistics.h" -#include "report_source_code.h" -#include "analyzer.h" -#include "change_impact.h" - -class deltacheck_analyzert:public messaget -{ -public: - deltacheck_analyzert( - const std::string &_path_old, - const goto_modelt &_goto_model_old, - const std::string &_path_new, - const goto_modelt &_goto_model_new, - const optionst &_options, - message_handlert &message_handler): - messaget(message_handler), - path_old(_path_old), - path_new(_path_new), - goto_model_old(_goto_model_old), - goto_model_new(_goto_model_new), - options(_options) - { - } - - statisticst statistics; - - void operator()(); - -protected: - const std::string &path_old; - const std::string &path_new; - const goto_modelt &goto_model_old; - const goto_modelt &goto_model_new; - const optionst &options; - - change_impactt change_impact; - - void check_function( - const irep_idt &, - std::ostream &global_report); - - void check_all(std::ostream &global_report); - - unsigned errors_in_file, passed_in_file, - unknown_in_file, unaffected_in_file, - LOCs_in_file; - - void collect_statistics(const propertiest &); - void collect_statistics(const goto_functionst::goto_functiont &); -}; - -/*******************************************************************\ - -Function: deltacheck_analyzert::check_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::check_function( - const irep_idt &function, - std::ostream &global_report) -{ - const goto_functionst::function_mapt::const_iterator - fmap_it_new=goto_model_new.goto_functions.function_map.find(function); - - if(fmap_it_new==goto_model_new.goto_functions.function_map.end()) - { - error() << "failed to find function `" << function - << "'" << eom; - return; - } - - const goto_functionst::goto_functiont &fkt_new= - fmap_it_new->second; - - // update statistics - LOCs_in_file+=fkt_new.body.instructions.size(); - collect_statistics(fkt_new); - statistics.number_map["Functions"]++; - - // Is this function at all affected? - if(!change_impact.function_map[function].is_affected()) - { - status() << "Function \"" << function << "\" is not affected" << eom; - - unsigned count=0; - forall_goto_program_instructions(i_it, fkt_new.body) - if(i_it->is_assert()) - count++; - - unaffected_in_file+=count; - statistics.number_map["Unaffected"]+=count; - return; // next function - } - - status() << "Checking \"" << function << "\"" << eom; - - const namespacet ns_new(goto_model_new.symbol_table); - const namespacet ns_old(goto_model_old.symbol_table); - - const symbolt &symbol_new=ns_new.lookup(function); - - // get corresponding goto_model_old function, if available - - const goto_functionst::function_mapt::const_iterator - fmap_it_old=goto_model_old.goto_functions.function_map.find(function); - - goto_functionst::goto_functiont fkt_old_dummy; - symbolt symbol_old_dummy; - - const goto_functionst::goto_functiont &fkt_old= - fmap_it_old==goto_model_old.goto_functions.function_map.end()?fkt_old_dummy: - fmap_it_old->second; - - const symbolt &symbol_old= - fmap_it_old==goto_model_old.goto_functions.function_map.end()?symbol_old_dummy: - ns_old.lookup(function); - - // set up report - - std::string report_file_name= - make_relative_path(path_new, "deltacheck."+id2string(function)+".html"); - - std::ofstream function_report(report_file_name.c_str()); - - html_report_header("Function "+id2string(symbol_new.display_name()), function_report); - - // build SSA for each - status() << "Building SSA" << eom; - statistics.start("SSA"); - local_SSAt SSA_old(fkt_old, ns_old, "@old"); - local_SSAt SSA_new(fkt_new, ns_new); - statistics.stop("SSA"); - - // add assertions in old version as assumptions - SSA_old.assertions_to_constraints(); - - // now do _joint_ fixed-point - namespacet joint_ns( - ns_new.get_symbol_table(), - ns_old.get_symbol_table()); - status() << "Joint data-flow fixed-point" << eom; - statistics.start("Fixed-point"); - ssa_fixed_pointt ssa_fixed_point(SSA_old, SSA_new, joint_ns); - statistics.stop("Fixed-point"); - - // now report on assertions - std::string description_old= - options.get_option("description-old"); - - std::string description_new= - options.get_option("description-new"); - - status() << "Reporting" << eom; - statistics.start("Reporting"); - //report_properties(ssa_fixed_point.properties, function_report); - report_properties(ssa_fixed_point.properties, *this); - report_countermodels(SSA_old, SSA_new, - ssa_fixed_point.properties, function_report); - report_source_code( - path_old, symbol_old.location, fkt_old.body, description_old, - path_new, symbol_new.location, fkt_new.body, description_new, - ssa_fixed_point.properties, - function_report, get_message_handler()); - statistics.stop("Reporting"); - - // dump statistics - statistics.html_report_last(function_report); - - // collect some more data - #if 0 - collect_statistics(ssa_fixed_point.properties); - #endif - - function_report << "\n"; - - #if 0 - global_report << "\n" - << "" - << "" - << "" - << "\n"; - #endif - - #if 0 - // add link to global report - global_report << "" - << "" - << "" - << "\n"; - } - - global_report << "
FileLOCs# Errors
" << html_escape(file_it->first) - << "" << LOCs_in_file << "" << errors_in_file << "
\n\n"; - #endif -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::check_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::check_all(std::ostream &global_report) -{ - // we do this by function in the new goto_model - for(goto_functionst::function_mapt::const_iterator - fmap_it=goto_model_new.goto_functions.function_map.begin(); - fmap_it!=goto_model_new.goto_functions.function_map.end(); - fmap_it++) - { - check_function(fmap_it->first, global_report); - } -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::collect_statistics - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::collect_statistics( - const goto_functionst::goto_functiont &goto_function) -{ - statistics.number_map["LOCs"]+=goto_function.body.instructions.size(); -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::collect_statistics - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::collect_statistics( - const propertiest &properties) -{ - for(propertiest::const_iterator - p_it=properties.begin(); - p_it!=properties.end(); - p_it++) - { - if(p_it->status.is_false()) - { - errors_in_file++; - statistics.number_map["Errors"]++; - } - else if(p_it->status.is_true()) - { - passed_in_file++; - statistics.number_map["Passed"]++; - } - else - { - unknown_in_file++; - statistics.number_map["Unknown"]++; - } - } -} - -/*******************************************************************\ - -Function: deltacheck_analyzert::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzert::operator()() -{ - statistics.start("Total-time"); - - std::string report_file_name= - make_relative_path(path_new, "deltacheck.html"); - - std::ofstream out(report_file_name.c_str()); - - if(!out) - { - error() << "failed to write to \"" - << report_file_name << "\"" << eom; - return; - } - - status() << "Writing report into \"" - << report_file_name << "\"" << eom; - - std::string title="DeltaCheck Summary"; - - html_report_header( - out, options.get_option("description-old"), - options.get_option("description-new"), title); - - statistics.start("Change-impact"); - status() << "Computing syntactic difference" << eom; - change_impact.diff(goto_model_old, goto_model_new); - status() << "Change-impact analysis" << eom; - change_impact.change_impact(goto_model_new); - statistics.stop("Change-impact"); - - status() << "Starting analysis" << eom; - - if(options.get_option("function")!="") - check_function(options.get_option("function"), out); - else - check_all(out); - - statistics.stop("Total-time"); - - // Report grand totals - - out << "

Summary statistics

\n"; - statistics.html_report_total(out); - - result() << "Properties unaffected: " << statistics.number_map["Unaffected"] << eom; - result() << "Properties passed: " << statistics.number_map["Passed"] << eom; - result() << "Properties failed: " << statistics.number_map["Errors"] << eom; - result() << "Properties warned: " << statistics.number_map["Unknown"] << eom; - - messaget::statistics() << "LOCs analyzed: " << statistics.number_map["LOCs"] << eom; - messaget::statistics() << "Functions analyzed: " << statistics.number_map["Functions"] << eom; - - memory_info(messaget::statistics()); - messaget::statistics() << eom; - - html_report_footer(out); - - // Write some statistics into a JSON file, for the benefit - // of other programs. - - std::string stat_file_name= - make_relative_path(path_new, "deltacheck-stat.json"); - std::ofstream json_out(stat_file_name.c_str()); - - json_out << "{\n"; - json_out << " \"properties\": {\n"; - json_out << " \"unaffected\": " << statistics.number_map["Unaffected"] << ",\n"; - json_out << " \"passed\": " << statistics.number_map["Passed"] << ",\n"; - json_out << " \"failed\": " << statistics.number_map["Errors"] << ",\n"; - json_out << " \"warned\": " << statistics.number_map["Unknown"] << "\n"; - json_out << " },\n"; - json_out << " \"program\": {\n"; - json_out << " \"LOCs\": " << statistics.number_map["LOCs"] << ",\n"; - json_out << " \"functions\": " << statistics.number_map["Functions"] << "\n"; - json_out << " }\n"; - json_out << "}\n"; -} - -/*******************************************************************\ - -Function: deltacheck_analyzer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void deltacheck_analyzer( - const std::string &path1, - const goto_modelt &goto_model1, - const std::string &path2, - const goto_modelt &goto_model2, - const optionst &options, - message_handlert &message_handler) -{ - deltacheck_analyzert checker( - path1, goto_model1, - path2, goto_model2, - options, message_handler); - checker(); -} diff --git a/src/deltacheck/analyzer.h b/src/deltacheck/analyzer.h deleted file mode 100644 index a83d0e163..000000000 --- a/src/deltacheck/analyzer.h +++ /dev/null @@ -1,26 +0,0 @@ -/*******************************************************************\ - -Module: Main Checker Interface - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CHECKER_H -#define CPROVER_DELTACHECK_CHECKER_H - -#include - -#include - -class message_handlert; - -void deltacheck_analyzer( - const std::string &path1, - const goto_modelt &goto_model1, - const std::string &path2, - const goto_modelt &goto_model2, - const optionst &options, - message_handlert &); - -#endif diff --git a/src/deltacheck/change_impact.cpp b/src/deltacheck/change_impact.cpp deleted file mode 100644 index 533512603..000000000 --- a/src/deltacheck/change_impact.cpp +++ /dev/null @@ -1,390 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include - -#include "../functions/get_function.h" -#include "change_impact.h" - -/*******************************************************************\ - -Function: change_impactt::diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::diff( - const goto_modelt &old_model, - const goto_modelt &new_model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=new_model.goto_functions.function_map.begin(); - new_fkt_it!=new_model.goto_functions.function_map.end(); - new_fkt_it++) - { - // try to find 'corresponding function' in old_model - goto_functionst::function_mapt::const_iterator - old_fkt_it=old_model.goto_functions.function_map.find(new_fkt_it->first); - - if(old_fkt_it==old_model.goto_functions.function_map.end()) - function_map[new_fkt_it->first].fully_changed=true; - else - diff_functions(new_fkt_it->first, old_fkt_it->second, new_fkt_it->second); - } -} - -/*******************************************************************\ - -Function: change_impactt::diff_functions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::diff_functions( - const irep_idt &function_id, - const goto_functionst::goto_functiont &old_f, - const goto_functionst::goto_functiont &new_f) -{ - const goto_programt &old_body=old_f.body; - const goto_programt &new_body=new_f.body; - - // build branch target maps - - std::map old_target_map; - - forall_goto_program_instructions(it, old_body) - { - unsigned nr=old_target_map.size(); - old_target_map[it->location_number]=nr; - } - - std::map new_target_map; - - forall_goto_program_instructions(it, new_body) - { - unsigned nr=new_target_map.size(); - new_target_map[it->location_number]=nr; - } - - // now diff - datat &data=function_map[function_id]; - - goto_programt::instructionst::const_iterator - old_it=old_body.instructions.begin(); - - forall_goto_program_instructions(new_it, new_body) - { - if(new_it->is_skip() || - new_it->is_location() || - new_it->is_end_function()) - continue; - - while(old_it!=old_body.instructions.end() && - (old_it->is_skip() || - old_it->is_location() || - old_it->is_end_function())) - old_it++; - - if(new_it->type!=old_it->type || - new_it->guard!=old_it->guard || - new_it->code!=old_it->code || - (new_it->is_goto() && - new_target_map[new_it->get_target()->location_number]!= - old_target_map[old_it->get_target()->location_number])) - data.locs_changed.insert(new_it->location_number); - - old_it++; - } -} - -/*******************************************************************\ - -Function: change_impactt::output_diff - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::output_diff(std::ostream &out) -{ - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - { - if(fkt_it->second.fully_changed) - out << fkt_it->first << ": *\n"; - else if(!fkt_it->second.locs_changed.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_changed.begin(); - l_it!=fkt_it->second.locs_changed.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: change_impactt::output_change_impact - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::output_change_impact(std::ostream &out) -{ - for(function_mapt::const_iterator - fkt_it=function_map.begin(); - fkt_it!=function_map.end(); - fkt_it++) - { - if(fkt_it->second.fully_affected) - out << fkt_it->first << "\n"; - else if(!fkt_it->second.locs_affected.empty()) - { - out << fkt_it->first << ":"; - for(std::set::const_iterator - l_it=fkt_it->second.locs_affected.begin(); - l_it!=fkt_it->second.locs_affected.end(); - l_it++) - out << " " << *l_it; - - out << "\n"; - } - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: change_impactt::change_impact - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::change_impact(const goto_modelt &new_model) -{ - std::stack working; - - // stash everything with change into the working set - for(function_mapt::const_iterator - function_it=function_map.begin(); - function_it!=function_map.end(); - function_it++) - { - if(function_it->second.has_change()) - { - working.push(function_it->first); - } - } - - // main loop - while(!working.empty()) - { - const irep_idt f_id=working.top(); - working.pop(); - - propagate_affected(new_model, f_id, working); - } -} - -/*******************************************************************\ - -Function: change_impactt::propagate_affected - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::propagate_affected( - const goto_modelt &new_model, - const irep_idt &f_id, - std::stack &working_fkts) -{ - datat &data=function_map[f_id]; - - if(data.fully_affected) return; // done already - - // get it - goto_functionst::function_mapt::const_iterator f_it= - new_model.goto_functions.function_map.find(f_id); - - if(f_it==new_model.goto_functions.function_map.end()) - return; // give up - - const goto_programt &body=f_it->second.body; - if(body.empty()) return; // give up - - std::stack working_locs; - - // put anything changed into working_locs - forall_goto_program_instructions(l, body) - if(data.locs_changed.find(l->location_number)!=data.locs_changed.end()) - working_locs.push(l); - - // put anything with an affected function call into working_locs - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - irep_idt called_f_id=symbol.get_identifier(); - if(function_map[called_f_id].is_affected()) - working_locs.push(l); - } - } - - while(!working_locs.empty()) - { - goto_programt::const_targett l=working_locs.top(); - working_locs.pop(); - - if(data.locs_affected.find(l->location_number)!=data.locs_affected.end()) - continue; // done already - - data.locs_affected.insert(l->location_number); - - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - irep_idt called_f_id=symbol.get_identifier(); - make_fully_affected(called_f_id); - } - } - - goto_programt::const_targetst successors; - - body.get_successors(l, successors); - - for(goto_programt::const_targetst::const_iterator - it=successors.begin(); - it!=successors.end(); - it++) - { - assert(body.instructions.end()!=*it); - working_locs.push(*it); - } - } -} - -/*******************************************************************\ - -Function: change_impactt::make_fully_affected - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::make_fully_affected(const irep_idt &f_id) -{ - std::stack working; - - working.push(f_id); - - while(!working.empty()) - { - const irep_idt f_id=working.top(); - working.pop(); - - datat &data=function_map[f_id]; - if(data.fully_affected) continue; - data.fully_affected=true; - - // recursively make all functions that are called fully affected - for(std::set::const_iterator - called_it=data.calls.begin(); - called_it!=data.calls.end(); - called_it++) - { - working.push(*called_it); - } - } -} - -/*******************************************************************\ - -Function: change_impactt::do_call_graph - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void change_impactt::do_call_graph( - const goto_modelt &model) -{ - for(goto_functionst::function_mapt::const_iterator - new_fkt_it=model.goto_functions.function_map.begin(); - new_fkt_it!=model.goto_functions.function_map.end(); - new_fkt_it++) - { - datat &data=function_map[new_fkt_it->first]; - - const goto_programt &body=new_fkt_it->second.body; - - forall_goto_program_instructions(l, body) - if(l->is_function_call()) - { - const code_function_callt &call=to_code_function_call(l->code); - if(call.function().id()==ID_symbol) - { - const symbol_exprt &symbol=to_symbol_expr(call.function()); - const irep_idt called_f_id=symbol.get_identifier(); - data.calls.insert(called_f_id); - } - } - } -} diff --git a/src/deltacheck/change_impact.h b/src/deltacheck/change_impact.h deleted file mode 100644 index 3860841c3..000000000 --- a/src/deltacheck/change_impact.h +++ /dev/null @@ -1,73 +0,0 @@ -/*******************************************************************\ - -Module: Change Impact - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_DELTACHECK_CHANGE_IMPACT_H -#define CPROVER_DELTACHECK_CHANGE_IMPACT_H - -#include - -class change_impactt:public messaget -{ -public: - void diff( - const goto_modelt &old_model, - const goto_modelt &new_model); - - void change_impact( - const goto_modelt &new_model); - - void output_diff(std::ostream &); - void output_change_impact(std::ostream &); - - struct datat - { - datat(): - fully_changed(false), - fully_affected(false) - { - } - - bool has_change() const - { - return fully_changed || !locs_changed.empty(); - } - - bool is_affected() const - { - return fully_affected || !locs_affected.empty(); - } - - bool fully_changed, fully_affected; - std::set locs_changed, locs_affected; - - std::set calls; - std::set called_by; - }; - - // functions to 'datat' map - typedef std::map function_mapt; - function_mapt function_map; - -protected: - void diff_functions( - const irep_idt &function_id, - const goto_functionst::goto_functiont &, - const goto_functionst::goto_functiont &); - - void propagate_affected( - const goto_modelt &new_index, - const irep_idt &id, - std::stack &working); - - void make_fully_affected(const irep_idt &); - - void do_call_graph( - const goto_modelt &); -}; - -#endif diff --git a/src/deltacheck/html_report.cpp b/src/deltacheck/html_report.cpp deleted file mode 100644 index 455845b21..000000000 --- a/src/deltacheck/html_report.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include "../html/html_escape.h" -#include "../html/logo.h" - -#include "html_report.h" -#include "version.h" - -/*******************************************************************\ - -Function: html_report_header - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const char header[]= -#include "report_header.inc" -; - -void html_report_header( - const std::string &title, - std::ostream &out) -{ - out << "\n\n"; - - out << "\n"; - - out << "" << html_escape(title) << "\n\n"; - - out << header; - - out << "\n"; - - out << "\n" - "\n"; -} - -/*******************************************************************\ - -Function: html_report_header - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void html_report_header( - std::ostream &out, - const std::string &old_desc, - const std::string &new_desc, - const std::string &title) -{ - html_report_header(title, out); - - out << "\"DeltaCheck\n\n"; - - out << "

" << html_escape(title) << "

\n\n"; - - out << "

DeltaCheck version: " << DELTACHECK_VERSION << "

\n"; - - out << "

Software under analysis

\n"; - - out << "

\n" - << "\n"; - out << "\n" - << "
Old version:" << html_escape(old_desc) << "
New version:" << html_escape(new_desc) << "

\n"; -} - -/*******************************************************************\ - -Function: html_report_footer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void html_report_footer(std::ostream &out) -{ - out << "
\n" - "\n" - "
\n" - "DeltaCheck is © 2011–2015 Daniel Kroening, University of Oxford.\n" - "
\n" - "\n" - "\n" - "\n"; -} diff --git a/src/deltacheck/html_report.h b/src/deltacheck/html_report.h deleted file mode 100644 index 226e4e3b6..000000000 --- a/src/deltacheck/html_report.h +++ /dev/null @@ -1,27 +0,0 @@ -/*******************************************************************\ - -Module: Delta Check HTML Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_HTML_REPORT_H -#define CPROVER_HTML_REPORT_H - -#include - -#include - -void html_report_header( - std::ostream &out, - const std::string &old_desc, const std::string &new_desc, - const std::string &title); - -void html_report_header( - const std::string &title, - std::ostream &out); - -void html_report_footer(std::ostream &out); - -#endif diff --git a/src/deltacheck/report_header.html b/src/deltacheck/report_header.html deleted file mode 100644 index cc5e08aea..000000000 --- a/src/deltacheck/report_header.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - - diff --git a/src/deltacheck/report_source_code.cpp b/src/deltacheck/report_source_code.cpp deleted file mode 100644 index 738db1301..000000000 --- a/src/deltacheck/report_source_code.cpp +++ /dev/null @@ -1,297 +0,0 @@ -/*******************************************************************\ - -Module: Source Code Reporting - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -//#define DEBUG - -#include - -#include "../html/html_escape.h" -#include "../html/syntax_highlighting.h" -#include "report_source_code.h" -#include "get_source.h" -#include "source_diff.h" - -/*******************************************************************\ - -Function: get_errors - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string get_errors( - const propertiest &properties, - const linet &line) -{ - std::string errors; - - for(propertiest::const_iterator - p_it=properties.begin(); p_it!=properties.end(); p_it++) - { - if(line.file==p_it->loc->source_location.get_file() && - i2string(line.line_no)==as_string(p_it->loc->source_location.get_line())) - { - if(p_it->status.is_false()) - { - if(!errors.empty()) errors+="
"; - - irep_idt property=p_it->loc->source_location.get_property_class(); - irep_idt comment=p_it->loc->source_location.get_comment(); - - if(comment=="") - errors+=as_string(property); - else - errors+=as_string(comment); - } - } - } - - return errors; -} - -/*******************************************************************\ - -Function: report_source_code - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_source_code( - const std::string &path_prefix, - const source_locationt &source_location, - const goto_programt &goto_program, - const propertiest &properties, - std::ostream &out, - message_handlert &message_handler) -{ - std::list lines; - get_source(path_prefix, source_location, goto_program, lines, message_handler); - - out << "

\n"; - out << "\n"; - - // error marking - - out << "\n"; - - // line numbers go into a column - - out << "\n"; - - // now do source text in another column - - out << "\n"; - - out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-  {
-    std::string errors=get_errors(properties, *l_it);
-    if(!errors.empty())
-    {
-      out << ""
-          << "✗"
-          << "";
-    }
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-    out << l_it->line_no << "\n";
-    
-  out << "
\n";
-  
-  syntax_highlightingt syntax_highlighting(out);
-  
-  for(std::list::const_iterator
-      l_it=lines.begin(); l_it!=lines.end(); l_it++)
-  {
-    syntax_highlighting.line_no=l_it->line_no;
-    syntax_highlighting(l_it->line);
-  }
-  
-  out << "
\n"; - out << "

\n"; -} - -/*******************************************************************\ - -Function: report_source_code - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void report_source_code( - const std::string &path_prefix_old, - const source_locationt &location_old, - const goto_programt &goto_program_old, - const std::string &description_old, - const std::string &path_prefix_new, - const source_locationt &location_new, - const goto_programt &goto_program_new, - const std::string &description_new, - const propertiest &properties, - std::ostream &out, - message_handlert &message_handler) -{ - // get sources - std::list lines_new, lines_old; - - get_source(path_prefix_old, location_old, goto_program_old, lines_old, message_handler); - get_source(path_prefix_new, location_new, goto_program_new, lines_new, message_handler); - - // run 'diff' - - diff_it(lines_old, lines_new); - - out << "

\n"; - out << "\n"; - out << "" - "" - "" - "" - "\n"; - - out << "\n"; - - // old version - - std::list::const_iterator l_old_it, l_it; - - out << "\n"; - - out << "\n"; - - // new version - - // error marking - out << "\n"; - - out << "\n"; - - - out << "\n"; - - out << "
" << html_escape(description_old) << "" << html_escape(description_new) << "
\n";
-  
-  for(std::list::const_iterator
-      l_old_it=lines_old.begin(); l_old_it!=lines_old.end(); l_old_it++)
-  {
-    if(l_old_it->line_no!=0) out << l_old_it->line_no;
-    out << "\n";
-  }
-    
-  out << "
\n";
-
-  {  
-    syntax_highlightingt syntax_highlighting(out);
-    syntax_highlighting.identifier_tooltip=true;
-    
-    for(l_old_it=lines_old.begin(), l_it=lines_new.begin();
-        l_old_it!=lines_old.end() && l_it!=lines_new.end();
-        l_old_it++, l_it++)
-    {
-      syntax_highlighting.strong_class=
-        (l_old_it->line!=l_it->line)?"different":"";
-      syntax_highlighting.line_no=l_it->line_no;
-      syntax_highlighting.id_suffix="@old";
-      syntax_highlighting(l_old_it->line);
-    }
-  }
-  
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines_new.begin(); l_it!=lines_new.end(); l_it++)
-  {
-    const linet &line=*l_it;
-
-    unsigned count=0;
-    
-    for(propertiest::const_iterator
-        p_it=properties.begin(); p_it!=properties.end(); p_it++, count++)
-    {
-      if(line.file==p_it->loc->source_location.get_file() &&
-         i2string(line.line_no)==as_string(p_it->loc->source_location.get_line()))
-      {
-        if(p_it->status.is_false())
-        {
-          irep_idt property=p_it->loc->source_location.get_property_class();
-          irep_idt comment=p_it->loc->source_location.get_comment();
-
-          std::string msg;
-
-          if(comment=="")
-            msg=as_string(property);
-          else
-            msg=as_string(comment);
-
-          out << ""
-              << "✗"
-              << "";
-        }
-      }
-    }
-  
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  for(std::list::const_iterator
-      l_it=lines_new.begin(); l_it!=lines_new.end(); l_it++)
-  {
-    if(l_it->line_no!=0) out << l_it->line_no;
-    out << "\n";
-  }
-    
-  out << "
\n";
-  
-  {
-    syntax_highlightingt syntax_highlighting(out);
-    syntax_highlighting.identifier_tooltip=true;
-  
-    for(l_old_it=lines_old.begin(), l_it=lines_new.begin();
-        l_old_it!=lines_old.end() && l_it!=lines_new.end();
-        l_old_it++, l_it++)
-    {
-      syntax_highlighting.strong_class=
-        (l_old_it->line!=l_it->line)?"different":"";
-      syntax_highlighting.line_no=l_it->line_no;
-      syntax_highlighting(l_it->line);
-    }
-  }
-  
-  out << "
\n"; - out << "

\n"; -} - diff --git a/src/deltagit/Makefile b/src/deltagit/Makefile deleted file mode 100644 index 4c481facf..000000000 --- a/src/deltagit/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = deltagit_main.cpp deltagit_parse_options.cpp show_jobs.cpp \ - shell_escape.cpp git_log.cpp git_branch.cpp job_status.cpp do_job.cpp \ - deltagit_config.cpp revisions_report.cpp init.cpp reset.cpp \ - reanalyse.cpp - -OBJ+= $(CBMC)/src/util/util$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - $(CBMC)/src/json/json$(LIBEXT) \ - $(CBMC)/src/big-int/big-int$(LIBEXT) \ - ../html/html_escape$(OBJEXT) \ - ../html/logo$(OBJEXT) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -LIBS = - -CLEANFILES = deltagit$(EXEEXT) - -all: deltagit$(EXEEXT) - -revisions_report$(OBJEXT): revisions_report_header.inc - -revisions_report_header.inc: revisions_report_header.html - ../html/to_c_string.perl < revisions_report_header.html > $@ - -############################################################################### - -deltagit$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/deltagit/deltagit_main.cpp b/src/deltagit/deltagit_main.cpp deleted file mode 100644 index 2115a268c..000000000 --- a/src/deltagit/deltagit_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "deltagit_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - deltagit_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - deltagit_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/deltagit/revisions_report.cpp b/src/deltagit/revisions_report.cpp deleted file mode 100644 index cb943234b..000000000 --- a/src/deltagit/revisions_report.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/*******************************************************************\ - -Module: Show the overview for a repository - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "../html/html_escape.h" -#include "../html/logo.h" - -#include "revisions_report.h" -#include "job_status.h" -#include "deltagit_config.h" -#include "log_scale.h" - -const char revisions_report_header[]= -#include "revisions_report_header.inc" -; - -/*******************************************************************\ - -Function: shorten_message - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string shorten_message(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - unsigned line_count=1; - - for(unsigned i=0; i20) // arbitrary magic number - { - result+="...\n"; - break; - } - } - } - - return result; -} - -/*******************************************************************\ - -Function: htmlize_message - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string htmlize_message(const std::string &src) -{ - std::string result; - - result.reserve(src.size()); - - for(unsigned i=0; i': result+=">"; break; - case '"': result+="""; break; - case '&': result+="&"; break; - case '\n': result+="
"; break; - - // ' does not seem to be universally supported, - // and Unicode seems to suggest to prefer ’ over ' - case '\'': result+="&8217;"; break; - - default: - if(src[i]>=' ') result+=src[i]; - } - - return result; -} - -/*******************************************************************\ - -Function: height - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -unsigned height(const job_statust &job_status) -{ - unsigned lines=job_status.added+job_status.deleted; - if(lines==0) return 0; - if(lines==1) return 1; - return log10(lines)*10; -} - -/*******************************************************************\ - -Function: revisions_report - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void revisions_report( - bool partial_html, - const std::string &rel_path, - unsigned max_revs) -{ - deltagit_configt deltagit_config; - - std::string title="DeltaCheck Summary of Revisions"; - if(deltagit_config.description!="") - title+=" "+deltagit_config.description; - - std::list jobs; - - get_jobs(jobs); - - unsigned max_height=44; // the hight of log_scale.png - - std::string outfile_name= - partial_html?"include.html":"index.html"; - - std::ofstream out(outfile_name.c_str()); - - if(!partial_html) - { - out << "\n"; - out << "\n" - "\n" - "\n"; - - out << "" << html_escape(title) << "\n"; - - out << revisions_report_header; - - out << "\n\n"; - - out << "\n\n"; - - out << "\"DeltaCheck\n\n"; - } - - out << "
" - << html_escape(deltagit_config.description) - << "
\n"; - - out << "
\n"; - - out << "\n" - << "\n
\n" - << "\n" - << "\n"; - - unsigned counter=0, number_of_jobs=jobs.size(); - - for(std::list::const_iterator - j_it=jobs.begin(); - j_it!=jobs.end(); - j_it++, counter++) - { - if(max_revs!=0 && - counter+max_revsget_wd()+"/deltacheck-stat.json"; - jsont deltacheck_summary; - null_message_handlert null_message_handler; - parse_json(summary_file_name, null_message_handler, deltacheck_summary); - const jsont &properties=deltacheck_summary["properties"]; - passed=unsafe_string2unsigned(properties["passed"].value); - failed=unsafe_string2unsigned(properties["failed"].value); - } - - std::string tooltip= - "
"+j_it->id+"
"+ - ""; - if(j_it->author!="") tooltip+="Author: "+html_escape(j_it->author)+"
"; - if(j_it->date!="") tooltip+="Date: "+html_escape(j_it->date)+"
"; - tooltip+=htmlize_message(shorten_message(j_it->message)); - if(j_it->stage!=job_statust::DONE) - { - tooltip+="
"+html_escape(as_string(j_it->stage)); - tooltip+=" "+html_escape(as_string(j_it->status)); - if(j_it->hostname!="" && j_it->status!=job_statust::WAITING) - tooltip+=" on "+html_escape(as_string(j_it->hostname)); - tooltip+=""; - } - tooltip+= - "
"; - - unsigned h=std::min(height(*j_it), max_height); - - std::string link; - std::string bar_color="#7070e0"; - - if(j_it->stage==job_statust::ANALYSE) - { - link=rel_path+"/"+j_it->get_wd()+"/deltacheck-diff.html"; - } - else if(j_it->stage==job_statust::DONE) - { - link=rel_path+"/"+j_it->get_wd()+"/deltacheck-diff.html"; - - unsigned r, g; - if(passed+failed==0) - { - r=0; - g=255; - } - else - { - r=(unsigned long long)255*failed/(passed+failed); - g=(unsigned long long)255*passed/(passed+failed); - } - - char buffer[100]; - snprintf(buffer, 100, "#%02x%02x30", r, g); - bar_color=buffer; - } - else - { - if(j_it->status==job_statust::FAILURE) - bar_color="#e0e0e0"; - } - - if(link!="") - out << ""; - - out << "
id << "\"" - " onMouseOver=\"tooltip.show('" << tooltip << "');\"" - " onMouseOut=\"tooltip.hide();\"" - ">"; - - out << "
"; - - out << "
"; - out << "
"; - - if(link!="") - out << "
"; - - out << "\n"; - } - - out << "
\n"; - - // revisions - out << "
\n"; - - if(!partial_html) - { - out << "\n"; - out << "\n"; - } -} diff --git a/src/domains/Makefile b/src/domains/Makefile index b3436a465..86018b186 100644 --- a/src/domains/Makefile +++ b/src/domains/Makefile @@ -7,9 +7,8 @@ SRC = tpolyhedra_domain.cpp equality_domain.cpp domain.cpp predabs_domain.cpp\ template_generator_base.cpp template_generator_summary.cpp \ template_generator_callingcontext.cpp template_generator_ranking.cpp \ strategy_solver_binsearch2.cpp strategy_solver_binsearch3.cpp \ - strategy_solver_predabs.cpp \ - disjunctive_analyzer.cpp \ - + strategy_solver_predabs.cpp disjunctive_analyzer.cpp \ + simplify_transformer.cpp simplify_bounds.cpp #solver_enumeration.cpp include ../config.inc diff --git a/src/domains/disjunctive_analyzer.cpp b/src/domains/disjunctive_analyzer.cpp index 9d619c364..28d506624 100644 --- a/src/domains/disjunctive_analyzer.cpp +++ b/src/domains/disjunctive_analyzer.cpp @@ -12,7 +12,6 @@ Author: Peter Schrammel, Kumar Madhukar #include "ssa_analyzer.h" #include "disjunctive_analyzer.h" - /*******************************************************************\ Function: disjunctive_analyzert::eliminate_implication() @@ -27,13 +26,11 @@ Function: disjunctive_analyzert::eliminate_implication() void disjunctive_analyzert::eliminate_implication(exprt &formula) { - if(formula.id() == ID_implies) - formula = or_exprt(not_exprt(formula.op0()), formula.op1()); - + if(formula.id()==ID_implies) + formula=or_exprt(not_exprt(formula.op0()), formula.op1()); + Forall_operands(it, formula) eliminate_implication(*it); - - return; } /*******************************************************************\ @@ -50,30 +47,34 @@ Function: disjunctive_analyzert::push_negation_to_atoms() void disjunctive_analyzert::push_negation_to_atoms(exprt &formula) { - if(formula.id() == ID_not){ - if((formula.op0()).id() == ID_not){ - formula = (formula.op0()).op0(); + if(formula.id()==ID_not) + { + if((formula.op0()).id()==ID_not) + { + formula=(formula.op0()).op0(); } - else{ + else + { exprt::operandst operands; Forall_operands(it, formula.op0()) - operands.push_back(not_exprt(*it)); - - if((formula.op0()).id() == ID_and){ - formula = disjunction(operands); + operands.push_back(not_exprt(*it)); + + if((formula.op0()).id()==ID_and) + { + formula=disjunction(operands); } - else{ - if((formula.op0()).id() == ID_or){ - formula = conjunction(operands); - } + else + { + if((formula.op0()).id()==ID_or) + { + formula=conjunction(operands); + } } } } Forall_operands(it, formula) push_negation_to_atoms(*it); - - return; } /*******************************************************************\ @@ -90,11 +91,9 @@ Function: disjunctive_analyzert::convert_to_dnf() void disjunctive_analyzert::convert_to_dnf(exprt &formula) { - this->eliminate_implication(formula); - this->push_negation_to_atoms(formula); - this->convert_to_dnf_rec(formula); - - return; + eliminate_implication(formula); + push_negation_to_atoms(formula); + convert_to_dnf_rec(formula); } /*******************************************************************\ @@ -111,44 +110,47 @@ Function: disjunctive_analyzert::convert_to_dnf_rec() void disjunctive_analyzert::convert_to_dnf_rec(exprt &formula) { - if(formula.id() == ID_or){ + if(formula.id()==ID_or) + { Forall_operands(it, formula) convert_to_dnf_rec(*it); } - else{ - if(formula.id() == ID_and){ - + else + { + if(formula.id()==ID_and) + { Forall_operands(it, formula) - convert_to_dnf_rec(*it); - - while((formula.operands()).size() > 1){ - exprt::operandst first_operands, second_operands, combination; - - if(((formula.operands()).back()).id() == ID_or) - first_operands = ((formula.operands()).back()).operands(); - else - first_operands.push_back((formula.operands()).back()); - formula.operands().pop_back(); - - if(((formula.operands()).back()).id() == ID_or) - second_operands = ((formula.operands()).back()).operands(); - else - second_operands.push_back((formula.operands()).back()); - formula.operands().pop_back(); - - for(exprt::operandst::iterator f_it=first_operands.begin();f_it!=first_operands.end();f_it++){ - for(exprt::operandst::iterator s_it=second_operands.begin();s_it!=second_operands.end();s_it++){ - combination.push_back(and_exprt(*f_it,*s_it)); - } - } - - (formula.operands()).push_back(disjunction(combination)); + convert_to_dnf_rec(*it); + + while((formula.operands()).size()>1) + { + exprt::operandst first_operands, second_operands, combination; + + if(((formula.operands()).back()).id()==ID_or) + first_operands=((formula.operands()).back()).operands(); + else + first_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + if(((formula.operands()).back()).id()==ID_or) + second_operands=((formula.operands()).back()).operands(); + else + second_operands.push_back((formula.operands()).back()); + formula.operands().pop_back(); + + for(const auto &fo : first_operands) + { + for(const auto &so : second_operands) + { + combination.push_back(and_exprt(fo, so)); + } + } + + formula.operands().push_back(disjunction(combination)); } - formula = formula.op0(); + formula=formula.op0(); } } - - return; } /*******************************************************************\ @@ -164,143 +166,150 @@ Function: disjunctive_analyzert::operator() \*******************************************************************/ bool disjunctive_analyzert::operator()( - incremental_solvert &solver, - local_SSAt &SSA, - const exprt &side_conditions, - template_generator_baset &template_generator, - const exprt &disjunctive_conditions, - exprt &result_expr, - const domaint::var_sett &vars - ) + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars) { - bool response = true; + bool response=true; exprt::operandst result; - //std::cout << "unsimplified disjunctive cond: " << from_expr(SSA.ns, "", disjunctive_conditions) << "\n\n"; - exprt simple_disjunctive_conditions = simplify_expr(disjunctive_conditions, SSA.ns); //disjunctive_conditions; - //std::cout << " simplified disjunctive cond: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n"; + exprt simple_disjunctive_conditions= + simplify_expr(disjunctive_conditions, SSA.ns); // disjunctive_conditions - //converting simple_disjunctive_conditions into DNF - //std::cout << "before conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; - this->convert_to_dnf(simple_disjunctive_conditions); - //std::cout << " after conversion: " << from_expr(SSA.ns, "", simple_disjunctive_conditions) << "\n\n\n"; - - if(simple_disjunctive_conditions.id() == ID_or){ + // converting simple_disjunctive_conditions into DNF + convert_to_dnf(simple_disjunctive_conditions); + if(simple_disjunctive_conditions.id()==ID_or) + { exprt::operandst processed_disjuncts; - + exprt::operandst disjuncts=simple_disjunctive_conditions.operands(); - for(exprt::operandst::iterator d_it=disjuncts.begin();d_it!=disjuncts.end();d_it++){ - if((*d_it).id() == ID_not){ - //std::cout << "processing (not) disjunct: " << from_expr(SSA.ns, "", *d_it) << "\n"; - - exprt::operandst ops = (*d_it).operands(); - for(exprt::operandst::iterator o_it=ops.begin();o_it!=ops.end();o_it++){ - if((*o_it).id() == ID_equal){ - exprt::operandst ops_equality = (*o_it).operands(); - equal_exprt equal_expr_in_not = to_equal_expr(*o_it); - - bool constant_comparison = false; - for(exprt::operandst::iterator oe_it=ops_equality.begin();oe_it!=ops_equality.end();oe_it++){ - if((*oe_it).id() == ID_constant) - constant_comparison = true; - } - if(constant_comparison){ - processed_disjuncts.push_back(binary_relation_exprt(equal_expr_in_not.rhs(),ID_gt,equal_expr_in_not.lhs())); - processed_disjuncts.push_back(binary_relation_exprt(equal_expr_in_not.rhs(),ID_lt,equal_expr_in_not.lhs())); - } - else{ - processed_disjuncts.push_back(*d_it); - } - } - else{ - processed_disjuncts.push_back(*d_it); - } - } + for(auto &d : disjuncts) + { + if(d.id()==ID_not) + { + exprt::operandst ops=d.operands(); + for(auto &op : ops) + { + if(op.id()==ID_equal) + { + exprt::operandst ops_equality=op.operands(); + equal_exprt equal_expr_in_not=to_equal_expr(op); + + bool constant_comparison=false; + for(auto &oe : ops_equality) + { + if(oe.id()==ID_constant) + constant_comparison=true; + } + if(constant_comparison) + { + processed_disjuncts.push_back( + binary_relation_exprt( + equal_expr_in_not.rhs(), ID_gt, equal_expr_in_not.lhs())); + processed_disjuncts.push_back( + binary_relation_exprt( + equal_expr_in_not.rhs(), ID_lt, equal_expr_in_not.lhs())); + } + else + { + processed_disjuncts.push_back(d); + } + } + else + { + processed_disjuncts.push_back(d); + } + } } - else{ - processed_disjuncts.push_back(*d_it); + else + { + processed_disjuncts.push_back(d); } } - - for(exprt::operandst::iterator it=processed_disjuncts.begin();it!=processed_disjuncts.end();it++){ - //std::cout << "disjunct: " << from_expr(SSA.ns, "", *it) << "\n"; - + for(auto &d : processed_disjuncts) + { std::set disjunct_symbols; - find_symbols(*it,disjunct_symbols); + find_symbols(d, disjunct_symbols); - //TODO: decompose into convex regions for all variables - //assert(disjunct_symbols.size() == 1); + // TODO: decompose into convex regions for all variables + // assert(disjunct_symbols.size()==1); + // TODO: unclear what this loop should be doing symbol_exprt var; - for(std::set::const_iterator ds_it=disjunct_symbols.begin(); - ds_it!=disjunct_symbols.end(); ds_it++){ - var = *ds_it; + for(const auto &ds : disjunct_symbols) + { + var=ds; } - //std::cout << "symbol expr in disjunct: " << var << "\n\n"; exprt::operandst split_disjuncts; - - if((var.type().id() == ID_signedbv) || (var.type().id() == ID_unsignedbv)){ - - exprt smallest; - if(var.type().id()==ID_signedbv) - smallest = to_signedbv_type(var.type()).smallest_expr(); - if(var.type().id()==ID_unsignedbv) - smallest = to_unsignedbv_type(var.type()).smallest_expr(); - - split_disjuncts.push_back - (and_exprt(*it,binary_relation_exprt(var,ID_ge,plus_exprt(smallest,from_integer(mp_integer(1),var.type()))))); - - split_disjuncts.push_back(and_exprt(*it,binary_relation_exprt(var,ID_equal,smallest))); + + if((var.type().id()==ID_signedbv) || (var.type().id()==ID_unsignedbv)) + { + exprt smallest; + if(var.type().id()==ID_signedbv) + smallest=to_signedbv_type(var.type()).smallest_expr(); + if(var.type().id()==ID_unsignedbv) + smallest=to_unsignedbv_type(var.type()).smallest_expr(); + + split_disjuncts.push_back( + and_exprt( + d, + binary_relation_exprt( + var, + ID_ge, + plus_exprt(smallest, from_integer(mp_integer(1), var.type()))))); + + split_disjuncts.push_back( + and_exprt(d, binary_relation_exprt(var, ID_equal, smallest))); } - else{ - split_disjuncts.push_back(*it); + else + { + split_disjuncts.push_back(d); } - for(exprt::operandst::iterator s_it=split_disjuncts.begin();s_it!=split_disjuncts.end();s_it++){ - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - exprt cc = simplify_expr((and_exprt(side_conditions, *s_it)), SSA.ns); - response = response && (analyzer(solver,SSA,cc,template_generator)); - - exprt res; - analyzer.get_result(res, vars); - result.push_back(res); - - //std::cout << "disjunct passed: " << from_expr(SSA.ns, "", cc) << "\n"; - //std::cout << "disjunct's result: " << from_expr(SSA.ns, "", res) << "\n"; - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); + for(auto &sd : split_disjuncts) + { + ssa_analyzert analyzer; + analyzer.set_message_handler(get_message_handler()); + exprt cc=simplify_expr((and_exprt(side_conditions, sd)), SSA.ns); + response=response && (analyzer(solver, SSA, cc, template_generator)); + + exprt res; + analyzer.get_result(res, vars); + result.push_back(res); + + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); } } } - else{ - //for the complete disjunctive_conditions at once - //std::cout << "disjunction not at top-level" << "\n"; - + else + { + // for the complete disjunctive_conditions at once ssa_analyzert analyzer; analyzer.set_message_handler(get_message_handler()); - exprt cc = simplify_expr((and_exprt(side_conditions, simple_disjunctive_conditions)), SSA.ns); + exprt cc=simplify_expr( + and_exprt(side_conditions, simple_disjunctive_conditions), SSA.ns); + + response=analyzer(solver, SSA, cc, template_generator); - response = analyzer(solver,SSA,cc,template_generator); - exprt res; analyzer.get_result(res, vars); result.push_back(res); - //std::cout << "disjunct passed: " << from_expr(SSA.ns, "", cc) << "\n"; - //std::cout << "disjunct's result: " << from_expr(SSA.ns, "", res) << "\n"; - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); + // statistics + solver_instances+=analyzer.get_number_of_solver_instances(); + solver_calls+=analyzer.get_number_of_solver_calls(); } - - result_expr = disjunction(result); + + result_expr=disjunction(result); return response; } diff --git a/src/domains/disjunctive_analyzer.h b/src/domains/disjunctive_analyzer.h index 322d4a0a6..1b021bbb3 100644 --- a/src/domains/disjunctive_analyzer.h +++ b/src/domains/disjunctive_analyzer.h @@ -6,46 +6,45 @@ Author: Peter Schrammel, Kumar Madhukar \*******************************************************************/ -#ifndef DELTACHECK_DISJUNCTIVE_ANALYZER_H -#define DELTACHECK_DISJUNCTIVE_ANALYZER_H +#ifndef CPROVER_2LS_DOMAINS_DISJUNCTIVE_ANALYZER_H +#define CPROVER_2LS_DOMAINS_DISJUNCTIVE_ANALYZER_H -class disjunctive_analyzert : public messaget +class disjunctive_analyzert:public messaget { public: - explicit disjunctive_analyzert() - : - solver_instances(0), + disjunctive_analyzert(): + solver_instances(0), solver_calls(0) - { - } - - ~disjunctive_analyzert() - { - } + { + } + + ~disjunctive_analyzert() + { + } void eliminate_implication(exprt &formula); void push_negation_to_atoms(exprt &formula); void convert_to_dnf(exprt &formula); void convert_to_dnf_rec(exprt &formula); - - bool operator()(incremental_solvert &solver, - local_SSAt &SSA, - const exprt &side_conditions, - template_generator_baset &template_generator, - const exprt &disjunctive_conditions, - exprt &result_expr, - const domaint::var_sett &vars - ); + + bool operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &side_conditions, + template_generator_baset &template_generator, + const exprt &disjunctive_conditions, + exprt &result_expr, + const domaint::var_sett &vars); unsigned get_number_of_solver_instances() { return solver_instances; } unsigned get_number_of_solver_calls() { return solver_calls; } - + protected: - //statistics + // statistics unsigned solver_instances; unsigned solver_calls; }; #endif - + diff --git a/src/domains/domain_refinement.h b/src/domains/domain_refinement.h new file mode 100644 index 000000000..7cd6155ae --- /dev/null +++ b/src/domains/domain_refinement.h @@ -0,0 +1,39 @@ +/*******************************************************************\ + +Module: Domain Refinement Interface + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_DOMAIN_REFINEMENT_H +#define CPROVER_2LS_DOMAINS_DOMAIN_REFINEMENT_H + +#include "incremental_solver.h" +#include "template_generator_base.h" +#include + +class domain_refinementt +{ +public: + explicit domain_refinementt( + const local_SSAt &_SSA, + incremental_solvert &_solver) + : + SSA(_SSA), + solver(_solver) + {} + + // refine, returns true if there are no more refinements + virtual bool operator()() { return true; } + + // template generators associated with this SSA and solver + typedef std::map template_generatorst; + template_generatorst template_generators; + +protected: + const local_SSAt &SSA; + incremental_solvert &solver; +}; + +#endif // CPROVER_2LS_DOMAINS_DOMAIN_REFINEMENT_H diff --git a/src/domains/domain_refinement_variables.cpp b/src/domains/domain_refinement_variables.cpp new file mode 100644 index 000000000..cb906d8d1 --- /dev/null +++ b/src/domains/domain_refinement_variables.cpp @@ -0,0 +1,20 @@ +/*******************************************************************\ + +Module: Domain Refinement: Choice of Variables + +Author: Peter Schrammel + +\*******************************************************************/ + +bool domain_refinementt::operator()() +{ + // TODO: analyze counterexample + for(template_generatorst::iterator it=template_generators.begin(); + it!=template_generators.end(); ++it) + { + // TODO: select refinement to be performed + + // TODO: update template and domain + // it->add_template(); + } +} diff --git a/src/domains/incremental_solver.cpp b/src/domains/incremental_solver.cpp index f25f23cdf..750073850 100644 --- a/src/domains/incremental_solver.cpp +++ b/src/domains/incremental_solver.cpp @@ -150,14 +150,23 @@ Function: incremental_solvert::debug_add_to_formula \*******************************************************************/ -void incremental_solvert::debug_add_to_formula(const exprt &expr) +void incremental_solvert::debug_add_to_formula( + const exprt &_expr, exprt activation) { + if(_expr.id()!=ID_and) + { #ifdef NON_INCREMENTAL - // no debug mode for non-incremental yet + // no debug mode for non-incremental yet + assert(0); #else - literalt l=solver->convert(expr); - if(l.is_false()) - { + exprt expr; + if(activation.is_nil()) + expr=_expr; + else + expr=or_exprt(_expr, activation); + literalt l=solver->convert(expr); + if(l.is_false()) + { #ifdef DEBUG_OUTPUT debug() << "literal " << l << ": false=" << from_expr(ns, "", expr) < #include #include +#include #include "domain.h" #include "util.h" -// #define DISPLAY_FORMULA // #define NO_ARITH_REFINEMENT // #define NON_INCREMENTAL // (experimental) @@ -94,8 +94,38 @@ class incremental_solvert:public messaget solver->set_assumptions(whole_formula); #endif #endif - +#if defined(DEBUG_FORMULA) || defined(DISPLAY_FORMULA) + decision_proceduret::resultt result=(*solver)(); +#endif +#if defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT) + if(result==decision_proceduret::D_UNSATISFIABLE) + { + for(unsigned i=0; iis_in_conflict(formula[i])) + std::cout << "is_in_conflict: " + << from_expr(ns, "", formula_expr[i]) << std::endl; + } + } +#endif +#if (defined(DEBUG_FORMULA) && defined(DEBUG_OUTPUT)) || defined(DISPLAY_FORMULA) // NOLINT(whitespace/line_length) + if(result==decision_proceduret::D_SATISFIABLE) + { + std::set vars; + for(unsigned i=0; i::const_iterator it=vars.begin(); + it!=vars.end(); ++it) + { + std::cout << "assignment: " << from_expr(ns, "", *it) << "=" + << from_expr(ns, "", solver->get(*it)) << std::endl; + } + } + return result; +#endif +#if !defined(DEBUG_FORMULA) && !defined(DISPLAY_FORMULA) return (*solver)(); +#endif } exprt get(const exprt& expr) { return solver->get(expr); } @@ -125,7 +155,8 @@ class incremental_solvert:public messaget // for debugging bvt formula; - void debug_add_to_formula(const exprt &expr); + exprt::operandst formula_expr; + void debug_add_to_formula(const exprt &expr, exprt activation=nil_exprt()); // context assumption literals bvt activation_literals; @@ -190,6 +221,7 @@ static inline incremental_solvert &operator<<( else std::cerr << "add_to_solver: " << from_expr(dest.ns, "", src) << std::endl; + dest.formula_expr.push_back(src); #endif #ifdef NON_INCREMENTAL @@ -204,7 +236,9 @@ static inline incremental_solvert &operator<<( #else if(!dest.activation_literals.empty()) dest.debug_add_to_formula( - or_exprt(src, literal_exprt(!dest.activation_literals.back()))); + or_exprt( + src, + literal_exprt(!dest.activation_literals.back()))); else dest.debug_add_to_formula(src); #endif diff --git a/src/domains/lexlinrank_solver_enumeration.cpp b/src/domains/lexlinrank_solver_enumeration.cpp index bd87b6af5..68633968f 100644 --- a/src/domains/lexlinrank_solver_enumeration.cpp +++ b/src/domains/lexlinrank_solver_enumeration.cpp @@ -1,6 +1,6 @@ /*******************************************************************\ -Module: Enumeration-based solver for lexicographic linear ranking +Module: Enumeration-based solver for lexicographic linear ranking functions Author: Peter Schrammel @@ -32,12 +32,13 @@ Function: lexlinrank_solver_enumerationt::iterate \*******************************************************************/ -bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) +lexlinrank_solver_enumerationt::progresst +lexlinrank_solver_enumerationt::iterate(invariantt &_rank) { lexlinrank_domaint::templ_valuet &rank= static_cast(_rank); - bool improved=false; + progresst progress=CONVERGED; static std::vector number_elements_per_row; number_elements_per_row.resize(rank.size()); @@ -45,7 +46,6 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) solver.new_context(); - // choose round to even rounding mode for template computations // not clear what its implications on soundness and // termination of the synthesis are @@ -190,7 +190,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) } } - improved=true; + progress=CHANGED; // update the current template lexlinrank_domain.set_row_value(row, new_row_values, rank); @@ -221,7 +221,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) if(lexlinrank_domain.refine()) { debug() << "refining..." << eom; - improved=true; // refinement possible + progress=CHANGED; // refinement possible if(!refinement_constraint.is_true()) inner_solver->pop_context(); @@ -254,7 +254,7 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) debug() << "Inner solver: " << "the number of inner iterations for row " << row << " was reset to " << number_inner_iterations << eom; - improved=true; + progress=CHANGED; } } } @@ -278,5 +278,5 @@ bool lexlinrank_solver_enumerationt::iterate(invariantt &_rank) } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/lexlinrank_solver_enumeration.h b/src/domains/lexlinrank_solver_enumeration.h index 23744aa51..257be310f 100644 --- a/src/domains/lexlinrank_solver_enumeration.h +++ b/src/domains/lexlinrank_solver_enumeration.h @@ -1,6 +1,6 @@ /*******************************************************************\ -Module: Enumeration-based solver for lexicographic linear ranking +Module: Enumeration-based solver for lexicographic linear ranking functions Author: Peter Schrammel @@ -26,7 +26,7 @@ class lexlinrank_solver_enumerationt:public strategy_solver_baset const namespacet &_ns, unsigned _max_elements, // lexicographic components unsigned _max_inner_iterations): - strategy_solver_baset(_solver, literalt(), _ns), + strategy_solver_baset(_solver, const_literal(true), _ns), lexlinrank_domain(_lexlinrank_domain), max_elements(_max_elements), max_inner_iterations(_max_inner_iterations), @@ -36,7 +36,7 @@ class lexlinrank_solver_enumerationt:public strategy_solver_baset solver_instances++; } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: lexlinrank_domaint &lexlinrank_domain; diff --git a/src/domains/predabs_domain.h b/src/domains/predabs_domain.h index b63e8520c..cb3095e85 100644 --- a/src/domains/predabs_domain.h +++ b/src/domains/predabs_domain.h @@ -73,8 +73,8 @@ class predabs_domaint:public domaint // printing virtual void output_value( std::ostream &out, - const valuet &value, const - namespacet &ns) const; + const valuet &value, + const namespacet &ns) const; virtual void output_domain(std::ostream &out, const namespacet &ns) const; // projection diff --git a/src/domains/ranking_solver_enumeration.cpp b/src/domains/ranking_solver_enumeration.cpp index 9d196369b..bd04196d9 100644 --- a/src/domains/ranking_solver_enumeration.cpp +++ b/src/domains/ranking_solver_enumeration.cpp @@ -28,12 +28,13 @@ Function: ranking_solver_enumerationt::iterate \*******************************************************************/ -bool ranking_solver_enumerationt::iterate(invariantt &_rank) +ranking_solver_enumerationt::progresst +ranking_solver_enumerationt::iterate(invariantt &_rank) { linrank_domaint::templ_valuet &rank= static_cast(_rank); - bool improved=false; + progresst progress=CONVERGED; // context for "outer" solver solver.new_context(); @@ -142,7 +143,7 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) // update the current template linrank_domain.set_row_value(row, new_row_values, rank); - improved=true; + progress=CHANGED; } else { @@ -151,7 +152,7 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) if(linrank_domain.refine()) { debug() << "refining..." << eom; - improved=true; // refinement possible + progress=CHANGED; // refinement possible } else { @@ -174,5 +175,5 @@ bool ranking_solver_enumerationt::iterate(invariantt &_rank) solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/ranking_solver_enumeration.h b/src/domains/ranking_solver_enumeration.h index 2daaccdbc..b6e810b35 100644 --- a/src/domains/ranking_solver_enumeration.h +++ b/src/domains/ranking_solver_enumeration.h @@ -24,7 +24,7 @@ class ranking_solver_enumerationt:public strategy_solver_baset incremental_solvert &_solver, const namespacet &_ns, unsigned _max_inner_iterations): - strategy_solver_baset(_solver, literalt(), _ns), + strategy_solver_baset(_solver, literalt(), _ns), linrank_domain(_linrank_domain), max_inner_iterations(_max_inner_iterations), inner_solver(_ns), @@ -33,7 +33,7 @@ class ranking_solver_enumerationt:public strategy_solver_baset solver_instances++; } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: linrank_domaint &linrank_domain; diff --git a/src/domains/simplify_bounds.cpp b/src/domains/simplify_bounds.cpp new file mode 100644 index 000000000..8c63fa646 --- /dev/null +++ b/src/domains/simplify_bounds.cpp @@ -0,0 +1,454 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_bounds.h" +#include "simplify_bounds_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_boundst::get_min_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_min_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_min_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_smallest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_min_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_max_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_min_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::get_max_bound + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::get_max_bound(const exprt &expr, mp_integer &value) +{ +#ifdef DEBUGX + std::cout << "get_max_bound: " << from_expr(ns, "", expr) + << "\n"; +#endif + + if(!is_bitvector_type(expr.type())) + return false; + + if(expr.id()==ID_symbol) + { + value=get_largest(expr.type()); + return true; + } + else if(expr.id()==ID_typecast) + { + return get_max_bound(to_typecast_expr(expr).op(), value); + } + else if(expr.id()==ID_unary_minus) + { + bool result=get_min_bound(to_unary_minus_expr(expr).op(), value); + value=-value; + return result; + } + else if(expr.id()==ID_plus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_max_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr+vl; + return true; + } + } + else if(expr.id()==ID_minus) + { + mp_integer vr, vl; + bool rr=get_max_bound(expr.op0(), vr); + bool rl=get_min_bound(expr.op1(), vl); + if(rr && rl) + { + value=vr-vl; + return true; + } + } + + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::simplify_rec(exprt &expr) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_le) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=true_exprt(); + result=false; + } + else + result=clean_up_typecast(expr, rhs_value); + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_ge) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_lt) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + } + } + else if(expr.id()==ID_gt) + { + binary_relation_exprt &e=to_binary_relation_expr(expr); + if(is_bitvector_type(e.rhs().type()) && e.rhs().id()==ID_constant && + e.lhs().id()!=ID_symbol) + { + mp_integer rhs_value, lhs_max, lhs_min; + to_integer(e.rhs(), rhs_value); + if(get_max_bound(e.lhs(), lhs_max)) + { + if(lhs_max<=rhs_value) + { + expr=false_exprt(); + result=false; + } + } + else if(get_min_bound(e.lhs(), lhs_min)) + { + if(lhs_min>rhs_value) + { + expr=true_exprt(); + result=false; + } + } + } + } + else + { + Forall_operands(it, expr) + if(!simplify_rec(*it)) + result=false; + } +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_typecast + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_typecast(exprt &expr, + const mp_integer &rhs_value) +{ + if(expr.id()==ID_le && expr.op0().id()==ID_unary_minus && + expr.op0().op0().id()==ID_typecast) + { + const typet &inner_type=expr.op0().op0().op0().type(); + if(-rhs_value>get_smallest(inner_type)) + { + expr=binary_relation_exprt(expr.op0().op0().op0(), ID_ge, + from_integer(-rhs_value, inner_type)); + return true; + } + } + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::clean_up_implications(exprt &expr) +{ + bool result=true; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(!clean_up_implications(*it)) + result=false; + exprt::operandst::iterator it=expr.operands().begin(); + while(it!=expr.operands().end()) + { + if(it->is_true()) + it=expr.operands().erase(it); + else + ++it; + } + if(expr.operands().empty()) + expr=true_exprt(); + else if(expr.operands().size()==1) + expr=expr.op0(); + } + else if(expr.id()==ID_implies) + { + if(expr.op1().is_true()) + { + expr=true_exprt(); + result=false; + } + } + return result; +} + +/*******************************************************************\ + +Function: simplify_boundst::clean_up_implications + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::regroup_implications(exprt &expr) +{ + std::map implication_map; + exprt::operandst r; + if(expr.id()==ID_and) + { + Forall_operands(it, expr) + if(it->id()==ID_implies) + implication_map[it->op0()].push_back(it->op1()); + else + r.push_back(*it); + } + else + return true; + for(auto &i : implication_map) + r.push_back(implies_exprt(i.first, conjunction(i.second))); + expr=conjunction(r); + return false; +} + +/*******************************************************************\ + +Function: simplify_boundst::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_boundst::operator()(exprt &expr) +{ + bool result=simplify_rec(expr); + if(!clean_up_implications(expr)) + result=false; + if(!regroup_implications(expr)) + result=false; + return result; +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_bounds(exprt &expr, + const namespacet &ns) +{ + return simplify_boundst(ns)(expr); +} + +/*******************************************************************\ + +Function: simplify_bounds + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_bounds(const exprt &src, + const namespacet &ns) +{ + exprt tmp=src; + simplify_boundst simplify_bounds(ns); + simplify_bounds(tmp); + return tmp; +} + diff --git a/src/domains/simplify_bounds.h b/src/domains/simplify_bounds.h new file mode 100644 index 000000000..df8ce143c --- /dev/null +++ b/src/domains/simplify_bounds.h @@ -0,0 +1,32 @@ +/*******************************************************************\ + +Module: Bounds simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H + +#include + +class exprt; +class namespacet; + +// +// simplify bounds +// +// true: did nothing +// false: simplified something +// + +bool simplify_bounds( + exprt &expr, + const namespacet &ns); + +exprt simplify_bounds( + const exprt &src, + const namespacet &ns); + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_H diff --git a/src/domains/simplify_bounds_class.h b/src/domains/simplify_bounds_class.h new file mode 100644 index 000000000..c4622c100 --- /dev/null +++ b/src/domains/simplify_bounds_class.h @@ -0,0 +1,69 @@ +/*******************************************************************\ + +Module: Bounds Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_boundst +{ +public: + explicit simplify_boundst(const namespacet &_ns): + ns(_ns) + { + } + + virtual ~simplify_boundst() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv; + } + inline static mp_integer get_largest(const typet &type) + { + if(type.id()==ID_signedbv) + return to_signedbv_type(type).largest(); + else if(type.id()==ID_unsignedbv) + return to_unsignedbv_type(type).largest(); + assert(false); + } + inline static mp_integer get_smallest(const typet &type) + { + if(type.id()==ID_signedbv) + return to_signedbv_type(type).smallest(); + else if(type.id()==ID_unsignedbv) + return to_unsignedbv_type(type).smallest(); + assert(false); + } + +protected: + const namespacet &ns; + + bool simplify_rec(exprt &expr); + bool get_min_bound(const exprt &expr, mp_integer &value); + bool get_max_bound(const exprt &expr, mp_integer &value); + + bool clean_up_implications(exprt &expr); + bool regroup_implications(exprt &expr); + bool clean_up_typecast(exprt &expr, const mp_integer &value); +}; + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_BOUNDS_CLASS_H diff --git a/src/domains/simplify_transformer.cpp b/src/domains/simplify_transformer.cpp new file mode 100644 index 000000000..ebddc12ea --- /dev/null +++ b/src/domains/simplify_transformer.cpp @@ -0,0 +1,259 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include "simplify_transformer.h" +#include "simplify_transformer_class.h" +#include + +#define DEBUGX + +#ifdef DEBUGX +#include +#include +#endif + +/*******************************************************************\ + +Function: simplify_transformert::collect_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void simplify_transformert::collect_node( + const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy) +{ + if(expr.id()==ID_equal) + { + const equal_exprt &e=to_equal_expr(expr); + + bool rhs_is_constant=e.rhs().id()==ID_constant; + bool rhs_is_symbol=e.rhs().id()==ID_symbol || + e.rhs().id()==ID_nondet_symbol; + bool rhs_is_frozen=rhs_is_symbol && + frozen_symbols.find(e.rhs().get(ID_identifier))!=frozen_symbols.end(); + bool lhs_is_constant=e.lhs().id()==ID_constant; + bool lhs_is_symbol=e.lhs().id()==ID_symbol || + e.lhs().id()==ID_nondet_symbol; + bool lhs_is_frozen=lhs_is_symbol && + frozen_symbols.find(e.lhs().get(ID_identifier))!=frozen_symbols.end(); + + exprt lhs, rhs; + lhs.make_nil(); + rhs.make_nil(); + // stupid matching + if((rhs_is_frozen || rhs_is_constant || !frozen_only) && + lhs_is_symbol && !lhs_is_frozen) + { + lhs=e.lhs(); + rhs=e.rhs(); + } + if((lhs_is_frozen || lhs_is_constant || !frozen_only) && + rhs_is_symbol && !rhs_is_frozen) + { + rhs=e.lhs(); + lhs=e.rhs(); + } + if(rhs.is_not_nil() && lhs.is_not_nil()) + { + if(make_copy) // make lazy copy + { + replace_mapt _subst=substitutions; + substitutions=_subst; + } + substitutions[lhs]=rhs; + } + } + +#ifdef DEBUGX + std::cout << "COLLECT: " << from_expr(ns, "", expr) << std::endl; + for(const auto &it : substitutions) + std::cout << from_expr(ns, "", it.first) + << "---> " << from_expr(ns, "", it.second) + << "\n"; +#endif +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_node + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_node( + exprt &expr, + const replace_mapt &substitutions) +{ + return replace_expr(substitutions, expr); +} + +/*******************************************************************\ + +Function: simplify_transformert::simplify_rec + + Inputs: + + Outputs: returns true if expression unchanged; + returns false if changed + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::simplify_rec( + exprt &expr, + replace_mapt &substitutions) +{ +#ifdef DEBUGX + exprt old(expr); +#endif + + bool result=true; + if(expr.id()==ID_and) + { + // first propagate from frozen symbols + bool res=false; + do + { + Forall_operands(it, expr) + collect_node(*it, substitutions, true, false); + + Forall_operands(it, expr) + if(!simplify_rec(*it, substitutions)) + result=false; + + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + + // simplify remaining equalities + Forall_operands(it, expr) + collect_node(*it, substitutions, false, false); + + res=false; + do + { + res=simplify_node(expr, substitutions); + if(!res) result=false; + } + while(!res); + } + +#if 0 // for later extension to disjunctions + // TODO: handle negation, too + else if(expr.id()==ID_or || expr.id()==ID_implies) + { + Forall_operands(it, expr) + { + collect_node(*it, substitutions, true); + if(!simplify_rec(*it, substitutions)) + result=false; + + bool res=false; + do + { + res=simplify_node(*it, substitutions); + if(!res) result=false; + } + while(!res); + } + } +#endif + +#ifdef DEBUGX + std::cout << "===== " << from_expr(ns, "", old) + << "\n ---> " << from_expr(ns, "", expr) + << "\n"; +#endif + + return result; +} + +/*******************************************************************\ + +Function: simplify_transformert::operator() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify_transformert::operator()(exprt &expr) +{ + replace_mapt substitutions; + return simplify_rec(expr, substitutions); +} + +/*******************************************************************\ + +Function: simplify + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool simplify( + exprt &expr, + const std::set &frozen_vars, + const namespacet &ns) +{ + return simplify_transformert(ns, frozen_vars)(expr); +} + +/*******************************************************************\ + +Function: simplify_transformer + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, + const namespacet &ns) +{ +#ifdef DEBUGX + std::cout << "FROZEN:"; + for(const auto &it : frozen_vars) + std::cout << " " << it; + std::cout << "\n"; +#endif + + exprt tmp=src; + simplify_transformert(ns, frozen_vars)(tmp); + return tmp; +} + diff --git a/src/domains/simplify_transformer.h b/src/domains/simplify_transformer.h new file mode 100644 index 000000000..31779d6f0 --- /dev/null +++ b/src/domains/simplify_transformer.h @@ -0,0 +1,35 @@ +/*******************************************************************\ + +Module: Transformer simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H + +#include +#include + +class exprt; +class namespacet; + +// +// simplify transformers by best-effort intermediate variable elimination +// +// true: did nothing +// false: simplified something +// + +bool simplify( + exprt &expr, + const std::set &frozen_vars, // do not eliminate these + const namespacet &ns); + +exprt simplify_transformer( + const exprt &src, + const std::set &frozen_vars, // do not eliminate these + const namespacet &ns); + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_H diff --git a/src/domains/simplify_transformer_class.h b/src/domains/simplify_transformer_class.h new file mode 100644 index 000000000..9a9218516 --- /dev/null +++ b/src/domains/simplify_transformer_class.h @@ -0,0 +1,61 @@ +/*******************************************************************\ + +Module: Transformer Simplification + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H +#define CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H + +#include +#include + +class exprt; +class namespacet; + +class simplify_transformert +{ +public: + simplify_transformert( + const namespacet &_ns, + const std::set &_frozen_symbols): + ns(_ns), + frozen_symbols(_frozen_symbols) + { + } + + virtual ~simplify_transformert() + { + } + + // These below all return 'true' if the simplification wasn't applicable. + // If false is returned, the expression has changed. + + // main recursion + bool simplify_rec(exprt &expr, replace_mapt &substitutions); + + virtual bool operator()(exprt &expr); + + inline static bool is_bitvector_type(const typet &type) + { + return type.id()==ID_unsignedbv || + type.id()==ID_signedbv || + type.id()==ID_bv; + } + +protected: + const namespacet &ns; + const std::set &frozen_symbols; + + void collect_node( + const exprt &expr, + replace_mapt &substitutions, + bool frozen_only, + bool make_copy); + + bool simplify_node(exprt &expr, const replace_mapt &substitutions); +}; + +#endif // CPROVER_2LS_DOMAINS_SIMPLIFY_TRANSFORMER_CLASS_H diff --git a/src/domains/ssa_analyzer.cpp b/src/domains/ssa_analyzer.cpp index f18e9d3ac..8546b5186 100644 --- a/src/domains/ssa_analyzer.cpp +++ b/src/domains/ssa_analyzer.cpp @@ -1,207 +1,215 @@ -/*******************************************************************\ - -Module: SSA Analyzer - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifdef DEBUG -#include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "strategy_solver_base.h" -#include "strategy_solver_enumeration.h" -#include "strategy_solver_binsearch.h" -#include "strategy_solver_binsearch2.h" -#include "strategy_solver_binsearch3.h" -#include "strategy_solver_equality.h" -#include "linrank_domain.h" -#include "lexlinrank_domain.h" -#include "ranking_solver_enumeration.h" -#include "lexlinrank_solver_enumeration.h" -#include "template_generator_ranking.h" -#include "strategy_solver_predabs.h" -#include "ssa_analyzer.h" - -#define BINSEARCH_SOLVER strategy_solver_binsearcht(\ - *static_cast(domain), solver, assertions_check, SSA.ns) -#if 0 -#define BINSEARCH_SOLVER strategy_solver_binsearch2t(\ - *static_cast(domain), solver, SSA.ns) -#define BINSEARCH_SOLVER strategy_solver_binsearch3t(\ - *static_cast(domain), solver, SSA, SSA.ns) -#endif - -/*******************************************************************\ - -Function: ssa_analyzert::operator() - - Inputs: - - Outputs: true if the computation was not aborted due to - assertion_checks that did not pass - Purpose: - -\*******************************************************************/ - -bool ssa_analyzert::operator()( - incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator, - bool check_assertions) -{ - if(SSA.goto_function.body.instructions.empty()) - return true; - - solver << SSA; - SSA.mark_nodes(); - - solver.new_context(); - solver << SSA.get_enabling_exprs(); - - // add precondition (or conjunction of asssertion in backward analysis) - solver << precondition; - - domain=template_generator.domain(); - - // get assertions if check_assertions is requested - literalt assertions_check=const_literal(false); - bvt assertion_literals; - - if(check_assertions) - { - exprt::operandst ll; - for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - assert(n_it->assertions.size()<=1); - for(local_SSAt::nodet::assertionst::const_iterator - a_it=n_it->assertions.begin(); - a_it!=n_it->assertions.end(); - a_it++) - { - literalt l = solver.solver->convert(*a_it); - assertion_literals.push_back(!l); - ll.push_back(literal_exprt(!l)); - nonpassed_assertions.push_back(n_it); - } - } - assertions_check = solver.solver->convert(disjunction(ll)); - } - - // get strategy solver from options - strategy_solver_baset *strategy_solver; - - if(template_generator.options.get_bool_option("compute-ranking-functions")) - { - if(template_generator.options.get_bool_option( - "monolithic-ranking-function")) - { - strategy_solver=new ranking_solver_enumerationt( - *static_cast(domain), solver, SSA.ns, - template_generator.options.get_unsigned_int_option( - "max-inner-ranking-iterations")); - result=new linrank_domaint::templ_valuet(); - } - else - { - strategy_solver=new lexlinrank_solver_enumerationt( - *static_cast(domain), solver, SSA.ns, - template_generator.options.get_unsigned_int_option( - "lexicographic-ranking-function"), - template_generator.options.get_unsigned_int_option( - "max-inner-ranking-iterations")); - result=new lexlinrank_domaint::templ_valuet(); - } - } - - else if(template_generator.options.get_bool_option("equalities")) - { - strategy_solver=new strategy_solver_equalityt( - *static_cast(domain), solver, assertions_check, SSA.ns); - result=new equality_domaint::equ_valuet(); - } - else - { - if(template_generator.options.get_bool_option("enum-solver")) - { - result=new tpolyhedra_domaint::templ_valuet(); - strategy_solver=new strategy_solver_enumerationt( - *static_cast(domain), solver, assertions_check, SSA.ns); - } - else if(template_generator.options.get_bool_option("predabs-solver")) - { - result=new predabs_domaint::templ_valuet(); - strategy_solver=new strategy_solver_predabst( - *static_cast(domain), solver, SSA.ns); - } - else if(template_generator.options.get_bool_option("binsearch-solver")) - { - result=new tpolyhedra_domaint::templ_valuet(); - strategy_solver=new BINSEARCH_SOLVER; - } - else - assert(false); - } - - strategy_solver->set_message_handler(get_message_handler()); - - // initialize inv - domain->initialize(*result); - - // iterate - while(strategy_solver->iterate(*result)) {} -/* - //get status of assertions - if(!assertions_check.is_false() && - status==strategy_solver_baset::FAILED) - { - nonpassed_assertionst::iterator it=nonpassed_assertions.begin(); - for(unsigned i=0;il_get(assertion_literals[i]).is_true()) - nonpassed_assertions.erase(it++); - else - ++it; - } - } - else - nonpassed_assertions.clear(); -*/ - solver.pop_context(); - - //statistics - solver_calls += strategy_solver->get_number_of_solver_calls(); - solver_instances += strategy_solver->get_number_of_solver_instances(); - - delete strategy_solver; - - //return (status == strategy_solver_baset::CONVERGED); -} - -/*******************************************************************\ - -Function: ssa_analyzert::get_result - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) -{ - domain->project_on_vars(*result, vars, _result); -} +/*******************************************************************\ + +Module: SSA Analyzer + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "strategy_solver_base.h" +#include "strategy_solver_enumeration.h" +#include "strategy_solver_binsearch.h" +#include "strategy_solver_binsearch2.h" +#include "strategy_solver_binsearch3.h" +#include "strategy_solver_equality.h" +#include "linrank_domain.h" +#include "lexlinrank_domain.h" +#include "ranking_solver_enumeration.h" +#include "lexlinrank_solver_enumeration.h" +#include "template_generator_ranking.h" +#include "strategy_solver_predabs.h" +#include "ssa_analyzer.h" + +#define BINSEARCH_SOLVER strategy_solver_binsearcht( \ + *static_cast(domain), \ + solver, assertions_check, SSA.ns) +#if 0 +#define BINSEARCH_SOLVER strategy_solver_binsearch2t( \ + *static_cast(domain), solver, \ + assertions_check, SSA.ns) +#define BINSEARCH_SOLVER strategy_solver_binsearch3t( \ + *static_cast(domain), solver, \ + assertions_check, SSA, SSA.ns) +#endif + +/*******************************************************************\ + +Function: ssa_analyzert::operator() + + Inputs: + + Outputs: true if the computation was not aborted due to + assertion_checks that did not pass + Purpose: + +\*******************************************************************/ + +bool ssa_analyzert::operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions) +{ + if(SSA.goto_function.body.instructions.empty()) + return true; + + solver << SSA; + SSA.mark_nodes(); + + solver.new_context(); + solver << SSA.get_enabling_exprs(); + + // add precondition (or conjunction of asssertion in backward analysis) + solver << precondition; + + domain=template_generator.domain(); + + // get assertions if check_assertions is requested + literalt assertions_check=const_literal(false); + bvt assertion_literals; + if(check_assertions) + { + exprt::operandst ll; + for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + assert(n_it->assertions.size()<=1); + for(local_SSAt::nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + { + literalt l=solver.solver->convert(*a_it); + assertion_literals.push_back(!l); + ll.push_back(literal_exprt(!l)); + nonpassed_assertions.push_back(n_it); + } + } + assertions_check=solver.solver->convert(disjunction(ll)); + } + + // get strategy solver from options + strategy_solver_baset *strategy_solver; + if(template_generator.options.get_bool_option("compute-ranking-functions")) + { + if(template_generator.options.get_bool_option( + "monolithic-ranking-function")) + { + strategy_solver=new ranking_solver_enumerationt( + *static_cast(domain), solver, SSA.ns, + template_generator.options.get_unsigned_int_option( + "max-inner-ranking-iterations")); + result=new linrank_domaint::templ_valuet(); + } + else + { + strategy_solver=new lexlinrank_solver_enumerationt( + *static_cast(domain), solver, SSA.ns, + template_generator.options.get_unsigned_int_option( + "lexicographic-ranking-function"), + template_generator.options.get_unsigned_int_option( + "max-inner-ranking-iterations")); + result=new lexlinrank_domaint::templ_valuet(); + } + } + else if(template_generator.options.get_bool_option("equalities")) + { + strategy_solver=new strategy_solver_equalityt( + *static_cast(domain), + solver, assertions_check, SSA.ns); + result=new equality_domaint::equ_valuet(); + } + else + { + if(template_generator.options.get_bool_option("enum-solver")) + { + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new strategy_solver_enumerationt( + *static_cast(domain), + solver, assertions_check, SSA.ns); + } + else if(template_generator.options.get_bool_option("predabs-solver")) + { + result=new predabs_domaint::templ_valuet(); + strategy_solver=new strategy_solver_predabst( + *static_cast(domain), + solver, assertions_check, SSA.ns); + } + else if(template_generator.options.get_bool_option("binsearch-solver")) + { + result=new tpolyhedra_domaint::templ_valuet(); + strategy_solver=new BINSEARCH_SOLVER; + } + else + assert(false); + } + + strategy_solver->set_message_handler(get_message_handler()); + + // initialize inv + domain->initialize(*result); + + strategy_solver_baset::progresst status; + + do + { + status=strategy_solver->iterate(*result); + } + while(status==strategy_solver_baset::CHANGED); + + // get status of assertions + if(!assertions_check.is_false() && + status==strategy_solver_baset::FAILED) + { + nonpassed_assertionst::iterator it=nonpassed_assertions.begin(); + for(unsigned i=0; il_get(assertion_literals[i]).is_true()) + nonpassed_assertions.erase(it++); + else + ++it; + } + } + else + nonpassed_assertions.clear(); + + solver.pop_context(); + + // statistics + solver_calls+=strategy_solver->get_number_of_solver_calls(); + solver_instances+=strategy_solver->get_number_of_solver_instances(); + + delete strategy_solver; + + return (status==strategy_solver_baset::CONVERGED); +} + +/*******************************************************************\ + +Function: ssa_analyzert::get_result + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_analyzert::get_result(exprt &_result, const domaint::var_sett &vars) +{ + domain->project_on_vars(*result, vars, _result); +} diff --git a/src/domains/ssa_analyzer.h b/src/domains/ssa_analyzer.h index 1c9050ff7..35cc63626 100644 --- a/src/domains/ssa_analyzer.h +++ b/src/domains/ssa_analyzer.h @@ -1,70 +1,67 @@ -/*******************************************************************\ - -Module: SSA Analyzer - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_2LS_DOMAINS_SSA_ANALYZER_H -#define CPROVER_2LS_DOMAINS_SSA_ANALYZER_H - -#include - -#include - -#include "strategy_solver_base.h" -#include "template_generator_base.h" - -class ssa_analyzert:public messaget -{ -public: - typedef strategy_solver_baset::constraintst constraintst; - typedef strategy_solver_baset::var_listt var_listt; - - ssa_analyzert(): - result(NULL), - solver_instances(0), - solver_calls(0) - { - } - - ~ssa_analyzert() - { - if(result!=NULL) - delete result; - } - - // returns true if the computation was not aborted due to - // assertion_checks that did not pass - bool operator()( - incremental_solvert &solver, - local_SSAt &SSA, - const exprt &precondition, - template_generator_baset &template_generator, - bool check_assertions=false); - - //retrieve the result if operator() returned true - void get_result(exprt &result, const domaint::var_sett &vars); - - //retrieve the non-passed assertions if operator() returned false - typedef std::list - nonpassed_assertionst; - nonpassed_assertionst get_nonpassed_assertions() - { return nonpassed_assertions; } - - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } - -protected: - domaint *domain; // template generator is responsable for the domain object - domaint::valuet *result; - nonpassed_assertionst nonpassed_assertions; - - // statistics - unsigned solver_instances; - unsigned solver_calls; -}; - -#endif - +/*******************************************************************\ + +Module: SSA Analyzer + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_DOMAINS_SSA_ANALYZER_H +#define CPROVER_2LS_DOMAINS_SSA_ANALYZER_H + +#include + +#include + +#include "strategy_solver_base.h" +#include "template_generator_base.h" + +class ssa_analyzert:public messaget +{ +public: + typedef strategy_solver_baset::constraintst constraintst; + typedef strategy_solver_baset::var_listt var_listt; + + ssa_analyzert(): + result(nullptr), + solver_instances(0), + solver_calls(0) + { + } + + ~ssa_analyzert() + { + if(result!=nullptr) + delete result; + } + + bool operator()( + incremental_solvert &solver, + local_SSAt &SSA, + const exprt &precondition, + template_generator_baset &template_generator, + bool check_assertions=false); + + // retrieve the result if operator() returned true + void get_result(exprt &result, const domaint::var_sett &vars); + + // retrieve the non-passed assertions if operator() returned false + typedef std::list + nonpassed_assertionst; + nonpassed_assertionst get_nonpassed_assertions() + { return nonpassed_assertions; } + + inline unsigned get_number_of_solver_instances() { return solver_instances; } + inline unsigned get_number_of_solver_calls() { return solver_calls; } + +protected: + domaint *domain; // template generator is responsable for the domain object + domaint::valuet *result; + nonpassed_assertionst nonpassed_assertions; + + // statistics + unsigned solver_instances; + unsigned solver_calls; +}; + +#endif diff --git a/src/domains/strategy_solver_base.h b/src/domains/strategy_solver_base.h index 11361519f..1244a7bdc 100644 --- a/src/domains/strategy_solver_base.h +++ b/src/domains/strategy_solver_base.h @@ -32,15 +32,14 @@ class strategy_solver_baset:public messaget solver_calls(0) {} - virtual bool iterate(invariantt &inv) { assert(false); } + virtual progresst iterate(invariantt &inv) { assert(false); } - inline unsigned get_number_of_solver_calls() { return solver_calls; } - inline unsigned get_number_of_solver_instances() { return solver_instances; } + unsigned get_number_of_solver_calls() { return solver_calls; } + unsigned get_number_of_solver_instances() { return solver_instances; } protected: incremental_solvert &solver; literalt assertion_check; - const namespacet &ns; // handles on values to retrieve from model diff --git a/src/domains/strategy_solver_binsearch.cpp b/src/domains/strategy_solver_binsearch.cpp index 97ec242df..687eed4da 100644 --- a/src/domains/strategy_solver_binsearch.cpp +++ b/src/domains/strategy_solver_binsearch.cpp @@ -25,12 +25,13 @@ Function: strategy_solver_binsearcht::iterate \*******************************************************************/ -bool strategy_solver_binsearcht::iterate(invariantt &_inv) +strategy_solver_baset::progresst strategy_solver_binsearcht::iterate( + invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved=false; + progresst progress=CONVERGED; solver.new_context(); // for improvement check @@ -66,7 +67,7 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) #endif solver << or_exprt(disjunction(strategy_cond_exprs), - literal_exprt(assertion_check)); + literal_exprt(assertion_check)); #if 0 debug() << "solve(): "; @@ -232,10 +233,8 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) debug() << "update value: " << from_expr(ns, "", lower) << eom; - solver.pop_context(); // symbolic value system - tpolyhedra_domain.set_row_value(row, lower, inv); - improved=true; + progress=CHANGED; } else { @@ -256,5 +255,5 @@ bool strategy_solver_binsearcht::iterate(invariantt &_inv) solver.pop_context(); // improvement check } - return improved; + return progress; } diff --git a/src/domains/strategy_solver_binsearch.h b/src/domains/strategy_solver_binsearch.h index 367bc9cc8..e47c6cfef 100644 --- a/src/domains/strategy_solver_binsearch.h +++ b/src/domains/strategy_solver_binsearch.h @@ -25,7 +25,7 @@ class strategy_solver_binsearcht:public strategy_solver_baset { } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; diff --git a/src/domains/strategy_solver_binsearch2.cpp b/src/domains/strategy_solver_binsearch2.cpp index 864a7a6b3..d97bb40a8 100644 --- a/src/domains/strategy_solver_binsearch2.cpp +++ b/src/domains/strategy_solver_binsearch2.cpp @@ -32,14 +32,13 @@ Function: strategy_solver_binsearch2t::iterate \*******************************************************************/ -//TODO: implement assertion check - -bool strategy_solver_binsearch2t::iterate(invariantt &_inv) +strategy_solver_binsearch2t::progresst +strategy_solver_binsearch2t::iterate(invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved=false; + progresst progress=CONVERGED; solver.new_context(); // for improvement check @@ -89,7 +88,7 @@ bool strategy_solver_binsearch2t::iterate(invariantt &_inv) #if 0 debug() << "SAT" << eom; #endif - improved=true; + progress=CHANGED; std::size_t row=0; for(; row(_inv); - bool improved=false; + progresst progress=CONVERGED; solver.new_context(); // for improvement check @@ -86,7 +85,7 @@ bool strategy_solver_binsearch3t::iterate(invariantt &_inv) #if 0 debug() << "SAT" << eom; #endif - improved=true; + progress=CHANGED; std::size_t row=0; for(; row -#include "../ssa/local_ssa.h" #include "strategy_solver_base.h" #include "tpolyhedra_domain.h" @@ -21,15 +20,15 @@ class strategy_solver_binsearch3t:public strategy_solver_baset explicit strategy_solver_binsearch3t( tpolyhedra_domaint &_tpolyhedra_domain, incremental_solvert &_solver, - literalt _assertion_check, local_SSAt& _SSA, - const namespacet &_ns) : - strategy_solver_baset( _solver, _assertion_check, _ns), + literalt _assertion_check, + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), SSA(_SSA), tpolyhedra_domain(_tpolyhedra_domain), sum_bound_counter(0) {} - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: local_SSAt &SSA; diff --git a/src/domains/strategy_solver_enumeration.cpp b/src/domains/strategy_solver_enumeration.cpp index be2ac3c4d..7d6f3e976 100644 --- a/src/domains/strategy_solver_enumeration.cpp +++ b/src/domains/strategy_solver_enumeration.cpp @@ -28,12 +28,13 @@ Function: strategy_solver_enumerationt::iterate \*******************************************************************/ -bool strategy_solver_enumerationt::iterate(invariantt &_inv) +strategy_solver_baset::progresst strategy_solver_enumerationt::iterate( + invariantt &_inv) { tpolyhedra_domaint::templ_valuet &inv= static_cast(_inv); - bool improved=false; + progresst progress=CONVERGED; solver.new_context(); @@ -68,8 +69,8 @@ bool strategy_solver_enumerationt::iterate(invariantt &_inv) debug() << eom; #endif - solver << or_exprt(disjunction(strategy_cond_exprs), - literal_exprt(assertion_check)); + solver << or_exprt(disjunction(strategy_cond_exprs), + literal_exprt(assertion_check)); #ifdef DEBUG_OUTPUT debug() << "solve(): "; @@ -145,9 +146,14 @@ bool strategy_solver_enumerationt::iterate(invariantt &_inv) << ", simplified value: " << from_expr(ns, "", v) << eom; tpolyhedra_domain.set_row_value(row, v, inv); + progress=CHANGED; } } - improved=true; + if(!progress==CHANGED) // only possible if assertion check fails + { + solver.pop_context(); + return FAILED; + } } else { @@ -167,5 +173,5 @@ bool strategy_solver_enumerationt::iterate(invariantt &_inv) } solver.pop_context(); - return improved; + return progress; } diff --git a/src/domains/strategy_solver_enumeration.h b/src/domains/strategy_solver_enumeration.h index edfb75a9e..4383e4d0e 100644 --- a/src/domains/strategy_solver_enumeration.h +++ b/src/domains/strategy_solver_enumeration.h @@ -19,13 +19,13 @@ class strategy_solver_enumerationt:public strategy_solver_baset tpolyhedra_domaint &_tpolyhedra_domain, incremental_solvert &_solver, literalt _assertion_check, - const namespacet &_ns): - strategy_solver_baset(_solver, _assertion_check, _ns), - tpolyhedra_domain(_tpolyhedra_domain) + const namespacet &_ns): + strategy_solver_baset(_solver, _assertion_check, _ns), + tpolyhedra_domain(_tpolyhedra_domain) { } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: tpolyhedra_domaint &tpolyhedra_domain; diff --git a/src/domains/strategy_solver_equality.cpp b/src/domains/strategy_solver_equality.cpp index b14446aa4..43602e881 100644 --- a/src/domains/strategy_solver_equality.cpp +++ b/src/domains/strategy_solver_equality.cpp @@ -21,11 +21,12 @@ Function: strategy_solver_equalityt::iterate Outputs: - Purpose: + Purpose: Comment: assertion check is not possible + because this is a gfp solver \*******************************************************************/ -bool strategy_solver_equalityt::iterate(invariantt &_inv) +strategy_solver_baset::progresst strategy_solver_equalityt::iterate(invariantt &_inv) { equality_domaint::equ_valuet &inv= static_cast(_inv); @@ -83,7 +84,7 @@ bool strategy_solver_equalityt::iterate(invariantt &_inv) { e_it=todo_disequs.begin(); if(e_it==todo_disequs.end()) - return false; // done + return CONVERGED; // done solver.new_context(); diff --git a/src/domains/strategy_solver_equality.h b/src/domains/strategy_solver_equality.h index 2dbf7f6a0..ae011cdf9 100644 --- a/src/domains/strategy_solver_equality.h +++ b/src/domains/strategy_solver_equality.h @@ -26,7 +26,7 @@ class strategy_solver_equalityt:public strategy_solver_baset equality_domain.get_index_set(todo_equs); } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: equality_domaint &equality_domain; diff --git a/src/domains/strategy_solver_predabs.cpp b/src/domains/strategy_solver_predabs.cpp index c3f5fe2ca..353c0ea4f 100644 --- a/src/domains/strategy_solver_predabs.cpp +++ b/src/domains/strategy_solver_predabs.cpp @@ -26,7 +26,8 @@ Function: strategy_solver_predabst::iterate \*******************************************************************/ -bool strategy_solver_predabst::iterate(invariantt &_inv) +strategy_solver_predabst::progresst +strategy_solver_predabst::iterate(invariantt &_inv) { predabs_domaint::templ_valuet &inv= static_cast(_inv); @@ -96,8 +97,8 @@ bool strategy_solver_predabst::iterate(invariantt &_inv) todo_preds.erase(e_it); - return true; + return CHANGED; } - return false; + return CONVERGED; } diff --git a/src/domains/strategy_solver_predabs.h b/src/domains/strategy_solver_predabs.h index 04c8134b3..b7ec0695d 100644 --- a/src/domains/strategy_solver_predabs.h +++ b/src/domains/strategy_solver_predabs.h @@ -18,14 +18,15 @@ class strategy_solver_predabst:public strategy_solver_baset explicit strategy_solver_predabst( predabs_domaint &_predabs_domain, incremental_solvert &_solver, + literalt _assertion_check, const namespacet &_ns): - strategy_solver_baset(_solver, literalt(), _ns), + strategy_solver_baset(_solver, _assertion_check, _ns), predabs_domain(_predabs_domain) { predabs_domain.get_row_set(todo_preds); } - virtual bool iterate(invariantt &inv); + virtual progresst iterate(invariantt &inv); protected: predabs_domaint &predabs_domain; diff --git a/src/domains/template_generator_base.cpp b/src/domains/template_generator_base.cpp index e95c83fcc..8a423b426 100644 --- a/src/domains/template_generator_base.cpp +++ b/src/domains/template_generator_base.cpp @@ -337,6 +337,29 @@ Function: template_generator_baset::add_vars \*******************************************************************/ +void template_generator_baset::add_vars( + const std::set &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs) +{ + for(const auto &v : vars_to_add) + add_var(v, pre_guard, post_guard, kind, var_specs); +} + +/*******************************************************************\ + +Function: template_generator_baset::add_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + void template_generator_baset::add_vars( const local_SSAt::var_listt &vars_to_add, const domaint::guardt &pre_guard, @@ -655,215 +678,6 @@ bool template_generator_baset::instantiate_custom_templates( /*******************************************************************\ -Function: template_generator_baset::build_custom_expr - - Inputs: - - Outputs: - - Purpose: rename custom template to correct SSA identifiers - -\*******************************************************************/ - -/* -bool template_generator_baset::replace_post(replace_mapt replace_map, exprt &expr) -{ - bool replaced = false; - if(expr.id()==ID_function_application) - { - const function_application_exprt &f = to_function_application_expr(expr); - if(f.function().get(ID_identifier) == TEMPLATE_NEWVAR) - { - assert(f.arguments().size()==1); - if(f.arguments()[0].id()==ID_typecast) - expr = replace_map[f.arguments()[0].op0()]; - else - expr = replace_map[f.arguments()[0]]; - return true; - } - } - for(unsigned i=0; iloophead->location].phi_nodes; - - for(local_SSAt::objectst::const_iterator - o_it=SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); - o_it++) - { - ssa_domaint::phi_nodest::const_iterator p_it= - phi_nodes.find(o_it->get_identifier()); - - if(p_it!=phi_nodes.end()) //modified in loop - { - //rename to pre - replace_map[o_it->get_expr()] = - SSA.name(*o_it, local_SSAt::LOOP_BACK, n_it->location); - - //rename to post - replace_post_map[o_it->get_expr()] = - SSA.read_rhs(*o_it, n_it->location); - //TODO: unwinding - } - else //not modified in loop - { - //rename to id valid at loop head - replace_map[o_it->get_expr()] = - SSA.read_rhs(*o_it,n_it->loophead->location); - //TODO: unwinding - } - } - - bool contains_newvar = replace_post(replace_post_map,expr); - replace_expr(replace_map,expr); - return contains_newvar; -} -*/ - -/*******************************************************************\ - -Function: template_generator_baset::instantiate_custom_templates - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -/* -bool template_generator_baset::instantiate_custom_templates( - const local_SSAt &SSA) -{ - // used for renaming map - var_listt pre_state_vars, post_state_vars; - - bool found_poly = false, found_predabs = false; - for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); - n_it!=SSA.nodes.end(); n_it++) - { - if(n_it->loophead != SSA.nodes.end()) //we've found a loop - { - exprt pre_guard, post_guard, aux_expr; - get_pre_post_guards(SSA,n_it,pre_guard, post_guard); - aux_expr = true_exprt(); //TODO: change to "standard" invariant semantics - bool add_post_vars = false; - - //search for templates in the loop - for(local_SSAt::nodest::const_iterator nn_it=n_it->loophead; - nn_it!=n_it; nn_it++) - { - if(nn_it->templates.empty()) continue; - if(nn_it->templates.size()>1000) continue; //TODO: there is an unwinder-related bug - for(local_SSAt::nodet::templatest::const_iterator - t_it=nn_it->templates.begin(); - t_it!=nn_it->templates.end(); t_it++) - { - debug() << "Template expression: " - << from_expr(SSA.ns,"",*t_it) << eom; - - // check whether it is a template polyhedra or a pred abs - std::set symbols; - find_symbols(*t_it, symbols); - - bool predabs = true; - for(std::set::iterator it = symbols.begin(); - it != symbols.end(); it++) - { - std::size_t found_param = - id2string(it->get_identifier()).find(TEMPLATE_PARAM_PREFIX); - if (found_param != std::string::npos) - { - predabs = false; - break; - } - } - - //template polyhedra - if(!predabs && t_it->id()==ID_le) - { - debug() << "Custom template polyhedron found" << eom; - if(!found_poly) //create domain - { - domain_ptr = new tpolyhedra_domaint(domain_number, - post_renaming_map); //TODO: aux_renaming_map - found_poly = true; - } - exprt expr = t_it->op0(); - bool contains_new_var = build_custom_expr(SSA,n_it,expr); - if(contains_new_var) add_post_vars = true; - static_cast(domain_ptr)->add_template_row( - expr,pre_guard, - contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); - } - // pred abs domain - else if (predabs) - { - options.set_option("predabs-solver",true); - - debug() << "Custom predicate template found" << eom; - if(!found_predabs) //create domain - { - domain_ptr = new predabs_domaint(domain_number, - post_renaming_map); //TODO: aux_renaming_map - found_predabs = true; - } - exprt expr = *t_it; - bool contains_new_var = build_custom_expr(SSA,n_it,expr); - if(contains_new_var) add_post_vars = true; - static_cast(domain_ptr)->add_template_row( - expr,pre_guard, - contains_new_var ? and_exprt(pre_guard,post_guard) : post_guard, - aux_expr, - contains_new_var ? domaint::OUT : domaint::LOOP); - - } - else // neither pred abs, nor polyhedra - warning() << "ignoring unsupported template " - << from_expr(SSA.ns,"",*t_it) << eom; - } - if(add_post_vars) //for result retrieval via all_vars() only - { - domaint::var_specst new_var_specs(var_specs); - var_specs.clear(); - for(domaint::var_specst::const_iterator v = new_var_specs.begin(); - v!=new_var_specs.end(); v++) - { - var_specs.push_back(*v); - if(v->kind==domaint::LOOP) - { - var_specs.push_back(*v); - var_specs.back().kind = domaint::OUTL; - replace_expr(aux_renaming_map,var_specs.back().var); - } - } - } - } - } - } - - return (found_poly || found_predabs); -} -*/ - -/*******************************************************************\ - Function: template_generator_baset::instantiate_standard_domains Inputs: diff --git a/src/domains/template_generator_base.h b/src/domains/template_generator_base.h index d745f1dbf..031d4ac49 100644 --- a/src/domains/template_generator_base.h +++ b/src/domains/template_generator_base.h @@ -50,9 +50,12 @@ class template_generator_baset:public messaget } virtual domaint::var_sett all_vars(); - - bool empty() { assert(domain_ptr!=NULL); return domain_ptr->is_spec_empty(); } - inline domaint *domain() { assert(domain_ptr!=NULL); return domain_ptr; } + bool empty() + { + assert(domain_ptr!=nullptr); + return domain_ptr->is_spec_empty(); + } + domaint *domain() { assert(domain_ptr!=nullptr); return domain_ptr; } domaint::var_specst var_specs; replace_mapt post_renaming_map; @@ -81,6 +84,12 @@ class template_generator_baset:public messaget domaint::guardt post_guard, const domaint::kindt &kind, domaint::var_specst &var_specs); + void add_vars( + const std::set &vars_to_add, + const domaint::guardt &pre_guard, + const domaint::guardt &post_guard, + const domaint::kindt &kind, + domaint::var_specst &var_specs); void add_vars( const var_listt &vars_to_add, const domaint::guardt &pre_guard, @@ -103,7 +112,8 @@ class template_generator_baset:public messaget void get_pre_post_guards( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, - exprt &pre_guard, exprt &post_guard); + exprt &pre_guard, + exprt &post_guard); void get_pre_var( const local_SSAt &SSA, local_SSAt::objectst::const_iterator o_it, diff --git a/src/domains/template_generator_summary.cpp b/src/domains/template_generator_summary.cpp index becceba5f..b9fc4f68b 100644 --- a/src/domains/template_generator_summary.cpp +++ b/src/domains/template_generator_summary.cpp @@ -103,6 +103,12 @@ void template_generator_summaryt::collect_variables_inout( last_guard, forward ? domaint::OUT : domaint::IN, var_specs); + + // add nondets for backwards analysis + if(!forward) + { + add_vars(SSA.nondets, first_guard, first_guard, domaint::OUT, var_specs); + } } /*******************************************************************\ diff --git a/src/domains/tpolyhedra_domain.cpp b/src/domains/tpolyhedra_domain.cpp index 161c9eeb8..b2c7a2075 100644 --- a/src/domains/tpolyhedra_domain.cpp +++ b/src/domains/tpolyhedra_domain.cpp @@ -17,6 +17,7 @@ Author: Peter Schrammel #include "tpolyhedra_domain.h" #include "util.h" +#include "simplify_bounds.h" #define SYMB_BOUND_VAR "symb_bound#" @@ -51,7 +52,7 @@ void tpolyhedra_domaint::initialize(valuet &value) v[row]=false_exprt(); // marker for -oo } - refine(); //initialise refinements + refine(); // initialise refinements } /*******************************************************************\ @@ -345,7 +346,7 @@ exprt tpolyhedra_domaint::get_row_post_constraint( #endif if(is_row_value_neginf(row_value)) - return implies_exprt(templ_row.post_guard,false_exprt()); + return implies_exprt(templ_row.post_guard, false_exprt()); if(is_row_value_inf(row_value)) return implies_exprt(templ_row.post_guard, true_exprt()); exprt c=implies_exprt( @@ -687,7 +688,7 @@ void tpolyhedra_domaint::project_on_vars( } else { - if(is_row_value_neginf(value, row)) + if(is_row_value_neginf(row_v)) c.push_back(false_exprt()); else if(is_row_value_inf(row_v)) c.push_back(true_exprt()); @@ -696,6 +697,8 @@ void tpolyhedra_domaint::project_on_vars( } } result=conjunction(c); + simplify_bounds(result, ns); + simplify_bounds(result, ns); } /*******************************************************************\ @@ -909,10 +912,22 @@ bool tpolyhedra_domaint::is_row_value_neginf( return row_value.get(ID_value)==ID_false; } +/*******************************************************************\ + + Function: tpolyhedra_domaint::is_row_value_neginf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + bool tpolyhedra_domaint::is_row_value_neginf( const valuet &value, const rowt &row) const { - const templ_valuet &v = static_cast(value); + const templ_valuet &v=static_cast(value); return v.at(row).get(ID_value)==ID_false; } @@ -928,12 +943,53 @@ bool tpolyhedra_domaint::is_row_value_neginf( \*******************************************************************/ -bool tpolyhedra_domaint::is_row_value_inf( - const row_valuet &row_value) const +bool tpolyhedra_domaint::is_row_value_inf(const row_valuet & row_value) const { return row_value.get(ID_value)==ID_true; } +/*******************************************************************\ + + Function: tpolyhedra_domaint::is_row_value_inf + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool tpolyhedra_domaint::is_row_value_inf( + const valuet &value, const rowt &row) const +{ + const templ_valuet &v=static_cast(value); + const row_valuet &row_value=v.at(row); + if(row_value.get(ID_value)==ID_true) + return true; + if(row_value==get_max_row_value(row)) + return true; + const row_exprt &row_expr=templ[row].expr; + if(row_expr.id()==ID_unary_minus && + row_expr.op0().id()==ID_typecast) + { + mp_integer rvalue; + to_integer(row_value, rvalue); + const typet &inner_type=row_expr.op0().op0().type(); + mp_integer smallest; + if(inner_type.id()==ID_unsignedbv) + smallest=to_unsignedbv_type(inner_type).smallest(); + else if(inner_type.id()==ID_signedbv) + smallest=to_signedbv_type(inner_type).smallest(); + else + return false; + if(smallest==rvalue) + return true; + } + + return false; +} + /*******************************************************************\ Function: tpolyhedra_domaint::rename_for_row @@ -972,7 +1028,7 @@ void tpolyhedra_domaint::rename_for_row(exprt &expr, const rowt &row) Outputs: - Purpose: x+-y<=c + Purpose: +-x<=c \*******************************************************************/ @@ -1177,8 +1233,7 @@ void tpolyhedra_domaint::add_sum_template( } } - -/******************************************************************* \ +/*******************************************************************\ Function: tpolyhedra_domaint::refine @@ -1192,41 +1247,41 @@ Function: tpolyhedra_domaint::refine void tpolyhedra_domaint::replace_comparison(exprt &expr, bool greater) { - //TODO + // TODO } bool tpolyhedra_domaint::refine() { return false; - //TODO - if(current_refinement==0) //initialise + // TODO + if(current_refinement==0) // initialise { if(refinement_exprs.size()==0) { - max_refinements = 0; + max_refinements=0; return false; } - max_refinements = 3; - current_refinement = 1; + max_refinements=3; + current_refinement=1; exprt::operandst c; - //TODO - current_refinement_expr = conjunction(c); + // TODO + current_refinement_expr=conjunction(c); return true; } - - if(current_refinement>max_refinements) + + if(current_refinement>max_refinements) return false; if(current_refinement==1) { exprt::operandst c; - //TODO - current_refinement_expr = conjunction(c); + // TODO + current_refinement_expr=conjunction(c); } else if(current_refinement==2) - current_refinement_expr = true_exprt(); - + current_refinement_expr=true_exprt(); + current_refinement++; return true; } diff --git a/src/domains/tpolyhedra_domain.h b/src/domains/tpolyhedra_domain.h index c8dbdd318..efe7ad4b6 100644 --- a/src/domains/tpolyhedra_domain.h +++ b/src/domains/tpolyhedra_domain.h @@ -43,16 +43,15 @@ class tpolyhedra_domaint:public domaint unsigned _domain_number, replace_mapt &_renaming_map, const namespacet &_ns): - domaint(_domain_number, _renaming_map, _ns), - current_refinement(0) + domaint(_domain_number, _renaming_map, _ns) { } // initialize value virtual void initialize(valuet &value); - virtual void reset_refinements() { current_refinement = 0; } - virtual bool refine(); //non-monotone condition refinement + virtual void reset_refinements() { current_refinement=0; } + virtual bool refine(); // non-monotone condition refinement std::vector &refinement_expressions() { return refinement_exprs; } virtual void join(valuet &value1, const valuet &value2); @@ -78,7 +77,8 @@ class tpolyhedra_domaint:public domaint exprt to_symb_post_constraints(const std::set &symb_rows); exprt get_row_symb_value_constraint( const rowt &row, - const row_valuet &row_value, bool geq=false); + const row_valuet &row_value, + bool geq=false); exprt get_row_symb_pre_constraint( const rowt &row, const row_valuet &row_value); @@ -142,7 +142,7 @@ class tpolyhedra_domaint:public domaint templatet templ; - //non-monotone condition refinement + // non-monotone condition refinement std::vector refinement_exprs; unsigned current_refinement, max_refinements; exprt current_refinement_expr; diff --git a/src/domains/util.cpp b/src/domains/util.cpp index e49925ee4..159a813d8 100644 --- a/src/domains/util.cpp +++ b/src/domains/util.cpp @@ -51,6 +51,7 @@ void extend_expr_types(exprt &expr) } if(expr.id()==ID_constant || expr.id()==ID_symbol || + expr.id()==ID_nondet_symbol || expr.id()==ID_index) return; if(expr.id()==ID_unary_minus) @@ -122,7 +123,6 @@ void extend_expr_types(exprt &expr) extend_expr_types(expr.op0()); extend_expr_types(expr.op1()); unsigned size0=get_bitvector_width(expr.op0()); -// std::cerr << "expr1: " << expr.op1() << std::endl; unsigned size1=get_bitvector_width(expr.op1()); assert(size0>0); assert(size1>0); @@ -132,8 +132,9 @@ void extend_expr_types(exprt &expr) expr.op1().type().id()==ID_signedbv)) { typet new_type=signedbv_typet(size0+size1+1); - expr=mult_exprt(typecast_exprt(expr.op0(), new_type), - typecast_exprt(expr.op1(), new_type)); + expr=mult_exprt( + typecast_exprt(expr.op0(), new_type), + typecast_exprt(expr.op1(), new_type)); return; } else if(expr.op0().type().id()==ID_floatbv && @@ -241,7 +242,7 @@ mp_integer simplify_const_int(const exprt &expr) assert(d!=0); return simplify_const_int(expr.op0())/d; } - if(expr.id()==ID_symbol) + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { #if 0 std::cerr << "substituting default value for " << expr << std::endl; @@ -332,7 +333,8 @@ ieee_floatt simplify_const_float(const exprt &expr) v1/=v2; return v1; } - if(expr.id()==ID_symbol) // default value if not substituted in expr + // default value if not substituted in expr + if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { ieee_floatt v; v.make_zero(); @@ -454,7 +456,7 @@ Function: pretty_print_termination_argument Outputs: - Purpose: + Purpose: print ranking argument expressions in a more readable format \*******************************************************************/ @@ -689,4 +691,3 @@ void clean_expr(exprt &expr) } } } - diff --git a/src/functions/Makefile b/src/functions/Makefile deleted file mode 100644 index f73eb0b85..000000000 --- a/src/functions/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = call_graph.cpp summary.cpp index.cpp get_function.cpp path_util.cpp - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - - -CP_CXXFLAGS += $(SUMMARIZERFLAGS) - -INCLUDES= -I $(CBMC)/src - -CLEANFILES = - -all: $(OBJ) - -############################################################################### - diff --git a/src/functions/get_function.cpp b/src/functions/get_function.cpp deleted file mode 100644 index 39bbb593f..000000000 --- a/src/functions/get_function.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "path_util.h" -#include "get_function.h" - -/*******************************************************************\ - -Function: get_functiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_functionst::goto_functiont * get_functiont::operator()(const irep_idt &id) -{ - // do we have it in our current file? - if(current_file_name!="") - { - const goto_functionst::function_mapt::iterator - f_it=goto_model.goto_functions.function_map.find(id); - - if(f_it!=goto_model.goto_functions.function_map.end()) - return &f_it->second; // found - } - - // find in index - indext::function_to_filet::const_iterator it= - index.function_to_file.find(id); - - if(it==index.function_to_file.end()) - return NULL; // not there - - // pick first file - assert(!it->second.empty()); - - irep_idt file_name=*(it->second.begin()); - - current_file_name=index.full_path(file_name); - - status() << "Reading \"" << id2string(current_file_name) << "\"" << eom; - - // read the file - goto_model.clear(); - - bool error=read_goto_binary( - id2string(current_file_name), - goto_model, - get_message_handler()); - - if(error) - return NULL; - - const goto_functionst::function_mapt::iterator - f_it=goto_model.goto_functions.function_map.find(id); - - assert(f_it!=goto_model.goto_functions.function_map.end()); - return &f_it->second; -} diff --git a/src/functions/index.cpp b/src/functions/index.cpp deleted file mode 100644 index 2de0dff1a..000000000 --- a/src/functions/index.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/*******************************************************************\ - -Module: Indexing - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include - -#include -#include - -#include "index.h" -#include "path_util.h" - -#define INDEX_VERSION "1.0" - -/*******************************************************************\ - -Function: indext::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::build(const std::vector &files, - const std::string &_description) -{ - description=_description; - path_prefix=""; - - for(std::vector::const_iterator - f_it=files.begin(); - f_it!=files.end(); - f_it++) - index_goto_binary(*f_it); -} - -/*******************************************************************\ - -Function: indext::write - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::write(std::ostream &out) const -{ - out << "\n"; - - out << "\n"; - - out << ""; - xmlt::escape(description, out); - out << "\n"; - out << "\n"; - - for(file_to_functiont::const_iterator - it=file_to_function.begin(); - it!=file_to_function.end(); - it++) - { - out << "first), out); - out << "\">\n"; - - const std::set &functions=it->second; - - for(std::set::const_iterator - f_it=functions.begin(); f_it!=functions.end(); f_it++) - { - out << " \n"; - } - - out << "\n"; - } - - out << "\n"; -} - -/*******************************************************************\ - -Function: indext::full_path - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string indext::full_path(const irep_idt &src) const -{ - return make_relative_path(path_prefix, id2string(src)); -} - -/*******************************************************************\ - -Function: indext::index_goto_binary - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::index_goto_binary(const irep_idt &file) -{ - std::string file_full_path=full_path(file); - - status() << "Reading `" << file_full_path << "'" << eom; - - goto_modelt goto_model; - - if(read_goto_binary(file_full_path, goto_model, get_message_handler())) - { - error() << "failed to read `" << file_full_path << "'" << eom; - return; - } - - // index the functions - for(goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.begin(); - f_it!=goto_model.goto_functions.function_map.end(); - f_it++) - { - if(f_it->second.body_available()) - { - function_to_file[f_it->first].insert(file); - file_to_function[file].insert(f_it->first); - } - } -} - -/*******************************************************************\ - -Function: indext::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void indext::read(const std::string &in_file_name) -{ - file_name=in_file_name; - path_prefix=get_directory(in_file_name); - - // figure out if this is a goto-binary or an index - if(is_goto_binary(file_name)) - { - index_goto_binary(get_file_name(file_name)); - } - else - { - xmlt xml; - if(parse_xml(in_file_name, get_message_handler(), xml)) - { - error() << "failed to read index XML `" << in_file_name << "'" << eom; - return; - } - - if(xml.name!="DeltaCheckIndex") - { - error() << "index XML `" << in_file_name << "' is malformed" << eom; - return; - } - - description=xml.get_element("description"); - - for(xmlt::elementst::const_iterator - file_it=xml.elements.begin(); - file_it!=xml.elements.end(); - file_it++) - { - if(file_it->name!="file") continue; - - irep_idt file_name=file_it->get_attribute("name"); - - // create map entry - file_to_function[file_name]; - - for(xmlt::elementst::const_iterator - fkt_it=file_it->elements.begin(); - fkt_it!=file_it->elements.end(); - fkt_it++) - { - irep_idt function_id=fkt_it->get_attribute("id"); - function_to_file[function_id].insert(file_name); - file_to_function[file_name].insert(function_id); - } - } - } -} - -/*******************************************************************\ - -Function: indext::get_file_for_function - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -irep_idt indext::get_file_for_function( - const irep_idt &preferred_file, - const irep_idt &function_id) const -{ - function_to_filet::const_iterator - it=function_to_file.find(function_id); - - // found at all? - if(it==function_to_file.end()) - return preferred_file; // function not found - - // function is in preferred file? - if(it->second.find(preferred_file)!=it->second.end()) - return preferred_file; // ok as given - - assert(!it->second.empty()); - - return *it->second.begin(); // fix file -} diff --git a/src/html/Makefile b/src/html/Makefile deleted file mode 100644 index 51760b59b..000000000 --- a/src/html/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -include ../config.inc -CBMC ?= ../.. - -SRC = logo.cpp html_escape.cpp syntax_highlighting.cpp - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -INCLUDES= -I $(CBMC)/src - -CLEANFILES = - -all: $(OBJ) - -############################################################################### - diff --git a/src/solver/Makefile b/src/solver/Makefile index bd0fe97b7..e473705db 100644 --- a/src/solver/Makefile +++ b/src/solver/Makefile @@ -1,7 +1,18 @@ -SRC = summarizer_base.cpp summarizer_bw.cpp \ - summarizer_bw_term.cpp summarizer_fw_contexts.cpp \ - summarizer_fw.cpp summarizer_fw_term.cpp \ - summary.cpp summary_db.cpp +SRC = summarizer_base.cpp \ + summarizer_bw.cpp \ + summarizer_bw_term.cpp \ + summarizer_bw_cex.cpp \ + summarizer_bw_cex_ai.cpp \ + summarizer_bw_cex_complete.cpp \ + summarizer_bw_cex_concrete.cpp \ + summarizer_bw_cex_all.cpp \ + summarizer_bw_cex_wp.cpp \ + summarizer_fw_contexts.cpp \ + summarizer_fw.cpp \ + summarizer_fw_term.cpp \ + summary.cpp \ + summary_db.cpp \ + # empty last line include ../config.inc include $(CBMC)/src/config.inc @@ -12,6 +23,8 @@ CP_CXXFLAGS += $(TWOLSFLAGS) INCLUDES= -I $(CBMC)/src -I .. +CP_CXXFLAGS += $(SUMMARIZERFLAGS) + CLEANFILES = solver$(LIBEXT) all: solver$(LIBEXT) diff --git a/src/solver/summarizer_base.cpp b/src/solver/summarizer_base.cpp index 7e481013a..9a951bda9 100644 --- a/src/solver/summarizer_base.cpp +++ b/src/solver/summarizer_base.cpp @@ -428,159 +428,3 @@ bool summarizer_baset::check_end_reachable( return result; } - -/*******************************************************************\ - -Function: summarizer_baset::get_loophead_selects - - Inputs: - - Outputs: - - Purpose: returns the select guards at the loop heads - e.g. in order to check whether a countermodel is spurious - -\*******************************************************************/ - -void summarizer_baset::get_loophead_selects( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, - exprt::operandst &loophead_selects) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - symbol_exprt lsguard = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(lsguard,*n_it,true); - loophead_selects.push_back(not_exprt(lsguard)); - solver.set_frozen(solver.convert(lsguard)); - } - literalt loophead_selects_literal = solver.convert(conjunction(loophead_selects)); - if(!loophead_selects_literal.is_constant()) - solver.set_frozen(loophead_selects_literal); - -#if 0 - std::cout << "loophead_selects: " - << from_expr(SSA.ns,"",conjunction(loophead_selects)) - << std::endl; -#endif -} - -/*******************************************************************\ - -Function: summarizer_baset::get_loop_continues - - Inputs: - - Outputs: - - Purpose: returns the loop continuation guards at the end of the - loops in order to check whether we can unroll further - -\*******************************************************************/ - -void summarizer_baset::get_loop_continues( - const local_SSAt &SSA, - ssa_local_unwindert &ssa_local_unwinder, - exprt::operandst &loop_continues) -{ - //TODO: this should be provided by unwindable_local_SSA - - ssa_local_unwinder.compute_loop_continuation_conditions(); - ssa_local_unwinder.loop_continuation_conditions(loop_continues); - if(loop_continues.size()==0) - { - //TODO: this should actually be done transparently by the unwinder - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - symbol_exprt guard = SSA.guard_symbol(n_it->location); - symbol_exprt cond = SSA.cond_symbol(n_it->location); - loop_continues.push_back(and_exprt(guard,cond)); - } - } - -#if 0 - std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; -#endif -} - -void summarizer_baset::get_loop_continues( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - const local_SSAt::locationt &loop_id, - exprt::operandst &loop_continues) -{ - //TODO: need to ask ssa_inliner regarding inlined functions - - //TODO: this should be provided by unwindable_local_SSA - - //ssa_local_unwinder.loop_continuation_conditions(loop_id, loop_continues); - ssa_local_unwinder.loop_continuation_conditions(loop_continues); - if(loop_continues.size()==0) - { - //TODO: this should actually be done transparently by the unwinder - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - if(n_it->loophead==SSA.nodes.end()) continue; - if(n_it->loophead->location!=loop_id) continue; - symbol_exprt guard = SSA.guard_symbol(n_it->location); - symbol_exprt cond = SSA.cond_symbol(n_it->location); - loop_continues.push_back(and_exprt(guard,cond)); - break; - } - } - -#if 0 - std::cout << "loophead_continues: " << from_expr(SSA.ns,"",disjunction(loop_continues)) << std::endl; -#endif -} - - -/*******************************************************************\ - -Function: summarizer_baset::is_fully_unwound - - Inputs: - - Outputs: - - Purpose: checks whether the loops have been fully unwound - -\*******************************************************************/ - -bool summarizer_baset::is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver) -{ - solver.new_context(); - solver << and_exprt(conjunction(loophead_selects), - disjunction(loop_continues)); - - solver_calls++; //statistics - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: - solver.pop_context(); - return false; - break; - - case decision_proceduret::D_UNSATISFIABLE: - solver.pop_context(); - solver << conjunction(loophead_selects); - return true; - break; - - case decision_proceduret::D_ERROR: - default: - throw "error from decision procedure"; - } -} - diff --git a/src/solver/summarizer_base.h b/src/solver/summarizer_base.h index 11a6cd0b2..d086d65bb 100644 --- a/src/solver/summarizer_base.h +++ b/src/solver/summarizer_base.h @@ -49,24 +49,6 @@ class summarizer_baset:public messaget virtual void summarize(); virtual void summarize(const function_namet &entry_function); - static void get_loop_continues( - const local_SSAt &SSA, - ssa_local_unwindert &ssa_local_unwinder, - exprt::operandst &loop_continues); - - static void get_loop_continues( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - const local_SSAt::locationt &loop_id, - exprt::operandst &loop_continues); - - static void get_loophead_selects( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver, - exprt::operandst &loophead_selects); - - unsigned get_number_of_solver_instances() { return solver_instances; } unsigned get_number_of_solver_calls() { return solver_calls; } unsigned get_number_of_summaries_used() { return summaries_used; } @@ -120,12 +102,6 @@ class summarizer_baset:public messaget local_SSAt &SSA, const exprt &cond); - bool is_fully_unwound( - const exprt::operandst &loop_continues, - const exprt::operandst &loophead_selects, - incremental_solvert &solver); - - // statistics unsigned solver_instances; unsigned solver_calls; diff --git a/src/solver/summarizer_bw_cex.cpp b/src/solver/summarizer_bw_cex.cpp new file mode 100644 index 000000000..33501d783 --- /dev/null +++ b/src/solver/summarizer_bw_cex.cpp @@ -0,0 +1,274 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize() +{ + assert(false); // unused +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec(function_name, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_baset::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_baset::check() +{ + assert(false); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loophead_selects + + Inputs: + + Outputs: + + Purpose: returns the select guards at the loop heads + in order to check whether a countermodel is spurious + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loophead_selects( + const irep_idt &function_name, + const local_SSAt &SSA, + prop_convt &solver, + exprt::operandst &loophead_selects) +{ + // TODO: this should be provided by unwindable_local_SSA + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + symbol_exprt lsguard= + SSA.name(SSA.guard_symbol(), local_SSAt::LOOP_SELECT, n_it->location); + ssa_unwinder.get(function_name).unwinder_rename(lsguard, *n_it, true); + loophead_selects.push_back(not_exprt(lsguard)); + solver.set_frozen(solver.convert(lsguard)); + } + literalt loophead_selects_literal= + solver.convert(conjunction(loophead_selects)); + if(!loophead_selects_literal.is_constant()) + solver.set_frozen(loophead_selects_literal); + +#if 0 + std::cout << "loophead_selects: " + << from_expr(SSA.ns, "", conjunction(loophead_selects)) + << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + loops in order to check whether we can unroll further + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + prop_convt &solver, + exprt::operandst &loop_continues) +{ + // TODO: this should be provided by unwindable_local_SSA + ssa_unwinder.get(function_name).loop_continuation_conditions(loop_continues); + if(loop_continues.size()==0) + { + // TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + symbol_exprt guard=SSA.guard_symbol(n_it->location); + symbol_exprt cond=SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard, cond)); + } + } + +#if 0 + std::cout << "loophead_continues: " + << from_expr(SSA.ns, "", disjunction(loop_continues)) << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::get_loop_continues + + Inputs: + + Outputs: + + Purpose: returns the loop continuation guards at the end of the + given loop in order to check whether we can unroll further + +\*******************************************************************/ + +void summarizer_bw_cex_baset::get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues) +{ + // TODO: need to ask ssa_inliner regarding inlined functions + + // TODO: this should be provided by unwindable_local_SSA + + ssa_unwinder.get(function_name) + .loop_continuation_conditions(loop_id, loop_continues); + if(loop_continues.empty()) + { + // TODO: this should actually be done transparently by the unwinder + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + n_it!=SSA.nodes.end(); n_it++) + { + if(n_it->loophead==SSA.nodes.end()) + continue; + if(n_it->loophead->location!=loop_id) + continue; + symbol_exprt guard=SSA.guard_symbol(n_it->location); + symbol_exprt cond=SSA.cond_symbol(n_it->location); + loop_continues.push_back(and_exprt(guard, cond)); + break; + } + } + +#if 0 + std::cout << "loophead_continues: " + << from_expr(SSA.ns, "", disjunction(loop_continues)) << std::endl; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_baset::is_fully_unwound + + Inputs: + + Outputs: + + Purpose: checks whether the loops have been fully unwound + +\*******************************************************************/ + +bool summarizer_bw_cex_baset::is_fully_unwound( + const exprt::operandst &loop_continues, + const exprt::operandst &loophead_selects, + incremental_solvert &solver) +{ + solver.new_context(); + solver << + and_exprt(conjunction(loophead_selects), disjunction(loop_continues)); + + solver_calls++; // statistics + + switch(solver()) + { + case decision_proceduret::D_SATISFIABLE: + solver.pop_context(); + return false; + break; + + case decision_proceduret::D_UNSATISFIABLE: + solver.pop_context(); + solver << conjunction(loophead_selects); + return true; + break; + + case decision_proceduret::D_ERROR: + default: + throw "error from decision procedure"; + } +} diff --git a/src/solver/summarizer_bw_cex.h b/src/solver/summarizer_bw_cex.h new file mode 100644 index 000000000..8e3f70e1b --- /dev/null +++ b/src/solver/summarizer_bw_cex.h @@ -0,0 +1,80 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis Base + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw.h" + +class summarizer_bw_cex_baset:public summarizer_bwt +{ +public: + typedef ssa_refiner_selectivet::reasont reasont; + + virtual void summarize(); + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + virtual void get_reason(reasont &_reason) { _reason.merge(reason); } + +protected: + function_namet entry_function; + function_namet error_function; + exprt error_assertion; + reasont reason; + + summarizer_bw_cex_baset( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bwt(_options, _summary_db, _ssa_db, _ssa_unwinder, _ssa_inliner), + entry_function(_entry_function), + error_function(_error_function) + { + } + + void get_loophead_selects( + const irep_idt &function_name, + const local_SSAt &, + prop_convt &, + exprt::operandst &loophead_selects); + + void get_loop_continues( + const irep_idt &function_name, + const local_SSAt &, + prop_convt &, + exprt::operandst &loop_continues); + + void get_loop_continues( + const irep_idt &function_name, + const local_SSAt &SSA, + const local_SSAt::locationt &loop_id, + exprt::operandst &loop_continues); + + bool is_fully_unwound( + const exprt::operandst& loop_continues, + const exprt::operandst& loophead_selects, + incremental_solvert&); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_H diff --git a/src/solver/summarizer_bw_cex_ai.cpp b/src/solver/summarizer_bw_cex_ai.cpp new file mode 100644 index 000000000..8607afa5c --- /dev/null +++ b/src/solver/summarizer_bw_cex_ai.cpp @@ -0,0 +1,529 @@ +/*******************************************************************\ + +Module: Summarizer for Backward Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_ai.h" +#include "summary_db.h" + +#include "../domains/ssa_analyzer.h" +#include "../domains/template_generator_summary.h" +#include "../domains/template_generator_callingcontext.h" + +#include "../domains/disjunctive_analyzer.h" + +#include "../ssa/local_ssa.h" +#include "../ssa/simplify_ssa.h" + + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec( + function_name, summaryt::entry_call_site, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_ait::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (abstract)..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_ait::check() +{ + property_checkert::resultt result=property_checkert::FAIL; + if(!summary_db.exists(entry_function)) + { + result=property_checkert::UNKNOWN; + } + else + { + const summaryt &summary=summary_db.get(entry_function); + if(summary.error_summaries.empty() || + summary.error_summaries.begin()->second.is_nil() || + summary.error_summaries.begin()->second.is_true()) + result=property_checkert::UNKNOWN; + } + + // we are only complete if we are in the entry function + if(result==property_checkert::UNKNOWN && + entry_function==error_function) + { + incremental_solvert &solver=ssa_db.get_solver(entry_function); + const local_SSAt &ssa=ssa_db.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects( + entry_function, ssa, *solver.solver, loophead_selects); + get_loop_continues(entry_function, ssa, *solver.solver, loop_continues); + // check whether loops have been fully unwound + bool fully_unwound= + is_fully_unwound(loop_continues, loophead_selects, solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result=property_checkert::PASS; + } + + return result; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + summaryt summary; + if(summary_db.exists(function_name)) + summary=summary_db.get(function_name); + else + { + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.nondets=SSA.nondets; + } + + // insert assertion + exprt end_guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition=implies_exprt(end_guard, _postcondition); + if(function_name==error_function) + { + postcondition=and_exprt(postcondition, not_exprt(error_assertion)); + } + + summary.bw_postcondition=_postcondition; + +#if 0 + debug() << "Postcondition: " << + from_expr(SSA.ns, "", postcondition) << eom; +#endif + + if(_postcondition.is_false()) + { + summary.error_summaries[call_site]=false_exprt(); + } + else + { + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + summary, + postcondition, + context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary( + function_name, + call_site, + SSA, + summary, + summary, + postcondition, + context_sensitive); + + if(function_name==error_function) + summary.has_assertion=true; + } + + summary_db.set(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + debug() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // TODO: maybe allow setting this separately on the command line + optionst _options=options; + _options.set_option("intervals", true); + _options.set_option("binsearch-solver", true); + + // TODO: use a template generator without invariants + template_generator_summaryt template_generator( + _options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, false); + + exprt::operandst c; + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + // backward summaries + assertion_flag= + ssa_inliner.get_summaries( + SSA, call_site, false, assert_postcond, noassert_postcond, c); + + assert_postcond.push_back(postcondition); // context + + // add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond), summary_vars); + for(const auto &var : summary_vars) + { + if(var.id()==ID_nondet_symbol) + summary.nondets.insert(var); + } + + // assumptions must hold + for(const auto &node : SSA.nodes) + for(const auto &assumption : node.assumptions) + c.push_back(assumption); + +#if 0 + std::cout << from_expr(SSA.ns, "", cc) << std::endl; +#endif + + // TODO: pushing loophead_selects into the solver + + summary.error_summaries[call_site]; + if(!template_generator.empty()) + { + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + + exprt cc=simplify_expr(conjunction(c), SSA.ns); + + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + disjunctive_analyzer( + solver, + SSA, + cc, + template_generator, + cc, + summary.error_summaries[call_site], + template_generator.inout_vars()); + +#if 0 + std::cout << "SUM: " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + + summary.error_summaries[call_site]= + simplify_expr(summary.error_summaries[call_site], SSA.ns); + +#if 0 + std::cout << "SUM (post simplification): " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + + // statistics + solver_instances+=disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls+=disjunctive_analyzer.get_number_of_solver_calls(); + } + else + { + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + // c.push_back(not_exprt(conjunction(assert_postcond))); + // c.push_back(not_exprt(disjunction(noassert_postcond))); + + exprt cc=simplify_expr(conjunction(c), SSA.ns); + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << cc; + exprt result=true_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) + result=false_exprt(); + solver.pop_context(); + summary.error_summaries[call_site]=result; + +#if 0 + std::cout << "SUM: " + << from_expr(SSA.ns, "", summary.error_summaries[call_site]) + << std::endl; +#endif + } + + summary.error_summaries[call_site]= + simplify_expr((summary.error_summaries[call_site]), SSA.ns); // not_exprt + + summary.has_assertion=assertion_flag; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_ait::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt postcondition_call=true_exprt(); + postcondition_call=compute_calling_context2( + function_name, SSA, old_summary, n_it, f_it, postcondition, sufficient); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec( + fname, + summaryt::call_sitet(n_it->location), + postcondition_call, + context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_ait::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_ait::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + incremental_solvert &solver=ssa_db.get_solver(function_name); + solver.set_message_handler(get_message_handler()); + + // analyze + disjunctive_analyzert disjunctive_analyzer; + disjunctive_analyzer.set_message_handler(get_message_handler()); + + // TODO: maybe allow setting this separately on the command line + optionst _options=options; + _options.set_option("intervals", true); + _options.set_option("binsearch-solver", true); + + // TODO: use a template generator without invariants + template_generator_callingcontextt template_generator( + _options, ssa_db, ssa_unwinder.get(function_name)); + template_generator.set_message_handler(get_message_handler()); + template_generator(solver.next_domain_number(), SSA, n_it, f_it, false); + + // collect globals at call site + std::map< + local_SSAt::nodet::function_callst::const_iterator, + local_SSAt::var_sett> + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries(SSA, summaryt::call_sitet(n_it->location), false, + assert_postcond, noassert_postcond, c); // backward summaries + assert_postcond.push_back(postcondition); // context + + + // TODO: pushing loophead_selects into the solver + + // set preconditions + local_SSAt &fSSA=ssa_db.get(fname); + + exprt postcondition_call; + + if(!template_generator.empty()) + { + // with negative information would need: not_exprt + c.push_back(conjunction(assert_postcond)); + // with negative information would need: not_exprt dis + c.push_back(conjunction(noassert_postcond)); + + disjunctive_analyzer(solver, SSA, conjunction(c), template_generator, + conjunction(c), postcondition_call, + template_generator.callingcontext_vars()); + + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + } + else + { + // TODO: yet another workaround for ssa_analyzer + // not being able to handle empty templates properly + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + + solver << SSA; + solver.new_context(); + solver << SSA.get_enabling_exprs(); + solver << conjunction(c); + + postcondition_call=false_exprt(); + if(solver()!=decision_proceduret::D_SATISFIABLE) + postcondition_call=true_exprt(); + solver.pop_context(); + } + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_instances+=disjunctive_analyzer.get_number_of_solver_instances(); + solver_calls+=disjunctive_analyzer.get_number_of_solver_calls(); + + return postcondition_call; +} + diff --git a/src/solver/summarizer_bw_cex_ai.h b/src/solver/summarizer_bw_cex_ai.h new file mode 100644 index 000000000..af779376f --- /dev/null +++ b/src/solver/summarizer_bw_cex_ai.h @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: Summarizer for Backwards Error Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H + +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_ait:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_ait( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function) + { + } + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_AI_H diff --git a/src/2ls/summarizer_bw_cex_all.cpp b/src/solver/summarizer_bw_cex_all.cpp similarity index 82% rename from src/2ls/summarizer_bw_cex_all.cpp rename to src/solver/summarizer_bw_cex_all.cpp index cefdd8757..4b1052fcc 100644 --- a/src/2ls/summarizer_bw_cex_all.cpp +++ b/src/solver/summarizer_bw_cex_all.cpp @@ -1,6 +1,6 @@ /*******************************************************************\ -Module: Counterexample-based Backward Analysis +Module: Counterexample-based Backward Analysis Author: Kumar Madhukar, Peter Schrammel @@ -39,20 +39,20 @@ Function: summarizer_bw_cex_allt::summarize() void summarizer_bw_cex_allt::summarize(const exprt &_error_assertion) { status() << "\nBackward error analysis (all)..." << eom; - error_assertion = _error_assertion; + error_assertion=_error_assertion; summarizer_bw_cex_concrete.summarize(error_assertion); - result = summarizer_bw_cex_concrete.check(); + result=summarizer_bw_cex_concrete.check(); - if(result == property_checkert::UNKNOWN) + if(result==property_checkert::UNKNOWN) { summarizer_bw_cex_ai.summarize(error_assertion); - result = summarizer_bw_cex_ai.check(); + result=summarizer_bw_cex_ai.check(); - if(result == property_checkert::UNKNOWN) + if(result==property_checkert::UNKNOWN) { summarizer_bw_cex_complete.summarize(error_assertion); - result = summarizer_bw_cex_complete.check(); + result=summarizer_bw_cex_complete.check(); } } } @@ -65,7 +65,7 @@ Function: summarizer_bw_cex_allt::check() Outputs: - Purpose: + Purpose: \*******************************************************************/ diff --git a/src/solver/summarizer_bw_cex_all.h b/src/solver/summarizer_bw_cex_all.h new file mode 100644 index 000000000..c12afcf8b --- /dev/null +++ b/src/solver/summarizer_bw_cex_all.h @@ -0,0 +1,93 @@ +/*******************************************************************\ + +Module: Counterexample-based Backward Analysis All + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H + +#include +#include +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex.h" +#include "summarizer_bw_cex_concrete.h" +#include "summarizer_bw_cex_ai.h" +#include "summarizer_bw_cex_complete.h" + +class summarizer_bw_cex_allt:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_allt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_concrete( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_ai( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + summarizer_bw_cex_complete( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _solver, + _entry_function, + _error_function), + result(property_checkert::UNKNOWN) + { + } + + virtual void summarize(const exprt &_error_assertion); + + virtual void set_message_handler(message_handlert &handler) + { + summarizer_bw_cex_concrete.set_message_handler(handler); + summarizer_bw_cex_ai.set_message_handler(handler); + summarizer_bw_cex_complete.set_message_handler(handler); + messaget::set_message_handler(handler); + } + + virtual property_checkert::resultt check(); + + protected: + summarizer_bw_cex_concretet summarizer_bw_cex_concrete; + summarizer_bw_cex_ait summarizer_bw_cex_ai; + summarizer_bw_cex_completet summarizer_bw_cex_complete; + property_checkert::resultt result; +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_ALL_H diff --git a/src/solver/summarizer_bw_cex_complete.cpp b/src/solver/summarizer_bw_cex_complete.cpp new file mode 100644 index 000000000..7df4f3c23 --- /dev/null +++ b/src/solver/summarizer_bw_cex_complete.cpp @@ -0,0 +1,735 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include +#include +#include + +#include +#include +#include + +#include "summarizer_bw_cex_complete.h" + +#define REFINE_ALL + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize +( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec(entry_function, dependency_set, -1); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_completet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (complete)..." << eom; + error_assertion=_error_assertion; + ssa_inliner.rename(error_assertion, -1, false); + /* + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; + */ + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_completet::inline_summaries +( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter) +{ + local_SSAt &SSA=ssa_db.get(function_name); +#if 0 + ssa_local_unwindert &ssa_local_unwinder=ssa_unwinder.get(function_name); + ssa_local_unwinder.compute_loop_continuation_conditions(); +#endif + // add enabling expressions + exprt enable_exprs=SSA.get_enabling_exprs(); + ssa_inliner.rename(enable_exprs, counter); + + solver << enable_exprs; + + // assumptions must hold + for(local_SSAt::nodest::const_iterator + n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); ++n_it) + for(local_SSAt::nodet::assumptionst::const_iterator + a_it=n_it->assumptions.begin(); a_it!=n_it->assumptions.end(); ++a_it) + { + exprt assumption=*a_it; + ssa_inliner.rename(assumption, counter); + solver << assumption; + } + +#ifdef REFINE_ALL + // TODO: let's just put all loops into the reason + for(const auto node : SSA.nodes) + if(node.loophead!=SSA.nodes.end()) + reason[function_name].loops.insert(node.loophead->location); +#endif + + ssa_dependency_grapht &ssa_depgraph=ssa_db.get_depgraph(function_name); + + struct worknodet + { + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index=0; + start_node.dependency_set=dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()) + { +#ifdef DEBUG + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + worknodet &worknode=worklist.front(); + const ssa_dependency_grapht::depnodet &depnode= + ssa_depgraph.depnodes_map[worknode.node_index]; + +#ifdef DEBUG + std::cout << "working node: " << function_name << ": " + << worknode.node_index << "\n"; + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n\n\n"; +#endif + + // return if the top most node is reached + if(worknode.node_index==ssa_depgraph.top_node_index) + return worknode.dependency_set; + + // modify worknode_dependency_set if the node is an assertion + if(depnode.is_assertion==true) + { +#ifdef DEBUG + std::cout << "\t\t an assertion node\n"; +#endif + for(find_symbols_sett::const_iterator d_it=depnode.used_symbols.begin(); + d_it!=depnode.used_symbols.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + } + + // if this is a function call + if(depnode.is_function_call==true) + { +#ifdef DEBUG + std::cout << "fcall: working node: " << function_name << ": " + << worknode.node_index << "\n"; +#endif + irep_idt fname= + to_symbol_expr( + (to_function_application_expr(depnode.node_info)).function()) + .get_identifier(); + + find_symbols_sett renamed_dependencies; + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename(renamed_id, depnode.rename_counter, false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + +#ifdef REFINE_ALL + // TODO: just put all function calls into reason + reason[function_name].functions.insert(depnode.location); +#endif + + // recurse + worknode.dependency_set= + compute_summary_rec( + fname, worknode.dependency_set, depnode.rename_counter); + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // attach the '@' symbol if not already there + ssa_inliner.rename(renamed_id, depnode.rename_counter, true); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols(depnode.guard, guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()) + { + exprt worknode_info=depnode.node_info; + + bool is_error_assertion=false; + if(depnode.is_assertion) + { +#ifdef DEBUG + std::cout << "assertion: " << from_expr(SSA.ns, "", error_assertion) + << std::endl; + std::cout << "to check: " << from_expr(SSA.ns, "", worknode_info) + << std::endl; +#endif + assert(error_assertion.id()==ID_not); + if(error_assertion.op0().id()!=ID_and) + is_error_assertion=(worknode_info==error_assertion.op0()); + else + { + forall_operands(a_it, error_assertion.op0()) + { + if(worknode_info==*a_it) + { + is_error_assertion=true; + break; + } + } + } + } + + if(worknode.node_index!=0) + { + if(!(depnode.is_function_call)) + { + if(!depnode.is_assertion || is_error_assertion) + { +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + ssa_inliner.rename(worknode_info, counter); +#if 0 + std::cout << "Solver <-- renamed assertion: " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + + if(depnode.is_assertion) // keep for later + renamed_error_assertion.push_back(worknode_info); + else + solver << worknode_info; + + if(depnode.is_loop) + { + // loop head selects + exprt lsguard=depnode.guard; + ssa_inliner.rename(lsguard, counter); + loophead_selects.push_back(lsguard); + add_reason_to_check( + lsguard, function_name, false, depnode.location); + + // loop continuations + exprt::operandst local_loop_continues; + get_loop_continues( + function_name, SSA, depnode.location, local_loop_continues); + for(size_t i=0; inode_index==pred_node_index) + { + dependencies_merged=true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); + a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!= + worknode.dependency_set.end()) + { + if((w_it->dependency_set).find(*a_it)== + (w_it->dependency_set).end()) + { + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged==false) + { + worknodet new_worknode; + new_worknode.node_index=pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!=worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#if 0 + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#if 0 + std::cout << function_name << ": covered : "; + for(int l=0; l &waitlisted_worknode_successors= + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i=0; iconvert(not_exprt(conjunction(renamed_error_assertion)))); + solver.solver->set_assumptions(formula); +#endif + + solver_calls++; // for statistics + if(solver()==decision_proceduret::D_SATISFIABLE) + { + // pop_context() not necessary + return property_checkert::FAIL; + } +#ifndef REFINE_ALL + else + { + const namespacet &ns=ssa_db.get(entry_function).ns; + // get reasons for spuriousness + for(unsigned i=0; iis_in_conflict(formula[i])) + { + debug() << "is_in_conflict: " + << from_expr(ns, "", formula_expr[i]) << eom; + const reason_to_checkt &r=reasons_to_check[i]; + if(r.is_function) + reason[r.function_name].functions.insert(r.info); + else + reason[r.function_name].loops.insert(r.info); + } + } + bvt assumptions; + solver.solver->set_assumptions(assumptions); + for(unsigned i=0; i " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it=dependency_set.begin(); + d_it!=dependency_set.end(); d_it++) + { + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_completet::add_reason_to_check + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_completet::add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt & info) +{ + literalt l=solver.solver->convert(expr); + if(l.is_false()) + { + literalt dummy= + solver.solver->convert(symbol_exprt("goto_symex::\\dummy", bool_typet())); + formula.push_back(dummy); + formula.push_back(!dummy); + } + else if(!l.is_true()) + { + formula.push_back(l); + formula_expr.push_back(expr); + reasons_to_check.push_back(reason_to_checkt()); + reason_to_checkt &r=reasons_to_check.back(); + r.function_name=function_name; + r.info=info; + r.is_function=is_function; + } +} diff --git a/src/solver/summarizer_bw_cex_complete.h b/src/solver/summarizer_bw_cex_complete.h new file mode 100644 index 000000000..098395872 --- /dev/null +++ b/src/solver/summarizer_bw_cex_complete.h @@ -0,0 +1,88 @@ +/*******************************************************************\ + +Module: Simple Complete Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_completet:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_completet( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + solver(_solver) + { + } + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + + protected: + incremental_solvert &solver; + bvt formula; // for UNSAT core + exprt::operandst formula_expr; // for debugging + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + exprt::operandst renamed_error_assertion; + + struct reason_to_checkt + { + function_namet function_name; + bool is_function; + local_SSAt::locationt info; + }; + std::vector reasons_to_check; + void add_reason_to_check( + const exprt &expr, + const function_namet &function_name, + bool is_function, + const local_SSAt::locationt &info); + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + find_symbols_sett &dependency_set, + int counter); + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_COMPLETE_H diff --git a/src/solver/summarizer_bw_cex_concrete.cpp b/src/solver/summarizer_bw_cex_concrete.cpp new file mode 100644 index 000000000..0e08832bf --- /dev/null +++ b/src/solver/summarizer_bw_cex_concrete.cpp @@ -0,0 +1,657 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +// #define OPT_11 // simplify before pushing to solver +#define OPT_12 // collect, conjunct, simplify and then push to the solver + +// #define OPT_2 // a fresh solver each time + +// TODO: a bug in the fresh solver case; does not compute +// calling contexts (see struct tests in regression) + +// #define DEBUG + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summarizer_bw_cex_concrete.h" +#include "summary_db.h" + +#include +#include +#include + +#include +#include + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const function_namet &function_name) +{ + exprt postcondition=true_exprt(); // initial calling context + + status() << "\nSummarizing function " << function_name << eom; + compute_summary_rec( + function_name, summaryt::entry_call_site, postcondition, true); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (concrete)..." << eom; + error_assertion=_error_assertion; + + summarize(entry_function); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::check() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +property_checkert::resultt summarizer_bw_cex_concretet::check() +{ + property_checkert::resultt result=property_checkert::UNKNOWN; + if(summary_db.exists(entry_function)) + { + const summaryt &summary=summary_db.get(entry_function); + if(!summary.error_summaries.empty() && + !summary.error_summaries.begin()->second.is_nil()) + { + if(summary.error_summaries.begin()->second.is_false()) + result=property_checkert::PASS; + else + result=property_checkert::FAIL; + } + } + + // we are only complete if everything was inlined + if(result==property_checkert::UNKNOWN && + options.get_bool_option("inline")) + { + incremental_solvert &solver=ssa_db.get_solver(entry_function); + const local_SSAt &ssa=ssa_db.get(entry_function); + exprt::operandst loophead_selects; + exprt::operandst loop_continues; + get_loophead_selects( + entry_function, ssa, *solver.solver, loophead_selects); + get_loop_continues(entry_function, ssa, *solver.solver, loop_continues); + // check whether loops have been fully unwound + bool fully_unwound= + is_fully_unwound(loop_continues, loophead_selects, solver); + status() << "Loops " << (fully_unwound ? "" : "not ") + << "fully unwound" << eom; + + if(fully_unwound) + result=property_checkert::PASS; + } + + return result; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_summary_rec() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &_postcondition, + bool context_sensitive) +{ + local_SSAt &SSA=ssa_db.get(function_name); + + // TODO: let's just put all loops into the reason + for(const auto &node : SSA.nodes) + if(node.loophead!=SSA.nodes.end()) + reason[function_name].loops.insert(node.loophead->location); + + summaryt summary; + if(summary_db.exists(function_name)) + summary=summary_db.get(function_name); + else + { + summary.params=SSA.params; + summary.globals_in=SSA.globals_in; + summary.globals_out=SSA.globals_out; + summary.nondets=SSA.nondets; + } + + // insert assertion + exprt end_guard=SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); + exprt postcondition=implies_exprt(end_guard, _postcondition); + if(function_name==error_function) + { + postcondition=and_exprt(postcondition, not_exprt(error_assertion)); + } + + summary.bw_postcondition=_postcondition; + +#if 0 + debug() << "Postcondition: " << from_expr(SSA.ns, "", postcondition) << eom; +#endif + + // recursively compute summaries for function calls + inline_summaries( + function_name, + SSA, + summary, + postcondition, + context_sensitive, + true); + + status() << "Analyzing function " << function_name << eom; + + do_summary( + function_name, + call_site, + SSA, + summary, + summary, + postcondition, + context_sensitive); + + if(function_name==error_function) + summary.has_assertion=true; + + summary_db.set(function_name, summary); + + { + std::ostringstream out; + out << std::endl << "Summary for function " << function_name << std::endl; + summary_db.get(function_name).output(out, SSA.ns); + debug() << out.str() << eom; + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::do_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive) +{ + status() << "Computing error summary" << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert *fresh_solver= + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + incremental_solvert &solver=(*fresh_solver); + SSA.unmark_nodes(); + exprt::operandst store; +#else + incremental_solvert &solver=ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + // ENHANCE: we could reuse the solver state, but it's difficult + // (the function maybe called several times) + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + bool assertion_flag; + // backward summaries + assertion_flag=ssa_inliner.get_summaries( + SSA, call_site, false, assert_postcond, noassert_postcond, c); + assert_postcond.push_back(postcondition); // context + + // add nondet variables from callees to summary.nondets + std::set summary_vars; + find_symbols(conjunction(assert_postcond), summary_vars); + for(std::set::const_iterator it=summary_vars.begin(); + it!=summary_vars.end(); ++it) + if(it->id()==ID_nondet_symbol) + summary.nondets.insert(*it); + +#ifdef DEBUG + std::cout << "Assert Summary: " + << from_expr(SSA.ns, "", conjunction(assert_postcond)) << "\n\n"; + std::cout << "Noassert Summary: " + << from_expr(SSA.ns, "", conjunction(noassert_postcond)) << "\n\n"; +#endif + + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#ifdef DEBUG + debug() << "Backward summaries: " << + from_expr(SSA.ns, "", simplify_expr(conjunction(c), SSA.ns)) << eom; +#endif + +#ifdef OPT_12 + store << SSA; +#else +#ifdef OPT_2 + store << SSA; +#else + solver << SSA; +#endif +#endif + +#ifndef OPT_2 + solver.new_context(); +#endif + + // assumptions must hold + for(const auto &node : SSA.nodes) + { + for(const auto &a : node.assumptions) + { +#ifdef OPT_11 + solver << simplify_expr(a, SSA.ns); +#else +#ifdef OPT_12 + store.push_back(a); +#else +#ifdef OPT_2 + store.push_back(a); +#else + solver << a; +#endif +#endif +#endif + } + } + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else +#ifdef OPT_2 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif +#endif + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else +#ifdef OPT_2 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif +#endif + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else +#ifdef OPT_2 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif +#endif + +#ifdef OPT_12 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in do_summary:" + << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif +#ifdef OPT_2 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in do_summary:" + << from_expr(SSA.ns, "", simplify_expr(conjunction(store), SSA.ns)) + << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + // statistics + solver_calls++; + + // solve + if(solver()==decision_proceduret::D_UNSATISFIABLE) + { + // TODO: this is likely to be incomplete + summary.error_summaries[call_site]=true_exprt(); + summary.has_assertion=assertion_flag; +#ifndef OPT_2 + solver.pop_context(); +#endif + + return; + } + + // build error summary and add to summary + exprt::operandst var_values; + + for(const auto &var : SSA.params) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.globals_in) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.globals_out) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + for(const auto &var : SSA.nondets) + { + exprt summ_value=solver.get(var); + if(!summ_value.is_nil()) + var_values.push_back(equal_exprt(var, summ_value)); + } + + summary.error_summaries[call_site]=not_exprt(conjunction(var_values)); + summary.has_assertion=assertion_flag; + +#ifndef OPT_2 + solver.pop_context(); +#endif + +#ifdef OPT_2 + delete fresh_solver; +#endif +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_concretet::inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient) +{ + for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.end(); + n_it!=SSA.nodes.begin(); ) + { + n_it--; + + for(local_SSAt::nodet::function_callst::const_iterator f_it= + n_it->function_calls.begin(); + f_it!=n_it->function_calls.end(); f_it++) + { + assert(f_it->function().id()==ID_symbol); // no function pointers + + exprt postcondition_call=true_exprt(); + postcondition_call=compute_calling_context2( + function_name, SSA, old_summary, n_it, f_it, postcondition, sufficient); + + // TODO: just put all function calls into reason + reason[function_name].functions.insert(n_it->location); + + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + status() << "Recursively summarizing function " << fname << eom; + compute_summary_rec(fname, summaryt::call_sitet(n_it->location), + postcondition_call, context_sensitive); + } + } +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_concretet::compute_calling_context2() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt summarizer_bw_cex_concretet::compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient) +{ + assert(f_it->function().id()==ID_symbol); // no function pointers + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + + status() << "Computing calling context for function " << fname << eom; + + // solver + +#ifdef OPT_2 + incremental_solvert *fresh_solver= + incremental_solvert::allocate(SSA.ns, options.get_bool_option("refine")); + incremental_solvert &solver=(*fresh_solver); +#else + incremental_solvert &solver=ssa_db.get_solver(function_name); +#endif + + solver.set_message_handler(get_message_handler()); + + // collect globals at call site + std::map< + local_SSAt::nodet::function_callst::const_iterator, + local_SSAt::var_sett> + cs_globals_out; + SSA.get_globals(n_it->location, cs_globals_out[f_it], false); + + exprt::operandst c; + +#ifdef OPT_12 + exprt::operandst store; +#endif + + // add forward information if available + if(!old_summary.fw_precondition.is_nil()) + c.push_back(old_summary.fw_precondition); + if(!old_summary.fw_invariant.is_nil()) + c.push_back(old_summary.fw_invariant); + c.push_back(ssa_inliner.get_summaries(SSA)); // forward summaries + + exprt::operandst assert_postcond, noassert_postcond; + // add error summaries for function calls + ssa_inliner.get_summaries( + SSA, + summaryt::call_sitet(n_it->location), + false, + assert_postcond, + noassert_postcond, + c); + assert_postcond.push_back(postcondition); // context + c.push_back(not_exprt(conjunction(assert_postcond))); + c.push_back(not_exprt(disjunction(noassert_postcond))); + +#ifdef OPT_12 + store << SSA; +#else + solver << SSA; +#endif + + solver.new_context(); + +#ifdef OPT_12 + store.push_back(SSA.get_enabling_exprs()); +#else + solver << SSA.get_enabling_exprs(); +#endif + +#ifdef OPT_11 + solver << simplify_expr(conjunction(c), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(c)); +#else + solver << conjunction(c); +#endif +#endif + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + +#ifdef OPT_11 + solver << simplify_expr(conjunction(loophead_selects), SSA.ns); +#else +#ifdef OPT_12 + store.push_back(conjunction(loophead_selects)); +#else + solver << conjunction(loophead_selects); +#endif +#endif + +#ifdef OPT_12 +#ifdef DEBUG + std::cout << "\n\n\n pushing to the solver in compute_calling_context2:" + << from_expr(SSA.ns, "", conjunction(store)) << "\n\n\n"; +#endif + solver << simplify_expr(conjunction(store), SSA.ns); +#endif + + + // build postcondition + exprt postcondition_call; + + if(solver()!=decision_proceduret::D_SATISFIABLE) + { + postcondition_call=true_exprt(); // TODO: this is likely to be incomplete + solver.pop_context(); + return postcondition_call; + } + + bool result=solver()==decision_proceduret::D_SATISFIABLE; + assert(result); + + exprt::operandst postcond_values; + for(local_SSAt::var_sett::const_iterator it=cs_globals_out[f_it].begin(); + it!=cs_globals_out[f_it].end(); it++) + { + exprt postc_value=solver.get(*it); + postcond_values.push_back(equal_exprt(*it, postc_value)); + } + postcondition_call=conjunction(postcond_values); + + solver.pop_context(); + + // get callee SSA and rename + local_SSAt &fSSA=ssa_db.get(fname); + ssa_inliner.rename_to_callee( + f_it, + fSSA.params, + cs_globals_out[f_it], + fSSA.globals_out, + postcondition_call); + + debug() << "Backward calling context for " + << from_expr(SSA.ns, "", *f_it) << ": " + << from_expr(SSA.ns, "", postcondition_call) << eom; + + // statistics + solver_calls++; + +#ifdef OPT_2 + delete fresh_solver; +#endif + + return not_exprt(postcondition_call); +} diff --git a/src/solver/summarizer_bw_cex_concrete.h b/src/solver/summarizer_bw_cex_concrete.h new file mode 100644 index 000000000..b13d9509c --- /dev/null +++ b/src/solver/summarizer_bw_cex_concrete.h @@ -0,0 +1,84 @@ +/*******************************************************************\ + +Module: Simple Counterexample-based Backward Analysis + +Author: Kumar Madhukar, Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H +#define CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_concretet:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_concretet( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + virtual void compute_summary_rec( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + const exprt &postcondition, + bool context_sensitive); + + virtual void inline_summaries( + const function_namet &function_name, + local_SSAt &SSA, + const summaryt &old_summary, + const exprt &postcondition, + bool context_sensitive, + bool sufficient); + + virtual void do_summary( + const function_namet &function_name, + const summaryt::call_sitet &call_site, + local_SSAt &SSA, + const summaryt &old_summary, + summaryt &summary, + const exprt &postcondition, + bool context_sensitive); + + virtual exprt compute_calling_context2( + const function_namet &function_name, + local_SSAt &SSA, + summaryt old_summary, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + const exprt &postcondition, + bool sufficient); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_CONCRETE_H diff --git a/src/solver/summarizer_bw_cex_wp.cpp b/src/solver/summarizer_bw_cex_wp.cpp new file mode 100644 index 000000000..5f4d43e0f --- /dev/null +++ b/src/solver/summarizer_bw_cex_wp.cpp @@ -0,0 +1,768 @@ +/*******************************************************************\ + +Module: Slicing-based WP Counterexample-based Backward Analysis + +Author: Madhukar Kumar, Peter Schrammel + +\*******************************************************************/ + +// #define DEBUG + +#ifdef DEBUG +#include +#endif + +#include +#include +#include +#include +#include + +#include "summary_db.h" + +#include +#include +#include + +#include +#include +#include + +#include "summarizer_bw_cex_wp.h" + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize( + const function_namet &entry_function) +{ + // no dependencies to begin with + find_symbols_sett dependency_set; + + status() << "\nSummarizing function " << entry_function << eom; + compute_summary_rec( + entry_function, dependency_set, -1, summaryt::entry_call_site); +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::summarize() + + Inputs: + + Outputs: + + Purpose: summarize backwards from given assertion + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::summarize(const exprt &_error_assertion) +{ + status() << "\nBackward error analysis (WP)..." << eom; + error_assertion=_error_assertion; +#ifdef DEBUG + std::cout << "error assertion: " + << from_expr(ssa_db.get(entry_function).ns, "", error_assertion) + << "\n"; +#endif + summarize(entry_function); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::inline_summaries() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +find_symbols_sett summarizer_bw_cex_wpt::inline_summaries( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary) +{ + exprt::operandst slice; + + local_SSAt &SSA=ssa_db.get(function_name); + + exprt::operandst loophead_selects; + get_loophead_selects(function_name, SSA, *solver.solver, loophead_selects); + exprt c=conjunction(loophead_selects); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name + << ": (conjunction of loophead_selects):" + << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + slice.push_back(c); + ssa_inliner.rename(c, counter); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name + << ": (conjunction of loophead_selects):" + << "\t renamed info ~ " + << from_expr(ssa_db.get(function_name).ns, "", c) << "\n"; +#endif + + solver << c; + + ssa_dependency_grapht &ssa_depgraph=ssa_db.get_depgraph(function_name); + + struct worknodet + { + int node_index; + find_symbols_sett dependency_set; + }; + + worknodet start_node; + start_node.node_index=0; + start_node.dependency_set=dependency_set; + + typedef std::list worklistt; + worklistt worklist, work_waitlist; + std::vector covered_nodes; + + worklist.push_back(start_node); + + while(!worklist.empty()) + { +#ifdef DEBUG + std::cout << "worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + worknodet &worknode=worklist.front(); + +#ifdef DEBUG + std::cout << "working node: " << function_name + << ": " << worknode.node_index << "\n"; + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n\n\n"; +#endif + + // return if the top most node is reached + if(worknode.node_index==ssa_depgraph.top_node_index) + { + find_symbols_sett vars=worknode.dependency_set; + vars.insert(dependency_set.begin(), dependency_set.end()); + error_summary=simplify_summary(SSA.ns, conjunction(slice), vars); + return worknode.dependency_set; + } + + // modify worknode_dependency_set if the node is an assertion + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + { +#ifdef DEBUG + std::cout << "\t\t an assertion node\n"; +#endif + for(find_symbols_sett::const_iterator d_it= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.begin(); + d_it!= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols.end(); + d_it++) + { + worknode.dependency_set.insert(*d_it); + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + } + + // if this is a function call + if(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call==true) + { +#ifdef DEBUG + std::cout << "fcall: working node: " << function_name << ": " + << worknode.node_index << "\n"; +#endif + irep_idt fname= + to_symbol_expr( + to_function_application_expr( + ssa_depgraph.depnodes_map[worknode.node_index].node_info) + .function()).get_identifier(); + + find_symbols_sett renamed_dependencies; + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename( + renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols( + ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + +#ifdef DEBUG + std::cout << "\t size of dependency set: " + << worknode.dependency_set.size() << "\n"; + + std::cout << "\t dependency set: "; + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + std::cout << *d_it; + } + std::cout << "\n"; +#endif + + worknode.dependency_set= + compute_summary_rec( + fname, + worknode.dependency_set, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + summaryt::call_sitet( + ssa_depgraph.depnodes_map[worknode.node_index].location)); + slice.push_back(ssa_depgraph.depnodes_map[worknode.node_index].node_info); + + renamed_dependencies.clear(); + + for(find_symbols_sett::iterator d_it=worknode.dependency_set.begin(); + d_it!=worknode.dependency_set.end(); d_it++) + { + irep_idt renamed_id=*d_it; + // detach the '@' symbol if there + ssa_inliner.rename( + renamed_id, + ssa_depgraph.depnodes_map[worknode.node_index].rename_counter, + false); + renamed_dependencies.insert(renamed_id); + } + + worknode.dependency_set=renamed_dependencies; + + if(!worknode.dependency_set.empty()) + { + find_symbols_sett guard_dependencies; + find_symbols( + ssa_depgraph.depnodes_map[worknode.node_index].guard, + guard_dependencies); + for(find_symbols_sett::const_iterator d_it=guard_dependencies.begin(); + d_it!=guard_dependencies.end(); d_it++) + { + worknode.dependency_set.insert(*d_it); + } + } + } + + // if the dependency set is non-empty + if(!worknode.dependency_set.empty()) + { + exprt worknode_info= + ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + worknode_info=not_exprt(worknode_info); + + if(worknode.node_index!=0) + { + if(!(ssa_depgraph.depnodes_map[worknode.node_index].is_function_call)) + { + if((ssa_depgraph.depnodes_map[worknode.node_index] + .is_assertion==false) || + (worknode_info==error_assertion)) + { +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + + slice.push_back(worknode_info); + ssa_inliner.rename(worknode_info, counter); + +#ifdef DEBUG + std::cout << "Solver <-- renamed assertion: " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; + std::cout << "Solver <-- " << function_name << ": (node) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + (ssa_db.get(function_name)).ns, "", worknode_info) + << "\n"; +#endif + solver << worknode_info; + } + } + else + { + exprt guard_binding= + ssa_depgraph.depnodes_map[worknode.node_index].guard; +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t original info ~ " + << from_expr(ssa_db.get(function_name).ns, "", guard_binding) + << "\n"; +#endif + + ssa_inliner.rename(guard_binding, counter); + +#ifdef DEBUG + std::cout << "Solver <-- " << function_name << ": (bind) node#:" + << worknode.node_index << "\t renamed info ~ " + << from_expr( + ssa_db.get(function_name).ns, "", guard_binding) + << "\n"; +#endif + solver << guard_binding; + slice.push_back(guard_binding); + } + } + } + + // if not a function call and the dependency set is non-empty + if((ssa_depgraph.depnodes_map[worknode.node_index] + .is_function_call==false) && + (!worknode.dependency_set.empty())) + { + exprt worknode_info= + ssa_depgraph.depnodes_map[worknode.node_index].node_info; + if(ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==true) + worknode_info=not_exprt(worknode_info); + + if((ssa_depgraph.depnodes_map[worknode.node_index].is_assertion==false) || + (worknode_info==error_assertion)) + { + worknode.dependency_set= + ssa_depgraph.depnodes_map[worknode.node_index].used_symbols; + } + } + + for(ssa_dependency_grapht::annotated_predecessorst::const_iterator + p_it=ssa_depgraph.depnodes_map[worknode.node_index] + .predecessors.begin(); + p_it!=ssa_depgraph.depnodes_map[worknode.node_index].predecessors.end(); + p_it++) + { + ssa_dependency_grapht::annotated_predecessort pred=*p_it; + int pred_node_index=pred.predecessor_node_index; + find_symbols_sett pred_annotation=pred.annotation; + + bool dependencies_merged=false; + for(worklistt::iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + if(w_it->node_index==pred_node_index) + { + dependencies_merged=true; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); + a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!= + worknode.dependency_set.end()) + { + if((w_it->dependency_set).find(*a_it)== + (w_it->dependency_set).end()) + { + (w_it->dependency_set).insert(*a_it); + } + } + } + break; + } + } + + if(dependencies_merged==false) + { + worknodet new_worknode; + new_worknode.node_index=pred_node_index; + + for(find_symbols_sett::const_iterator + a_it=pred_annotation.begin(); a_it!=pred_annotation.end(); a_it++) + { + if(worknode.dependency_set.find(*a_it)!=worknode.dependency_set.end()) + new_worknode.dependency_set.insert(*a_it); + } + + work_waitlist.push_back(new_worknode); + } + } + +#ifdef DEBUG + std::cout << function_name << ": worklist: "; + for(worklistt::const_iterator w_it=worklist.begin(); + w_it!=worklist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; + + std::cout << "\t" << function_name << ": waitlist: "; + for(worklistt::const_iterator w_it=work_waitlist.begin(); + w_it!=work_waitlist.end(); w_it++) + { + std::cout << w_it->node_index << " "; + } + std::cout << "\n"; +#endif + + covered_nodes.push_back(worknode.node_index); + worklist.pop_front(); + +#ifdef DEBUG + std::cout << function_name << ": covered : "; + for(int l=0; l &waitlisted_worknode_successors= + ssa_depgraph.depnodes_map[waitlisted_worknode.node_index].successors; + + for(unsigned i=0; i " << function_name + << " ; dependency_set -> "; + for(find_symbols_sett::iterator d_it=dependency_set.begin(); + d_it!=dependency_set.end(); d_it++) + { + std::cout << *d_it << ", "; + } + std::cout << "\n"; +} + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::simplify_summary() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void summarizer_bw_cex_wpt::simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr) +{ + if(expr.id()==ID_equal) + { + replace_map[expr.op0()]=expr.op1(); + return; + } + forall_operands(it, expr) + simplify_summary_build_map(replace_map, *it); +} + + +/*******************************************************************\ + +Function: summarizer_bw_cex_wpt::simplify_summary_replace() + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool summarizer_bw_cex_wpt::simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr) +{ + if(expr.id()==ID_function_application) + { + bool result=true; + exprt::operandst &args=to_function_application_expr(expr).arguments(); + for(size_t i=0; i +#include +#include +#include +#include +#include +#include + +#include + +#include "summarizer_bw_cex.h" + +class summarizer_bw_cex_wpt:public summarizer_bw_cex_baset +{ +public: + summarizer_bw_cex_wpt( + optionst &_options, + summary_dbt &_summary_db, + ssa_dbt &_ssa_db, + ssa_unwindert &_ssa_unwinder, + ssa_inlinert &_ssa_inliner, + incremental_solvert &_solver, + function_namet _entry_function, + function_namet _error_function): + summarizer_bw_cex_baset( + _options, + _summary_db, + _ssa_db, + _ssa_unwinder, + _ssa_inliner, + _entry_function, + _error_function), + solver(_solver) + {} + + virtual void summarize(const function_namet &entry_function); + virtual void summarize(const exprt &_error_assertion); + + virtual property_checkert::resultt check(); + +protected: + incremental_solvert &solver; + + virtual find_symbols_sett inline_summaries( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + exprt &error_summary); + + virtual find_symbols_sett compute_summary_rec( + const function_namet &function_name, + const find_symbols_sett &dependency_set, + int counter, + const summaryt::call_sitet &call_site); + + virtual void debug_print( + const function_namet &function_name, + find_symbols_sett &dependency_set); + + exprt simplify_summary( + const namespacet &ns, + exprt summary, + const find_symbols_sett &vars); + + void simplify_summary_build_map( + replace_mapt &replace_map, const exprt &expr); + + bool simplify_summary_replace( + const replace_mapt &replace_map, exprt &expr); + + void simplify_summary_cleanup( + const find_symbols_sett &vars, exprt &expr); +}; + +#endif // CPROVER_2LS_SOLVER_SUMMARIZER_BW_CEX_WP_H diff --git a/src/solver/summary.cpp b/src/solver/summary.cpp index 0539d9ecd..547002037 100644 --- a/src/solver/summary.cpp +++ b/src/solver/summary.cpp @@ -14,10 +14,10 @@ Author: Peter Schrammel #include "summary.h" -const summaryt::call_sitet summaryt::entry_call_site; - // #define PRETTY_PRINT +const summaryt::call_sitet summaryt::entry_call_site; + /*******************************************************************\ Function: summaryt::output @@ -79,6 +79,18 @@ void summaryt::output(std::ostream &out, const namespacet &ns) const #endif out << std::endl; out << "terminates: " << threeval2string(terminates) << std::endl; + for(error_summariest::const_iterator + it=error_summaries.begin(); + it!=error_summaries.end(); it++) + { + out << "error summary for "; + if(it->first==entry_call_site) + out << "entry point"; + else + out << "location " << it->first.location_number; + out << ": " << std::endl + << " " << from_expr(ns, "", it->second) << std::endl; + } } /*******************************************************************\ diff --git a/src/solver/summary.h b/src/solver/summary.h index 24f142d35..c7e68a6c6 100644 --- a/src/solver/summary.h +++ b/src/solver/summary.h @@ -15,7 +15,7 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../ssa/local_ssa.h" +#include typedef enum {YES, NO, UNKNOWN} threevalt; @@ -38,15 +38,11 @@ class summaryt bw_invariant(nil_exprt()), termination_argument(nil_exprt()), terminates(UNKNOWN), - mark_recompute(false), - has_assertion(false) - { - } + mark_recompute(false) {} var_listt params; var_sett globals_in, globals_out; expr_sett nondets; - predicatet fw_precondition; // accumulated calling contexts (over-approx) // predicatet fw_postcondition; // we are not projecting that out currently predicatet fw_transformer; // forward summary (over-approx) @@ -59,32 +55,39 @@ class summaryt predicatet termination_argument; threevalt terminates; - bool mark_recompute; //to force recomputation of the summary - // (used for invariant reuse in k-induction) - - //-------------- + // -------------- // the following is for generating interprocedural counterexample - bool has_assertion; + bool has_assertion; std::list nonpassed_assertions; - struct call_sitet { //TODO: we also need unwinding information here - call_sitet() - : location_number(UINT_MAX) {} - explicit call_sitet(local_SSAt::locationt loc) - : location_number(loc->location_number) {} + struct call_sitet + { // TODO: we also need unwinding information here + call_sitet():location_number(UINT_MAX) {} + explicit call_sitet(local_SSAt::locationt loc): + location_number(loc->location_number) + { + } unsigned location_number; bool operator<(const call_sitet &other) const - { return (location_number < other.location_number); } + { + return (location_number error_summariest; error_summariest error_summaries; + // -------------- + + bool mark_recompute; // to force recomputation of the summary + // (used for invariant reuse in k-induction) void output(std::ostream &out, const namespacet &ns) const; @@ -97,5 +100,4 @@ class summaryt std::string threeval2string(threevalt v); - #endif diff --git a/src/solver/summary_db.h b/src/solver/summary_db.h index 34c761335..04145d8a5 100644 --- a/src/solver/summary_db.h +++ b/src/solver/summary_db.h @@ -25,8 +25,8 @@ class summary_dbt:public messaget summaryt get(const function_namet &function_name) const { return store.at(function_name); } - void set(const function_namet &function_name, const summaryt &summary) - { store[function_name] = summary; } + void set(const function_namet &function_name, const summaryt &summary) + { store[function_name]=summary; } bool exists(const function_namet &function_name) const { return store.find(function_name)!=store.end(); } void put(const function_namet &function_name, const summaryt &summary); diff --git a/src/ssa/Makefile b/src/ssa/Makefile index 91f1b3d96..b997fff90 100644 --- a/src/ssa/Makefile +++ b/src/ssa/Makefile @@ -1,11 +1,23 @@ SRC = local_ssa.cpp \ - ssa_domain.cpp translate_union_member.cpp malloc_ssa.cpp \ - guard_map.cpp ssa_object.cpp assignments.cpp ssa_dereference.cpp \ - ssa_value_set.cpp address_canonizer.cpp simplify_ssa.cpp \ - ssa_build_goto_trace.cpp ssa_inliner.cpp ssa_unwinder.cpp \ - unwindable_local_ssa.cpp ssa_db.cpp \ - ssa_dependency_graph.cpp \ + ssa_domain.cpp \ + translate_union_member.cpp \ + malloc_ssa.cpp \ + guard_map.cpp \ + ssa_object.cpp \ + assignments.cpp \ + ssa_dereference.cpp \ + ssa_value_set.cpp \ + address_canonizer.cpp \ + simplify_ssa.cpp \ + ssa_build_goto_trace.cpp \ + ssa_inliner.cpp \ + ssa_unwinder.cpp \ + unwindable_local_ssa.cpp \ ssa_const_propagator.cpp \ + ssa_dependency_graph.cpp \ + ssa_refiner_monolithic.cpp \ + ssa_refiner_selective.cpp \ + # empty last line include ../config.inc include $(CBMC)/src/config.inc diff --git a/src/ssa/local_ssa.cpp b/src/ssa/local_ssa.cpp index 5102df7a5..e997d27d3 100644 --- a/src/ssa/local_ssa.cpp +++ b/src/ssa/local_ssa.cpp @@ -19,6 +19,9 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include + + #include #include "local_ssa.h" @@ -66,11 +69,6 @@ void local_SSAt::build_SSA() // entry and exit variables get_entry_exit_vars(); - -#ifdef ASSERTION_HOISTING - // collect assertions after loop exit (for k-induction assertion hoisting) - assertions_after_loop(); -#endif } /*******************************************************************\ @@ -104,16 +102,16 @@ void local_SSAt::get_entry_exit_vars() if(ns.follow(symbol->type).id()==ID_struct) { - exprt param = read_rhs(symbol->symbol_expr(), first); + exprt param=read_rhs(symbol->symbol_expr(), first); #if 0 - std::cout << "param: " + std::cout << "param: " << from_expr(ns, "", param) << std::endl; #endif forall_operands(it, param) params.push_back(to_symbol_expr(*it)); } else - params.push_back(symbol->symbol_expr()); + params.push_back(symbol->symbol_expr()); } // get globals in @@ -123,8 +121,58 @@ void local_SSAt::get_entry_exit_vars() goto_programt::const_targett last=goto_function.body.instructions.end(); last--; get_globals(last, globals_out, true, true, last->function); + + // get nondeterministic variables + get_nondet_vars(); } +/*******************************************************************\ + +Function: local_SSAt::get_nondet_vars + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void local_SSAt::get_nondet_vars(const exprt &expr) +{ + if(expr.id()==ID_nondet_symbol) + nondets.insert(expr); + else + forall_operands(it, expr) + get_nondet_vars(*it); +} + +void local_SSAt::get_nondet_vars() +{ + for(nodest::iterator n_it=nodes.begin(); + n_it!=nodes.end(); n_it++) + { + for(nodet::equalitiest::const_iterator + e_it=n_it->equalities.begin(); + e_it!=n_it->equalities.end(); + e_it++) + get_nondet_vars(*e_it); + + for(nodet::constraintst::const_iterator + c_it=n_it->constraints.begin(); + c_it!=n_it->constraints.end(); + c_it++) + get_nondet_vars(*c_it); + + for(nodet::assertionst::const_iterator + a_it=n_it->assertions.begin(); + a_it!=n_it->assertions.end(); + a_it++) + get_nondet_vars(*a_it); + } +} + + /*******************************************************************\ Function: local_SSAt::get_globals @@ -154,21 +202,26 @@ void local_SSAt::get_globals( << from_expr(ns, "", read_lhs(it->get_expr(), loc)) << std::endl; #endif - if(!with_returns && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos) + bool is_return=id2string(it->get_identifier()).find( + "#return_value")!=std::string::npos; + if(!with_returns && is_return) continue; // filter out return values of other functions if(with_returns && returns_for_function!="" && - id2string(it->get_identifier()).find( - "#return_value")!=std::string::npos && + is_return && id2string(it->get_identifier()).find( id2string(returns_for_function)+"#return_value")==std::string::npos) continue; if(rhs_value) { + // workaround for the problem that + // rhs() for a return value is always the "input" return value + #if 0 // --loc may be invalid + const exprt &expr=is_return ? + read_lhs(it->get_expr(), --loc) : read_rhs(it->get_expr(), loc); + #endif const exprt &expr=read_rhs(it->get_expr(), loc); globals.insert(to_symbol_expr(expr)); } @@ -299,14 +352,15 @@ Function: local_SSAt::find_location_by_number \*******************************************************************/ -local_SSAt::locationt local_SSAt::find_location_by_number(unsigned location_number) const +local_SSAt::locationt local_SSAt::find_location_by_number( + unsigned location_number) const { - local_SSAt::nodest::const_iterator n_it =nodes.begin(); - for(; n_it != nodes.end(); n_it++) + for(const auto &node : nodes) { - if(n_it->location->location_number == location_number) break; + if(node.location->location_number==location_number) + return node.location; } - return n_it->location; + assert(false); } /*******************************************************************\ @@ -373,15 +427,8 @@ void local_SSAt::build_phi_nodes(locationt loc) // ignore custom template variables if(id2string(o_it->get_identifier()). - find(TEMPLATE_PREFIX)!=std::string::npos) continue; - - #ifdef DEBUG - std::cout << "PHI " << o_it->get_identifier() << "\n"; - #endif - - //ignore custom template variables - if(id2string(o_it->get_identifier()). - find(TEMPLATE_PREFIX)!=std::string::npos) continue; + find(TEMPLATE_PREFIX)!=std::string::npos) + continue; // Yes. Get the source -> def map. const ssa_domaint::loc_def_mapt &incoming=p_it->second; @@ -584,25 +631,28 @@ void local_SSAt::build_function_call(locationt loc) for(exprt::operandst::iterator it=f.arguments().begin(); it!=f.arguments().end(); ++it, ++i) { - symbol_exprt arg(id2string(fname)+"#"+i2string(loc->location_number)+ - "#arg"+i2string(i),it->type()); - const typet &argtype = ns.follow(it->type()); + symbol_exprt arg( + id2string(fname)+"#"+i2string(loc->location_number)+ + "#arg"+i2string(i), it->type()); + const typet &argtype=ns.follow(it->type()); if(argtype.id()==ID_struct) { - exprt lhs = read_rhs(arg, loc); - for(int j=0; jequalities.push_back(equal_exprt(lhs.operands()[j], - it->operands()[j])); - } + n_it->equalities.push_back( + equal_exprt(lhs.operands()[j], it->operands()[j])); + } } else { - n_it->equalities.push_back(equal_exprt(arg,*it)); + n_it->equalities.push_back(equal_exprt(arg, *it)); } - *it = arg; + *it=arg; } - n_it->function_calls.push_back(to_function_application_expr(f)); + + n_it->function_calls.push_back( + to_function_application_expr(f)); } } @@ -631,7 +681,8 @@ void local_SSAt::build_cond(locationt loc) { equal_exprt equality(cond_symbol(loc), true_exprt()); (--nodes.end())->equalities.push_back(equality); - }} + } +} /*******************************************************************\ @@ -782,64 +833,6 @@ void local_SSAt::assertions_to_constraints() /*******************************************************************\ -Function: local_SSAt::assertions_after_loop - - Inputs: - - Outputs: - - Purpose: find assertions after loop exit - -\*******************************************************************/ - -void local_SSAt::assertions_after_loop() -{ - if(nodes.empty()) - return; - std::map assertion_map; - std::list::iterator> loopheads; - loopheads.push_back(nodes.begin()); - nodest::iterator n_it=nodes.end(); - while(n_it!=nodes.begin()) //collect assertions backwards - { - n_it--; - -#if 0 - std::cout << "location: " << n_it->location->location_number << std::endl; - std::cout << "loophead: " << loopheads.back()->location->location_number << std::endl; -#endif - - if(n_it==loopheads.back()) //assign parent-level assertions at loophead - { - loopheads.pop_back(); - if(!loopheads.empty()) - { - const exprt::operandst &assertions = - assertion_map[loopheads.back()->location]; - n_it->assertions_after_loop.insert(n_it->assertions_after_loop.end(), - assertions.begin(),assertions.end()); - assertion_map[loopheads.back()->location].clear(); - //do not consider assertions after another loop - } - else // we should have reached the beginning - assert(n_it==nodes.begin()); - } - if(n_it->loophead!=nodes.end()) - { - assert(!loopheads.empty()); - loopheads.push_back(n_it->loophead); - } - if(!n_it->assertions.empty() && !loopheads.empty()) - { - exprt::operandst &a = assertion_map[loopheads.back()->location]; - a.insert(a.end(),n_it->assertions.begin(),n_it->assertions.end()); - } - //TODO: could also add assertions that are on a direct path within a loop - } -} - -/*******************************************************************\ - Function: local_SSAt::cond_symbol Inputs: @@ -937,7 +930,6 @@ exprt local_SSAt::read_lhs( #ifdef DEBUG std::cout << from_expr(ns, "", tmp1) << "is_object" << '\n'; #endif - // yes, it is if(assignments.assigns(loc, object)) return name(object, OUT, loc); @@ -1283,7 +1275,10 @@ symbol_exprt local_SSAt::name( unsigned cnt=loc->location_number; irep_idt new_id=id2string(id)+"#"+ - (kind==PHI?"phi":kind==LOOP_BACK?"lb":kind==LOOP_SELECT?"ls":"")+ + (kind==PHI?"phi": + kind==LOOP_BACK?"lb": + kind==LOOP_SELECT?"ls": + "")+ i2string(cnt)+ (kind==LOOP_SELECT?std::string(""):suffix); @@ -1501,7 +1496,7 @@ void local_SSAt::assign_rec( assign_rec(new_lhs, new_rhs, guard, loc); } else - throw "UNKNOWN LHS: "+lhs.id_string(); + throw "UNKNOWN LHS: "+lhs.id_string(); // NOLINT(readability/throw) } /*******************************************************************\ @@ -1579,7 +1574,7 @@ void local_SSAt::nodet::output( std::ostream &out, const namespacet &ns) const { - if(!enabling_expr.is_true()) + if(!enabling_expr.is_true()) out << "(enable) " << from_expr(ns, "", enabling_expr) << "\n"; #if 0 if(!marked) @@ -1608,12 +1603,6 @@ void local_SSAt::nodet::output( f_it!=function_calls.end(); f_it++) out << "(F) " << from_expr(ns, "", *f_it) << "\n"; - -#if 0 - if(!assertions_after_loop.empty()) - out << "(assertions-after-loop) " - << from_expr(ns, "", conjunction(assertions_after_loop)) << "\n"; -#endif } /*******************************************************************\ @@ -1676,42 +1665,37 @@ Function: local_SSAt::operator << \*******************************************************************/ -std::vector & operator << ( +std::vector &operator<<( std::vector &dest, const local_SSAt &src) { -#ifdef SLICING - ssa_slicert ssa_slicer; - ssa_slicer(dest,src); -#else - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) { - if(n_it->marked) continue; + if(n_it->marked) + continue; for(local_SSAt::nodet::equalitiest::const_iterator - e_it=n_it->equalities.begin(); + e_it=n_it->equalities.begin(); e_it!=n_it->equalities.end(); e_it++) { - if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *e_it)); else dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator - c_it=n_it->constraints.begin(); + c_it=n_it->constraints.begin(); c_it!=n_it->constraints.end(); c_it++) { - if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *c_it)); else dest.push_back(*c_it); } } -#endif - return dest; } @@ -1727,14 +1711,10 @@ Function: local_SSAt::operator << \*******************************************************************/ -std::list & operator << ( +std::list &operator<<( std::list &dest, const local_SSAt &src) { -#ifdef SLICING - ssa_slicert ssa_slicer; - ssa_slicer(dest, src); -#else for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); n_it!=src.nodes.end(); n_it++) { @@ -1745,10 +1725,10 @@ std::list & operator << ( e_it!=n_it->equalities.end(); e_it++) { - if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*e_it)); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *e_it)); else - dest.push_back(*e_it); + dest.push_back(*e_it); } for(local_SSAt::nodet::constraintst::const_iterator @@ -1756,14 +1736,12 @@ std::list & operator << ( c_it!=n_it->constraints.end(); c_it++) { - if(!n_it->enabling_expr.is_true()) - dest.push_back(implies_exprt(n_it->enabling_expr,*c_it)); + if(!n_it->enabling_expr.is_true()) + dest.push_back(implies_exprt(n_it->enabling_expr, *c_it)); else - dest.push_back(*c_it); + dest.push_back(*c_it); } } -#endif - return dest; } @@ -1783,13 +1761,6 @@ decision_proceduret &operator<<( decision_proceduret &dest, const local_SSAt &src) { -#ifdef SLICING - std::list tmp; - tmp << src; - for(std::list::const_iterator it=tmp.begin(); - it!=tmp.end(); it++) - dest << *it; -#else for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); n_it!=src.nodes.end(); n_it++) { @@ -1800,10 +1771,24 @@ decision_proceduret &operator<<( e_it!=n_it->equalities.end(); e_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*e_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *e_it); else - dest << *e_it; + dest << *e_it; + +#if 0 + // freeze cond variables + if(e_it->op0().id()==ID_symbol && + e_it->op0().type().id()==ID_bool) + { + const symbol_exprt &symbol=to_symbol_expr(e_it->op0()); + if(id2string(symbol.get_identifier()).find("ssa::$cond")!= + std::string::npos) + { + dest.solver->set_frozen(dest.solver->convert(symbol)); + } + } +#endif } for(local_SSAt::nodet::constraintst::const_iterator @@ -1811,13 +1796,12 @@ decision_proceduret &operator<<( c_it!=n_it->constraints.end(); c_it++) { - if(!n_it->enabling_expr.is_true()) - dest << implies_exprt(n_it->enabling_expr,*c_it); + if(!n_it->enabling_expr.is_true()) + dest << implies_exprt(n_it->enabling_expr, *c_it); else - dest << *c_it; + dest << *c_it; } } -#endif return dest; } @@ -1837,13 +1821,6 @@ incremental_solvert &operator<<( incremental_solvert &dest, const local_SSAt &src) { -#ifdef SLICING - std::list tmp; - tmp << src; - for(std::list::const_iterator it=tmp.begin(); - it!=tmp.end(); it++) - dest << *it; -#else for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); n_it!=src.nodes.end(); n_it++) { @@ -1885,7 +1862,6 @@ incremental_solvert &operator<<( dest << *c_it; } } -#endif return dest; } @@ -1905,10 +1881,10 @@ exprt local_SSAt::get_enabling_exprs() const { exprt::operandst result; result.reserve(enabling_exprs.size()); - for(std::list::const_iterator it=enabling_exprs.begin(); + for(std::vector::const_iterator it=enabling_exprs.begin(); it!=enabling_exprs.end(); ++it) { - std::list::const_iterator lh=it; ++lh; + std::vector::const_iterator lh=it; ++lh; if(lh!=enabling_exprs.end()) result.push_back(not_exprt(*it)); else @@ -1943,4 +1919,3 @@ bool local_SSAt::has_function_calls() const } return found; } - diff --git a/src/ssa/local_ssa.h b/src/ssa/local_ssa.h index 0c87c160d..7e70a5e97 100644 --- a/src/ssa/local_ssa.h +++ b/src/ssa/local_ssa.h @@ -13,7 +13,8 @@ Author: Daniel Kroening, kroening@kroening.com #include -#include "../domains/incremental_solver.h" +#include + #include "ssa_domain.h" #include "guard_map.h" #include "ssa_object.h" @@ -57,18 +58,15 @@ class local_SSAt public: inline nodet( locationt _location, - std::list::iterator _loophead) - : + std::list::iterator _loophead): + function_calls_inlined(false), enabling_expr(true_exprt()), - marked(false), function_calls_inlined(false), + marked(false), location(_location), loophead(_loophead) { } - exprt enabling_expr; // for incremental unwinding - bool marked; // for incremental unwinding - typedef std::vector equalitiest; equalitiest equalities; @@ -77,7 +75,6 @@ class local_SSAt typedef std::vector assertionst; assertionst assertions; - exprt::operandst assertions_after_loop; //for k-induction assertion hoisting typedef std::vector assumptionst; assertionst assumptions; @@ -86,6 +83,8 @@ class local_SSAt function_callst function_calls; bool function_calls_inlined; + exprt enabling_expr; // for incremental unwinding + bool marked; // for incremental unwinding // custom invariant templates typedef std::vector templatest; @@ -113,17 +112,17 @@ class local_SSAt void mark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=true; + for(auto &n : nodes) + n.marked=true; } void unmark_nodes() { - for(nodest::iterator n_it=nodes.begin(); - n_it!=nodes.end(); n_it++) n_it->marked=false; + for(auto &n : nodes) + n.marked=false; } // for incremental unwinding - std::list enabling_exprs; + std::vector enabling_exprs; exprt get_enabling_exprs() const; // function entry and exit variables @@ -205,7 +204,6 @@ class local_SSAt assert(it!=location_map.end()); return it->second; } - locationt find_location_by_number(unsigned location_number) const; protected: @@ -222,12 +220,14 @@ class local_SSAt void build_function_call(locationt loc); void build_assertions(locationt loc); void build_assumptions(locationt loc); - void assertions_after_loop(); // custom templates void collect_custom_templates(); replace_mapt template_newvars; exprt template_last_newvar; + + void get_nondet_vars(const exprt &expr); + void get_nondet_vars(); }; std::vector & operator << diff --git a/src/ssa/malloc_ssa.cpp b/src/ssa/malloc_ssa.cpp index 9e93714b8..fd5246b4e 100644 --- a/src/ssa/malloc_ssa.cpp +++ b/src/ssa/malloc_ssa.cpp @@ -6,8 +6,6 @@ Author: Daniel Kroening, kroening@kroening.com \*******************************************************************/ -#include - #include #include #include diff --git a/src/ssa/ssa_const_propagator.cpp b/src/ssa/ssa_const_propagator.cpp index 4ca296238..ab5c9fd68 100644 --- a/src/ssa/ssa_const_propagator.cpp +++ b/src/ssa/ssa_const_propagator.cpp @@ -2,11 +2,11 @@ Module: SSA Constant Propagator -Author: Kumar Madhukar, Peter Schrammel +Author: Kumar Madhukar \*******************************************************************/ -//#define DEBUG +// #define DEBUG #ifdef DEBUG #include @@ -19,10 +19,6 @@ Author: Kumar Madhukar, Peter Schrammel #include "ssa_const_propagator.h" - -//bool iterate = true; - - /*******************************************************************\ Function: ssa_const_propagatort::operator() @@ -35,169 +31,173 @@ Function: ssa_const_propagatort::operator() \*******************************************************************/ -void ssa_const_propagatort::operator()(std::list &dest, - const local_SSAt &src) +void ssa_const_propagatort::operator()( + std::list &dest, const local_SSAt &src) { - values.iterate = true; - while(values.iterate){ - values.iterate = false; - for(local_SSAt::nodest::const_iterator n_it = src.nodes.begin(); - n_it != src.nodes.end(); n_it++) + values.iterate=true; + while(values.iterate) + { + values.iterate=false; + for(local_SSAt::nodest::const_iterator n_it=src.nodes.begin(); + n_it!=src.nodes.end(); n_it++) + { + const local_SSAt::nodet &node=*n_it; + + // check if node is active; 'continue' if not active + if(!node.enabling_expr.is_true()) + continue; + + // if src.enabling_exprs does not have a true in the end, + // then also continue + if(src.enabling_exprs.size()>0) + if((src.enabling_exprs.back()).is_true()) + continue; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) { - const local_SSAt::nodet &node=*n_it; - - // check if node is active; 'continue' if not active - if(!node.enabling_expr.is_true()) - continue; - - // if src.enabling_exprs does not have a true in the end, then also continue - if(src.enabling_exprs.size() > 0) - if((src.enabling_exprs.back()).is_true()) - continue; - - for(local_SSAt::nodet::equalitiest::const_iterator e_it=node.equalities.begin(); - e_it!=node.equalities.end(); e_it++) - { - equal_exprt e = to_equal_expr(*e_it); - const exprt &lhs = e.lhs(); - exprt &temprhs = e.rhs(); - - // resolving conditional expressions - while(temprhs.id() == ID_if){ - exprt simp_guard; + equal_exprt e=to_equal_expr(*e_it); + const exprt &lhs=e.lhs(); + exprt &temprhs=e.rhs(); + + // resolving conditional expressions + while(temprhs.id()==ID_if) + { + exprt simp_guard; #ifdef DEBUG - std::cout << "guard: " << from_expr(src.ns, "", temprhs.op0()) << std::endl; + std::cout << "guard: " << from_expr(src.ns, "", temprhs.op0()) + << std::endl; #endif - simp_guard = simplify_expr(temprhs.op0(), src.ns); + simp_guard=simplify_expr(temprhs.op0(), src.ns); #ifdef DEBUG - std::cout << "simplified: " << from_expr(src.ns, "", simp_guard) << std::endl; + std::cout << "simplified: " << from_expr(src.ns, "", simp_guard) + << std::endl; #endif - if(simp_guard.is_true()) - temprhs = temprhs.op1(); - else if(simp_guard.is_false()) - temprhs = temprhs.op2(); - else - break; - } - - const exprt &rhs = temprhs; - valuest copy_values = values; - - if(!copy_values.maps_to_top(rhs)) - assign(copy_values,lhs,rhs,src.ns); - else - copy_values.set_to_top(lhs); - + if(simp_guard.is_true()) + temprhs=temprhs.op1(); + else if(simp_guard.is_false()) + temprhs=temprhs.op2(); + else + break; + } + + const exprt &rhs=temprhs; + valuest copy_values=values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values, lhs, rhs, src.ns); + else + copy_values.set_to_top(lhs); + #ifdef DEBUG - copy_values.output(std::cout,src.ns); + copy_values.output(std::cout, src.ns); #endif - - if(rhs.id() == ID_symbol){ - if(!values.maps_to_top(lhs)) - assign(values,rhs,lhs,src.ns); - else - values.set_to_top(rhs); - } - + + if(rhs.id()==ID_symbol) + { + if(!values.maps_to_top(lhs)) + assign(values, rhs, lhs, src.ns); + else + values.set_to_top(rhs); + } + #ifdef DEBUG - values.output(std::cout,src.ns); + values.output(std::cout, src.ns); #endif - - values.merge(copy_values); - + + values.merge(copy_values); + #ifdef DEBUG - values.output(std::cout,src.ns); + values.output(std::cout, src.ns); #endif - } - - for(local_SSAt::nodet::constraintst::const_iterator c_it=n_it->constraints.begin(); - c_it!=n_it->constraints.end(); c_it++) - { - if(c_it->id()!=ID_equal) - continue; - - const equal_exprt e = to_equal_expr(*c_it); - const exprt &lhs = e.lhs(); - const exprt &rhs = e.rhs(); - - // if lhs is a variable and rhs is a constant expression - - valuest copy_values = values; - - if(!copy_values.maps_to_top(rhs)) - assign(copy_values,lhs,rhs,src.ns); - else - copy_values.set_to_top(lhs); + } + + for(local_SSAt::nodet::constraintst::const_iterator c_it= + n_it->constraints.begin(); + c_it!=n_it->constraints.end(); c_it++) + { + if(c_it->id()!=ID_equal) + continue; + + const equal_exprt e=to_equal_expr(*c_it); + const exprt &lhs=e.lhs(); + const exprt &rhs=e.rhs(); + + // if lhs is a variable and rhs is a constant expression + + valuest copy_values=values; + + if(!copy_values.maps_to_top(rhs)) + assign(copy_values, lhs, rhs, src.ns); + else + copy_values.set_to_top(lhs); #ifdef DEBUG - copy_values.output(std::cout,src.ns); + copy_values.output(std::cout, src.ns); #endif - - // if rhs is a variable and lhs is a constant expression - - if(!values.maps_to_top(lhs)) - assign(values,rhs,lhs,src.ns); - else - values.set_to_top(rhs); - + + // if rhs is a variable and lhs is a constant expression + + if(!values.maps_to_top(lhs)) + assign(values, rhs, lhs, src.ns); + else + values.set_to_top(rhs); + #ifdef DEBUG - values.output(std::cout,src.ns); + values.output(std::cout, src.ns); #endif - values.merge(copy_values); + values.merge(copy_values); #ifdef DEBUG - values.output(std::cout,src.ns); + values.output(std::cout, src.ns); #endif - - } } + } } - + #ifdef DEBUG - values.output(std::cout,src.ns); + values.output(std::cout, src.ns); #endif - + // iterate over values and get all equalities for(replace_symbolt::expr_mapt::const_iterator it=values.replace_const.expr_map.begin(); it!=values.replace_const.expr_map.end(); - ++it){ - - //std::cout << ' ' << it->first << " = " << - // from_expr(src.ns, "", it->second) << std::endl; - - dest.push_back(equal_exprt(symbol_exprt(it->first,it->second.type()),it->second)); - + ++it) + { + dest.push_back( + equal_exprt( + symbol_exprt(it->first, it->second.type()), it->second)); } } bool ssa_const_propagatort::valuest::maps_to_top(const exprt &expr) const { find_symbols_sett symbols; - find_symbols(expr,symbols); - for(find_symbols_sett::const_iterator it = symbols.begin(); - it != symbols.end(); ++it) - { - if(replace_const.expr_map.find(*it) - == replace_const.expr_map.end()) - return true; - } + find_symbols(expr, symbols); + for(find_symbols_sett::const_iterator it=symbols.begin(); + it!=symbols.end(); ++it) + { + if(replace_const.expr_map.find(*it)== + replace_const.expr_map.end()) + return true; + } return false; } - void ssa_const_propagatort::assign( - valuest &dest, - const exprt &lhs, - exprt rhs, - const namespacet &ns) const + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const { #ifdef DEBUG std::cout << "assign: " << from_expr(ns, "", lhs) << " := " << from_expr(ns, "", rhs) << std::endl; #endif - + values.replace_const(rhs); #ifdef DEBUG @@ -205,31 +205,31 @@ void ssa_const_propagatort::assign( << " := " << from_expr(ns, "", rhs) << std::endl; #endif - rhs = simplify_expr(rhs,ns); + rhs=simplify_expr(rhs, ns); #ifdef DEBUG std::cout << "simplified: " << from_expr(ns, "", lhs) << " := " << from_expr(ns, "", rhs) << std::endl; #endif - dest.set_to(lhs,rhs); + dest.set_to(lhs, rhs); } bool ssa_const_propagatort::valuest::set_to_top(const irep_idt &id) { - bool result = false; - replace_symbolt::expr_mapt::iterator r_it = + bool result=false; + replace_symbolt::expr_mapt::iterator r_it= replace_const.expr_map.find(id); - if(r_it != replace_const.expr_map.end()) - { - replace_const.expr_map.erase(r_it); - result = true; - } + if(r_it!=replace_const.expr_map.end()) + { + replace_const.expr_map.erase(r_it); + result=true; + } if(top_ids.find(id)==top_ids.end()) - { - top_ids.insert(id); - result = true; - } + { + top_ids.insert(id); + result=true; + } return result; } @@ -238,36 +238,39 @@ bool ssa_const_propagatort::valuest::set_to_top(const exprt &expr) return set_to_top(to_symbol_expr(expr).get_identifier()); } -void ssa_const_propagatort::valuest::set_to(const irep_idt &lhs_id, - const exprt &rhs_val) +void ssa_const_propagatort::valuest::set_to( + const irep_idt &lhs_id, + const exprt &rhs_val) { - - if(replace_const.expr_map[lhs_id] != rhs_val){ - replace_const.expr_map[lhs_id] = rhs_val; - iterate = true; + if(replace_const.expr_map[lhs_id]!=rhs_val) + { + replace_const.expr_map[lhs_id]=rhs_val; + iterate=true; } - std::set::iterator it = top_ids.find(lhs_id); - if(it!=top_ids.end()) top_ids.erase(it); + std::set::iterator it=top_ids.find(lhs_id); + if(it!=top_ids.end()) + top_ids.erase(it); } -void ssa_const_propagatort::valuest::set_to(const exprt &lhs, - const exprt &rhs_val) +void ssa_const_propagatort::valuest::set_to( + const exprt &lhs, + const exprt &rhs_val) { - const irep_idt &lhs_id = to_symbol_expr(lhs).get_identifier(); - set_to(lhs_id,rhs_val); + const irep_idt &lhs_id=to_symbol_expr(lhs).get_identifier(); + set_to(lhs_id, rhs_val); } void ssa_const_propagatort::valuest::output( - std::ostream &out, - const namespacet &ns) const + std::ostream &out, + const namespacet &ns) const { out << "const map: " << std::endl; for(replace_symbolt::expr_mapt::const_iterator it=replace_const.expr_map.begin(); it!=replace_const.expr_map.end(); ++it) - out << ' ' << it->first << "=" << - from_expr(ns, "", it->second) << std::endl; + out << ' ' << it->first << "=" + << from_expr(ns, "", it->second) << std::endl; out << "top ids: " << std::endl; for(std::set::const_iterator it=top_ids.begin(); @@ -278,28 +281,27 @@ void ssa_const_propagatort::valuest::output( bool ssa_const_propagatort::valuest::merge(const valuest &src) { - bool changed = false; + bool changed=false; for(replace_symbolt::expr_mapt::const_iterator it=src.replace_const.expr_map.begin(); it!=src.replace_const.expr_map.end(); ++it) + { + replace_symbolt::expr_mapt::iterator + c_it=replace_const.expr_map.find(it->first); + if(c_it!=replace_const.expr_map.end()) { - replace_symbolt::expr_mapt::iterator - c_it = replace_const.expr_map.find(it->first); - if(c_it != replace_const.expr_map.end()) - { - if(c_it->second != it->second) - { - set_to_top(it->first); - changed = true; - } - } - else if(top_ids.find(it->first)==top_ids.end()) - { - set_to(it->first,it->second); - changed = true; - } + if(c_it->second!=it->second) + { + set_to_top(it->first); + changed=true; + } + } + else if(top_ids.find(it->first)==top_ids.end()) + { + set_to(it->first, it->second); + changed=true; } + } return changed; } - diff --git a/src/ssa/ssa_const_propagator.h b/src/ssa/ssa_const_propagator.h index ee89071db..c8db8df51 100644 --- a/src/ssa/ssa_const_propagator.h +++ b/src/ssa/ssa_const_propagator.h @@ -2,24 +2,24 @@ Module: SSA Constant Propagator -Author: Kumar Madhukar, Peter Schrammel +Author: Kumar Madhukar \*******************************************************************/ -#ifndef CPROVER_SSA_CONST_PROPAGATOR_H -#define CPROVER_SSA_CONST_PROPAGATOR_H +#ifndef CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H +#define CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H #include #include #include "local_ssa.h" -class ssa_const_propagatort : public messaget +class ssa_const_propagatort:public messaget { - public: - - void operator()(std::list &dest, - const local_SSAt &src); +public: + void operator()( + std::list &dest, + const local_SSAt &src); struct valuest { @@ -33,7 +33,7 @@ class ssa_const_propagatort : public messaget bool merge(const valuest &src); - inline void clear() + void clear() { replace_const.expr_map.clear(); replace_const.type_map.clear(); @@ -53,28 +53,23 @@ class ssa_const_propagatort : public messaget bool maps_to_top(const exprt &expr) const; bool set_to_top(const exprt &expr); bool set_to_top(const irep_idt &id); - - bool iterate; + bool iterate; }; valuest values; - protected: - +protected: void assign( - valuest &dest, - const exprt &lhs, - exprt rhs, - const namespacet &ns) const; + valuest &dest, + const exprt &lhs, + exprt rhs, + const namespacet &ns) const; exprt evaluate_casts_in_constants( - exprt expr, - const typet& parent_type, - bool &valid) const; - - - + exprt expr, + const typet& parent_type, + bool &valid) const; }; -#endif +#endif // CPROVER_2LS_SSA_SSA_CONST_PROPAGATOR_H diff --git a/src/ssa/ssa_db.cpp b/src/ssa/ssa_db.cpp index fbbb80ef3..cfb570f0e 100644 --- a/src/ssa/ssa_db.cpp +++ b/src/ssa/ssa_db.cpp @@ -8,11 +8,14 @@ Author: Peter Schrammel #include "ssa_db.h" -//void ssa_dbt::depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner) -void ssa_dbt::depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner, bool entry) +void ssa_dbt::depgraph_create( + const function_namet &function_name, + const namespacet &ns, + ssa_inlinert &ssa_inliner, + bool entry) { - depgraph_store[function_name] = new ssa_dependency_grapht(*this,ns); - const local_SSAt &SSA=this->get(function_name); + depgraph_store[function_name]=new ssa_dependency_grapht(*this, ns); + const local_SSAt &SSA=this->get(function_name); depgraph_store[function_name]->create(SSA, ssa_inliner, entry); } diff --git a/src/ssa/ssa_db.h b/src/ssa/ssa_db.h index 7b4ee9237..c3c332074 100644 --- a/src/ssa/ssa_db.h +++ b/src/ssa/ssa_db.h @@ -12,13 +12,10 @@ Author: Peter Schrammel #include #include +#include #include #include -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_dependency_graph.h" - class ssa_inlinert; class ssa_dependency_grapht; @@ -41,19 +38,22 @@ class ssa_dbt delete item.second; for(auto &item : the_solvers) delete item.second; + for(auto &item : depgraph_store) + delete item.second; } - inline local_SSAt &get(const function_namet &function_name) const + local_SSAt &get(const function_namet &function_name) const { return *store.at(function_name); } - ssa_dependency_grapht &get_depgraph(const function_namet &function_name) const - { + ssa_dependency_grapht &get_depgraph( + const function_namet &function_name) const + { return *depgraph_store.at(function_name); } - inline incremental_solvert &get_solver(const function_namet &function_name) + incremental_solvert &get_solver(const function_namet &function_name) { solverst::iterator it=the_solvers.find(function_name); if(it!=the_solvers.end()) @@ -66,15 +66,15 @@ class ssa_dbt return *the_solvers.at(function_name); } - inline functionst &functions() { return store; } - inline solverst &solvers() { return the_solvers; } + functionst &functions() { return store; } + solverst &solvers() { return the_solvers; } - inline bool exists(const function_namet &function_name) const + bool exists(const function_namet &function_name) const { return store.find(function_name)!=store.end(); } - inline void create( + void create( const function_namet &function_name, const goto_functionst::goto_functiont &goto_function, const namespacet &ns) @@ -82,10 +82,17 @@ class ssa_dbt store[function_name]=new unwindable_local_SSAt(goto_function, ns); } - void depgraph_create(const function_namet &function_name, - const namespacet &ns, - ssa_inlinert &ssa_inliner, - bool entry); + void depgraph_create( + const function_namet &function_name, + const namespacet &ns, + ssa_inlinert &ssa_inliner, + bool entry) + { + depgraph_store[function_name]=new ssa_dependency_grapht(*this, ns); + const local_SSAt &SSA=this->get(function_name); + depgraph_store[function_name]->create(SSA, ssa_inliner, entry); + } + protected: const optionst &options; diff --git a/src/ssa/ssa_dependency_graph.cpp b/src/ssa/ssa_dependency_graph.cpp index c737a7ed7..e7490c685 100644 --- a/src/ssa/ssa_dependency_graph.cpp +++ b/src/ssa/ssa_dependency_graph.cpp @@ -1,81 +1,104 @@ +/*******************************************************************\ + +Module: SSA Dependency Graph + +Author: Madhukar Kumar + +\*******************************************************************/ #include #include #include +#include + #include "ssa_dependency_graph.h" void ssa_dependency_grapht::output(std::ostream &out) const { - for(unsigned index = 0; index < depnodes_map.size(); index++) + for(unsigned index=0; index Used Symbols: "; + for(find_symbols_sett::const_iterator u_it= + depnodes_map[index].used_symbols.begin(); + u_it!=depnodes_map[index].used_symbols.end(); u_it++) { - out << "Node#" << index << "; info: " << from_expr(ns, "", depnodes_map[index].node_info) << "\n"; - out << "Node#" << index << "; -> Used Symbols: "; - for(find_symbols_sett::const_iterator u_it=depnodes_map[index].used_symbols.begin(); - u_it!=depnodes_map[index].used_symbols.end(); u_it++){ - out << *u_it << " "; - } - out << "\n"; + out << *u_it << " "; + } + out << "\n"; - out << "Node#" << index << "; -> Modified Symbols: "; - for(find_symbols_sett::const_iterator m_it=depnodes_map[index].modified_symbols.begin(); - m_it!=depnodes_map[index].modified_symbols.end(); m_it++){ - out << *m_it << " "; - } - out << "\n"; + out << "Node#" << index << "; -> Modified Symbols: "; + for(find_symbols_sett::const_iterator m_it= + depnodes_map[index].modified_symbols.begin(); + m_it!=depnodes_map[index].modified_symbols.end(); m_it++) + { + out << *m_it << " "; + } + out << "\n"; + + out << "Successors: "; + for(unsigned i=0; i::const_iterator it=SSA.enabling_exprs.begin(); - it!=SSA.enabling_exprs.end(); ++it) + const irep_idt &enable= + to_symbol_expr(node.enabling_expr).get_identifier(); + for(size_t i=0; ilocation; - - //loop-head select - //TODO: this is an ugly hack (this can be changed as soon as unwindable_local_SSA provides smooth renaming with odometers) - //if(n_it->loophead!=SSA.nodes.end()) - if(e_it->op1().id()==ID_if && - e_it->op1().op0().id()==ID_symbol) - { - std::string var_string = id2string(e_it->op1().op0().get(ID_identifier)); - if(((var_string.substr(0,14)) == "ssa::$guard#ls")) + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) { - temp_node.is_loop = true; -/* symbol_exprt lsguard = SSA.name(SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, n_it->location); - ssa_local_unwinder.unwinder_rename(lsguard,*n_it,true);*/ - temp_node.guard = not_exprt(e_it->op1().op0()); - } - } - //temp_node.trivial_guard = true; - - equal_exprt e = to_equal_expr(*e_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - - find_symbols(rhs,temp_node.used_symbols); - find_symbols(lhs,temp_node.modified_symbols); - - if(!ignore_equality_done){ - std::string var_string = id2string(to_symbol_expr(lhs).get_identifier()); - if(((var_string.substr(0,11)) == "ssa::$guard") && (rhs.is_true())){ - ignore_equality = true; - ignore_equality_done = true; - } - } - - if(first_node && ignore_equality){ - if(entry){ - depnodes_map.push_back(temp_node); - //std::cout << "created equality node, with info: " << from_expr(ns, "", *e_it) << "\n"; - } - ignore_equality = false; - } - else{ - depnodes_map.push_back(temp_node); - //std::cout << "created equality node, with info: " << from_expr(ns, "", *e_it) << "\n"; - } - + find_symbols(*e_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*e_it; + temp_node.location=n_it->location; + + // loop-head select + // TODO: this is an ugly hack (this can be changed + // as soon as unwindable_local_SSA provides + // smooth renaming with odometers) + if(e_it->op1().id()==ID_if && + e_it->op1().op0().id()==ID_symbol) + { + std::string var_string=id2string(e_it->op1().op0().get(ID_identifier)); + if(((var_string.substr(0, 14))=="ssa::$guard#ls")) + { + temp_node.is_loop=true; + temp_node.guard=not_exprt(e_it->op1().op0()); + } } - // collecting symbols from constraints and populating dependency graph nodes - for(local_SSAt::nodet::constraintst::const_iterator c_it=node.constraints.begin(); - c_it!=node.constraints.end(); c_it++) + equal_exprt e=to_equal_expr(*e_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + + if(!ignore_equality_done) { - find_symbols(*c_it,all_ssa_symbols); - - depnodet temp_node; - temp_node.is_assertion = false; - temp_node.is_function_call = false; - temp_node.is_loop = false; - temp_node.node_info = *c_it; - temp_node.location = n_it->location; - find_symbols(*c_it,temp_node.used_symbols); - find_symbols(*c_it,temp_node.modified_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created constraint node, with info: " << from_expr(ns, "", *c_it) << "\n"; + std::string var_string=id2string(to_symbol_expr(lhs).get_identifier()); + if(((var_string.substr(0, 11))=="ssa::$guard") && (rhs.is_true())) + { + ignore_equality=true; + ignore_equality_done=true; + } } - - // collecting symbols from assertionst and populating dependency graph nodes - for(local_SSAt::nodet::assertionst::const_iterator a_it=node.assertions.begin(); - a_it!=node.assertions.end(); a_it++) + + if(first_node && ignore_equality) { - find_symbols(*a_it,all_ssa_symbols); - - depnodet temp_node; - temp_node.is_assertion = true; - temp_node.is_function_call = false; - temp_node.is_loop = false; - temp_node.node_info = *a_it; - temp_node.location = n_it->location; - find_symbols(*a_it,temp_node.used_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created assertion node, with info: " << from_expr(ns, "", *a_it) << "\n"; + if(entry) + { + depnodes_map.push_back(temp_node); + } + ignore_equality=false; } - - /* - // collecting symbols from assumptionst and populating dependency graph nodes - for(local_SSAt::nodet::assumptionst::const_iterator a_it=node.assumptions.begin(); - a_it!=node.assumptions.end(); a_it++) + else { - find_symbols(*a_it,all_ssa_symbols); - - depnodet temp_node; - temp_node.is_assertion = false; - temp_node.is_function_call = false; - temp_node.node_info = *a_it; - find_symbols(*a_it,temp_node.used_symbols); - find_symbols(*a_it,temp_node.modified_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created assumption node, with info: " << from_expr(ns, "", *a_it) << "\n"; + depnodes_map.push_back(temp_node); } - */ + } + + // collecting symbols from constraints and populating dependency graph nodes + for(local_SSAt::nodet::constraintst::const_iterator c_it= + node.constraints.begin(); + c_it!=node.constraints.end(); c_it++) + { + find_symbols(*c_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*c_it; + temp_node.location=n_it->location; + find_symbols(*c_it, temp_node.used_symbols); + find_symbols(*c_it, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + // collecting symbols from assertionst and populating dependency graph nodes + for(local_SSAt::nodet::assertionst::const_iterator a_it= + node.assertions.begin(); + a_it!=node.assertions.end(); a_it++) + { + find_symbols(*a_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=true; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*a_it; + temp_node.location=n_it->location; + find_symbols(*a_it, temp_node.used_symbols); + depnodes_map.push_back(temp_node); + } + +#if 0 + // collecting symbols from assumptionst + // and populating dependency graph nodes + for(local_SSAt::nodet::assumptionst::const_iterator a_it= + node.assumptions.begin(); + a_it!=node.assumptions.end(); a_it++) + { + find_symbols(*a_it, all_ssa_symbols); + + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.node_info=*a_it; + find_symbols(*a_it, temp_node.used_symbols); + find_symbols(*a_it, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } +#endif - // collecting symbols from function_callst and populating dependency graph nodes - for(local_SSAt::nodet::function_callst::const_iterator f_it=node.function_calls.begin(); - f_it!=node.function_calls.end(); f_it++) + // collecting symbols from function_callst + // and populating dependency graph nodes + for(local_SSAt::nodet::function_callst::const_iterator f_it= + node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + if(ssa_db.exists(fname)) { - //find_symbols(*f_it,all_ssa_symbols); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - if(ssa_db.exists(fname)) - { - const local_SSAt &fSSA = ssa_db.get(fname); - - /******************************************************************/ - /******* additional nodes needed to fix the dependency tree *******/ - - exprt guard_binding; - exprt::operandst bindings_in, bindings_out; - int counter = ssa_inliner.get_rename_counter(); - - /**/ - ssa_inliner.get_guard_binding(SSA,fSSA,n_it,guard_binding,counter); - //std::cout << "guard binding: " << from_expr(ns, "", guard_binding) << "\n"; - /* - { - depnodet temp_node; - temp_node.is_assertion = false; - temp_node.is_function_call = false; - temp_node.node_info = guard_binding; - - equal_exprt e = to_equal_expr(guard_binding); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - - find_symbols(rhs,temp_node.used_symbols); - find_symbols(lhs,temp_node.modified_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created guard binding node, with info: " << from_expr(ns, "", guard_binding) << "\n"; - } - */ - /**/ - - ssa_inliner.get_bindings(SSA,fSSA,n_it,f_it,bindings_in,bindings_out,counter); - - for(exprt::operandst::const_iterator b_it=bindings_in.begin(); - b_it!=bindings_in.end(); b_it++){ - - //std::cout << "binding: " << from_expr(ns, "", *b_it) << "\n"; - - depnodet temp_node; - temp_node.is_assertion = false; - temp_node.is_function_call = false; - temp_node.is_loop = false; - temp_node.node_info = *b_it; - temp_node.location = n_it->location; - - equal_exprt e = to_equal_expr(*b_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - - find_symbols(rhs,temp_node.used_symbols); - find_symbols(lhs,temp_node.modified_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created binding in node, with info: " << from_expr(ns, "", *b_it) << "\n"; - } - - for(exprt::operandst::const_iterator b_it=bindings_out.begin(); - b_it!=bindings_out.end(); b_it++){ - - //std::cout << "binding: " << from_expr(ns, "", *b_it) << "\n"; - - depnodet temp_node; - temp_node.is_assertion = false; - temp_node.is_function_call = false; - temp_node.is_loop = false; - temp_node.node_info = *b_it; - temp_node.location = n_it->location; - - equal_exprt e = to_equal_expr(*b_it); - exprt &lhs = e.lhs(); exprt &rhs = e.rhs(); - - find_symbols(rhs,temp_node.used_symbols); - find_symbols(lhs,temp_node.modified_symbols); - depnodes_map.push_back(temp_node); - //std::cout << "created binding out node, with info: " << from_expr(ns, "", *b_it) << "\n"; - } - - /******************************************************************/ - - depnodet temp_node; - temp_node.guard = guard_binding; - temp_node.is_assertion = false; - temp_node.is_function_call = true; - temp_node.is_loop = false; - temp_node.node_info = *f_it; - temp_node.rename_counter = counter; - temp_node.location = n_it->location; - - find_symbols(guard_binding,temp_node.used_symbols); - - for(local_SSAt::var_listt::const_iterator p_it = fSSA.params.begin(); - p_it != fSSA.params.end(); p_it++){ - irep_idt id = (*p_it).get(ID_identifier); - ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(id); - temp_node.used_symbols.insert(id); - } - - for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_in.begin(); - g_it != fSSA.globals_in.end(); g_it++){ - irep_idt id = (*g_it).get(ID_identifier); - ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(id); - temp_node.used_symbols.insert(id); - } - - for(local_SSAt::var_sett::const_iterator g_it = fSSA.globals_out.begin(); - g_it != fSSA.globals_out.end(); g_it++){ - irep_idt id = (*g_it).get(ID_identifier); - ssa_inliner.rename(id, counter); - all_ssa_symbols.insert(id); - temp_node.modified_symbols.insert(id); - } - - depnodes_map.push_back(temp_node); - //std::cout << "created function node, with info: " << from_expr(ns, "", *f_it) << "\n"; - } + const local_SSAt &fSSA=ssa_db.get(fname); + + /******************************************************************/ + /******* additional nodes needed to fix the dependency tree *******/ + + exprt guard_binding; + exprt::operandst bindings_in, bindings_out; + int counter=ssa_inliner.get_rename_counter(); + + ssa_inliner.get_guard_binding(SSA, fSSA, n_it, guard_binding, counter); + +#if 0 + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.node_info=guard_binding; + + equal_exprt e=to_equal_expr(guard_binding); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } +#endif + + ssa_inliner.get_bindings( + SSA, fSSA, n_it, f_it, bindings_in, bindings_out, counter); + + for(exprt::operandst::const_iterator b_it=bindings_in.begin(); + b_it!=bindings_in.end(); b_it++) + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*b_it; + temp_node.location=n_it->location; + + equal_exprt e=to_equal_expr(*b_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + for(exprt::operandst::const_iterator b_it=bindings_out.begin(); + b_it!=bindings_out.end(); b_it++) + { + depnodet temp_node; + temp_node.is_assertion=false; + temp_node.is_function_call=false; + temp_node.is_loop=false; + temp_node.node_info=*b_it; + temp_node.location=n_it->location; + + equal_exprt e=to_equal_expr(*b_it); + exprt &lhs=e.lhs(); exprt &rhs=e.rhs(); + + find_symbols(rhs, temp_node.used_symbols); + find_symbols(lhs, temp_node.modified_symbols); + depnodes_map.push_back(temp_node); + } + + /******************************************************************/ + + depnodet temp_node; + temp_node.guard=guard_binding; + temp_node.is_assertion=false; + temp_node.is_function_call=true; + temp_node.is_loop=false; + temp_node.node_info=*f_it; + temp_node.rename_counter=counter; + temp_node.location=n_it->location; + + find_symbols(guard_binding, temp_node.used_symbols); + + for(local_SSAt::var_listt::const_iterator p_it=fSSA.params.begin(); + p_it!=fSSA.params.end(); p_it++) + { + irep_idt id=(*p_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it=fSSA.globals_in.begin(); + g_it!=fSSA.globals_in.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.used_symbols.insert(id); + } + + for(local_SSAt::var_sett::const_iterator g_it=fSSA.globals_out.begin(); + g_it!=fSSA.globals_out.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); + ssa_inliner.rename(id, counter); + all_ssa_symbols.insert(id); + temp_node.modified_symbols.insert(id); + } + + depnodes_map.push_back(temp_node); } + } } - first_node = false; - + first_node=false; + depnodet source_node; - source_node.is_assertion = false; - source_node.is_function_call = false; - source_node.is_loop = false; - + source_node.is_assertion=false; + source_node.is_function_call=false; + source_node.is_loop=false; + // params and globals_in are the modified_symbols at source_node - - for(local_SSAt::var_listt::const_iterator p_it = SSA.params.begin(); - p_it != SSA.params.end(); p_it++){ - irep_idt id = (*p_it).get(ID_identifier); + + for(local_SSAt::var_listt::const_iterator p_it=SSA.params.begin(); + p_it!=SSA.params.end(); p_it++) + { + irep_idt id=(*p_it).get(ID_identifier); source_node.modified_symbols.insert(id); } - - for(local_SSAt::var_sett::const_iterator g_it = SSA.globals_in.begin(); - g_it != SSA.globals_in.end(); g_it++){ - irep_idt id = (*g_it).get(ID_identifier); + + for(local_SSAt::var_sett::const_iterator g_it=SSA.globals_in.begin(); + g_it!=SSA.globals_in.end(); g_it++) + { + irep_idt id=(*g_it).get(ID_identifier); source_node.modified_symbols.insert(id); } - + depnodes_map.push_back(source_node); // source_node - //std::cout << "created source node, without any info" << "\n"; - - top_node_index = depnodes_map.size() - 1; - + + top_node_index=depnodes_map.size()-1; + for(find_symbols_sett::const_iterator - s_it=all_ssa_symbols.begin(); s_it!=all_ssa_symbols.end(); s_it++){ - - for(unsigned m_index = 0; m_index < depnodes_map.size(); m_index++){ - if(depnodes_map[m_index].modified_symbols.find(*s_it) != depnodes_map[m_index].modified_symbols.end()){ - - for(unsigned u_index = 0; u_index < depnodes_map.size(); u_index++){ - - if(m_index != u_index){ - if(depnodes_map[u_index].used_symbols.find(*s_it) != depnodes_map[u_index].used_symbols.end()){ - annotated_predecessort temp_pred; - temp_pred.predecessor_node_index = m_index; - temp_pred.annotation.insert(*s_it); - depnodes_map[u_index].predecessors.push_back(temp_pred); - depnodes_map[m_index].successors.push_back(u_index); - } - } - } + s_it=all_ssa_symbols.begin(); s_it!=all_ssa_symbols.end(); s_it++) + { + for(unsigned m_index=0; m_index #include -#include "../ssa/ssa_db.h" -#include "../ssa/ssa_inliner.h" -#include "../ssa/local_ssa.h" +#include "local_ssa.h" class ssa_inlinert; class ssa_dbt; -class ssa_dependency_grapht{ - public: +class ssa_dependency_grapht +{ +public: + ssa_dependency_grapht(ssa_dbt &_db, const namespacet &_ns): + ssa_db(_db), + ns(_ns) + { + } - inline ssa_dependency_grapht(ssa_dbt &_db, const namespacet &_ns): - ssa_db(_db), - ns(_ns) - {}; - - struct annotated_predecessort{ + struct annotated_predecessort + { int predecessor_node_index; find_symbols_sett annotation; }; - + typedef std::list annotated_predecessorst; - struct depnodet{ + struct depnodet + { exprt node_info; - exprt guard; //guard binding or loop-head select + exprt guard; // guard binding or loop-head select bool is_assertion; bool is_function_call; bool is_loop; - //bool trivial_guard; + // bool trivial_guard; int rename_counter; find_symbols_sett used_symbols; find_symbols_sett modified_symbols; @@ -41,23 +49,23 @@ class ssa_dependency_grapht{ std::vector successors; local_SSAt::locationt location; }; - - //typedef std::map depnodest; + + // typedef std::map depnodest; typedef std::vector depnodest; depnodest depnodes_map; int top_node_index; - //special source_node and sink_node - //depnodet source_node = depnodes_map[top_node_index]; - //depnodet sink_node = depnodes_map[0]; - + // special source_node and sink_node + // depnodet source_node=depnodes_map[top_node_index]; + // depnodet sink_node=depnodes_map[0]; + void create(const local_SSAt &SSA, ssa_inlinert &ssa_inliner, bool entry); void output(std::ostream &) const; - - protected: + +protected: ssa_dbt &ssa_db; const namespacet &ns; }; -#endif +#endif // CPROVER_2LS_SSA_SSA_DEPENDENCY_GRAPH_H diff --git a/src/ssa/ssa_inliner.cpp b/src/ssa/ssa_inliner.cpp index 71bd8ac6f..9949eb0dc 100644 --- a/src/ssa/ssa_inliner.cpp +++ b/src/ssa/ssa_inliner.cpp @@ -2,7 +2,7 @@ Module: SSA Inliner -Author: Peter Schrammel +Author: Peter Schrammel, Madhukar Kumar \*******************************************************************/ @@ -17,25 +17,25 @@ Function: ssa_inlinert::get_guard_binding Inputs: - Outputs: + Outputs: Purpose: get guard binding for function call \*******************************************************************/ void ssa_inlinert::get_guard_binding( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - exprt &guard_binding, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter) { exprt callee_guard, caller_guard; - callee_guard = fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); + callee_guard=fSSA.guard_symbol(fSSA.goto_function.body.instructions.begin()); rename(callee_guard, counter); - caller_guard = SSA.guard_symbol(n_it->location); + caller_guard=SSA.guard_symbol(n_it->location); - guard_binding = equal_exprt(callee_guard, caller_guard); + guard_binding=equal_exprt(callee_guard, caller_guard); } /*******************************************************************\ @@ -44,60 +44,149 @@ Function: ssa_inlinert::get_bindings Inputs: - Outputs: + Outputs: Purpose: get bindings for function call \*******************************************************************/ void ssa_inlinert::get_bindings( - const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - exprt::operandst &bindings_in, - exprt::operandst &bindings_out, - int counter) + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter) { - //getting globals at call site - local_SSAt::var_sett cs_globals_in, cs_globals_out; - goto_programt::const_targett loc = n_it->location; - - SSA.get_globals(loc,cs_globals_in); - SSA.get_globals(loc,cs_globals_out,false); - + // getting globals at call site + local_SSAt::var_sett cs_globals_in, cs_globals_out; + goto_programt::const_targett loc=n_it->location; + + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); + #if 0 std::cout << "cs_globals_in: "; - for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); - it != cs_globals_in.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; std::cout << "cs_globals_out: "; - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) - std::cout << from_expr(SSA.ns,"",*it) << " "; + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) + std::cout << from_expr(SSA.ns, "", *it) << " "; std::cout << std::endl; #endif - //equalities for arguments - //bindings_in.push_back(get_replace_params(SSA.params,*f_it)); - get_replace_params(SSA,fSSA.params,n_it,*f_it,bindings_in,counter); + // equalities for arguments + get_replace_params(SSA, fSSA.params, n_it, *f_it, bindings_in, counter); - //equalities for globals_in - //bindings_in.push_back(get_replace_globals_in(SSA.globals_in,cs_globals_in)); + // equalities for globals_in + get_replace_globals_in( + fSSA.globals_in, *f_it, cs_globals_in, bindings_in, counter); + + // equalities for globals out (including unmodified globals) + get_replace_globals_out( + fSSA.globals_out, + *f_it, + cs_globals_in, + cs_globals_out, + bindings_out, + counter); +} + +/*******************************************************************\ - //get_replace_globals_in(fSSA.globals_in,cs_globals_in,bindings_in,counter); - get_replace_globals_in(fSSA.globals_in,*f_it,cs_globals_in,bindings_in,counter); +Function: ssa_inlinert::get_inlined + + Inputs: + + Outputs: - //equalities for globals out (including unmodified globals) - //bindings_out.push_back(get_replace_globals_out(SSA.globals_out,cs_globals_in,cs_globals_out)); + Purpose: get inlined function call - //get_replace_globals_out(fSSA.globals_out,cs_globals_in,cs_globals_out,bindings_out,counter); - get_replace_globals_out(fSSA.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings_out,counter); +\*******************************************************************/ + +bool ssa_inlinert::get_inlined( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + int counter, + bool error_summ) +{ + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const local_SSAt &fSSA=ssa_db.get(fname); + + bool assertion_flag=get_summaries( + fSSA, + summaryt::call_sitet(fSSA.goto_function.body.instructions.end()), + forward, + assert_summaries, + noassert_summaries, + bindings, + assertion_map, + error_summ); + + // bindings + exprt guard_binding; + get_guard_binding(SSA, fSSA, n_it, guard_binding, counter); + bindings.push_back(guard_binding); + get_bindings(SSA, fSSA, n_it, f_it, bindings, bindings, counter); + + bool first_equality=true; + for(local_SSAt::nodest::const_iterator n_it=fSSA.nodes.begin(); + n_it!=fSSA.nodes.end(); n_it++) + { + const local_SSAt::nodet &fnode=*n_it; + + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + fnode.equalities.begin(); e_it!=fnode.equalities.end(); e_it++) + { + // unless lhs starts with "ssa::guard" and rhs is true + // because that one is replaced by the guard binding + const equal_exprt &e=to_equal_expr(*e_it); + const exprt &lhs=e.lhs(); const exprt &rhs=e.rhs(); + std::string var_string=id2string(to_symbol_expr(lhs).get_identifier()); + if((var_string.substr(0, 11)=="ssa::$guard") && + rhs.is_true() && first_equality) + { + first_equality=false; + } + else + { + noassert_summaries.push_back(*e_it); + rename(noassert_summaries.back(), counter); + } + } + for(local_SSAt::nodet::constraintst::const_iterator c_it= + fnode.constraints.begin(); c_it!=fnode.constraints.end(); c_it++) + { + noassert_summaries.push_back(*c_it); + rename(noassert_summaries.back(), counter); + } + for(local_SSAt::nodet::assertionst::const_iterator a_it= + fnode.assertions.begin(); a_it!=fnode.assertions.end(); a_it++) + { +#if 0 + assert_summaries.push_back(*a_it); + rename(assert_summaries.back(), counter); +#endif + assertion_flag=true; + } + } + + return assertion_flag; } + /*******************************************************************\ Function: ssa_inlinert::get_summary @@ -106,34 +195,29 @@ Function: ssa_inlinert::get_summary Outputs: - Purpose: get summary for function call + Purpose: get summary for non-inlined function calls \*******************************************************************/ -void ssa_inlinert::get_summary( +bool ssa_inlinert::get_summary( const local_SSAt &SSA, local_SSAt::nodest::const_iterator n_it, local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, bool forward, - exprt::operandst &summaries, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, exprt::operandst &bindings, int counter, bool error_summ) { + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); + const summaryt &summary=summary_db.get(fname); + // getting globals at call site local_SSAt::var_sett cs_globals_in, cs_globals_out; goto_programt::const_targett loc=n_it->location; - if(forward) - { - SSA.get_globals(loc, cs_globals_in); - SSA.get_globals(loc, cs_globals_out, false); - } - else - { - SSA.get_globals(loc, cs_globals_out); - SSA.get_globals(loc, cs_globals_in, false); - } + SSA.get_globals(loc, cs_globals_in); + SSA.get_globals(loc, cs_globals_out, false); #if 0 std::cout << "cs_globals_in: "; @@ -149,58 +233,61 @@ void ssa_inlinert::get_summary( std::cout << std::endl; #endif - //equalities for arguments - get_replace_params(SSA,summary.params,n_it,*f_it,bindings,counter); + // equalities for arguments + get_replace_params(SSA, summary.params, n_it, *f_it, bindings, counter); - //equalities for globals_in - if(forward){ - //get_replace_globals_in(summary.globals_in,cs_globals_in,bindings,counter); - get_replace_globals_in(summary.globals_in,*f_it,cs_globals_in,bindings,counter); - } - else{ - //get_replace_globals_in(summary.globals_out,cs_globals_out,bindings,counter); - get_replace_globals_in(summary.globals_out,*f_it,cs_globals_out,bindings,counter); - } + // equalities for globals_in + get_replace_globals_in( + summary.globals_in, *f_it, cs_globals_in, bindings, counter); - //constraints for transformer + // constraints for transformer exprt transformer; if(error_summ) - { - // update transformer using the error_summaries map - summaryt::call_sitet call_site(loc); - summaryt::error_summariest::const_iterator e_it = - summary.error_summaries.find(call_site); - if(e_it != summary.error_summaries.end() && - !e_it->second.is_nil()) - transformer = e_it->second; - else - transformer = true_exprt(); - } - else - { - if(forward) - transformer=summary.fw_transformer.is_nil() ? true_exprt() : - summary.fw_transformer; + { + // update transformer using the error_summaries map + summaryt::call_sitet call_site(loc); + summaryt::error_summariest::const_iterator e_it= + summary.error_summaries.find(call_site); + if(e_it!=summary.error_summaries.end() && + !e_it->second.is_nil()) + transformer=e_it->second; + else + transformer=true_exprt(); + } else - transformer = summary.bw_transformer.is_nil() ? true_exprt() : - summary.bw_transformer; - } - - rename(transformer,counter); - summaries.push_back(implies_exprt(SSA.guard_symbol(n_it->location), - transformer)); - - //equalities for globals out (including unmodified globals) - if(forward){ - //get_replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,bindings,counter); - get_replace_globals_out(summary.globals_out,*f_it,cs_globals_in,cs_globals_out,bindings,counter); + { + if(forward) + transformer=summary.fw_transformer.is_nil() ? true_exprt() : + summary.fw_transformer; + else + transformer=summary.bw_transformer.is_nil() ? true_exprt() : + summary.bw_transformer; } - else{ - //get_replace_globals_out(summary.globals_in,cs_globals_out,cs_globals_in,bindings,counter); - get_replace_globals_out(summary.globals_in,*f_it,cs_globals_out,cs_globals_in,bindings,counter); + + rename(transformer, counter); + if(summary.has_assertion) + { + assert_summaries.push_back( + implies_exprt(SSA.guard_symbol(n_it->location), transformer)); } + else + { + noassert_summaries.push_back( + implies_exprt(SSA.guard_symbol(n_it->location), transformer)); + } + + // equalities for globals out (including unmodified globals) + get_replace_globals_out( + summary.globals_out, + *f_it, + cs_globals_in, + cs_globals_out, + bindings, + counter); + + return summary.has_assertion; } /*******************************************************************\ @@ -222,16 +309,40 @@ exprt ssa_inlinert::get_summaries(const local_SSAt &SSA) return and_exprt(conjunction(bindings), conjunction(summaries)); } - -exprt ssa_inlinert::get_summaries(const local_SSAt &SSA, +exprt ssa_inlinert::get_summaries( + const local_SSAt &SSA, assertion_mapt &assertion_map) { - exprt::operandst summaries,bindings; - get_summaries(SSA,true,summaries,bindings,assertion_map); - return and_exprt(conjunction(bindings),conjunction(summaries)); + exprt::operandst summaries, bindings; + get_summaries(SSA, true, summaries, bindings, assertion_map); + return and_exprt(conjunction(bindings), conjunction(summaries)); } +void ssa_inlinert::get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings) +{ + assertion_mapt assertion_map; + get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, summaries, summaries, bindings, assertion_map); +} +void ssa_inlinert::get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map) +{ + get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, summaries, summaries, bindings, assertion_map); +} /*******************************************************************\ @@ -245,137 +356,82 @@ Function: ssa_inlinert::get_summaries \*******************************************************************/ -void ssa_inlinert::get_summaries( +bool ssa_inlinert::get_summaries( const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings) + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ) { + assertion_mapt assertion_map; + return get_summaries( + SSA, + summaryt::call_sitet(SSA.goto_function.body.instructions.end()), + forward, assert_summaries, noassert_summaries, bindings, assertion_map); +} + +bool ssa_inlinert::get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + bool error_summ) +{ + bool assertion_flag=false; for(local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) { + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); + a_it!=n_it->assertions.end(); a_it++) + { + assertion_map[n_it->location].push_back(*a_it); + rename(assertion_map[n_it->location].back(), counter); + } for(local_SSAt::nodet::function_callst::const_iterator f_it= n_it->function_calls.begin(); f_it!=n_it->function_calls.end(); f_it++) { - assert(f_it->function().id()==ID_symbol); // no function pointers + // do not use summary for current call site + summaryt::call_sitet this_call_site(n_it->location); + if(current_call_site==this_call_site) + continue; + irep_idt fname=to_symbol_expr(f_it->function()).get_identifier(); - if(summary_db.exists(fname)) + // get inlined function + if(n_it->function_calls_inlined) { - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname), - forward,summaries,bindings,counter); + counter++; + bool new_assertion_flag= + get_inlined( + SSA, n_it, f_it, + forward, assert_summaries, noassert_summaries, + bindings, assertion_map, counter, error_summ); + assertion_flag=assertion_flag || new_assertion_flag; } - } - } -} - - -void ssa_inlinert::get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map) -{ - get_summaries(SSA, - summaryt::call_sitet(SSA.goto_function.body.instructions.end()), - forward,summaries,summaries,bindings,assertion_map); -} - -bool ssa_inlinert::get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings) -{ - bool assertion_flag = false; - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) + // get summary + else if(summary_db.exists(fname)) { - //do not use summary for current call site - summaryt::call_sitet this_call_site(n_it->location); - if(current_call_site == this_call_site) - continue; - - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries - if(summary_db.exists(fname)) - { - summaryt summary = summary_db.get(fname); - if(summary.has_assertion == true){ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); - assertion_flag = true; + counter++; + bool new_assertion_flag= + get_summary( + SSA, n_it, f_it, + forward, assert_summaries, noassert_summaries, + bindings, counter, error_summ); + assertion_flag=assertion_flag || new_assertion_flag; } - else{ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); } - } - } } return assertion_flag; } -bool ssa_inlinert::get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map) -{ - bool assertion_flag = false; - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); - a_it != n_it->assertions.end(); a_it++) - { - assertion_map[n_it->location].push_back(*a_it); - rename(assertion_map[n_it->location].back(), counter); - } - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - //do not use summary for current call site - summaryt::call_sitet this_call_site(n_it->location); - if(current_call_site == this_call_site) - continue; - - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - //TODO: we need a get_summary variant that retrieves the summary according the call_site from summary.error_summaries - if(summary_db.exists(fname)) - { - summaryt summary = summary_db.get(fname); - if(summary.has_assertion == true){ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,assert_summaries,bindings,counter,true); - assertion_flag = true; - } - else{ - counter++; - get_summary(SSA,n_it,f_it,summary_db.get(fname),forward,noassert_summaries,bindings,counter,true); - } - } - } - } - return assertion_flag; -} - /*******************************************************************\ Function: ssa_inlinert::replace @@ -393,8 +449,8 @@ Function: ssa_inlinert::replace void ssa_inlinert::replace( local_SSAt &SSA, bool forward, - bool preconditions_as_assertions, - int counter) + bool preconditions_as_assertions, + int counter) { for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) @@ -418,9 +474,10 @@ void ssa_inlinert::replace( SSA.get_globals(loc, cs_globals_in); SSA.get_globals(loc, cs_globals_out, false); - //replace - replace(SSA,n_it,f_it,cs_globals_in,cs_globals_out,summary, - forward,preconditions_as_assertions,counter); + // replace + replace( + SSA, n_it, f_it, cs_globals_in, cs_globals_out, summary, + forward, preconditions_as_assertions, counter); // remove function_call rm_function_calls.insert(f_it); @@ -447,10 +504,12 @@ Function: ssa_inlinert::replace \*******************************************************************/ -void ssa_inlinert::replace(local_SSAt &SSA, - const ssa_dbt &ssa_db, - int counter, - bool recursive, bool rename) +void ssa_inlinert::replace( + local_SSAt &SSA, + const ssa_dbt &ssa_db, + int counter, + bool recursive, + bool rename) { for(local_SSAt::nodest::iterator n_it=SSA.nodes.begin(); n_it!=SSA.nodes.end(); n_it++) @@ -477,11 +536,14 @@ void ssa_inlinert::replace(local_SSAt &SSA, if(recursive) { - replace(fSSA,ssa_db,true,counter); - } + replace(fSSA, ssa_db, true, counter); + } - //replace - replace(SSA.nodes,n_it,f_it,cs_globals_in,cs_globals_out,fSSA,counter); + // replace + replace( + SSA.nodes, n_it, f_it, + cs_globals_in, cs_globals_out, + fSSA, counter); } else // just add to nodes { @@ -523,14 +585,14 @@ void ssa_inlinert::replace( const local_SSAt::var_sett &cs_globals_out, const summaryt &summary, bool forward, - bool preconditions_as_assertions, - int counter) + bool preconditions_as_assertions, + int counter) { - //equalities for arguments - replace_params(summary.params,*f_it,counter); + // equalities for arguments + replace_params(summary.params, *f_it, counter); - //equalities for globals_in - replace_globals_in(summary.globals_in,cs_globals_in,counter); + // equalities for globals_in + replace_globals_in(summary.globals_in, cs_globals_in, counter); // constraints for precondition and transformer exprt precondition; @@ -540,30 +602,35 @@ void ssa_inlinert::replace( precondition=summary.bw_precondition; if(!preconditions_as_assertions) { - rename(precondition,counter); + rename(precondition, counter); node->constraints.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } else { - rename(precondition,counter); + rename(precondition, counter); node->assertions.push_back( - implies_exprt(SSA.guard_symbol(node->location), - precondition)); + implies_exprt( + SSA.guard_symbol(node->location), + precondition)); } exprt transformer; - if(forward) transformer = summary.fw_transformer; - else transformer = summary.bw_transformer; - node->constraints.push_back(transformer); //copy - exprt &_transformer = node->constraints.back(); - rename(_transformer,counter); + if(forward) + transformer=summary.fw_transformer; + else + transformer=summary.bw_transformer; + node->constraints.push_back(transformer); // copy + exprt &_transformer=node->constraints.back(); + rename(_transformer, counter); // remove function call rm_function_calls.insert(f_it); - //equalities for globals out (including unmodified globals) - replace_globals_out(summary.globals_out,cs_globals_in,cs_globals_out,counter); + // equalities for globals out (including unmodified globals) + replace_globals_out( + summary.globals_out, cs_globals_in, cs_globals_out, counter); } /*******************************************************************\ @@ -588,29 +655,30 @@ void ssa_inlinert::replace( local_SSAt::nodet::function_callst::iterator f_it, const local_SSAt::var_sett &cs_globals_in, const local_SSAt::var_sett &cs_globals_out, - const local_SSAt &function, - int counter) + const local_SSAt &function, + int counter) { - //equalities for arguments - replace_params(function.params,*f_it,counter); + // equalities for arguments + replace_params(function.params, *f_it, counter); - //equalities for globals_in - replace_globals_in(function.globals_in,cs_globals_in,counter); + // equalities for globals_in + replace_globals_in(function.globals_in, cs_globals_in, counter); // add function body for(local_SSAt::nodest::const_iterator n_it=function.nodes.begin(); n_it!=function.nodes.end(); n_it++) { - local_SSAt::nodet n = *n_it; //copy - rename(n,counter); + local_SSAt::nodet n=*n_it; // copy + rename(n, counter); new_nodes.push_back(n); } // remove function call rm_function_calls.insert(f_it); - //equalities for globals out (including unmodified globals) - replace_globals_out(function.globals_out,cs_globals_in,cs_globals_out,counter); + // equalities for globals out (including unmodified globals) + replace_globals_out( + function.globals_out, cs_globals_in, cs_globals_out, counter); } /*******************************************************************\ @@ -625,26 +693,27 @@ Function: ssa_inlinert::get_replace_globals_in \*******************************************************************/ -void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &globals, - exprt::operandst &c, - int counter) +void ssa_inlinert::get_replace_globals_in( + const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter) { - std::string suffix = id2string(funapp_expr.get(ID_suffix)); + std::string suffix=id2string(funapp_expr.get(ID_suffix)); - //equalities for globals_in - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) - { - symbol_exprt lhs = *it; //copy - rename(lhs,counter); + // equalities for globals_in + for(summaryt::var_sett::const_iterator it=globals_in.begin(); + it!=globals_in.end(); it++) + { + symbol_exprt lhs=*it; // copy + rename(lhs, counter); symbol_exprt rhs; if(find_corresponding_symbol(*it, globals, rhs)) { - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - - debug() << "binding: " << lhs.get_identifier() << " == " + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + + debug() << "binding: " << lhs.get_identifier() << "==" << rhs.get_identifier() << eom; c.push_back(equal_exprt(lhs, rhs)); } @@ -654,19 +723,20 @@ void ssa_inlinert::get_replace_globals_in(const local_SSAt::var_sett &globals_in << "' not bound in caller" << eom; #endif } - //return conjunction(c); + // return conjunction(c); } -void ssa_inlinert::replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals, - int counter) +void ssa_inlinert::replace_globals_in( + const local_SSAt::var_sett &globals_in, + const local_SSAt::var_sett &globals, + int counter) { // equalities for globals_in for(summaryt::var_sett::const_iterator it=globals_in.begin(); it!=globals_in.end(); it++) { - symbol_exprt lhs = *it; //copy - rename(lhs,counter); + symbol_exprt lhs=*it; // copy + rename(lhs, counter); symbol_exprt rhs; if(find_corresponding_symbol(*it, globals, rhs)) { @@ -694,70 +764,67 @@ Function: ssa_inlinert::get_replace_params \*******************************************************************/ -void ssa_inlinert::get_replace_params(const local_SSAt &SSA, - const local_SSAt::var_listt ¶ms, - local_SSAt::nodest::const_iterator n_it, - const function_application_exprt &funapp_expr, - exprt::operandst &c, - int counter) +void ssa_inlinert::get_replace_params( + const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter) { - //std::string suffix = id2string(funapp_expr.get(ID_suffix)); - - //equalities for arguments - local_SSAt::var_listt::const_iterator p_it = params.begin(); - for(exprt::operandst::const_iterator it = funapp_expr.arguments().begin(); - it != funapp_expr.arguments().end(); it++, p_it++) - { + // equalities for arguments + local_SSAt::var_listt::const_iterator p_it=params.begin(); + for(exprt::operandst::const_iterator it=funapp_expr.arguments().begin(); + it!=funapp_expr.arguments().end(); it++, p_it++) + { #if 0 - std::cout << "replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*it) << std::endl; + std::cout << "replace param " << from_expr(SSA.ns, "", *p_it) + << "==" << from_expr(SSA.ns, "", *it) << std::endl; #endif - + #if 0 - local_SSAt::var_listt::const_iterator next_p_it = p_it; - if(funapp_expr.arguments().size() != params.size() && - ++next_p_it==params.end()) //TODO: handle ellipsis + local_SSAt::var_listt::const_iterator next_p_it=p_it; + if(funapp_expr.arguments().size()!=params.size() && + ++next_p_it==params.end()) // TODO: handle ellipsis { warning() << "ignoring excess function arguments" << eom; break; } #endif - if(SSA.ns.follow(it->type()).id()==ID_struct) - { - exprt rhs = SSA.read_rhs(*it, n_it->location); //copy + if(SSA.ns.follow(it->type()).id()==ID_struct) + { + exprt rhs=SSA.read_rhs(*it, n_it->location); // copy #if 0 - std::cout << "split param " << from_expr(SSA.ns,"",*it) - << " into " << from_expr(SSA.ns,"",rhs) << std::endl; + std::cout << "split param " << from_expr(SSA.ns, "", *it) + << " into " << from_expr(SSA.ns, "", rhs) << std::endl; #endif - forall_operands(o_it, rhs) - { - assert(p_it!=params.end()); - exprt lhs = *p_it; //copy - rename(lhs,counter); + forall_operands(o_it, rhs) + { + assert(p_it!=params.end()); + exprt lhs=*p_it; // copy + rename(lhs, counter); #if 0 - std::cout << "split replace param " << from_expr(SSA.ns,"",*p_it) - << " == " << from_expr(SSA.ns,"",*o_it) << std::endl; + std::cout << "split replace param " << from_expr(SSA.ns, "", *p_it) + << "==" << from_expr(SSA.ns, "", *o_it) << std::endl; #endif - c.push_back(equal_exprt(lhs,*o_it)); - ++p_it; - } - } - else - { - exprt lhs = *p_it; //copy - rename(lhs,counter); - c.push_back(equal_exprt(lhs,*it)); - //symbol_exprt sexpr = to_symbol_expr(*it); - //sexpr.set_identifier(id2string(sexpr.get_identifier())+suffix); - //c.push_back(equal_exprt(lhs,sexpr)); + c.push_back(equal_exprt(lhs, *o_it)); + ++p_it; } + } + else + { + exprt lhs=*p_it; // copy + rename(lhs, counter); + c.push_back(equal_exprt(lhs, *it)); + } } } -void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr, - int counter) +void ssa_inlinert::replace_params( + const local_SSAt::var_listt ¶ms, + const function_application_exprt &funapp_expr, + int counter) { // equalities for arguments local_SSAt::var_listt::const_iterator p_it=params.begin(); @@ -772,15 +839,15 @@ void ssa_inlinert::replace_params(const local_SSAt::var_listt ¶ms, break; } - exprt lhs = *p_it; //copy - rename(lhs,counter); - new_equs.push_back(equal_exprt(lhs,*it)); + exprt lhs=*p_it; // copy + rename(lhs, counter); + new_equs.push_back(equal_exprt(lhs, *it)); } } /*******************************************************************\ -Function: ssa_inlinert::get_replace_globals_out +Function: ssa_inlinert::replace_globals_out Inputs: @@ -790,39 +857,42 @@ Function: ssa_inlinert::get_replace_globals_out \*******************************************************************/ -void ssa_inlinert::get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const function_application_exprt &funapp_expr, +void ssa_inlinert::get_replace_globals_out( + const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - exprt::operandst &c, - int counter) + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter) { - std::string suffix = id2string(funapp_expr.get(ID_suffix)); - - //equalities for globals_out - for(summaryt::var_sett::const_iterator it = cs_globals_out.begin(); - it != cs_globals_out.end(); it++) - { - symbol_exprt lhs = *it; //copy + std::string suffix=id2string(funapp_expr.get(ID_suffix)); - lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + // equalities for globals_out + for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); + it!=cs_globals_out.end(); it++) + { + symbol_exprt lhs=*it; // copy - symbol_exprt rhs; - if(find_corresponding_symbol(*it,globals_out,rhs)) - rename(rhs,counter); - else{ - bool found = find_corresponding_symbol(*it,cs_globals_in,rhs); - assert(found); - rhs.set_identifier(id2string(rhs.get_identifier())+suffix); - } - c.push_back(equal_exprt(lhs,rhs)); + lhs.set_identifier(id2string(lhs.get_identifier())+suffix); + + symbol_exprt rhs; + if(find_corresponding_symbol(*it, globals_out, rhs)) + rename(rhs, counter); + else + { + bool found=find_corresponding_symbol(*it, cs_globals_in, rhs); + assert(found); + rhs.set_identifier(id2string(rhs.get_identifier())+suffix); + } + c.push_back(equal_exprt(lhs, rhs)); } } -void ssa_inlinert::replace_globals_out(const local_SSAt::var_sett &globals_out, - const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - int counter) +void ssa_inlinert::replace_globals_out( + const local_SSAt::var_sett &globals_out, + const local_SSAt::var_sett &cs_globals_in, + const local_SSAt::var_sett &cs_globals_out, + int counter) { // equalities for globals_out for(summaryt::var_sett::const_iterator it=cs_globals_out.begin(); @@ -830,8 +900,8 @@ void ssa_inlinert::replace_globals_out(const local_SSAt::var_sett &globals_out, { symbol_exprt rhs=*it; // copy symbol_exprt lhs; - if(find_corresponding_symbol(*it,globals_out,lhs)) - rename(lhs,counter); + if(find_corresponding_symbol(*it, globals_out, lhs)) + rename(lhs, counter); else assert(find_corresponding_symbol(*it, cs_globals_in, lhs)); new_equs.push_back(equal_exprt(lhs, rhs)); @@ -870,95 +940,41 @@ Function: ssa_inlinert::rename \*******************************************************************/ -irep_idt ssa_inlinert::rename(irep_idt &id, int counter) +void ssa_inlinert::rename(irep_idt &id, int counter, bool attach) { - return id2string(id)+"@"+i2string(counter); -} - - -/*******************************************************************\ - -Function: ssa_inlinert::rename() - - Inputs: - - Outputs: + std::string id_str=id2string(id); - Purpose: - -\*******************************************************************/ - -irep_idt ssa_inlinert::rename(irep_idt &id, int counter, bool attach){ - - std::string id_str = id2string(id); - - if(attach == false){ - //find first @ where afterwards there are no letters - size_t pos = std::string::npos; - for(size_t i=0;i=0) + id=id_str+"@"+i2string(counter); + } } /*******************************************************************\ -Function: ssa_inlinert::rename() +Function: ssa_inlinert::rename Inputs: @@ -972,19 +988,18 @@ void ssa_inlinert::rename(exprt &expr, int counter, bool attach) { if(expr.id()==ID_symbol || expr.id()==ID_nondet_symbol) { - irep_idt id = expr.get(ID_identifier); + irep_idt id=expr.get(ID_identifier); rename(id, counter, attach); - - expr.set(ID_identifier,id); + + expr.set(ID_identifier, id); } - for(exprt::operandst::iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) + for(exprt::operandst::iterator it=expr.operands().begin(); + it!=expr.operands().end(); it++) { rename(*it, counter, attach); } } - /*******************************************************************\ Function: ssa_inlinert::rename @@ -997,16 +1012,29 @@ Function: ssa_inlinert::rename \*******************************************************************/ -void ssa_inlinert::rename(local_SSAt::nodet &node, int counter) +void ssa_inlinert::rename(local_SSAt::nodet &node, int counter) { - for(auto &e : node.equalities) - rename(e, counter); - for(auto &c : node.constraints) - rename(c, counter); - for(auto &a : node.assertions) - rename(a, counter); - for(auto &f : node.function_calls) - rename(f, counter); + for(local_SSAt::nodet::equalitiest::iterator e_it=node.equalities.begin(); + e_it!=node.equalities.end(); e_it++) + { + rename(*e_it, counter); + } + for(local_SSAt::nodet::constraintst::iterator c_it=node.constraints.begin(); + c_it!=node.constraints.end(); c_it++) + { + rename(*c_it, counter); + } + for(local_SSAt::nodet::assertionst::iterator a_it=node.assertions.begin(); + a_it!=node.assertions.end(); a_it++) + { + rename(*a_it, counter); + } + for(local_SSAt::nodet::function_callst::iterator + f_it=node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + rename(*f_it, counter); + } } /*******************************************************************\ @@ -1102,8 +1130,13 @@ void ssa_inlinert::rename_to_callee( replace_map[*it]=*p_it; } - for(summaryt::var_sett::const_iterator it = cs_globals_in.begin(); - it != cs_globals_in.end(); it++) + /* replace_expr(replace_map, expr); + + replace_map.clear(); // arguments might contain globals, + // thus, we have to replace them separately + */ + for(summaryt::var_sett::const_iterator it=cs_globals_in.begin(); + it!=cs_globals_in.end(); it++) { symbol_exprt cg; if(find_corresponding_symbol(*it, globals_in, cg)) @@ -1249,4 +1282,3 @@ irep_idt ssa_inlinert::get_original_identifier(const symbol_exprt &s) id=id.substr(0, pos); return id; } - diff --git a/src/ssa/ssa_inliner.h b/src/ssa/ssa_inliner.h index f1af18142..f8aaae6bc 100644 --- a/src/ssa/ssa_inliner.h +++ b/src/ssa/ssa_inliner.h @@ -2,7 +2,7 @@ Module: SSA Inliner -Author: Peter Schrammel +Author: Peter Schrammel, Kumar Madhukar \*******************************************************************/ @@ -17,15 +17,13 @@ Author: Peter Schrammel #include "ssa_db.h" #include "local_ssa.h" -class summary_dbt; -class ssa_dbt; - class ssa_inlinert:public messaget { -public: - explicit ssa_inlinert(summary_dbt &_summary_db, - ssa_dbt &_ssa_db): - counter(0), + public: + ssa_inlinert( + summary_dbt &_summary_db, + ssa_dbt &_ssa_db): + counter(-1), summary_db(_summary_db), ssa_db(_ssa_db) { @@ -33,96 +31,106 @@ class ssa_inlinert:public messaget typedef std::map assertion_mapt; - - void get_guard_binding(const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - exprt &guard_binding, - int counter); - - void get_bindings(const local_SSAt &SSA, - const local_SSAt &fSSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - exprt::operandst &bindings_in, - exprt::operandst &bindings_out, - int counter); - - void get_summary(const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const summaryt &summary, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings, - int counter, - bool error_summ = false); - - void get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter - - void get_summaries(const local_SSAt &SSA, - bool forward, - exprt::operandst &summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter - - bool get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings); //TODO: need to explicitly pass the correct counter - - bool get_summaries(const local_SSAt &SSA, - const summaryt::call_sitet ¤t_call_site, - bool forward, - exprt::operandst &assert_summaries, - exprt::operandst &noassert_summaries, - exprt::operandst &bindings, - assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter - - exprt get_summaries(const local_SSAt &SSA); //TODO: need to explicitly pass the correct counter - - exprt get_summaries(const local_SSAt &SSA, - assertion_mapt &assertion_map); //TODO: need to explicitly pass the correct counter + void get_guard_binding( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + exprt &guard_binding, + int counter); + void get_bindings( + const local_SSAt &SSA, + const local_SSAt &fSSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + exprt::operandst &bindings_in, + exprt::operandst &bindings_out, + int counter); + bool get_summary( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + int counter, + bool error_summ=false); + bool get_inlined( + const local_SSAt &SSA, + local_SSAt::nodest::const_iterator n_it, + local_SSAt::nodet::function_callst::const_iterator f_it, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + int counter, + bool error_summ); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map); + void get_summaries( + const local_SSAt &SSA, + bool forward, + exprt::operandst &summaries, + exprt::operandst &bindings); + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + bool error_summ=false); + bool get_summaries( + const local_SSAt &SSA, + const summaryt::call_sitet ¤t_call_site, + bool forward, + exprt::operandst &assert_summaries, + exprt::operandst &noassert_summaries, + exprt::operandst &bindings, + assertion_mapt &assertion_map, + bool error_summ=false); + + // TODO: need to explicitly pass the correct counter + exprt get_summaries(const local_SSAt &SSA); + exprt get_summaries( + const local_SSAt &SSA, + assertion_mapt &assertion_map); void replace( local_SSAt &SSA, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - // outgoing globals at call site + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site const summaryt &summary, bool forward, - bool preconditions_as_assertions, - int counter); + bool preconditions_as_assertions, + int counter); void replace( local_SSAt &SSA, bool forward, - bool preconditions_as_assertions, - int counter); + bool preconditions_as_assertions, + int counter); void replace( local_SSAt::nodest &nodes, local_SSAt::nodest::iterator node, local_SSAt::nodet::function_callst::iterator f_it, - const local_SSAt::var_sett &cs_globals_in, - // incoming globals at call site - const local_SSAt::var_sett &cs_globals_out, - //outgoing globals at call site - const local_SSAt &function, - int counter); + const local_SSAt::var_sett &cs_globals_in, // incoming globals at call site + const local_SSAt::var_sett &cs_globals_out, // outgoing globals at call site + const local_SSAt &function, + int counter); void replace( local_SSAt &SSA, const ssa_dbt &ssa_db, - int counter, + int counter, bool recursive=false, bool rename=true); @@ -157,21 +165,15 @@ class ssa_inlinert:public messaget static irep_idt get_original_identifier(const symbol_exprt &s); - irep_idt rename(irep_idt &id, int counter); - - irep_idt rename(irep_idt &id, int counter, bool attach); + void rename(irep_idt &id, int counter, bool attach=true); - int get_rename_counter(){ - counter++; - return counter; - } + int get_rename_counter() { return ++counter; } // moved from protected to public, for use in summarizer_bw_cex_complete - void rename(exprt &expr, int counter); - void rename(exprt &expr, int counter, bool attach); - - protected: - unsigned counter; + void rename(exprt &expr, int counter, bool attach=true); + +protected: + int counter; summary_dbt &summary_db; ssa_dbt &ssa_db; @@ -179,37 +181,42 @@ class ssa_inlinert:public messaget local_SSAt::nodet::equalitiest new_equs; std::set rm_function_calls; - void replace_globals_in(const local_SSAt::var_sett &globals_in, - const local_SSAt::var_sett &globals, - int counter); - void replace_params(const local_SSAt::var_listt ¶ms, - const function_application_exprt &funapp_expr, - int counter); - void replace_globals_out(const local_SSAt::var_sett &globals_out, + void replace_globals_in( + const local_SSAt::var_sett &globals_in, + const local_SSAt::var_sett &globals, + int counter); + void replace_params( + const local_SSAt::var_listt ¶ms, + const function_application_exprt &funapp_expr, + int counter); + void replace_globals_out( + const local_SSAt::var_sett &globals_out, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - int counter); - - void get_replace_globals_in(const local_SSAt::var_sett &globals_in, - const function_application_exprt &funapp_expr, - const local_SSAt::var_sett &globals, - exprt::operandst &c, - int counter); - void get_replace_params(const local_SSAt &SSA, - const local_SSAt::var_listt ¶ms, - local_SSAt::nodest::const_iterator n_it, - const function_application_exprt &funapp_expr, - exprt::operandst &c, - int counter); - void get_replace_globals_out(const local_SSAt::var_sett &globals_out, - const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &cs_globals_out, + int counter); + + void get_replace_globals_in( + const local_SSAt::var_sett &globals_in, + const function_application_exprt &funapp_expr, + const local_SSAt::var_sett &globals, + exprt::operandst &c, + int counter); + void get_replace_params( + const local_SSAt &SSA, + const local_SSAt::var_listt ¶ms, + local_SSAt::nodest::const_iterator n_it, + const function_application_exprt &funapp_expr, + exprt::operandst &c, + int counter); + void get_replace_globals_out( + const local_SSAt::var_sett &globals_out, + const function_application_exprt &funapp_expr, const local_SSAt::var_sett &cs_globals_in, - const local_SSAt::var_sett &cs_globals_out, - exprt::operandst &c, - int counter); + const local_SSAt::var_sett &cs_globals_out, + exprt::operandst &c, + int counter); void rename(local_SSAt::nodet &node, int counter); - }; #endif diff --git a/src/ssa/ssa_refiner.h b/src/ssa/ssa_refiner.h index 0c933e0da..087dd2e91 100644 --- a/src/ssa/ssa_refiner.h +++ b/src/ssa/ssa_refiner.h @@ -6,16 +6,16 @@ Author: Peter Schrammel \*******************************************************************/ -#ifndef CPROVER_SSA_REFINER_H -#define CPROVER_SSA_REFINER_H +#ifndef CPROVER_SSA_SSA_REFINER_H +#define CPROVER_SSA_SSA_REFINER_H #include -class ssa_refinert : public messaget +class ssa_refinert:public messaget { public: virtual bool operator()() { assert(false); } virtual unsigned get_unwind() { assert(false); } }; -#endif +#endif // CPROVER_SSA_SSA_REFINER_H diff --git a/src/ssa/ssa_refiner_monolithic.cpp b/src/ssa/ssa_refiner_monolithic.cpp new file mode 100644 index 000000000..6d168fc23 --- /dev/null +++ b/src/ssa/ssa_refiner_monolithic.cpp @@ -0,0 +1,30 @@ +/*******************************************************************\ + +Module: SSA Refiner for Monolithic Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#include "ssa_refiner_monolithic.h" + +/*******************************************************************\ + +Function: ssa_refiner_monolithict::operator() + + Inputs: + + Outputs: + + Purpose: refine all + +\*******************************************************************/ + +bool ssa_refiner_monolithict::operator()() +{ + status() << "Unwinding (k=" << unwind << ")" << eom; + summary_db.mark_recompute_all(); // TODO: recompute only functions with loops + ssa_unwinder.unwind_all(unwind); + + return unwind++<=max_unwind; +} diff --git a/src/ssa/ssa_refiner_monolithic.h b/src/ssa/ssa_refiner_monolithic.h new file mode 100644 index 000000000..e14e23b01 --- /dev/null +++ b/src/ssa/ssa_refiner_monolithic.h @@ -0,0 +1,44 @@ +/*******************************************************************\ + +Module: SSA Refiner for Monolithic Analysis + +Author: Peter Schrammel + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H +#define CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H + +#include "ssa_refiner.h" + +#include +#include "ssa_unwinder.h" + +class summary_dbt; +class ssa_unwindert; + +class ssa_refiner_monolithict:public ssa_refinert +{ +public: + ssa_refiner_monolithict( + summary_dbt &_summary_db, + ssa_unwindert &_ssa_unwinder, + unsigned _max_unwind): + summary_db(_summary_db), + ssa_unwinder(_ssa_unwinder), + max_unwind(_max_unwind), + unwind(0) + { + } + + virtual bool operator()(); + virtual unsigned get_unwind() { return unwind>0 ? unwind-1 : 0; } + +protected: + summary_dbt &summary_db; + ssa_unwindert &ssa_unwinder; + const unsigned max_unwind; + unsigned unwind; +}; + +#endif // CPROVER_2LS_SSA_SSA_REFINER_MONOLITHIC_H diff --git a/src/ssa/ssa_refiner_selective.cpp b/src/ssa/ssa_refiner_selective.cpp index 77e520194..ae16ad2bc 100644 --- a/src/ssa/ssa_refiner_selective.cpp +++ b/src/ssa/ssa_refiner_selective.cpp @@ -23,35 +23,35 @@ Function: ssa_refiner_selectivet::operator() bool ssa_refiner_selectivet::operator()() { // unwind loops "selectively" (those that seem to be the "reason") - for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + for(reasont::const_iterator it=reason.begin(); it!=reason.end(); ++it) { - for(std::set::const_iterator l_it = + for(std::set::const_iterator l_it= it->second.loops.begin(); - l_it != it->second.loops.end(); l_it++) + l_it!=it->second.loops.end(); l_it++) { - unsigned new_unwind = ssa_unwinder.unwind_loop_once_more(it->first, - (*l_it)->location_number); - debug() << "Refining function " << it->first << ": unwinding loop at " + unsigned new_unwind= + ssa_unwinder.unwind_loop_once_more(it->first, (*l_it)->location_number); + debug() << "Refining function " << it->first << ": unwinding loop at " << (*l_it)->location_number << " (k=" << new_unwind << ")" << eom; - unwind = std::max(unwind, new_unwind); + unwind=std::max(unwind, new_unwind); } } // inline functions "selectively" (those that seem to be the "reason") - for(reasont::const_iterator it = reason.begin(); it != reason.end(); ++it) + for(reasont::const_iterator it=reason.begin(); it!=reason.end(); ++it) { - for(std::set::const_iterator f_it = + for(std::set::const_iterator f_it= it->second.functions.begin(); - f_it != it->second.functions.end(); f_it++) + f_it!=it->second.functions.end(); f_it++) { - local_SSAt &SSA = ssa_db.get(it->first); - local_SSAt::nodest::iterator n_it = SSA.find_node(*f_it); + local_SSAt &SSA=ssa_db.get(it->first); + local_SSAt::nodest::iterator n_it=SSA.find_node(*f_it); assert(n_it->function_calls.size()==1); - n_it->function_calls_inlined = true; + n_it->function_calls_inlined=true; - irep_idt fname = to_symbol_expr(n_it->function_calls.begin() + irep_idt fname=to_symbol_expr(n_it->function_calls.begin() ->function()).get_identifier(); - debug() << "Refining function " << it->first << ": inlining call to " + debug() << "Refining function " << it->first << ": inlining call to " << fname << " at " << (*f_it)->location_number<< eom; } } diff --git a/src/ssa/ssa_refiner_selective.h b/src/ssa/ssa_refiner_selective.h index 48ec41028..2ffcd166c 100644 --- a/src/ssa/ssa_refiner_selective.h +++ b/src/ssa/ssa_refiner_selective.h @@ -6,63 +6,64 @@ Author: Peter Schrammel, Madhukar Kumar \*******************************************************************/ -#ifndef CPROVER_SSA_REFINER_SELECTIVE_H -#define CPROVER_SSA_REFINER_SELECTIVE_H +#ifndef CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H +#define CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H #include "ssa_refiner.h" -#include "../ssa/ssa_db.h" +#include #include "ssa_inliner.h" #include "ssa_unwinder.h" class ssa_dbt; -class ssa_refiner_selectivet : public ssa_refinert +class ssa_refiner_selectivet:public ssa_refinert { - public: +public: struct reason_infot { - typedef local_SSAt::locationt function_infot; //call_site; restriction: we assume that there is a single function call in an SSA node + // call_site; restriction: + // we assume that there is a single function call in an SSA node + typedef local_SSAt::locationt function_infot; typedef local_SSAt::locationt loop_infot; std::set functions; - std::set loops; + std::set loops; }; - class reasont : public std::map + class reasont:public std::map { public: void merge(const reasont &other) { - for(reasont::const_iterator it = other.begin(); - it != other.end(); ++it) + for(const auto &reason : other) { - reason_infot &r = (*this)[it->first]; - r.functions.insert(it->second.functions.begin(), - it->second.functions.end()); - r.loops.insert(it->second.loops.begin(), it->second.loops.end()); + reason_infot &r=(*this)[reason.first]; + r.functions.insert( + reason.second.functions.begin(), + reason.second.functions.end()); + r.loops.insert(reason.second.loops.begin(), reason.second.loops.end()); } } }; - - explicit ssa_refiner_selectivet( + ssa_refiner_selectivet( ssa_dbt &_ssa_db, ssa_unwindert &_ssa_unwinder, unsigned _max_unwind, ssa_inlinert &_ssa_inliner, - reasont &_reason - ) : - ssa_db(_ssa_db), - ssa_unwinder(_ssa_unwinder), - max_unwind(_max_unwind), - ssa_inliner(_ssa_inliner), - unwind(0), - reason(_reason) - {} + reasont &_reason): + ssa_db(_ssa_db), + ssa_unwinder(_ssa_unwinder), + max_unwind(_max_unwind), + ssa_inliner(_ssa_inliner), + unwind(0), + reason(_reason) + { + } + + virtual bool operator()(); + virtual unsigned get_unwind() { return unwind; } - virtual bool operator()(); - virtual unsigned get_unwind() { return unwind; } - protected: ssa_dbt &ssa_db; ssa_unwindert &ssa_unwinder; @@ -72,4 +73,4 @@ class ssa_refiner_selectivet : public ssa_refinert reasont &reason; }; -#endif +#endif // CPROVER_2LS_SSA_SSA_REFINER_SELECTIVE_H diff --git a/src/ssa/ssa_unwinder.cpp b/src/ssa/ssa_unwinder.cpp index 08649c47f..1c8dead21 100644 --- a/src/ssa/ssa_unwinder.cpp +++ b/src/ssa/ssa_unwinder.cpp @@ -1,985 +1,1143 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Peter Schrammel, Saurabh Joshi - -\*******************************************************************/ - -// #define DEBUG - -#include - -#include "ssa_unwinder.h" - -/*******************************************************************\ - -Function: ssa_local_unwindert::init - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_local_unwindert::init() -{ - build_loop_tree(); - build_pre_post_map(); - build_exit_conditions(); - unwind(0); -#ifdef DEBUG - SSA.output_verbose(std::cout); -#endif -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::build_loop_tree - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_local_unwindert::build_loop_tree() -{ - // build loop tree structure - // Assumes that initially the nodes are in the same order - // as in the goto program - std::list loopheads; - local_SSAt::nodest::iterator n_it=--SSA.nodes.end(); - while(n_it!=SSA.nodes.begin()) - { - // end of loop found - if(n_it->loophead!=SSA.nodes.end()) - { - loopt &loop=loops[n_it->loophead->location->location_number]; - if(loopheads.empty()) - { - loop.is_root=true; - } - else - { - loops[loopheads.back()->location->location_number]. - loop_nodes.push_back( - n_it->loophead->location->location_number); - } - loopheads.push_back(n_it->loophead); - loop.body_nodes.push_front(*n_it); -#ifdef DEBUG - std::cout << "pop " << n_it->location->location_number - << " for " << n_it->loophead->location->location_number - << std::endl; -#endif - // this test is ambiguous if the loop condition is true, - // but shouldn't have any consequences - assert(n_it->location->is_backwards_goto()); - loop.is_dowhile=!n_it->location->guard.is_true(); - SSA.nodes.erase(n_it--); - } - // beginning of loop found - else if(!loopheads.empty() && n_it==loopheads.back()) - { -#ifdef DEBUG - std::cout << "push " << n_it->location->location_number << std::endl; -#endif - loops[n_it->location->location_number].body_nodes.push_front(*n_it); - loopheads.pop_back(); - loops[n_it->location->location_number].body_nodes.back().loophead= - loops[n_it->location->location_number].body_nodes.begin(); - SSA.nodes.erase(n_it--); - } - // collect loop body nodes - else if(!loopheads.empty()) - { -#ifdef DEBUG - std::cout << "add " << n_it->location->location_number - << " for " << loopheads.back()->location->location_number - << std::endl; -#endif - loops[loopheads.back()->location->location_number]. - body_nodes.push_front(*n_it); - SSA.nodes.erase(n_it--); - } - else - --n_it; - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::build_pre_post_map - - Inputs: - - Outputs: - - Purpose: find variables at loop head and backedge - -\*******************************************************************/ - -void ssa_local_unwindert::build_pre_post_map() -{ - for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) - { - assert(!it->second.body_nodes.empty()); - const locationt &pre_loc=it->second.body_nodes.begin()->location; - const locationt &post_loc=(--it->second.body_nodes.end())->location; - SSA.current_location=pre_loc; // TODO: must go away - - // modified variables - const ssa_domaint::phi_nodest &phi_nodes= - SSA.ssa_analysis[pre_loc].phi_nodes; - for(local_SSAt::objectst::const_iterator o_it= - SSA.ssa_objects.objects.begin(); - o_it!=SSA.ssa_objects.objects.end(); o_it++) - { - ssa_domaint::phi_nodest::const_iterator p_it=phi_nodes.find( - o_it->get_identifier()); - - if(p_it==phi_nodes.end()) - continue; // object not modified in this loop - - it->second.modified_vars.push_back(o_it->get_expr()); - symbol_exprt pre=SSA.name(*o_it, local_SSAt::PHI, pre_loc); - it->second.pre_post_map[pre]=SSA.read_rhs(*o_it, post_loc); - } - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::build_exit_conditions - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_local_unwindert::build_exit_conditions() -{ - for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) - { - unsigned location_number_end= - it->second.body_nodes.back().location->location_number; -#ifdef DEBUG - std::cout << "end: " << location_number_end << std::endl; -#endif - for(local_SSAt::nodest::iterator n_it=it->second.body_nodes.begin(); - n_it!=it->second.body_nodes.end(); n_it++) - { - if(!n_it->location->is_goto()) - continue; - local_SSAt::nodest::iterator next=n_it; ++next; - SSA.current_location=n_it->location; // TODO: must go away - if(n_it->location->get_target()->location_number>location_number_end) - { - exprt g=SSA.guard_symbol(n_it->location); - exprt c=SSA.cond_symbol(n_it->location); - // need disjunction of all exit conditions - // for each symbol name at exit location - for(exprt::operandst::const_iterator - s_it=it->second.modified_vars.begin(); - s_it!=it->second.modified_vars.end(); s_it++) - { - exprt e=SSA.read_rhs(*s_it, n_it->location); - it->second.exit_map[e].push_back(and_exprt(g, c)); - } - it->second.exit_map[g].push_back(and_exprt(g, c)); - it->second.exit_map[c].push_back(and_exprt(g, c)); - // collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g, c)); - } - } - else if(next==it->second.body_nodes.end() && - !n_it->location->guard.is_true()) - { // this happens in do-whiles - // ENHANCE: transform goto-program to make SSA uniform in this respect - exprt g=SSA.guard_symbol(n_it->location); - exprt c=SSA.cond_symbol(n_it->location); - for(exprt::operandst::const_iterator - s_it=it->second.modified_vars.begin(); - s_it!=it->second.modified_vars.end(); s_it++) - { - exprt e=SSA.read_rhs(*s_it, n_it->location); - it->second.exit_map[e].push_back( - and_exprt(g, not_exprt(c))); - } - it->second.exit_map[g].push_back(and_exprt(g, not_exprt(c))); - it->second.exit_map[c].push_back(and_exprt(g, not_exprt(c))); - // collect exits for assertion hoisting - if(it->second.is_root) - { - it->second.assertion_hoisting_map[n_it->location->get_target()] - .exit_conditions.push_back(and_exprt(g, not_exprt(c))); - } - } - } - } - // collect assertions for hoisting - for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) - { - if(!it->second.is_root) - continue; - for(loopt::assertion_hoisting_mapt::iterator - h_it=it->second.assertion_hoisting_map.begin(); - h_it!=it->second.assertion_hoisting_map.end(); ++h_it) - { - local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); - // find jump target in nodes - while(n_it->location!=h_it->first && - n_it!=SSA.nodes.end()) ++n_it; - local_SSAt::nodest::const_iterator prev=n_it; - for(; n_it!=SSA.nodes.end(); ++n_it, ++prev) - { - // we collect only on top level, but not beyond loops - // so, if there is a gap in the nodes, we would jump over a loop - if(n_it!=prev && // this would fail in the first iteration otherwise - prev->location->location_number+1!= - n_it->location->location_number) - break; - if(n_it==prev) - --prev; - for(local_SSAt::nodet::assertionst::const_iterator a_it= - n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) - { - h_it->second.assertions.push_back(*a_it); - } - } - } - } -} - - -/*******************************************************************\ - -Function: ssa_local_unwindert::unwind - - Inputs: - - Outputs: - - Purpose: unwind all loops up to k starting from previous unwindings - -\*******************************************************************/ - -void ssa_local_unwindert::unwind(unsigned k) -{ - if(SSA.current_unwinding>=(long)k) - return; - - current_enabling_expr= - symbol_exprt("unwind::"+id2string(fname)+"::enable"+i2string(k), - bool_typet()); - SSA.enabling_exprs.push_back(current_enabling_expr); - - // TODO: just for exploratory integration, must go away - SSA.current_unwinding=k; - - // recursively unwind everything - SSA.current_unwindings.clear(); - for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) - { - if(!it->second.is_root) - continue; - unwind(it->second, k, false); // recursive - assert(SSA.current_unwindings.empty()); - } - // update current unwinding - for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) - { - it->second.current_unwinding=k; - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::unwind - - Inputs: - - Outputs: - - Purpose: unwind all instances of given loop up to k - starting from previous unwindings, and recur - -\*******************************************************************/ - -void ssa_local_unwindert::unwind(loopt &loop, unsigned k, bool is_new_parent) -{ - odometert context=SSA.current_unwindings; -#ifdef DEBUG - std::cout << "unwind(k=" << k << ", is_new_parent=" << is_new_parent << "), "; - std::cout << "context=" << SSA.odometer_to_string(context, context.size()) - << std::endl; -#endif - SSA.increment_unwindings(1); - for(long i=0; i<=k; ++i) - { - // add new unwindings of this loop - if(i>loop.current_unwinding || is_new_parent) - { - add_loop_body(loop); - // set new loop end node - if(i==0) - { - assert(loop.end_nodes.find(context)==loop.end_nodes.end()); - loop.end_nodes[context]=--SSA.nodes.end(); - assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); -#ifdef DEBUG - std::cout << "end node for context " - << SSA.odometer_to_string(context, context.size()) << ": " - << loop.end_nodes[context]->location->location_number << "==" - << loop.body_nodes.back().location->location_number - << std::endl; -#endif - } - if(i>0) - { - add_loop_connector(loop); - } - // in while loops we can only hoist in iterations %2 and higher - // otherwise we would block the loop exit that is only - // reachable via !guardls - bool is_last=(loop.is_dowhile && i==0) || - (!loop.is_dowhile && (i==0 || i==1)); - add_assertions(loop, is_last); - add_hoisted_assertions(loop, is_last); - } - if(i==k) - { - add_loop_head(loop); - // update loop head -#ifdef DEBUG - std::cout << "update loop head for context " - << SSA.odometer_to_string(context, context.size()) << ": " - << loop.body_nodes.begin()->location->location_number - << std::endl; -#endif - assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); - loop.end_nodes[context]->loophead=--SSA.nodes.end(); - assert( - loop.end_nodes[context]->loophead->location->location_number== - loop.body_nodes.begin()->location->location_number); - } - // recurse into child loops - for(const auto &l : loop.loop_nodes) - { -#ifdef DEBUG - std::cout << i << ">" << loop.current_unwinding << std::endl; -#endif - unwind(loops[l], k, i>loop.current_unwinding || is_new_parent); - } - SSA.increment_unwindings(0); - } - SSA.increment_unwindings(-1); - add_exit_merges(loop, k); -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_loop_body - - Inputs: - - Outputs: - - Purpose: duplicates the loop body for the current instance - -\*******************************************************************/ - -void ssa_local_unwindert::add_loop_body(loopt &loop) -{ - local_SSAt::nodest::iterator it=loop.body_nodes.begin(); - ++it; // skip loop head, we'll do that separately - for(; it!=loop.body_nodes.end(); ++it) - { - if(it->equalities.empty() && - it->constraints.empty() && - it->function_calls.empty()) - continue; - -#ifdef DEBUG - std::cout << "add body node: " - << it->location->location_number << std::endl; -#endif - SSA.nodes.push_back(*it); // copy - local_SSAt::nodet &node=SSA.nodes.back(); - node.loophead=SSA.nodes.end(); - node.marked=false; - for(local_SSAt::nodet::equalitiest::iterator e_it= - node.equalities.begin(); e_it!=node.equalities.end(); e_it++) - { - SSA.rename(*e_it, node.location); - } - for(local_SSAt::nodet::constraintst::iterator c_it= - node.constraints.begin(); c_it!=node.constraints.end(); c_it++) - { - SSA.rename(*c_it, node.location); - } - for(local_SSAt::nodet::function_callst::iterator f_it= - node.function_calls.begin(); - f_it!=node.function_calls.end(); f_it++) - { - SSA.rename(*f_it, node.location); - } - // will do assertions separately - node.assertions.clear(); - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_assertions - - Inputs: - - Outputs: - - Purpose: adds the assertions and assumptions - -\*******************************************************************/ - -void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) -{ - for(local_SSAt::nodest::iterator it=loop.body_nodes.begin(); - it!=loop.body_nodes.end(); ++it) - { - if(it->assertions.empty()) - continue; - - SSA.nodes.push_back( - local_SSAt::nodet(it->location, SSA.nodes.end())); // add new node - local_SSAt::nodet &node=SSA.nodes.back(); - node.assertions=it->assertions; - SSA.current_location=node.location; // TODO: must go away - for(local_SSAt::nodet::assertionst::iterator a_it= - node.assertions.begin(); a_it!=node.assertions.end(); a_it++) - { - SSA.rename(*a_it, node.location); - if(!is_last) // only add assumptions if we are not in %0 iteration - { - if(is_kinduction) - node.constraints.push_back(*a_it); - else if(is_bmc) - { - // only add in base case - exprt gs= - SSA.name( - SSA.guard_symbol(), - local_SSAt::LOOP_SELECT, - loop.body_nodes.back().location); - node.constraints.push_back(implies_exprt(not_exprt(gs), *a_it)); - } - } - } - if(!is_last && (is_bmc || is_kinduction)) - { - node.assertions.clear(); - } - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_loop_head - - Inputs: - - Outputs: - - Purpose: adds the new loop head - -\*******************************************************************/ - -void ssa_local_unwindert::add_loop_head(loopt &loop) -{ - // new connecting loop head for the current instance - // (enabled for this iteration) - SSA.nodes.push_back(loop.body_nodes.front()); // copy loop head - local_SSAt::nodet &node=SSA.nodes.back(); - node.marked=false; - node.enabling_expr=current_enabling_expr; - for(local_SSAt::nodet::equalitiest::iterator e_it= - node.equalities.begin(); e_it!=node.equalities.end(); e_it++) - { - SSA.rename(*e_it, node.location); - } - assert(node.constraints.empty()); - assert(node.function_calls.empty()); - assert(node.assertions.empty()); -#ifdef DEBUG - std::cout << "add loop head: " << node.location->location_number << std::endl; -#endif -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_loop_connector - - Inputs: - - Outputs: - - Purpose: adds a connector to the previous iteration - -\*******************************************************************/ - -void ssa_local_unwindert::add_loop_connector(loopt &loop) -{ - // connector to previous iteration (permanently added) - const local_SSAt::nodet &orig_node=loop.body_nodes.front(); - SSA.nodes.push_back( - local_SSAt::nodet(orig_node.location, SSA.nodes.end())); // add new node - local_SSAt::nodet &node=SSA.nodes.back(); - SSA.current_location=node.location; // TODO: must go away - for(local_SSAt::nodet::equalitiest::const_iterator e_it= - orig_node.equalities.begin(); - e_it!=orig_node.equalities.end(); e_it++) - { - if(e_it->rhs().id()==ID_if) - { - node.equalities.push_back(*e_it); - node.equalities.back().rhs()= - loop.pre_post_map[to_symbol_expr(e_it->lhs())]; - SSA.rename(node.equalities.back().rhs(), node.location); - SSA.decrement_unwindings(0); - SSA.rename(node.equalities.back().lhs(), node.location); - SSA.increment_unwindings(0); - } - else if(e_it->lhs().id()==ID_symbol && - !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), - "ssa::$guard")) - { // this is needed for while loops - node.equalities.push_back(*e_it); - SSA.decrement_unwindings(0); - SSA.rename(node.equalities.back(), node.location); - SSA.increment_unwindings(0); - } - } - // continuation guard and condition - exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); - SSA.decrement_unwindings(0); - exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); - SSA.increment_unwindings(0); - node.equalities.push_back(equal_exprt(g_lhs, g_rhs)); -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_exit_merges - - Inputs: - - Outputs: - - Purpose: adds the merge connector for the loop exits for the current instance - -\*******************************************************************/ - -void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) -{ - SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, - SSA.nodes.end())); // add new node - local_SSAt::nodet &node=SSA.nodes.back(); - node.enabling_expr=current_enabling_expr; - - for(loopt::exit_mapt::const_iterator x_it=loop.exit_map.begin(); - x_it!=loop.exit_map.end(); x_it++) - { - node.equalities.push_back( - build_exit_merge( - x_it->first, disjunction(x_it->second), k, node.location)); - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::build_exit_merge - - Inputs: - - Outputs: - - Purpose: generates exit merge expression for a given expression - -\*******************************************************************/ - -equal_exprt ssa_local_unwindert::build_exit_merge( - exprt e, const exprt &exits, unsigned k, locationt loc) -{ - exprt re=e; - SSA.increment_unwindings(1); - SSA.rename(re, loc); // %0 - for(long i=1; i<=k; i++) - { - SSA.increment_unwindings(0); - exprt cond_expr=exits; - SSA.rename(cond_expr, loc); - exprt true_expr=e; - SSA.rename(true_expr, loc); - exprt false_expr=re; - re=if_exprt(cond_expr, true_expr, false_expr); - } - SSA.increment_unwindings(-1); - SSA.rename(e, loc); // lhs - return equal_exprt(e, re); -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::add_hoisted_assertions - - Inputs: - - Outputs: - - Purpose: adds the assumptions for hoisted assertions - for the current instance - -\*******************************************************************/ - -void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) -{ - for(loopt::assertion_hoisting_mapt::const_iterator - it=loop.assertion_hoisting_map.begin(); - it!=loop.assertion_hoisting_map.end(); ++it) - { - if(!is_last // only add assumptions if we are not in %0 iteration - && is_kinduction && !it->second.assertions.empty()) - { - exprt e=disjunction(it->second.exit_conditions); - SSA.rename(e, loop.body_nodes.begin()->location); - SSA.nodes.push_back( - local_SSAt::nodet(it->first, SSA.nodes.end())); // add new node - local_SSAt::nodet &node=SSA.nodes.back(); - node.constraints.push_back( - implies_exprt(e, conjunction(it->second.assertions))); -#ifdef DEBUG - std::cout << "adding hoisted assumption: " - << from_expr(SSA.ns, "", node.constraints.back()) - << std::endl; -#endif - } - } -} - - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::compute_loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : compute loop continuation conditions for all loops in this function - * - *****************************************************************************/ - -void ssa_local_unwindert::compute_loop_continuation_conditions() -{ - //clear old ones - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - it->second.current_continuation_conditions.clear(); - } - //compute new - SSA.current_unwindings.clear(); - for(loop_mapt::iterator it = loops.begin(); it != loops.end(); ++it) - { - if(!it->second.is_root) - continue; - compute_loop_continuation_conditions(it->second); //recursive - assert(SSA.current_unwindings.empty()); - } -} - -/*****************************************************************************\ - * - * Function : ssa_local_unwindert::compute_loop_continuation_conditions - * - * Input : - * - * Output : - * - * Purpose : recursively construct loop continuation conditions - * - *****************************************************************************/ - -void ssa_local_unwindert::compute_loop_continuation_conditions( - loopt& loop) -{ - SSA.increment_unwindings(1); - loop.current_continuation_conditions.push_back( - get_continuation_condition(loop)); //%0 - for(long i=0; i<=loop.current_unwinding; ++i) - { - //recurse into child loops - for(std::vector::iterator l_it = loop.loop_nodes.begin(); - l_it != loop.loop_nodes.end(); ++l_it) - { - compute_loop_continuation_conditions(loops.at(*l_it)); - } - SSA.increment_unwindings(0); - } - SSA.increment_unwindings(-1); -} - - -/*******************************************************************\ - -Function: ssa_local_unwindert::loop_continuation_conditions - - Inputs: - - Outputs: - - Purpose: return loop continuation conditions for all loops in this function - -\*******************************************************************/ - -void ssa_local_unwindert::loop_continuation_conditions( - exprt::operandst& loop_cont) const -{ - SSA.current_unwindings.clear(); - for(loop_mapt::const_iterator it=loops.begin(); it!=loops.end(); ++it) - { - if(!it->second.is_root) - continue; - loop_continuation_conditions(it->second, loop_cont); // recursive - assert(SSA.current_unwindings.empty()); - } -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::get_continuation_condition - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const -{ - SSA.current_location=loop.body_nodes.back().location; // TODO: must go away - return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), - SSA.cond_symbol(loop.body_nodes.back().location)); -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::loop_continuation_conditions - - Inputs: - - Outputs: - - Purpose: recursively construct loop continuation conditions - -\*******************************************************************/ - -void ssa_local_unwindert::loop_continuation_conditions( - const loopt& loop, exprt::operandst& loop_cont) const -{ - SSA.increment_unwindings(1); - loop_cont.push_back(get_continuation_condition(loop)); // %0 - for(long i=0; i<=loop.current_unwinding; ++i) - { - // recurse into child loops - for(const auto &l : loop.loop_nodes) - { - loop_continuation_conditions(loops.at(l), loop_cont); - } - SSA.increment_unwindings(0); - } - SSA.increment_unwindings(-1); -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::unwinder_rename - - Inputs: var, node - - Outputs: var is returned with a suffix that reflects the current unwinding - with the context taken from the node - - Purpose: E.g. node must look like - cond"somesuffix"==TRUE, e.g. cond%1%2%5%0==TRUE - and variable might be guard#ls25 - if the current_unwinding is 6 - the variable should be converted to guard#ls25%1%2%5%5 - - Note that if current_unwinding is X then suffixes can have at most - X-1 in its parts - -\*******************************************************************/ - -void ssa_local_unwindert::unwinder_rename( - symbol_exprt &var, - const local_SSAt::nodet &node, - bool pre) const -{ - // TODO: replace this by SSA.rename - // this requires odometer information in the nodes - - // only to be called for backedge nodes - // This is very dirty hack - if(SSA.current_unwinding<0) - return; - - assert(node.equalities.size()>=1); - // copy suffix from equality lhs to var - std::string id= - id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); - size_t pos=id.find_first_of("%"); - if(pos==std::string::npos) - return; - size_t pos1=id.find_last_of("%"); - std::string suffix; - unsigned unwinding=pre ? SSA.current_unwinding : 0; - if(pos==pos1) - { - suffix="%"+i2string(unwinding); - } - else - { - suffix=id.substr(pos, pos1-pos); - suffix+="%"+i2string(unwinding); - } - - var.set_identifier(id2string(var.get_identifier())+suffix); -#ifdef DEBUG - std::cout << "new id: " << var.get_identifier() << std::endl; -#endif -} - -/*******************************************************************\ - -Function: ssa_local_unwindert::find_loop - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool ssa_local_unwindert::find_loop( - unsigned location_number, const loopt *&loop) const -{ - loop_mapt::const_iterator it=loops.find(location_number); - if(it!=loops.end()) - { - loop=&it->second; - return true; - } - return false; -} - -/*******************************************************************\ - -Function: ssa_unwindert::unwind - - Inputs: fname-name of the goto-function to be unwound, k-unwinding depth - - Outputs: false-if id does not correspond to any goto-function in the - unwinder_map - - Purpose: incrementally unwind a function 'id' up to depth k. Initializer - must have been invoked before calling this function - -\*******************************************************************/ - -void ssa_unwindert::unwind(const irep_idt fname, unsigned int k) -{ - assert(is_initialized); - unwinder_mapt::iterator it=unwinder_map.find(fname); - assert(it!=unwinder_map.end()); - it->second.unwind(k); -} - -/*******************************************************************\ - -Function: ssa_unwindert::unwind_all - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_unwindert::unwind_all(unsigned int k) -{ - assert(is_initialized); - - for(auto &local_unwinder : unwinder_map) - local_unwinder.second.unwind(k); -} - -/*******************************************************************\ - -Function: ssa_unwindert::init - - Inputs: - - Outputs: - - Purpose: Initialize unwinder_map by computing hierarchical tree_loopnodet - for every goto-function - Set is_initialized to true. Initializer must be called before - unwind funcitions are called. - -\*******************************************************************/ - -void ssa_unwindert::init(bool is_kinduction, bool is_bmc) -{ - ssa_dbt::functionst& funcs=ssa_db.functions(); - for(auto &f : funcs) - { - unwinder_map.insert(unwinder_pairt( - f.first, - ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); - } -} - -/*******************************************************************\ - -Function: ssa_unwindert::init_localunwinders - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void ssa_unwindert::init_localunwinders() -{ - for(auto &local_unwinder : unwinder_map) - local_unwinder.second.init(); - - is_initialized=true; -} +/*******************************************************************\ + +Module: SSA Unwinder + +Author: Peter Schrammel, Saurabh Joshi + +\*******************************************************************/ + +// #define DEBUG + +#include + +#include "ssa_unwinder.h" + +/*******************************************************************\ + +Function: ssa_local_unwindert::init + + Inputs: + + Outputs: + + Purpose: builds data structures for unwinder and transforms SSA (rename to %0) + +\*******************************************************************/ + +void ssa_local_unwindert::init() +{ + build_loop_tree(); + build_pre_post_map(); + build_exit_conditions(); + unwind(0); + compute_enable_expr(); +#ifdef DEBUG + SSA.output_verbose(std::cout); +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_loop_tree + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_local_unwindert::build_loop_tree() +{ + // build loop tree structure + // Assumes that initially the nodes are in the same order + // as in the goto program + std::list loopheads; + local_SSAt::nodest::iterator n_it=--SSA.nodes.end(); + while(n_it!=SSA.nodes.begin()) + { + // end of loop found + if(n_it->loophead!=SSA.nodes.end()) + { + loopt &loop=loops[n_it->loophead->location->location_number]; + if(loopheads.empty()) + { + loop.is_root=true; + } + else + { + loops[loopheads.back()->location->location_number]. + loop_nodes.push_back( + n_it->loophead->location->location_number); + } + loopheads.push_back(n_it->loophead); + loop.body_nodes.push_front(*n_it); +#ifdef DEBUG + std::cout << "pop " << n_it->location->location_number + << " for " << n_it->loophead->location->location_number + << std::endl; +#endif + // this test is ambiguous if the loop condition is true, + // but shouldn't have any consequences + assert(n_it->location->is_backwards_goto()); + loop.is_dowhile=!n_it->location->guard.is_true(); + SSA.nodes.erase(n_it--); + } + // beginning of loop found + else if(!loopheads.empty() && n_it==loopheads.back()) + { +#ifdef DEBUG + std::cout << "push " << n_it->location->location_number << std::endl; +#endif + loops[n_it->location->location_number].body_nodes.push_front(*n_it); + loopheads.pop_back(); + loops[n_it->location->location_number].body_nodes.back().loophead= + loops[n_it->location->location_number].body_nodes.begin(); + SSA.nodes.erase(n_it--); + } + // collect loop body nodes + else if(!loopheads.empty()) + { +#ifdef DEBUG + std::cout << "add " << n_it->location->location_number + << " for " << loopheads.back()->location->location_number + << std::endl; +#endif + loops[loopheads.back()->location->location_number]. + body_nodes.push_front(*n_it); + SSA.nodes.erase(n_it--); + } + else + --n_it; + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_pre_post_map + + Inputs: + + Outputs: + + Purpose: find variables at loop head and backedge + +\*******************************************************************/ + +void ssa_local_unwindert::build_pre_post_map() +{ + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + assert(!it->second.body_nodes.empty()); + const locationt &pre_loc=it->second.body_nodes.begin()->location; + const locationt &post_loc=(--it->second.body_nodes.end())->location; + SSA.current_location=pre_loc; // TODO: must go away + + // modified variables + const ssa_domaint::phi_nodest &phi_nodes= + SSA.ssa_analysis[pre_loc].phi_nodes; + for(local_SSAt::objectst::const_iterator o_it= + SSA.ssa_objects.objects.begin(); + o_it!=SSA.ssa_objects.objects.end(); o_it++) + { + ssa_domaint::phi_nodest::const_iterator p_it=phi_nodes.find( + o_it->get_identifier()); + + if(p_it==phi_nodes.end()) + continue; // object not modified in this loop + + it->second.modified_vars.push_back(o_it->get_expr()); + symbol_exprt pre=SSA.name(*o_it, local_SSAt::PHI, pre_loc); + it->second.pre_post_map[pre]=SSA.read_rhs(*o_it, post_loc); + } + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_conditions + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_local_unwindert::build_exit_conditions() +{ + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + unsigned location_number_end= + it->second.body_nodes.back().location->location_number; +#ifdef DEBUG + std::cout << "end: " << location_number_end << std::endl; +#endif + for(local_SSAt::nodest::iterator n_it=it->second.body_nodes.begin(); + n_it!=it->second.body_nodes.end(); n_it++) + { + if(!n_it->location->is_goto()) + continue; + local_SSAt::nodest::iterator next=n_it; ++next; + SSA.current_location=n_it->location; // TODO: must go away + if(n_it->location->get_target()->location_number>location_number_end) + { + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + // need disjunction of all exit conditions + // for each symbol name at exit location + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back(and_exprt(g, c)); + } + it->second.exit_map[g].push_back(and_exprt(g, c)); + it->second.exit_map[c].push_back(and_exprt(g, c)); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, c)); + } + } + else if(next==it->second.body_nodes.end() && + !n_it->location->guard.is_true()) + { // this happens in do-whiles + // ENHANCE: transform goto-program to make SSA uniform in this respect + exprt g=SSA.guard_symbol(n_it->location); + exprt c=SSA.cond_symbol(n_it->location); + for(exprt::operandst::const_iterator + s_it=it->second.modified_vars.begin(); + s_it!=it->second.modified_vars.end(); s_it++) + { + exprt e=SSA.read_rhs(*s_it, n_it->location); + it->second.exit_map[e].push_back( + and_exprt(g, not_exprt(c))); + } + it->second.exit_map[g].push_back(and_exprt(g, not_exprt(c))); + it->second.exit_map[c].push_back(and_exprt(g, not_exprt(c))); + // collect exits for assertion hoisting + if(it->second.is_root) + { + it->second.assertion_hoisting_map[n_it->location->get_target()] + .exit_conditions.push_back(and_exprt(g, not_exprt(c))); + } + } + } + } + // collect assertions for hoisting + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + for(loopt::assertion_hoisting_mapt::iterator + h_it=it->second.assertion_hoisting_map.begin(); + h_it!=it->second.assertion_hoisting_map.end(); ++h_it) + { + local_SSAt::nodest::const_iterator n_it=SSA.nodes.begin(); + // find jump target in nodes + while(n_it->location!=h_it->first && + n_it!=SSA.nodes.end()) ++n_it; + local_SSAt::nodest::const_iterator prev=n_it; + for(; n_it!=SSA.nodes.end(); ++n_it, ++prev) + { + // we collect only on top level, but not beyond loops + // so, if there is a gap in the nodes, we would jump over a loop + if(n_it!=prev && // this would fail in the first iteration otherwise + prev->location->location_number+1!= + n_it->location->location_number) + break; + if(n_it==prev) + --prev; + for(local_SSAt::nodet::assertionst::const_iterator a_it= + n_it->assertions.begin(); a_it!=n_it->assertions.end(); a_it++) + { + h_it->second.assertions.push_back(*a_it); + } + } + } + } +} + +/***************************************************************************** + * + * Function : ssa_local_unwindert::unwind_loop_at_location() + * + * Input : + * + * Output : + * + * Purpose : unwind the loop at the given location, up to k starting from + * previous unwindings + * + * + *****************************************************************************/ + +unsigned ssa_local_unwindert::unwind_loop_at_location(unsigned loc) +{ + unsigned k=loops.at(loc).current_unwinding+1; + unwind_loop_at_location(loc, k); + return k; +} + +void ssa_local_unwindert::unwind_loop_at_location(unsigned loc, unsigned k) +{ + if(SSA.current_unwinding>=(long)k) + return; + + loopt &loop=loops[loc]; + loop.loop_enabling_exprs.push_back( + symbol_exprt( + "unwind$"+id2string(fname)+"$loc"+i2string(loc)+"$enable"+i2string(k), + bool_typet())); + + // TODO: just for exploratory integration, must go away + SSA.current_unwinding=k; + + // recursively unwind everything + SSA.current_unwindings.clear(); + + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + + if(it->first==loc) + unwind(it->second, k, false, false); // recursive + else + unwind(it->second, it->second.current_unwinding, false, true, k, loc); + // recursive + + assert(SSA.current_unwindings.empty()); + } + + // update current unwinding + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(it->first==loc) + it->second.current_unwinding=k; + } + + compute_enable_expr(); + return; +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: + + Outputs: + + Purpose: unwind all loops up to k starting from previous unwindings + +\*******************************************************************/ + +void ssa_local_unwindert::unwind(unsigned k) +{ + if(SSA.current_unwinding>=(long)k) + return; + + current_enabling_expr= + symbol_exprt("unwind$"+id2string(fname)+"$enable"+i2string(k), + bool_typet()); + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + it->second.loop_enabling_exprs.push_back( + symbol_exprt( + "unwind$"+id2string(fname)+"$loc"+i2string(it->first)+ + "$enable"+i2string(k), + bool_typet())); + } + + // TODO: just for exploratory integration, must go away + SSA.current_unwinding=k; + + // recursively unwind everything + SSA.current_unwindings.clear(); + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + if(!it->second.is_root) + continue; + // unwind(it->second, k, false, false); // recursive + unwind(it->second, k, false, true, k, 0, true); // recursive + assert(SSA.current_unwindings.empty()); + } + // update current unwinding + for(loop_mapt::iterator it=loops.begin(); it!=loops.end(); ++it) + { + it->second.current_unwinding=k; + } + + compute_enable_expr(); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind + + Inputs: + + Outputs: + + Purpose: unwind all instances of given loop up to k + starting from previous unwindings, and recur + +\*******************************************************************/ + +void ssa_local_unwindert::unwind( + loopt &loop, + unsigned k, + bool is_new_parent, + bool propagate, + unsigned prop_unwind, + unsigned prop_loc, + bool propagate_all) +{ + odometert context=SSA.current_unwindings; +#ifdef DEBUG + std::cout << "unwind(k=" << k << ", is_new_parent=" << is_new_parent << "), "; + std::cout << "context=" << SSA.odometer_to_string(context, context.size()) + << std::endl; +#endif + SSA.increment_unwindings(1); + for(long i=0; i<=k; ++i) + { + // add new unwindings of this loop + if(i>loop.current_unwinding || is_new_parent) + { + add_loop_body(loop); + // set new loop end node + if(i==0) + { + assert(loop.end_nodes.find(context)==loop.end_nodes.end()); + loop.end_nodes[context]=--SSA.nodes.end(); + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); +#ifdef DEBUG + std::cout << "end node for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.end_nodes[context]->location->location_number << "==" + << loop.body_nodes.back().location->location_number + << std::endl; +#endif + } + if(i>0) + { + add_loop_connector(loop); + } + // in while loops we can only hoist in iterations %2 and higher + // otherwise we would block the loop exit that is only + // reachable via !guardls + bool is_last=(loop.is_dowhile && i==0) || + (!loop.is_dowhile && (i==0 || i==1)); + add_assertions(loop, is_last); + add_hoisted_assertions(loop, is_last); + } + if(i==k) + { + add_loop_head(loop); + // update loop head +#ifdef DEBUG + std::cout << "update loop head for context " + << SSA.odometer_to_string(context, context.size()) << ": " + << loop.body_nodes.begin()->location->location_number + << std::endl; +#endif + assert(loop.end_nodes.find(context)!=loop.end_nodes.end()); + loop.end_nodes[context]->loophead=--SSA.nodes.end(); + assert( + loop.end_nodes[context]->loophead->location->location_number== + loop.body_nodes.begin()->location->location_number); + } + // recurse into child loops + for(const auto &l : loop.loop_nodes) + { +#ifdef DEBUG + std::cout << i << ">" << loop.current_unwinding << std::endl; +#endif + if(propagate_all==true) + { + unwind( + loops[l], k, i>loop.current_unwinding || is_new_parent, false); + } + else + { + if(propagate==true) + { + // if this child loop is the desired loop then unwind k + // and do not propagate + // else unwind loop.current_unwinding and propagate + if(l==prop_loc) + { + unwind( + loops[l], k, i>loop.current_unwinding || is_new_parent, false); + } + else + { + unwind( + loops[l], + loops[l].current_unwinding, + i>loop.current_unwinding || is_new_parent, + true, + prop_unwind, + prop_loc); + } + } + else + { + unwind( + loops[l], + loops[l].current_unwinding, + i>loop.current_unwinding || is_new_parent, + false); + } + } + } + SSA.increment_unwindings(0); + } + SSA.increment_unwindings(-1); +#if 0 + std::cout << "calling add_exit_merge with k=" << k << "\n"; +#endif + add_exit_merges(loop, k); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_body + + Inputs: + + Outputs: + + Purpose: duplicates the loop body for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_body(loopt &loop) +{ + local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + ++it; // skip loop head, we'll do that separately + for(; it!=loop.body_nodes.end(); ++it) + { + if(it->equalities.empty() && + it->constraints.empty() && + it->function_calls.empty()) + continue; + +#ifdef DEBUG + std::cout << "add body node: " + << it->location->location_number << std::endl; +#endif + SSA.nodes.push_back(*it); // copy + local_SSAt::nodet &node=SSA.nodes.back(); + node.loophead=SSA.nodes.end(); + node.marked=false; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) + { + SSA.rename(*e_it, node.location); + } + for(local_SSAt::nodet::constraintst::iterator c_it= + node.constraints.begin(); c_it!=node.constraints.end(); c_it++) + { + SSA.rename(*c_it, node.location); + } + for(local_SSAt::nodet::function_callst::iterator f_it= + node.function_calls.begin(); + f_it!=node.function_calls.end(); f_it++) + { + SSA.rename(*f_it, node.location); + } + // will do assertions separately + node.assertions.clear(); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_assertions + + Inputs: + + Outputs: + + Purpose: adds the assertions and assumptions + +\*******************************************************************/ + +void ssa_local_unwindert::add_assertions(loopt &loop, bool is_last) +{ + for(local_SSAt::nodest::iterator it=loop.body_nodes.begin(); + it!=loop.body_nodes.end(); ++it) + { + if(it->assertions.empty()) + continue; + + SSA.nodes.push_back( + local_SSAt::nodet(it->location, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.assertions=it->assertions; + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::assertionst::iterator a_it= + node.assertions.begin(); a_it!=node.assertions.end(); a_it++) + { + SSA.rename(*a_it, node.location); + if(!is_last) // only add assumptions if we are not in %0 iteration + { + if(is_kinduction) + node.constraints.push_back(*a_it); + else if(is_bmc) + { + // only add in base case + exprt gs= + SSA.name( + SSA.guard_symbol(), + local_SSAt::LOOP_SELECT, + loop.body_nodes.back().location); + node.constraints.push_back(implies_exprt(not_exprt(gs), *a_it)); + } + } + } + if(!is_last && (is_bmc || is_kinduction)) + { + node.assertions.clear(); + } + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_head + + Inputs: + + Outputs: + + Purpose: adds the new loop head + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_head(loopt &loop) +{ + // new connecting loop head for the current instance + // (enabled for this iteration) + SSA.nodes.push_back(loop.body_nodes.front()); // copy loop head + local_SSAt::nodet &node=SSA.nodes.back(); + node.marked=false; + node.enabling_expr=current_enabling_expr; + for(local_SSAt::nodet::equalitiest::iterator e_it= + node.equalities.begin(); e_it!=node.equalities.end(); e_it++) + { + SSA.rename(*e_it, node.location); + } + assert(node.constraints.empty()); + assert(node.function_calls.empty()); + assert(node.assertions.empty()); +#ifdef DEBUG + std::cout << "add loop head: " << node.location->location_number << std::endl; +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_loop_connector + + Inputs: + + Outputs: + + Purpose: adds a connector to the previous iteration + +\*******************************************************************/ + +void ssa_local_unwindert::add_loop_connector(loopt &loop) +{ + // connector to previous iteration (permanently added) + const local_SSAt::nodet &orig_node=loop.body_nodes.front(); + SSA.nodes.push_back( + local_SSAt::nodet(orig_node.location, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + SSA.current_location=node.location; // TODO: must go away + for(local_SSAt::nodet::equalitiest::const_iterator e_it= + orig_node.equalities.begin(); + e_it!=orig_node.equalities.end(); e_it++) + { + if(e_it->rhs().id()==ID_if) + { + node.equalities.push_back(*e_it); + node.equalities.back().rhs()= + loop.pre_post_map[to_symbol_expr(e_it->lhs())]; + SSA.rename(node.equalities.back().rhs(), node.location); + SSA.decrement_unwindings(0); + SSA.rename(node.equalities.back().lhs(), node.location); + SSA.increment_unwindings(0); + } + else if(e_it->lhs().id()==ID_symbol && + !has_prefix(id2string(to_symbol_expr(e_it->lhs()).get_identifier()), + "ssa::$guard")) + { // this is needed for while loops + node.equalities.push_back(*e_it); + SSA.decrement_unwindings(0); + SSA.rename(node.equalities.back(), node.location); + SSA.increment_unwindings(0); + } + } + // continuation guard and condition + exprt g_rhs=and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); + SSA.decrement_unwindings(0); + exprt g_lhs=SSA.guard_symbol(loop.body_nodes.begin()->location); + SSA.increment_unwindings(0); + node.equalities.push_back(equal_exprt(g_lhs, g_rhs)); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_exit_merges + + Inputs: + + Outputs: + + Purpose: adds the merge connector for the loop exits for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_exit_merges(loopt &loop, unsigned k) +{ + SSA.nodes.push_back(local_SSAt::nodet(loop.body_nodes.begin()->location, + SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.enabling_expr=current_enabling_expr; + + for(loopt::exit_mapt::const_iterator x_it=loop.exit_map.begin(); + x_it!=loop.exit_map.end(); x_it++) + { + node.equalities.push_back( + build_exit_merge( + x_it->first, disjunction(x_it->second), k, node.location)); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::build_exit_merge + + Inputs: + + Outputs: + + Purpose: generates exit merge expression for a given expression + +\*******************************************************************/ + +equal_exprt ssa_local_unwindert::build_exit_merge( + exprt e, const exprt &exits, unsigned k, locationt loc) +{ + exprt re=e; + SSA.increment_unwindings(1); + SSA.rename(re, loc); // %0 + for(long i=1; i<=k; i++) + { + SSA.increment_unwindings(0); + exprt cond_expr=exits; + SSA.rename(cond_expr, loc); + exprt true_expr=e; + SSA.rename(true_expr, loc); + exprt false_expr=re; + re=if_exprt(cond_expr, true_expr, false_expr); + } + SSA.increment_unwindings(-1); + SSA.rename(e, loc); // lhs + return equal_exprt(e, re); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::add_hoisted_assertions + + Inputs: + + Outputs: + + Purpose: adds the assumptions for hoisted assertions + for the current instance + +\*******************************************************************/ + +void ssa_local_unwindert::add_hoisted_assertions(loopt &loop, bool is_last) +{ + for(loopt::assertion_hoisting_mapt::const_iterator + it=loop.assertion_hoisting_map.begin(); + it!=loop.assertion_hoisting_map.end(); ++it) + { + if(!is_last // only add assumptions if we are not in %0 iteration + && is_kinduction && !it->second.assertions.empty()) + { + exprt e=disjunction(it->second.exit_conditions); + SSA.rename(e, loop.body_nodes.begin()->location); + SSA.nodes.push_back( + local_SSAt::nodet(it->first, SSA.nodes.end())); // add new node + local_SSAt::nodet &node=SSA.nodes.back(); + node.constraints.push_back( + implies_exprt(e, conjunction(it->second.assertions))); +#ifdef DEBUG + std::cout << "adding hoisted assumption: " + << from_expr(SSA.ns, "", node.constraints.back()) + << std::endl; +#endif + } + } +} + +/*****************************************************************************\ + * + * Function : ssa_local_unwindert::compute_enable_expr + * + * Input : + * + * Output : + * + * Purpose : updates the enable_expr + * + *****************************************************************************/ + +void ssa_local_unwindert::compute_enable_expr() +{ + SSA.enabling_exprs.clear(); + for(loop_mapt::const_iterator it=loops.begin(); it!=loops.end(); ++it) + { + for(exprt::operandst::const_iterator + e_it=((it->second).loop_enabling_exprs).begin(); + e_it!=((it->second).loop_enabling_exprs).end(); e_it++) + { + exprt::operandst::const_iterator lh=e_it; lh++; + if(lh!=((it->second).loop_enabling_exprs).end()) + SSA.enabling_exprs.push_back(not_exprt(*e_it)); + else + SSA.enabling_exprs.push_back(*e_it); + } + } +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: return loop continuation conditions for all loops in this function + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + exprt::operandst& loop_cont) const +{ + SSA.current_unwindings.clear(); + for(const auto &l : loops) + { + if(l.second.is_root) + continue; + loop_continuation_conditions(l.second, loop_cont); // recursive + assert(SSA.current_unwindings.empty()); + } +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: return loop continuation conditions for all instances + of the given loop in this function + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + const locationt& loop_id, + exprt::operandst &loop_cont) const +{ + if(loops.empty()) // ignore silently, TBD + return; + + const loopt &loop=loops.at(loop_id->location_number); + loop_cont.insert(loop_cont.end(), + loop.current_continuation_conditions.begin(), + loop.current_continuation_conditions.end()); +} +/*******************************************************************\ + +Function: ssa_local_unwindert::get_continuation_condition + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +exprt ssa_local_unwindert::get_continuation_condition(const loopt& loop) const +{ + SSA.current_location=loop.body_nodes.back().location; // TODO: must go away + return and_exprt(SSA.guard_symbol(loop.body_nodes.back().location), + SSA.cond_symbol(loop.body_nodes.back().location)); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::loop_continuation_conditions + + Inputs: + + Outputs: + + Purpose: recursively construct loop continuation conditions + +\*******************************************************************/ + +void ssa_local_unwindert::loop_continuation_conditions( + const loopt& loop, exprt::operandst& loop_cont) const +{ + SSA.increment_unwindings(1); + loop_cont.push_back(get_continuation_condition(loop)); // %0 + for(long i=0; i<=loop.current_unwinding; ++i) + { + // recur into child loops + for(const auto &l : loop.loop_nodes) + { + loop_continuation_conditions(loops.at(l), loop_cont); + } + SSA.increment_unwindings(0); + } + SSA.increment_unwindings(-1); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwinder_rename + + Inputs: var, node + + Outputs: var is returned with a suffix that reflects the current unwinding + with the context taken from the node + + Purpose: E.g. node must look like + cond"somesuffix"==TRUE, e.g. cond%1%2%5%0==TRUE + and variable might be guard#ls25 + if the current_unwinding is 6 + the variable should be converted to guard#ls25%1%2%5%5 + + Note that if current_unwinding is X then suffixes can have at most + X-1 in its parts + +\*******************************************************************/ + +void ssa_local_unwindert::unwinder_rename( + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const +{ + // TODO: replace this by SSA.rename + // this requires odometer information in the nodes + + // only to be called for backedge nodes + // This is very dirty hack + if(SSA.current_unwinding<0) + return; + + assert(node.equalities.size()>=1); + // copy suffix from equality lhs to var + std::string id= + id2string(to_symbol_expr(node.equalities[0].op0()).get_identifier()); + size_t pos=id.find_first_of("%"); + if(pos==std::string::npos) + return; + size_t pos1=id.find_last_of("%"); + std::string suffix; + unsigned unwinding=pre ? SSA.current_unwinding : 0; + if(pos==pos1) + { + suffix="%"+i2string(unwinding); + } + else + { + suffix=id.substr(pos, pos1-pos); + suffix+="%"+i2string(unwinding); + } + + var.set_identifier(id2string(var.get_identifier())+suffix); +#ifdef DEBUG + std::cout << "new id: " << var.get_identifier() << std::endl; +#endif +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind_loop_alone + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::unwind_loop_alone( + const irep_idt fname, + unsigned loc, + unsigned int k) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + it->second.unwind_loop_at_location(loc, k); +} + +/*******************************************************************\ + +Function: ssa_local_unwindert::unwind_loop_once_more + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +unsigned ssa_unwindert::unwind_loop_once_more( + const irep_idt fname, + unsigned loc) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + return it->second.unwind_loop_at_location(loc); +} + + +/*******************************************************************\ + +Function: ssa_local_unwindert::find_loop + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +bool ssa_local_unwindert::find_loop( + unsigned location_number, const loopt *&loop) const +{ + loop_mapt::const_iterator it=loops.find(location_number); + if(it!=loops.end()) + { + loop=&it->second; + return true; + } + return false; +} + +/*******************************************************************\ + +Function: ssa_unwindert::unwind + + Inputs: fname-name of the goto-function to be unwound, k-unwinding depth + + Outputs: false-if id does not correspond to any goto-function in the + unwinder_map + + Purpose: incrementally unwind a function 'id' up to depth k. Initializer + must have been invoked before calling this function + +\*******************************************************************/ + +void ssa_unwindert::unwind(const irep_idt fname, unsigned int k) +{ + assert(is_initialized); + unwinder_mapt::iterator it=unwinder_map.find(fname); + assert(it!=unwinder_map.end()); + it->second.unwind(k); +} + +/*******************************************************************\ + +Function: ssa_unwindert::unwind_all + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::unwind_all(unsigned int k) +{ + assert(is_initialized); + + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.unwind(k); +} + +/*******************************************************************\ + +Function: ssa_unwindert::init + + Inputs: + + Outputs: + + Purpose: Initialize unwinder_map by computing hierarchical tree_loopnodet + for every goto-function + Set is_initialized to true. Initializer must be called before + unwind funcitions are called. + +\*******************************************************************/ + +void ssa_unwindert::init(bool is_kinduction, bool is_bmc) +{ + ssa_dbt::functionst& funcs=ssa_db.functions(); + for(auto &f : funcs) + { + unwinder_map.insert( + unwinder_pairt( + f.first, + ssa_local_unwindert(f.first, (*(f.second)), is_kinduction, is_bmc))); + } +} + +/*******************************************************************\ + +Function: ssa_unwindert::init_localunwinders + + Inputs: + + Outputs: + + Purpose: + +\*******************************************************************/ + +void ssa_unwindert::init_localunwinders() +{ + for(auto &local_unwinder : unwinder_map) + local_unwinder.second.init(); + is_initialized=true; +} diff --git a/src/ssa/ssa_unwinder.h b/src/ssa/ssa_unwinder.h index 551bf1bf5..57a02c007 100644 --- a/src/ssa/ssa_unwinder.h +++ b/src/ssa/ssa_unwinder.h @@ -1,165 +1,182 @@ -/*******************************************************************\ - -Module: SSA Unwinder - -Author: Peter Schrammel, Saurabh Joshi - -\*******************************************************************/ - -#ifndef CPROVER_2LS_SSA_SSA_UNWINDER_H -#define CPROVER_2LS_SSA_SSA_UNWINDER_H - -#include "unwindable_local_ssa.h" -#include "ssa_db.h" - -class ssa_local_unwindert -{ -public: - typedef local_SSAt::locationt locationt; - typedef unwindable_local_SSAt::odometert odometert; - - ssa_local_unwindert( - const irep_idt _fname, - unwindable_local_SSAt& _SSA, - bool _is_kinduction, - bool _is_bmc): - fname(_fname), - SSA(_SSA), - is_kinduction(_is_kinduction), - is_bmc(_is_bmc) - { - } - - void init(); - - void unwind(unsigned k); - -#if 0 - // TODO: not yet sure how to do that - void unwind(locationt loop_head_loc, unsigned k) - { - unwind(loops[loop_head_loc], k); - } -#endif - - void compute_loop_continuation_conditions(); - - // TODO: this should be loop specific in future, - // maybe move to unwindable_local_ssa as it is not really unwinder related - void loop_continuation_conditions(exprt::operandst& loop_cont) const; - -#if 0 - // TODO: these two should be possible with unwindable_local_ssa facilities - exprt rename_invariant(const exprt& inv_in) const; - void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; -#endif - - // TODO: this must go away, should use SSA.rename instead - void unwinder_rename( - symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; - - class loopt // loop tree - { - public: - loopt(): - is_dowhile(false), - is_root(false), - current_unwinding(-1) - { - } - - local_SSAt::nodest body_nodes; - // pointer to loop end nodes in SSA for updating current loop head - std::map end_nodes; - std::vector loop_nodes; // child loops - bool is_dowhile; - bool is_root; - long current_unwinding; - - exprt::operandst current_continuation_conditions; - - typedef std::map exit_mapt; - exit_mapt exit_map; - std::map pre_post_map; - std::vector modified_vars; - - // for assertion hoisting - typedef struct - { - exprt::operandst exit_conditions; - exprt::operandst assertions; - } assertions_after_loopt; - typedef std::map assertion_hoisting_mapt; - assertion_hoisting_mapt assertion_hoisting_map; - }; - - bool find_loop(unsigned location_number, const loopt *&loop) const; - -protected: - const irep_idt fname; - unwindable_local_SSAt &SSA; - bool is_kinduction, is_bmc; - symbol_exprt current_enabling_expr; // TODO must become loop-specific - - // use location numbers as indices, as target addresses make - // things non-deterministic - typedef std::map loop_mapt; - loop_mapt loops; - - void build_loop_tree(); - void build_pre_post_map(); - void build_exit_conditions(); - - void unwind(loopt &loop, unsigned k, bool is_new_parent); - - exprt get_continuation_condition(const loopt& loop) const; - void compute_loop_continuation_conditions(loopt& loop); - void loop_continuation_conditions( - const loopt& loop, exprt::operandst &loop_cont) const; - - void add_loop_body(loopt &loop); - void add_assertions(loopt &loop, bool is_last); - void add_loop_head(loopt &loop); - void add_loop_connector(loopt &loop); - void add_exit_merges(loopt &loop, unsigned k); - equal_exprt build_exit_merge( - exprt e, const exprt &exits, unsigned k, locationt loc); - void add_hoisted_assertions(loopt &loop, bool is_last); -}; - -class ssa_unwindert:public messaget -{ -public: - typedef std::map unwinder_mapt; - typedef std::pair unwinder_pairt; - - explicit ssa_unwindert(ssa_dbt& _db): - ssa_db(_db), - is_initialized(false) - { - } - - void init(bool is_kinduction, bool is_bmc); - void init_localunwinders(); - - void unwind(const irep_idt fname, unsigned k); - void unwind_all(unsigned k); - - inline ssa_local_unwindert &get(const irep_idt& fname) - { - return unwinder_map.at(fname); - } - - inline const ssa_local_unwindert &get(const irep_idt& fname) const - { - return unwinder_map.at(fname); - } - -protected: - ssa_dbt &ssa_db; - bool is_initialized; - unwinder_mapt unwinder_map; -}; - -#endif +/*******************************************************************\ + +Module: SSA Unwinder + +Author: Peter Schrammel, Saurabh Joshi + +\*******************************************************************/ + +#ifndef CPROVER_2LS_SSA_SSA_UNWINDER_H +#define CPROVER_2LS_SSA_SSA_UNWINDER_H + +#include "unwindable_local_ssa.h" +#include "ssa_db.h" + +class ssa_local_unwindert +{ +public: + typedef local_SSAt::locationt locationt; + typedef unwindable_local_SSAt::odometert odometert; + + ssa_local_unwindert( + const irep_idt _fname, + unwindable_local_SSAt& _SSA, + bool _is_kinduction, + bool _is_bmc): + fname(_fname), + SSA(_SSA), + is_kinduction(_is_kinduction), + is_bmc(_is_bmc) + { + } + + void init(); + + void unwind_loop_at_location(unsigned loc, unsigned k); + unsigned unwind_loop_at_location(unsigned loc); + void unwind(unsigned k); + +#if 0 + // TODO: not yet sure how to do that + void unwind(locationt loop_head_loc, unsigned k) + { + unwind(loops[loop_head_loc], k); + } +#endif + + // TODO: this should be loop specific in future, + // maybe move to unwindable_local_ssa as it is not really unwinder related + void compute_enable_expr(); + void loop_continuation_conditions(exprt::operandst &loop_cont) const; + void loop_continuation_conditions( + const locationt& loop_id, + exprt::operandst &loop_cont) const; + +#if 0 + // TODO: these two should be possible with unwindable_local_ssa facilities + exprt rename_invariant(const exprt& inv_in) const; + void unwinder_rename( + symbol_exprt &var, const local_SSAt::nodet &node, bool pre) const; +#endif + + // TODO: this must go away, should use SSA.rename instead + void unwinder_rename( + symbol_exprt &var, + const local_SSAt::nodet &node, + bool pre) const; + + class loopt // loop tree + { + public: + loopt(): + is_dowhile(false), + is_root(false), + current_unwinding(-1) + { + } + + local_SSAt::nodest body_nodes; + // pointer to loop end nodes in SSA for updating current loop head + std::map end_nodes; + std::vector loop_nodes; // child loops + bool is_dowhile; + bool is_root; + long current_unwinding; + + // to have an enabling_expr and current_unwindings (odometert) + exprt::operandst loop_enabling_exprs; + + exprt::operandst current_continuation_conditions; + + typedef std::map exit_mapt; + exit_mapt exit_map; + std::map pre_post_map; + std::vector modified_vars; + + // for assertion hoisting + typedef struct + { + exprt::operandst exit_conditions; + exprt::operandst assertions; + } assertions_after_loopt; + typedef std::map assertion_hoisting_mapt; + assertion_hoisting_mapt assertion_hoisting_map; + }; + + bool find_loop(unsigned location_number, const loopt *&loop) const; + +protected: + const irep_idt fname; + unwindable_local_SSAt &SSA; + bool is_kinduction, is_bmc; + symbol_exprt current_enabling_expr; // TODO must become loop-specific + + // use location numbers as indices, as target addresses make + // things non-deterministic + typedef std::map loop_mapt; + loop_mapt loops; + + void build_loop_tree(); + void build_pre_post_map(); + void build_exit_conditions(); + + void unwind( + loopt &loop, + unsigned k, + bool is_new_parent, + bool propagate=false, + unsigned prop_unwind=0, + unsigned prop_loc=0, + bool propagate_all=false); + + exprt get_continuation_condition(const loopt& loop) const; + void loop_continuation_conditions( + const loopt& loop, exprt::operandst &loop_cont) const; + + void add_loop_body(loopt &loop); + void add_assertions(loopt &loop, bool is_last); + void add_loop_head(loopt &loop); + void add_loop_connector(loopt &loop); + void add_exit_merges(loopt &loop, unsigned k); + equal_exprt build_exit_merge( + exprt e, const exprt &exits, unsigned k, locationt loc); + void add_hoisted_assertions(loopt &loop, bool is_last); +}; + +class ssa_unwindert:public messaget +{ +public: + typedef std::map unwinder_mapt; + typedef std::pair unwinder_pairt; + + explicit ssa_unwindert(ssa_dbt& _db): + ssa_db(_db), + is_initialized(false) + { + } + + void init(bool is_kinduction, bool is_bmc); + void init_localunwinders(); + + void unwind_loop_alone(const irep_idt fname, unsigned loc, unsigned k); + unsigned unwind_loop_once_more(const irep_idt fname, unsigned loc); + void unwind(const irep_idt fname, unsigned k); + void unwind_all(unsigned k); + + ssa_local_unwindert &get(const irep_idt& fname) + { + return unwinder_map.at(fname); + } + + const ssa_local_unwindert &get(const irep_idt& fname) const + { + return unwinder_map.at(fname); + } + +protected: + ssa_dbt &ssa_db; + bool is_initialized; + unwinder_mapt unwinder_map; +}; + +#endif // CPROVER_2LS_SSA_SSA_UNWINDER_H diff --git a/src/ssa/unwindable_local_ssa.cpp b/src/ssa/unwindable_local_ssa.cpp index 8b49f8e76..4dc6d1c99 100644 --- a/src/ssa/unwindable_local_ssa.cpp +++ b/src/ssa/unwindable_local_ssa.cpp @@ -279,6 +279,12 @@ Function: unwindable_local_SSAt::rename void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { + if(expr.id()==ID_function_application) + { + std::string unwind_suffix= + odometer_to_string(current_unwindings, current_unwindings.size()); + expr.set(ID_suffix, unwind_suffix); + } if(expr.id()==ID_symbol) { symbol_exprt &s=to_symbol_expr(expr); @@ -292,7 +298,7 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) std::string unwind_suffix= odometer_to_string(current_unwindings, def_level); s.set_identifier(id2string(id)+unwind_suffix); - + s.set(ID_suffix, unwind_suffix); #if 0 std::cout << "DEF_LOC: " << def_loc->location_number << std::endl; std::cout << "DEF_LEVEL: " << def_level << std::endl; @@ -307,13 +313,9 @@ void unwindable_local_SSAt::rename(exprt &expr, locationt current_loc) { std::string unwind_suffix= odometer_to_string(current_unwindings, current_unwindings.size()); - std::string identifier=id2string(expr.get(ID_identifier)); - std::size_t pos=identifier.find("%"); - if(pos!=std::string::npos) - identifier=identifier.substr(0, pos); - expr.set( - ID_identifier, - identifier+unwind_suffix+suffix); + expr.set(ID_suffix, unwind_suffix); + expr.set(ID_identifier, + id2string(expr.get(ID_identifier))+unwind_suffix+suffix); } Forall_operands(it, expr) rename(*it, current_loc); diff --git a/src/summarizer/Makefile b/src/summarizer/Makefile deleted file mode 100644 index bcc3b454b..000000000 --- a/src/summarizer/Makefile +++ /dev/null @@ -1,120 +0,0 @@ -include ../config.inc - -SRC = summarizer_base.cpp \ - summarizer_fw.cpp summarizer_bw.cpp \ - summarizer_bw_cex.cpp summarizer_bw_cex_complete.cpp \ - summarizer_bw_cex_concrete.cpp \ - summarizer_bw_cex_wp.cpp \ - summarizer_bw_cex_ai.cpp summarizer_bw_cex_all.cpp \ - summarizer_fw_contexts.cpp \ - summarizer_main.cpp summarizer_parse_options.cpp \ - show.cpp summary_checker_base.cpp \ - summary_checker_ai.cpp summary_checker_bmc.cpp \ - summary_checker_kind.cpp \ - cover_goals_ext.cpp \ - summary_db.cpp summary.cpp ssa_db.cpp \ - array_abstraction.cpp preprocessing_util.cpp \ - instrument_goto.cpp function_signature.cpp - -OBJ+= $(CBMC)/src/ansi-c/ansi-c$(LIBEXT) \ - $(CBMC)/src/linking/linking$(LIBEXT) \ - $(CBMC)/src/assembler/assembler$(LIBEXT) \ - $(CBMC)/src/big-int/big-int$(LIBEXT) \ - $(CBMC)/src/goto-programs/goto-programs$(LIBEXT) \ - $(CBMC)/src/goto-symex/goto-symex$(LIBEXT) \ - $(CBMC)/src/analyses/analyses$(LIBEXT) \ - $(CBMC)/src/pointer-analysis/pointer-analysis$(LIBEXT) \ - $(CBMC)/src/langapi/langapi$(LIBEXT) \ - $(CBMC)/src/xmllang/xmllang$(LIBEXT) \ - $(CBMC)/src/json/json$(LIBEXT) \ - $(CBMC)/src/solvers/solvers$(LIBEXT) \ - $(CBMC)/src/util/util$(LIBEXT) \ - - -DELTA_OBJ+=\ - $(CBMC)/src/goto-instrument/unwind$(OBJEXT) \ - ../ssa/ssa_slicer$(OBJEXT) \ - ../ssa/malloc_ssa$(OBJEXT) \ - ../ssa/ssa_domain$(OBJEXT) \ - ../ssa/assignments$(OBJEXT) \ - ../ssa/guard_map$(OBJEXT) \ - ../ssa/ssa_object$(OBJEXT) \ - ../ssa/address_canonizer$(OBJEXT) \ - ../ssa/ssa_dereference$(OBJEXT) \ - ../ssa/simplify_ssa$(OBJEXT) \ - ../ssa/ssa_build_goto_trace$(OBJEXT) \ - ../ssa/split_loopheads$(OBJEXT)\ - ../ssa/ssa_inliner$(OBJEXT)\ - ../ssa/ssa_unwinder$(OBJEXT)\ - ../ssa/ssa_value_set$(OBJEXT) \ - ../ssa/const_propagator$(OBJEXT) \ - ../ssa/replace_symbol_ext$(OBJEXT) \ - ../ssa/ssa_const_propagator$(OBJEXT) \ - ../ssa/ssa_dependency_graph$(OBJEXT) \ - ../functions/summary$(OBJEXT) \ - ../functions/get_function$(OBJEXT) \ - ../functions/path_util$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../functions/index$(OBJEXT) \ - ../domains/fixed_point$(OBJEXT) \ - ../domains/ssa_fixed_point$(OBJEXT) \ - ../domains/tpolyhedra_domain$(OBJEXT) \ - ../domains/predabs_domain$(OBJEXT) \ - ../domains/equality_domain$(OBJEXT) \ - ../domains/domain$(OBJEXT) \ - ../domains/util$(OBJEXT) \ - ../domains/incremental_solver$(OBJEXT) \ - ../domains/strategy_solver_base$(OBJEXT) \ - ../domains/strategy_solver_predabs$(OBJEXT) \ - ../domains/strategy_solver_equality$(OBJEXT) \ - ../domains/strategy_solver_enumeration$(OBJEXT) \ - ../domains/strategy_solver_binsearch$(OBJEXT) \ - ../domains/strategy_solver_binsearch2$(OBJEXT) \ - ../domains/strategy_solver_binsearch3$(OBJEXT) \ - ../domains/template_generator_base$(OBJEXT) \ - ../domains/template_generator_summary$(OBJEXT) \ - ../domains/template_generator_callingcontext$(OBJEXT) \ - ../domains/ssa_analyzer$(OBJEXT) \ - ../domains/disjunctive_analyzer$(OBJEXT) \ - ../domains/predicate$(OBJEXT) -# ../domains/solver_enumeration$(OBJEXT) - - -OBJ+=$(DELTA_OBJ) - -include $(CBMC)/src/config.inc -include $(CBMC)/src/common - -CP_CXXFLAGS+= $(SUMMARIZERFLAGS) - -INCLUDES= -I $(CBMC)/src -# \ -# -I $(CUDD)/cudd -I $(CUDD)/obj -I $(CUDD)/mtr -I $(CUDD)/epd - -LIBS = -#LIBS = $(CUDD)/cudd/libcudd.a $(CUDD)/mtr/libmtr.a \ -# $(CUDD)/st/libst.a $(CUDD)/epd/libepd.a $(CUDD)/util/libutil.a - -#CP_CXXFLAGS +=-g -DSHOW_CALLING_CONTEXTS - -# $(CUDD)/obj/libobj.a - -CLEANFILES = summarizer$(EXEEXT) $(DELTA_OBJ) - -all: summarizer$(EXEEXT) - -ifneq ($(wildcard $(CBMC)/src/cpp/Makefile),) - OBJ += $(CBMC)/src/cpp/cpp$(LIBEXT) - CP_CXXFLAGS += -DHAVE_CPP -endif - -ifneq ($(wildcard $(CBMC)/src/java/Makefile),) - OBJ += $(CBMC)/src/java/java$(LIBEXT) - CXXFLAGS += -DHAVE_JAVA -endif - -############################################################################### - -summarizer$(EXEEXT): $(OBJ) - $(LINKBIN) - diff --git a/src/summarizer/array_abstraction.cpp b/src/summarizer/array_abstraction.cpp deleted file mode 100644 index 438c59ac2..000000000 --- a/src/summarizer/array_abstraction.cpp +++ /dev/null @@ -1,2053 +0,0 @@ -/*******************************************************************\ - -Module: String Abstraction - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include "array_abstraction.h" - -#define DEBUG - -#ifdef DEBUG -#include -#endif - - - - -/*******************************************************************\ - -Function: array_abstractiont::build_wrap - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_wrap(const exprt &object, exprt &dest, bool write) -{ - // debugging - return build(object, dest, write); -} - -/*******************************************************************\ - -Function: array_abstractiont::is_ptr_string_struct - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::is_ptr_string_struct(const typet &type) const -{ - return type.id()==ID_pointer && - type_eq(type.subtype(), string_struct, ns); -} - -static inline bool is_ptr_argument(const typet &type) -{ - return type.id()==ID_pointer; -} - -/*******************************************************************\ - -Function: array_abstraction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_programt &dest) -{ - array_abstractiont array_abstraction(symbol_table, message_handler); - array_abstraction(dest); -} - -/*******************************************************************\ - -Function: array_abstraction - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_functionst &dest) -{ - array_abstractiont array_abstraction(symbol_table, message_handler); - array_abstraction(dest); -} - -/*******************************************************************\ - -Function: array_abstractiont::array_abstractiont - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -array_abstractiont::array_abstractiont( - symbol_tablet &_symbol_table, - message_handlert &_message_handler): - message_streamt(_message_handler), - arg_suffix("#strarg"), - sym_suffix("#str$fcn"), - symbol_table(_symbol_table), - ns(_symbol_table), - temporary_counter(0) -{ - struct_typet s; - - s.components().resize(3); - - s.components()[0].set_name("is_zero"); - s.components()[0].set_pretty_name("is_zero"); - s.components()[0].type()=build_type(IS_ZERO); - - s.components()[1].set_name("length"); - s.components()[1].set_pretty_name("length"); - s.components()[1].type()=build_type(LENGTH); - - s.components()[2].set_name("size"); - s.components()[2].set_pretty_name("size"); - s.components()[2].type()=build_type(SIZE); - - string_struct=s; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_type - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -typet array_abstractiont::build_type(whatt what) -{ - typet type; - - switch(what) - { - case IS_ZERO: type=bool_typet(); break; - case LENGTH: type=size_type(); break; - case SIZE: type=size_type(); break; - } - - return type; -} - -/*******************************************************************\ - -Function: array_abstractiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::operator()(goto_functionst &dest) -{ - Forall_goto_functions(it, dest) - { - // don't instrument our internal functions - if(has_prefix(id2string(it->first), CPROVER_PREFIX)) - continue; - - sym_suffix="#str$"+id2string(it->first); - add_str_arguments(it->first, it->second); - abstract(it->second.body); - current_args.clear(); - } - - // do we have a main? - goto_functionst::function_mapt::iterator - m_it=dest.function_map.find(dest.entry_point()); - - if(m_it!=dest.function_map.end()) - { - goto_programt &main=m_it->second.body; - - // do initialization - initialization.destructive_append(main); - main.swap(initialization); - initialization.clear(); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::operator() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::operator()(goto_programt &dest) -{ - abstract(dest); - - // do initialization - initialization.destructive_append(dest); - dest.swap(initialization); - initialization.clear(); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_str_arguments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::add_str_arguments( - const irep_idt & name, - goto_functionst::goto_functiont &fct) -{ - symbol_tablet::symbolst::iterator sym_entry=symbol_table.symbols.find(name); - assert(sym_entry!=symbol_table.symbols.end()); - symbolt &fct_symbol=sym_entry->second; - - code_typet::parameterst ¶meters= - to_code_type(fct.type).parameters(); - code_typet::parameterst str_args; - - for(code_typet::parameterst::iterator - it=parameters.begin(); - it!=parameters.end(); - ++it) - { - const typet &abstract_type=build_abstraction_type(it->type()); - if(abstract_type.is_nil()) - continue; - - const irep_idt &identifier=it->get_identifier(); - if(identifier=="") continue; // ignore - - add_argument(str_args, fct_symbol, abstract_type, - id2string(it->get_base_name())+arg_suffix, - id2string(identifier)+arg_suffix); - - current_args.insert(identifier); - } - - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct.type).return_type()); - if(!abstract_ret_type.is_nil()) - { - add_argument(str_args, fct_symbol, abstract_ret_type, - "$return_value_str_abst"+arg_suffix, - abstract_ret_val_name(fct_symbol)); - } - - parameters.insert(parameters.end(), str_args.begin(), str_args.end()); - code_typet::parameterst &symb_parameters= - to_code_type(fct_symbol.type).parameters(); - symb_parameters.insert(symb_parameters.end(), str_args.begin(), str_args.end()); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_argument - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::add_argument( - code_typet::parameterst &str_args, - const symbolt &fct_symbol, - const typet &type, - const irep_idt &base_name, - const irep_idt &identifier) -{ - str_args.push_back(code_typet::parametert(type)); - str_args.back().add_source_location()=fct_symbol.location; - str_args.back().set_base_name(base_name); - str_args.back().set_identifier(identifier); - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=str_args.back().source_location(); - new_symbol.name=str_args.back().get_identifier(); - new_symbol.module=fct_symbol.module; - new_symbol.base_name=str_args.back().get_base_name(); - new_symbol.mode=fct_symbol.mode; - new_symbol.pretty_name=str_args.back().get_base_name(); - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::abstract(goto_programt &dest) -{ - locals.clear(); - - Forall_goto_program_instructions(it, dest) - it=abstract(dest, it); - - if(locals.empty()) return; - - // go over it again for the newly added locals - declare_define_locals(dest); - locals.clear(); -} - -/*******************************************************************\ - -Function: array_abstractiont::declare_define_locals - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::declare_define_locals(goto_programt &dest) -{ - typedef hash_map_cont - available_declst; - available_declst available_decls; - - Forall_goto_program_instructions(it, dest) - if(it->is_decl()) - { - // same name may exist several times due to inlining, make sure the first - // declaration is used - available_decls.insert(std::make_pair( - to_code_decl(it->code).get_identifier(), it)); - } - - // declare (and, if necessary, define) locals - for(localst::const_iterator l_it=locals.begin(); - l_it!=locals.end(); - ++l_it) - { - goto_programt::targett ref_instr=dest.instructions.begin(); - bool has_decl=false; - - available_declst::const_iterator entry=available_decls.find(l_it->first); - - if(available_declst::const_iterator(available_decls.end())!=entry) - { - ref_instr=entry->second; - has_decl=true; - } - - goto_programt tmp; - make_decl_and_def(tmp, ref_instr, l_it->second, l_it->first); - - if(has_decl) ++ref_instr; - dest.insert_before_swap(ref_instr, tmp); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::make_decl_and_def - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::make_decl_and_def(goto_programt &dest, - goto_programt::targett ref_instr, - const irep_idt &identifier, - const irep_idt &source_sym) -{ - const symbolt &symbol=ns.lookup(identifier); - symbol_exprt sym_expr=symbol.symbol_expr(); - - goto_programt::targett decl1=dest.add_instruction(); - decl1->make_decl(); - decl1->source_location=ref_instr->source_location; - decl1->function=ref_instr->function; - decl1->code=code_declt(sym_expr); - decl1->code.add_source_location()=ref_instr->source_location; - - exprt val=symbol.value; - // initialize pointers with suitable objects - if(val.is_nil()) - { - const symbolt &orig=ns.lookup(source_sym); - val=make_val_or_dummy_rec(dest, ref_instr, symbol, ns.follow(orig.type)); - } - - // may still be nil (structs, then assignments have been done already) - if(val.is_not_nil()) - { - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(sym_expr, val); - assignment1->code.add_source_location()=ref_instr->source_location; - } -} - -/*******************************************************************\ - -Function: array_abstractiont::make_val_or_dummy_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::make_val_or_dummy_rec(goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, const typet &source_type) -{ - const typet &eff_type=ns.follow(symbol.type); - - if(type_eq(eff_type, string_struct, ns)) - { - symbol_exprt sym_expr=add_dummy_symbol_and_value( - dest, ref_instr, symbol, irep_idt(), - eff_type, source_type); - - return sym_expr; - } - else if(eff_type.id()==ID_union || - (eff_type.id()==ID_struct && !type_eq(eff_type, string_struct, ns))) - { - const struct_union_typet &su_source=to_struct_union_type(source_type); - const struct_union_typet::componentst &s_components= - su_source.components(); - const struct_union_typet &struct_union_type=to_struct_union_type(eff_type); - const struct_union_typet::componentst &components= - struct_union_type.components(); - unsigned seen=0; - - struct_union_typet::componentst::const_iterator it2=components.begin(); - for(struct_union_typet::componentst::const_iterator - it=s_components.begin(); - it!=s_components.end() && it2!=components.end(); - ++it) - { - if(it->get_name()!=it2->get_name()) - continue; - - const typet &eff_sub_type=ns.follow(it2->type()); - if(eff_sub_type.id() == ID_pointer || - eff_sub_type.id() == ID_array || - eff_sub_type.id() == ID_struct || - eff_sub_type.id() == ID_union) - { - symbol_exprt sym_expr=add_dummy_symbol_and_value( - dest, ref_instr, symbol, it2->get_name(), - it2->type(), ns.follow(it->type())); - - member_exprt member(symbol.symbol_expr(), it2->get_name(), it2->type()); - - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(member, sym_expr); - assignment1->code.add_source_location()=ref_instr->source_location; - } - - ++seen; - ++it2; - } - - assert(components.size()==seen); - } - - return nil_exprt(); -} - -/*******************************************************************\ - -Function: array_abstractiont::add_dummy_symbol_and_value - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -symbol_exprt array_abstractiont::add_dummy_symbol_and_value( - goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, - const irep_idt &component_name, - const typet &type, - const typet &source_type) -{ - std::string suffix="$strdummy"; - if(!component_name.empty()) - suffix="#"+id2string(component_name)+suffix; - - irep_idt dummy_identifier=id2string(symbol.name)+suffix; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=ref_instr->source_location; - new_symbol.name=dummy_identifier; - new_symbol.module=symbol.module; - new_symbol.base_name=id2string(symbol.base_name)+suffix; - new_symbol.mode=symbol.mode; - new_symbol.pretty_name=id2string( - symbol.pretty_name.empty()?symbol.base_name:symbol.pretty_name)+suffix; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_exprt sym_expr=new_symbol.symbol_expr(); - - // make sure it is declared before the recursive call - goto_programt::targett decl=dest.add_instruction(); - decl->make_decl(); - decl->source_location=ref_instr->source_location; - decl->function=ref_instr->function; - decl->code=code_declt(sym_expr); - decl->code.add_source_location()=ref_instr->source_location; - - // set the value - may be nil - if(source_type.id()==ID_array && - type_eq(type, string_struct, ns)) - { - new_symbol.value=struct_exprt(string_struct); - new_symbol.value.operands().resize(3); - new_symbol.value.op0()=build_unknown(IS_ZERO, false); - new_symbol.value.op1()=build_unknown(LENGTH, false); - new_symbol.value.op2()=to_array_type(source_type).size().id()==ID_infinity? - build_unknown(SIZE, false):to_array_type(source_type).size(); - make_type(new_symbol.value.op2(), build_type(SIZE)); - } - - if(new_symbol.value.is_not_nil()) - { - goto_programt::targett assignment1=dest.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=ref_instr->source_location; - assignment1->function=ref_instr->function; - assignment1->code=code_assignt(sym_expr, new_symbol.value); - assignment1->code.add_source_location()=ref_instr->source_location; - } - - symbol_table.move(new_symbol); - - return sym_expr; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract( - goto_programt &dest, - goto_programt::targett it) -{ - switch(it->type) - { - case ASSIGN: - it=abstract_assign(dest, it); - break; - - case GOTO: - it=read_rec(it->guard, dest, it); - break; - case ASSERT: - case ASSUME: - it=read_rec(it->guard, dest, it); - if(has_string_macros(it->guard)) - replace_string_macros(it->guard, false, it->source_location); - break; - - case FUNCTION_CALL: - abstract_function_call(dest, it); - break; - - case RETURN: - it=abstract_return(dest, it); - break; - - case END_FUNCTION: - case START_THREAD: - case END_THREAD: - case ATOMIC_BEGIN: - case ATOMIC_END: - case DECL: - case DEAD: - case CATCH: - case THROW: - case SKIP: - case OTHER: - case LOCATION: - break; - case NO_INSTRUCTION_TYPE: - assert(false); - break; - } - - return it; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_assign( - goto_programt &dest, - goto_programt::targett target) -{ - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt &rhs=assign.rhs(); - - if(has_string_macros(lhs)) - { - replace_string_macros(lhs, true, target->source_location); - move_lhs_arithmetic(lhs, rhs); - } - - if(has_string_macros(rhs)) - { - replace_string_macros(rhs, false, target->source_location); - } - - target=read_rec(rhs, dest, target); - - const typet &type=ns.follow(lhs.type()); - if(type.id()==ID_pointer || type.id()==ID_array) - return abstract_pointer_assign(dest, target); - else if(is_char_type(type)) - return abstract_char_assign(dest, target); - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_function_call - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::abstract_function_call( - goto_programt &dest, - goto_programt::targett target) -{ - code_function_callt &call=to_code_function_call(target->code); - code_function_callt::argumentst &arguments=call.arguments(); - code_function_callt::argumentst str_args; - - const symbolt &fct_symbol=ns.lookup(call.function().get(ID_identifier)); - const code_typet::parameterst &formal_params= - to_code_type(fct_symbol.type).parameters(); - - code_function_callt::argumentst::const_iterator it1=arguments.begin(); - for(code_typet::parameterst::const_iterator it2=formal_params.begin(); - it2!=formal_params.end(); - it2++, it1++) - { - const typet &abstract_type=build_abstraction_type(it2->type()); - if(abstract_type.is_nil()) continue; - - if(it1==arguments.end()) - { - err_location(target->source_location); - throw "function call: not enough arguments " + id2string(call.function().get(ID_identifier)) + " " - + from_expr(ns, "", *it2); - } - - str_args.push_back(exprt()); - // if function takes void*, build for *it1 will fail if actual parameter - // is of some other pointer type; then just introduce an unknown - if(build_wrap(*it1, str_args.back(), false)) - str_args.back()=build_unknown(abstract_type, false); - // array -> pointer translation - if(str_args.back().type().id()==ID_array && - abstract_type.id()==ID_pointer) - { - assert(type_eq(str_args.back().type().subtype(), - abstract_type.subtype(), ns)); - - index_exprt idx(str_args.back(), gen_zero(index_type())); - // disable bounds check on that one - idx.set("bounds_check", false); - - str_args.back()=idx; - } - - - if(!is_ptr_argument(abstract_type)) - str_args.back()=str_args.back(); - } - - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct_symbol.type).return_type()); - if(!abstract_ret_type.is_nil()) - { - const exprt &lhs = call.lhs(); - exprt new_lhs; - - if(lhs.is_nil() || - build_wrap(lhs, new_lhs, false)) - str_args.push_back(null_pointer_exprt( - is_ptr_argument(abstract_ret_type)? - to_pointer_type(abstract_ret_type): - pointer_typet(abstract_ret_type))); - else - { - assert(type_eq(new_lhs.type(), - abstract_ret_type, ns)); - - //if(is_ptr_argument(abstract_ret_type)) - str_args.push_back(new_lhs); - //else - // str_args.push_back(address_of_exprt(new_lhs)); - } - } - - arguments.insert(arguments.end(), str_args.begin(), str_args.end()); -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_return - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_return( - goto_programt &dest, - goto_programt::targett target) -{ - code_returnt &ret=to_code_return(target->code); - - if(!ret.has_return_value()) - return target; - - exprt &retval=ret.return_value(); - - replace_string_macros(retval, false, target->source_location); - - const symbolt &fct_symbol=ns.lookup(target->function); - const typet &abstract_ret_type=build_abstraction_type( - to_code_type(fct_symbol.type).return_type()); - if(abstract_ret_type.is_nil()) - return target; - - irep_idt identifier=abstract_ret_val_name(fct_symbol); - const symbolt &str_symbol=ns.lookup(identifier); - symbol_exprt sym_expr=str_symbol.symbol_expr(); - - goto_programt::instructiont is_null; - is_null.function=target->function; - is_null.source_location=target->source_location; - dest.insert_before_swap(target, is_null); - goto_programt::targett next=target; - ++next; - target->make_goto(next, equal_exprt(sym_expr, - null_pointer_exprt(to_pointer_type(sym_expr.type())))); - - exprt new_retval; - // may fail if returning a constant like NULL - if(build_wrap(retval, new_retval, false)) - new_retval=build_unknown(abstract_ret_type, false); - - if(is_ptr_argument(abstract_ret_type)) - return value_assignments(dest, next, sym_expr, new_retval); - else - { - exprt lhs_deref=dereference_exprt(sym_expr, - sym_expr.type().subtype()); - - return value_assignments(dest, next, lhs_deref, new_retval); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_ret_val_name - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -irep_idt array_abstractiont::abstract_ret_val_name(const symbolt &fct) -{ - return id2string(fct.module)+ - "::"+id2string(fct.base_name)+ - "::$return_value_str_abst"+arg_suffix; -} - -/*******************************************************************\ - -Function: array_abstractiont::has_string_macros - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::has_string_macros(const exprt &expr) -{ - if(expr.id()=="is_zero_string" || - expr.id()=="zero_string_length" || - expr.id()=="buffer_size") - return true; - - forall_operands(it, expr) - if(has_string_macros(*it)) - return true; - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::replace_string_macros - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::replace_string_macros( - exprt &expr, - bool lhs, - const source_locationt &location) -{ - if(expr.id()=="is_zero_string") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), IS_ZERO, lhs, location); - expr.swap(tmp); - } - else if(expr.id()=="zero_string_length") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), LENGTH, lhs, location); - expr.swap(tmp); - } - else if(expr.id()=="buffer_size") - { - assert(expr.operands().size()==1); - exprt tmp=build(expr.op0(), SIZE, false, location); - expr.swap(tmp); - } - else - Forall_operands(it, expr) - replace_string_macros(*it, lhs, location); -} - -/*******************************************************************\ - -Function: array_abstractiont::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build( - const exprt &pointer, - whatt what, - bool write, - const source_locationt &location) -{ - // take care of pointer typecasts now - if(pointer.id()==ID_typecast) - { - // cast from another pointer type? - assert(pointer.operands().size()==1); - if(pointer.op0().type().id()!=ID_pointer) - return build_unknown(what, write); - - // recursive call - return build(pointer.op0(), what, write, location); - } - - exprt str_struct; - if(build_wrap(pointer, str_struct, write)) assert(false); - - exprt result=member(str_struct, what); - - if(what==LENGTH || what==SIZE) - { - // adjust for offset - exprt pointer_offset(ID_pointer_offset, size_type()); - pointer_offset.copy_to_operands(pointer); - if(pointer_offset.is_not_nil() && - !pointer_offset.is_zero()) - result=minus_exprt(result, pointer_offset); - } - - return result; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_abstraction_type - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const typet& array_abstractiont::build_abstraction_type(const typet &type) -{ - const typet &eff_type=ns.follow(type); - abstraction_types_mapt::const_iterator map_entry= - abstraction_types_map.find(eff_type); - if(map_entry!=abstraction_types_map.end()) - return map_entry->second; - - abstraction_types_mapt tmp; - tmp.swap(abstraction_types_map); - build_abstraction_type_rec(eff_type, tmp); - - abstraction_types_map.swap(tmp); - map_entry=tmp.find(eff_type); - assert(map_entry!=tmp.end()); - return abstraction_types_map.insert( - std::make_pair(eff_type, map_entry->second)).first->second; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_abstraction_type_rec - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -const typet& array_abstractiont::build_abstraction_type_rec(const typet &type, - const abstraction_types_mapt &known) -{ - const typet &eff_type=ns.follow(type); - abstraction_types_mapt::const_iterator known_entry=known.find(eff_type); - if(known_entry!=known.end()) - return known_entry->second; - - ::std::pair< abstraction_types_mapt::iterator, bool > map_entry( - abstraction_types_map.insert(::std::make_pair( - eff_type, nil_typet()))); - if(!map_entry.second) - return map_entry.first->second; - - if(eff_type.id()==ID_array || eff_type.id()==ID_pointer) - { - map_entry.first->second=string_struct; - - /* - // char* or void* or char[] - if(is_char_type(eff_type.subtype()) || - eff_type.subtype().id() == ID_empty) - map_entry.first->second=string_struct; - else - { - const typet& subt=build_abstraction_type_rec(eff_type.subtype(), known); - if(!subt.is_nil()) - { - if(eff_type.id()==ID_array) - map_entry.first->second= - array_typet(subt, to_array_type(eff_type).size()); - else - map_entry.first->second= - pointer_typet(subt); - } - } - */ - } - else if(eff_type.id()==ID_struct || eff_type.id()==ID_union) - { - const struct_union_typet &struct_union_type=to_struct_union_type(eff_type); - const struct_union_typet::componentst &components= - struct_union_type.components(); - - struct_union_typet::componentst new_comp; - for(struct_union_typet::componentst::const_iterator - it=components.begin(); - it!=components.end(); - it++) - { - if(it->get_anonymous()) continue; - typet subt=build_abstraction_type_rec(it->type(), known); - if(subt.is_nil()) continue; // also precludes structs with pointers to the same datatype - - new_comp.push_back(struct_union_typet::componentt()); - new_comp.back().set_name(it->get_name()); - new_comp.back().set_pretty_name(it->get_pretty_name()); - new_comp.back().type()=subt; - } - if(!new_comp.empty()) - { - struct_union_typet t(eff_type.id()); - t.components().swap(new_comp); - map_entry.first->second=t; - } - } - - return map_entry.first->second; -} - -/*******************************************************************\ - -Function: array_abstractiont::build - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build(const exprt &object, exprt &dest, bool write) -{ - const typet &abstract_type=build_abstraction_type(object.type()); - if(abstract_type.is_nil()) { - return true; - } - - if(object.id()==ID_typecast) - { - if(build(to_typecast_expr(object).op(), dest, write)) - { - return true; - } - - return !(type_eq(dest.type(), abstract_type, ns) || - (dest.type().id()==ID_array && - abstract_type.id()==ID_pointer && - type_eq(dest.type().subtype(), abstract_type.subtype(), ns))); - } - - if(object.id()==ID_string_constant) - { - mp_integer str_len=strlen(object.get(ID_value).c_str()); - return build_symbol_constant(str_len, str_len+1, dest); - } - - if(object.id()==ID_array && is_char_type(object.type().subtype())) - return build_array(to_array_expr(object), dest, write); - - // other constants aren't useful - if(object.is_constant()) - return true; - - if(object.id()==ID_symbol) - return build_symbol(to_symbol_expr(object), dest); - - if(object.id()==ID_if) - return build_if(to_if_expr(object), dest, write); - - if(object.id()==ID_member) - { - const member_exprt &o_mem=to_member_expr(object); - dest=member_exprt(exprt(), o_mem.get_component_name(), abstract_type); - return build_wrap(o_mem.struct_op(), dest.op0(), write); - } - - if(object.id()==ID_dereference) - { - const dereference_exprt &o_deref=to_dereference_expr(object); - dest=dereference_exprt(exprt(), abstract_type); - return build_wrap(o_deref.pointer(), dest.op0(), write); - } - - if(object.id()==ID_index) - { - const index_exprt &o_index=to_index_expr(object); - dest=index_exprt(exprt(), o_index.index(), abstract_type); - return build_wrap(o_index.array(), dest.op0(), write); - } - - // handle pointer stuff - if(object.type().id()==ID_pointer) - { - return build_pointer(object, dest, write); - } - - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_if - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_if(const if_exprt &o_if, - exprt &dest, bool write) -{ - if_exprt new_if(o_if.cond(), exprt(), exprt()); - - // recursive calls - bool op1_err=build_wrap(o_if.true_case(), new_if.true_case(), write); - bool op2_err=build_wrap(o_if.false_case(), new_if.false_case(), write); - if(op1_err && op2_err) return true; - // at least one of them gave proper results - if(op1_err) - { - new_if.type()=new_if.false_case().type(); - new_if.true_case()=build_unknown(new_if.type(), write); - } - else if(op2_err) - { - new_if.type()=new_if.true_case().type(); - new_if.false_case()=build_unknown(new_if.type(), write); - } - else - new_if.type()=new_if.true_case().type(); - - dest.swap(new_if); - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_array - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_array(const array_exprt &object, - exprt &dest, bool write) -{ - assert(is_char_type(object.type().subtype())); - - // writing is invalid - if(write) return true; - - const exprt &a_size=to_array_type(object.type()).size(); - mp_integer size; - // don't do anything, if we cannot determine the size - if (to_integer(a_size, size)) return true; - assert(size==object.operands().size()); - - exprt::operandst::const_iterator it=object.operands().begin(); - for(mp_integer i=0; iis_zero()) - return build_symbol_constant(i, size, dest); - - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_pointer - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_pointer(const exprt &object, - exprt &dest, bool write) -{ - assert(object.type().id()==ID_pointer); - - pointer_arithmetict ptr(object); - if(ptr.pointer.id()==ID_address_of) - { - const address_of_exprt &a=to_address_of_expr(ptr.pointer); - - if(a.object().id()==ID_index) - return build_wrap(to_index_expr(a.object()).array(), dest, write); - - // writing is invalid - if(write) return true; - - if(build_wrap(a.object(), dest, write)) return true; - //dest=address_of_exprt(dest); - return false; - } - else if(ptr.pointer.id()==ID_symbol && - is_char_type(object.type().subtype())) - // recursive call; offset will be handled by pointer_offset in SIZE/LENGTH - // checks - return build_wrap(ptr.pointer, dest, write); - - // we don't handle other pointer arithmetic - return true; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build_unknown(whatt what, bool write) -{ - typet type=build_type(what); - - if(write) - return exprt("NULL-object", type); - - exprt result; - - switch(what) - { - case IS_ZERO: - result=false_exprt(); - break; - - case LENGTH: - case SIZE: - result=side_effect_expr_nondett(type); - break; - } - - return result; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_unknown - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::build_unknown(const typet &type, bool write) -{ - if(write) - return exprt("NULL-object", type); - - // create an uninitialized dummy symbol - // because of a lack of contextual information we can't build a nice name - // here, but moving that into locals should suffice for proper operation - irep_idt identifier="$tmp::nondet_str#str$"+i2string(++temporary_counter); - // ensure decl and initialization - locals[identifier]=identifier; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.name=identifier; - new_symbol.module="$tmp"; - new_symbol.base_name=identifier; - new_symbol.mode=ID_C; - new_symbol.pretty_name=identifier; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=false; - new_symbol.is_thread_local=true; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); - - return ns.lookup(identifier).symbol_expr(); -} - -/*******************************************************************\ - -Function: array_abstractiont::build_symbol - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_symbol(const symbol_exprt &sym, exprt &dest) -{ - const symbolt &symbol=ns.lookup(sym.get_identifier()); - - const typet &abstract_type=build_abstraction_type(symbol.type); - assert(!abstract_type.is_nil()); - - irep_idt identifier=""; - - if(current_args.find(symbol.name)!=current_args.end()) - identifier=id2string(symbol.name)+arg_suffix; - else - { - identifier=id2string(symbol.name)+sym_suffix; - if(symbol_table.symbols.find(identifier)==symbol_table.symbols.end()) - build_new_symbol(symbol, identifier, abstract_type); - } - - const symbolt &str_symbol=ns.lookup(identifier); - dest=str_symbol.symbol_expr(); - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::build_new_symbol - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::build_new_symbol(const symbolt &symbol, - const irep_idt &identifier, const typet &type) -{ - if(!symbol.is_static_lifetime) - locals[symbol.name]=identifier; - - symbolt new_symbol; - new_symbol.type=type; - new_symbol.value.make_nil(); - new_symbol.location=symbol.location; - new_symbol.name=identifier; - new_symbol.module=symbol.module; - new_symbol.base_name=id2string(symbol.base_name)+sym_suffix; - new_symbol.mode=symbol.mode; - new_symbol.pretty_name=id2string( - symbol.pretty_name.empty()?symbol.base_name:symbol.pretty_name)+sym_suffix; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=symbol.is_static_lifetime; - new_symbol.is_thread_local=symbol.is_thread_local; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=true; - - symbol_table.move(new_symbol); - - if(symbol.is_static_lifetime) - { - goto_programt::targett dummy_loc=initialization.add_instruction(); - dummy_loc->source_location=symbol.location; - make_decl_and_def(initialization, dummy_loc, identifier, symbol.name); - initialization.instructions.erase(dummy_loc); - } -} - -/*******************************************************************\ - -Function: array_abstractiont::build_symbol_constant - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -bool array_abstractiont::build_symbol_constant(const mp_integer &zero_length, - const mp_integer &buf_size, exprt &dest) -{ - irep_idt base="$string_constant_str_"+integer2string(zero_length) - +"_"+integer2string(buf_size); - irep_idt identifier="array_abstraction::"+id2string(base); - - if(symbol_table.symbols.find(identifier)== - symbol_table.symbols.end()) - { - symbolt new_symbol; - new_symbol.type=string_struct; - new_symbol.value.make_nil(); - new_symbol.name=identifier; - new_symbol.base_name=base; - new_symbol.mode=ID_C; - new_symbol.pretty_name=base; - new_symbol.is_state_var=true; - new_symbol.is_static_lifetime=true; - new_symbol.is_thread_local=false; - new_symbol.is_lvalue=true; - new_symbol.is_file_local=false; - - { - struct_exprt value(string_struct); - value.operands().resize(3); - - value.op0()=true_exprt(); - value.op1()=from_integer(zero_length, build_type(LENGTH)); - value.op2()=from_integer(buf_size, build_type(SIZE)); - - // initialization - goto_programt::targett assignment1=initialization.add_instruction(); - assignment1->make_assignment(); - assignment1->code=code_assignt(new_symbol.symbol_expr(), value); - } - - symbol_table.move(new_symbol); - } - - dest=symbol_exprt(identifier, string_struct); - - return false; -} - -/*******************************************************************\ - -Function: array_abstractiont::move_lhs_arithmetic - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void array_abstractiont::move_lhs_arithmetic(exprt &lhs, exprt &rhs) -{ - if(lhs.id()==ID_minus) - { - // move op1 to rhs - exprt rest=lhs.op0(); - rhs=plus_exprt(rhs, lhs.op1()); - rhs.type()=lhs.type(); - lhs=rest; - } -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_pointer_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_pointer_assign( - goto_programt &dest, - goto_programt::targett target) -{ - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt rhs=assign.rhs(); - exprt *rhsp=&(assign.rhs()); - - while(rhsp->id()==ID_typecast) - rhsp=&(rhsp->op0()); - - const typet& abstract_type=build_abstraction_type(lhs.type()); - if(abstract_type.is_nil()) return target; - - exprt new_lhs, new_rhs; - if(build_wrap(lhs, new_lhs, true)) return target; - - bool unknown=(abstract_type!=build_abstraction_type(rhsp->type()) || - build_wrap(rhs, new_rhs, false)); - if(unknown) - new_rhs=build_unknown(abstract_type, false); - - if(!unknown) - { - goto_programt::instructiont assignment; - assignment.make_assignment(); - assignment.source_location=target->source_location; - assignment.function=target->function; - assignment.code=code_assignt(new_lhs, new_rhs); - assignment.code.add_source_location()=target->source_location; - dest.insert_before_swap(target, assignment); - ++target; - - return target; - } - else - { - return value_assignments(dest, target, new_lhs, new_rhs); - } -} - - -goto_programt::targett array_abstractiont::bounds_check( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &index) -{ - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - goto_programt tmp; - goto_programt::targett assertion0=tmp.add_instruction(); - exprt upper_bound=binary_relation_exprt(typecast_exprt(index, size.type()), ID_lt, size); - assertion0->make_assertion(upper_bound); - assertion0->source_location=target->source_location; - assertion0->source_location.set("property", "array"); - assertion0->source_location.set("comment", "upper bound for index expression"); - - goto_programt::targett assertion1=tmp.add_instruction(); - exprt lower_bound(binary_relation_exprt(typecast_exprt(index, size.type()), ID_ge, gen_zero(build_type(SIZE)))); - - assertion1->make_assertion(lower_bound); - assertion1->source_location=target->source_location; - assertion1->source_location.set("property", "array"); - assertion1->source_location.set("comment", "lower bound for index expression"); - - dest.insert_before_swap(target, tmp); - ++target; - ++target; - - return target; -} - - -goto_programt::targett array_abstractiont::read_rec( - const exprt &src, - goto_programt &dest, - goto_programt::targett target) -{ - if(src.id()==ID_symbol) - { - } - else if(src.id()==ID_index) - { - assert(src.operands().size()==2); - const index_exprt index_expr=to_index_expr(src); - target=read_rec(src.op0(), dest, target); - target=read_rec(src.op1(), dest, target); - - exprt tmp; - - if(!build_wrap(index_expr.array(), tmp, false)) - { - target=bounds_check(dest, target, tmp, src.op1()); - } - } - else if(src.id()==ID_dereference) - { - pointer_arithmetict ptr(to_dereference_expr(src).pointer()); - - exprt tmp; - - if(!build_wrap(ptr.pointer, tmp, false)) - { - exprt size=member(tmp, SIZE); - assert(size.is_not_nil()); - - exprt object=to_dereference_expr(src).pointer(); - - if(object.id()==ID_plus) - { - exprt index=object.op1(); - - target=bounds_check(dest, target, tmp, index); - } - } - } - else if(src.id()==ID_member) - { - member_exprt tmp=to_member_expr(src); - target=read_rec(tmp.struct_op(), dest, target); - } - else if(src.id()==ID_string_constant) - { - } - else if(src.id()==ID_label) - { - } - else if(src.id()==ID_byte_extract_big_endian || - src.id()==ID_byte_extract_little_endian) - { - assert(src.operands().size()==2); - target=read_rec(src.op0(), dest, target); - target=read_rec(src.op1(), dest, target); - } - else if(src.id()==ID_if) - { - if_exprt if_expr=to_if_expr(src); - target=read_rec(if_expr.true_case(), dest, target); - target=read_rec(if_expr.false_case(), dest, target); - target=read_rec(if_expr.cond(), dest, target); - - } - else if(src.id()==ID_typecast) - { - target=read_rec(src.op0(), dest, target); - } - else { - - forall_operands(it, src) - { - target=read_rec(*it, dest, target); - } - } - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::abstract_char_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::abstract_char_assign( - goto_programt &dest, - goto_programt::targett target) -{ - - code_assignt &assign=to_code_assign(target->code); - - exprt &lhs=assign.lhs(); - exprt *rhsp=&(assign.rhs()); - - while(rhsp->id()==ID_typecast) - rhsp=&(rhsp->op0()); - - // BW - if(lhs.id()==ID_index) - { - - const index_exprt &i_lhs=to_index_expr(lhs); - - exprt new_lhs; - if(!build_wrap(i_lhs.array(), new_lhs, true)) - { - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - exprt index=i_lhs.index(); - - target=bounds_check(dest, target, new_lhs, index); - return target; - } - } - else if(lhs.id()==ID_dereference) - { - pointer_arithmetict ptr(to_dereference_expr(lhs).pointer()); - exprt new_lhs; - if(!build_wrap(ptr.pointer, new_lhs, true)) - { - exprt size=member(new_lhs, SIZE); - assert(size.is_not_nil()); - - exprt object=to_dereference_expr(lhs).pointer(); - - if(object.id()==ID_plus) - { - exprt index=object.op1(); - - target=bounds_check(dest, target, new_lhs, index); - } - } - } - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::char_assign - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::char_assign( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &lhs, - const exprt &rhs) -{ - goto_programt tmp; - - const exprt i1=member(new_lhs, IS_ZERO); - assert(i1.is_not_nil()); - - goto_programt::targett assignment1=tmp.add_instruction(); - assignment1->make_assignment(); - assignment1->source_location=target->source_location; - assignment1->function=target->function; - assignment1->code=code_assignt(i1, true_exprt()); - assignment1->code.add_source_location()=target->source_location; - - goto_programt::targett assignment2=tmp.add_instruction(); - assignment2->make_assignment(); - assignment2->source_location=target->source_location; - assignment2->function=target->function; - assignment2->code=code_assignt(lhs, rhs); - assignment2->code.add_source_location()=target->source_location; - - move_lhs_arithmetic( - assignment2->code.op0(), - assignment2->code.op1()); - - dest.insert_before_swap(target, tmp); - ++target; - ++target; - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs) -{ - if(rhs.id()==ID_if) - return value_assignments_if(dest, target, lhs, to_if_expr(rhs)); - - assert(type_eq(lhs.type(), rhs.type(), ns)); - - if(lhs.type().id()==ID_array) - { - const exprt &a_size=to_array_type(lhs.type()).size(); - mp_integer size; - // don't do anything, if we cannot determine the size - if (to_integer(a_size, size)) return target; - for(mp_integer i=0; iget_name().empty()); - - target=value_assignments(dest, target, - member_exprt(lhs, it->get_name(), it->type()), - member_exprt(rhs, it->get_name(), it->type())); - } - } - - return target; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments_if - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments_if( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const if_exprt& rhs) -{ - goto_programt tmp; - - goto_programt::targett goto_else=tmp.add_instruction(GOTO); - goto_programt::targett goto_out=tmp.add_instruction(GOTO); - goto_programt::targett else_target=tmp.add_instruction(SKIP); - goto_programt::targett out_target=tmp.add_instruction(SKIP); - - goto_else->function=target->function; - goto_else->source_location=target->source_location; - goto_else->make_goto(else_target, rhs.cond()); - goto_else->guard.make_not(); - - goto_out->function=target->function; - goto_out->source_location=target->source_location; - goto_out->make_goto(out_target, true_exprt()); - - else_target->function=target->function; - else_target->source_location=target->source_location; - - out_target->function=target->function; - out_target->source_location=target->source_location; - - value_assignments(tmp, goto_out, lhs, rhs.true_case()); - value_assignments(tmp, else_target, lhs, rhs.false_case()); - - goto_programt::targett last=target; - ++last; - dest.insert_before_swap(target, tmp); - --last; - - return last; -} - -/*******************************************************************\ - -Function: array_abstractiont::value_assignments_string_struct - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -goto_programt::targett array_abstractiont::value_assignments_string_struct( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs) -{ - // copy all the values - goto_programt tmp; - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, IS_ZERO), - member(rhs, IS_ZERO)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, LENGTH), - member(rhs, LENGTH)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - { - goto_programt::targett assignment=tmp.add_instruction(ASSIGN); - assignment->code=code_assignt( - member(lhs, SIZE), - member(rhs, SIZE)); - assignment->code.add_source_location()=target->source_location; - assignment->function=target->function; - assignment->source_location=target->source_location; - } - - goto_programt::targett last=target; - ++last; - dest.insert_before_swap(target, tmp); - --last; - - return last; -} - -/*******************************************************************\ - -Function: array_abstractiont::member - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt array_abstractiont::member(const exprt &a, whatt what) -{ - if(a.is_nil()) return a; - assert(type_eq(a.type(), string_struct, ns) || - is_ptr_string_struct(a.type())); - - member_exprt result(build_type(what)); - result.struct_op()=a.type().id()==ID_pointer? - dereference_exprt(a, string_struct):a; - - switch(what) - { - case IS_ZERO: result.set_component_name("is_zero"); break; - case SIZE: result.set_component_name("size"); break; - case LENGTH: result.set_component_name("length"); break; - } - - return result; -} - diff --git a/src/summarizer/array_abstraction.h b/src/summarizer/array_abstraction.h deleted file mode 100644 index c1c203d44..000000000 --- a/src/summarizer/array_abstraction.h +++ /dev/null @@ -1,205 +0,0 @@ -/*******************************************************************\ - -Module: String Abstraction - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_GOTO_PROGRAMS_array_abstraction_H -#define CPROVER_GOTO_PROGRAMS_array_abstraction_H - -#include -#include -#include -#include - -#include - -/*******************************************************************\ - - Class: array_abstractiont - - Purpose: - -\*******************************************************************/ - -class array_abstractiont:public message_streamt -{ -public: - array_abstractiont( - symbol_tablet &_symbol_table, - message_handlert &_message_handler); - - void operator()(goto_programt &dest); - void operator()(goto_functionst &dest); - -protected: - const std::string arg_suffix; - std::string sym_suffix; - symbol_tablet &symbol_table; - namespacet ns; - unsigned temporary_counter; - - typedef ::std::map< typet, typet > abstraction_types_mapt; - abstraction_types_mapt abstraction_types_map; - - ::std::set< irep_idt > current_args; - - static bool has_string_macros(const exprt &expr); - - void replace_string_macros( - exprt &expr, - bool lhs, - const source_locationt &location); - - void move_lhs_arithmetic(exprt &lhs, exprt &rhs); - - bool is_char_type(const typet &type) const - { - if(type.id()==ID_symbol) - return is_char_type(ns.follow(type)); - - /* - if(type.id()!=ID_signedbv && - type.id()!=ID_unsignedbv) - return false; - - return to_bitvector_type(type).get_width()==config.ansi_c.char_width; - - */ - return to_bitvector_type(type).get_width()==config.ansi_c.char_width - || to_bitvector_type(type).get_width()==config.ansi_c.int_width; - } - - inline bool is_ptr_string_struct(const typet &type) const; - - void make_type(exprt &dest, const typet &type) - { - if(dest.is_not_nil() && - ns.follow(dest.type())!=ns.follow(type)) - dest.make_typecast(type); - } - - goto_programt::targett abstract(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_assign(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_pointer_assign(goto_programt &dest, goto_programt::targett it); - goto_programt::targett abstract_char_assign(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett char_assign( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &lhs, - const exprt &rhs); - - void abstract_function_call(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett abstract_return(goto_programt &dest, goto_programt::targett it); - - goto_programt::targett value_assignments(goto_programt &dest, - goto_programt::targett it, - const exprt& lhs, const exprt& rhs); - - goto_programt::targett value_assignments_if( - goto_programt &dest, - goto_programt::targett target, - const exprt &lhs, const if_exprt &rhs); - - goto_programt::targett value_assignments_string_struct( - goto_programt &dest, - goto_programt::targett target, - const exprt& lhs, const exprt& rhs); - - typedef enum { IS_ZERO, LENGTH, SIZE } whatt; - - static typet build_type(whatt what); - exprt build( - const exprt &pointer, - whatt what, - bool write, - const source_locationt &location); - - bool build(const exprt &object, exprt &dest, bool write); - bool build_wrap(const exprt &object, exprt &dest, bool write); - bool build_if(const if_exprt &o_if, exprt &dest, bool write); - bool build_array(const array_exprt &object, exprt &dest, bool write); - bool build_symbol(const symbol_exprt &sym, exprt &dest); - bool build_symbol_constant(const mp_integer &zero_length, - const mp_integer &buf_size, exprt &dest); - - exprt build_unknown(whatt what, bool write); - exprt build_unknown(const typet &type, bool write); - const typet& build_abstraction_type(const typet &type); - const typet& build_abstraction_type_rec(const typet &type, - const abstraction_types_mapt &known); - bool build_pointer(const exprt &object, exprt &dest, bool write); - void build_new_symbol(const symbolt &symbol, - const irep_idt &identifier, const typet &type); - - exprt member(const exprt &a, whatt what); - irep_idt abstract_ret_val_name(const symbolt &fct); - - typet string_struct; - goto_programt initialization; - - typedef hash_map_cont localst; - localst locals; - - void abstract(goto_programt &dest); - - void add_str_arguments( - const irep_idt& name, - goto_functionst::goto_functiont &fct); - - void add_argument( - code_typet::parameterst &str_args, - const symbolt &fct_symbol, - const typet &type, - const irep_idt &base_name, - const irep_idt &identifier); - - void make_decl_and_def(goto_programt &dest, goto_programt::targett ref_instr, - const irep_idt &identifier, const irep_idt &source_sym); - - exprt make_val_or_dummy_rec(goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, const typet &source_type); - - symbol_exprt add_dummy_symbol_and_value( - goto_programt &dest, - goto_programt::targett ref_instr, - const symbolt &symbol, - const irep_idt &component_name, - const typet &type, - const typet &source_type); - - void declare_define_locals(goto_programt &dest); - - // BW - goto_programt::targett read_rec( - const exprt &src, - goto_programt &dest, - goto_programt::targett target); - - // BW - goto_programt::targett bounds_check( - goto_programt &dest, - goto_programt::targett target, - const exprt &new_lhs, - const exprt &index); -}; - -// keep track of length of strings - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_programt &dest); - -void array_abstraction( - symbol_tablet &symbol_table, - message_handlert &message_handler, - goto_functionst &dest); - -#endif diff --git a/src/summarizer/show.cpp b/src/summarizer/show.cpp deleted file mode 100644 index 88751bfd0..000000000 --- a/src/summarizer/show.cpp +++ /dev/null @@ -1,618 +0,0 @@ -/*******************************************************************\ - -Module: Showing various debugging information - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../ssa/ssa_domain.h" -#include "../ssa/guard_map.h" -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" -#include "../ssa/ssa_value_set.h" - -#include "../domains/ssa_fixed_point.h" - -#include "summary.h" - -/*******************************************************************\ - -Function: show_assignments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_assignments( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); - assignments.output(ns, goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_assignments - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_assignments( - const goto_modelt &goto_model, - const irep_idt &function, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_assignments(f_it->second, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - out << ">>>> Function " << f_it->first << "\n"; - - show_assignments(f_it->second, ns, out); - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - assignmentst assignments(goto_function.body, ns, ssa_objects, ssa_value_ai); - ssa_ait ssa_analysis(assignments); - ssa_analysis(goto_function, ns); - ssa_analysis.output(ns, goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_defs - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_defs( - const goto_modelt &goto_model, - const irep_idt &function, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_defs(f_it->second, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - out << ">>>> Function " << f_it->first << "\n"; - - show_defs(f_it->second, ns, out); - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - guard_mapt guard_map(goto_function.body); - guard_map.output(goto_function.body, out); -} - -/*******************************************************************\ - -Function: show_guards - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_guards( - const goto_modelt &goto_model, - const irep_idt &function, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_guards(f_it->second, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - out << ">>>> Function " << f_it->first << "\n"; - - show_guards(f_it->second, ns, out); - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const goto_functionst::goto_functiont &goto_function, - bool simplify, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - if(simplify) ::simplify(local_SSA, ns); - local_SSA.output_verbose(out); -} - -/*******************************************************************\ - -Function: show_ssa - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_ssa( - const goto_modelt &goto_model, - const irep_idt &function, - bool simplify, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - out << ">>>> Function " << function << "\n"; - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_ssa(f_it->second, simplify, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - if(f_it->first=="assert") continue; - if(f_it->first=="__CPROVER_assume") continue; - - out << ">>>> Function " << f_it->first << "\n"; - - show_ssa(f_it->second, simplify, ns, out); - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: show_fixed_point - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_point( - const goto_functionst::goto_functiont &goto_function, - bool simplify, - const namespacet &ns, - std::ostream &out) -{ - local_SSAt local_SSA(goto_function, ns); - if(simplify) ::simplify(local_SSA, ns); - ssa_fixed_point(local_SSA); - local_SSA.output(out); -} - -/*******************************************************************\ - -Function: show_fixed_points - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_fixed_points( - const goto_modelt &goto_model, - const irep_idt &function, - bool simplify, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_fixed_point(f_it->second, simplify, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - out << ">>>> Function " << f_it->first << "\n"; - - show_fixed_point(f_it->second, simplify, ns, out); - - out << "\n"; - } - } -} - -/*******************************************************************\ - -Function: show_error_trace - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void print_symbol_values(const local_SSAt &SSA, - prop_convt &solver, - std::ostream &out, - const exprt &expr) -{ -// if(expr.id()==ID_symbol) - { - out << from_expr(SSA.ns, "",expr) << " == " << - from_expr(SSA.ns, "", solver.get(expr)) << "\n"; - // return; - } - for(exprt::operandst::const_iterator it = expr.operands().begin(); - it != expr.operands().end(); it++) - { - print_symbol_values(SSA,solver,out,*it); - } -} - -void show_error_trace(const irep_idt &property_id, - const local_SSAt &SSA, - prop_convt &solver, - std::ostream &out, - message_handlert &message_handler) - -{ - out << "\n*** Error trace for property " << property_id << "\n"; - for (local_SSAt::nodest::const_iterator n_it = - SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) - { - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) - { - print_symbol_values(SSA,solver,out,*e_it); - // out << from_expr(SSA.ns, "", e_it->op0()) << " == " << - // from_expr(SSA.ns, "", solver.get(e_it->op0())) << "\n"; - } - for (local_SSAt::nodet::constraintst::const_iterator c_it = - n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) - { - print_symbol_values(SSA,solver,out,*c_it); - } - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) - { - print_symbol_values(SSA,solver,out,*a_it); - } - } - out << "\n"; -} - -/*******************************************************************\ - -Function: show_invariants - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -local_SSAt::locationt find_loc_by_guard(const local_SSAt &SSA, - const symbol_exprt &guard) -{ - std::string gstr = id2string(guard.get_identifier()); - unsigned pos1 = gstr.find("#")+1; - unsigned pos2 = gstr.find("%",pos1); - unsigned n = safe_string2unsigned(gstr.substr(pos1,pos2)); - return SSA.find_location_by_number(n); -} - -void purify_identifiers(exprt &expr) -{ - if(expr.id()==ID_symbol) - { - std::string idstr = id2string(to_symbol_expr(expr).get_identifier()); - to_symbol_expr(expr).set_identifier(idstr.substr(0,idstr.find("#"))); - } - for(unsigned i=0; i inv - const exprt &impl = expr.op0(); - exprt inv = expr.op1(); //copy - local_SSAt::locationt loc; - if(impl.id()==ID_symbol) - { - loc = find_loc_by_guard(SSA,to_symbol_expr(impl)); - } - else if(impl.id()==ID_and) - { - assert(impl.op0().id()==ID_symbol); - loc = find_loc_by_guard(SSA,to_symbol_expr(impl.op0())); - } - else assert(false); - - out << "\n** invariant: " << loc->source_location << "\n"; - purify_identifiers(inv); - out << " " << from_expr(SSA.ns,"",inv) << "\n"; -} - -void show_invariants(const local_SSAt &SSA, - const summaryt &summary, - std::ostream &out) -{ - if(summary.fw_invariant.is_nil()) return; - if(summary.fw_invariant.is_true()) return; - - //expected format /\_i g_i => inv_i - if(summary.fw_invariant.id()==ID_implies) - { - show_invariant(SSA,summary.fw_invariant,out); - } - else if(summary.fw_invariant.id()==ID_and) - { - for(unsigned i=0; i symbols; - out << "\n*** SSA Symbols " << "\n"; - for (local_SSAt::nodest::const_iterator n_it = - SSA.nodes.begin(); n_it != SSA.nodes.end(); n_it++) - { - for (local_SSAt::nodet::equalitiest::const_iterator e_it = - n_it->equalities.begin(); e_it != n_it->equalities.end(); e_it++) - { - find_symbols(*e_it,symbols); - } - for (local_SSAt::nodet::constraintst::const_iterator c_it = - n_it->constraints.begin(); c_it != n_it->constraints.end(); c_it++) - { - find_symbols(*c_it,symbols); - } - for (local_SSAt::nodet::assertionst::const_iterator a_it = - n_it->assertions.begin(); a_it != n_it->assertions.end(); a_it++) - { - find_symbols(*a_it,symbols); - } - find_symbols(n_it->enabling_expr,symbols); - } - - for(std::set::const_iterator it = symbols.begin(); - it != symbols.end(); it++) - { - out << from_type(SSA.ns, "",it->type()) << " " << - from_expr(SSA.ns, "", *it) << ";\n"; - } - out << "\n"; -} - -/*******************************************************************\ - -Function: show_value_set - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_value_set( - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns, - std::ostream &out) -{ - ssa_objectst ssa_objects(goto_function, ns); - ssa_value_ait ssa_value_ai(goto_function, ns); - ssa_value_ai.output(ns, goto_function, out); -} - -/*******************************************************************\ - -Function: show_value_sets - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void show_value_sets( - const goto_modelt &goto_model, - const irep_idt &function, - std::ostream &out, - message_handlert &message_handler) -{ - const namespacet ns(goto_model.symbol_table); - - if(!function.empty()) - { - goto_functionst::function_mapt::const_iterator - f_it=goto_model.goto_functions.function_map.find(function); - if(f_it==goto_model.goto_functions.function_map.end()) - out << "function " << function << " not found\n"; - else - show_value_set(f_it->second, ns, out); - } - else - { - forall_goto_functions(f_it, goto_model.goto_functions) - { - out << ">>>> Function " << f_it->first << "\n"; - - show_value_set(f_it->second, ns, out); - - out << "\n"; - } - } -} - diff --git a/src/summarizer/ssa_db.h b/src/summarizer/ssa_db.h deleted file mode 100644 index 22642b260..000000000 --- a/src/summarizer/ssa_db.h +++ /dev/null @@ -1,83 +0,0 @@ -/*******************************************************************\ - -Module: Storage for Function SSAs - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#ifndef CPROVER_LOCAL_SSA_DB_H -#define CPROVER_LOCAL_SSA_DB_H - -#include - -#include "../ssa/local_ssa.h" -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_dependency_graph.h" -#include "../domains/incremental_solver.h" -#include - -class ssa_inlinert; -class ssa_dependency_grapht; - -class ssa_dbt -{ - public: - typedef irep_idt function_namet; - typedef std::map functionst; - typedef std::map depgrapht; - typedef std::map solverst; - - explicit ssa_dbt(const optionst &_options) - : options(_options) - { } - - ~ssa_dbt() - { - for(functionst::iterator it = store.begin(); - it != store.end(); it++) - delete it->second; - for(solverst::iterator it = the_solvers.begin(); - it != the_solvers.end(); it++) - delete it->second; - } - - local_SSAt &get(const function_namet &function_name) const - { return *store.at(function_name); } - - ssa_dependency_grapht &get_depgraph(const function_namet &function_name) const - { return *depgraph_store.at(function_name); } - - incremental_solvert &get_solver(const function_namet &function_name) - { - solverst::iterator it = the_solvers.find(function_name); - if(it!=the_solvers.end()) return *(it->second); - the_solvers[function_name] = - incremental_solvert::allocate(store.at(function_name)->ns, - options.get_bool_option("refine")); - return *the_solvers.at(function_name); - } - - functionst &functions() { return store; } - solverst &solvers() { return the_solvers; } - - bool exists(const function_namet &function_name) const - { return store.find(function_name)!=store.end(); } - - void create(const function_namet &function_name, - const goto_functionst::goto_functiont &goto_function, - const namespacet &ns) - { - store[function_name] = new local_SSAt(goto_function,ns); - } - - void depgraph_create(const function_namet &function_name, const namespacet &ns, ssa_inlinert &ssa_inliner, bool entry); - - protected: - const optionst &options; - functionst store; - depgrapht depgraph_store; - solverst the_solvers; -}; - -#endif diff --git a/src/summarizer/summarizer_base.h b/src/summarizer/summarizer_base.h deleted file mode 100644 index be8d80ae3..000000000 --- a/src/summarizer/summarizer_base.h +++ /dev/null @@ -1,108 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Base - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BASE_H -#define CPROVER_SUMMARIZER_BASE_H - -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_baset : public messaget -{ - public: - explicit summarizer_baset(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - options(_options), - summary_db(_summary_db), - ssa_db(_ssa_db), - ssa_unwinder(_ssa_unwinder), - ssa_inliner(_ssa_inliner), - nonpassed_assertions(false), - solver_instances(0), - solver_calls(0), - summaries_used(0) - {} - - typedef summaryt::predicatet preconditiont; - typedef irep_idt function_namet; - typedef local_SSAt function_bodyt; - typedef std::map preconditionst; - typedef ssa_dbt::functionst functionst; - typedef functionst::value_type functiont; - - virtual void summarize(); - virtual void summarize(const function_namet &entry_function); - - unsigned get_number_of_solver_instances() { return solver_instances; } - unsigned get_number_of_solver_calls() { return solver_calls; } - unsigned get_number_of_summaries_used() { return summaries_used; } - - static exprt::operandst get_loophead_selects( - const local_SSAt &SSA, - const ssa_local_unwindert &ssa_local_unwinder, - prop_convt &solver); - - protected: - optionst &options; - summary_dbt &summary_db; - ssa_dbt &ssa_db; - ssa_unwindert &ssa_unwinder; - ssa_inlinert &ssa_inliner; - bool nonpassed_assertions; - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) - { assert(false); } - - bool check_call_reachable(const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt& precondition, - bool forward); - - virtual exprt compute_calling_context( - const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool forward); - - virtual bool check_precondition(const function_namet &function_name, - local_SSAt &SSA, - local_SSAt::nodest::const_iterator node, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool context_sensitive); - - void get_assertions(const local_SSAt &SSA, - exprt::operandst &assertions); - - bool check_end_reachable(const function_namet &function_name, - local_SSAt &SSA, - const exprt &cond); - - //statistics - unsigned solver_instances; - unsigned solver_calls; - unsigned summaries_used; -}; - - -#endif diff --git a/src/summarizer/summarizer_bw.cpp b/src/summarizer/summarizer_bw.cpp deleted file mode 100644 index cfaf5449e..000000000 --- a/src/summarizer/summarizer_bw.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_bw.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - - -/*******************************************************************\ - -Function: summarizer_bwt::summarize() - - Inputs: - - Outputs: - - Purpose: analyze only functions reachable in a previous forward analysis - -\*******************************************************************/ - -void summarizer_bwt::summarize() -{ - status() << "\nBackward analysis..." << eom; - - exprt postcondition = true_exprt(); //initial calling context - for(functionst::const_iterator it = ssa_db.functions().begin(); - it!=ssa_db.functions().end(); it++) - { - status() << "\nSummarizing function " << it->first << eom; - if(summary_db.exists(it->first) && - summary_db.get(it->first).bw_precondition.is_nil()) - compute_summary_rec(it->first,postcondition,false); - else status() << "Skipping function " << it->first << eom; - } -} - -/*******************************************************************\ - -Function: summarizer_bwt::summarize() - - Inputs: - - Outputs: - - Purpose: summarize from given entry point - -\*******************************************************************/ - -void summarizer_bwt::summarize(const function_namet &function_name) -{ - status() << "\nBackward analysis..." << eom; - - exprt postcondition = true_exprt(); //initial calling context - - status() << "\nSummarizing function " << function_name << eom; - if(summary_db.exists(function_name)) - { - compute_summary_rec(function_name,postcondition,true); - } - else status() << "Skipping function " << function_name << eom; -} - - -/*******************************************************************\ - -Function: summarizer_bwt::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); - - const summaryt &old_summary = summary_db.get(function_name); - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,old_summary, - postcondition,context_sensitive, - options.get_bool_option("sufficient")); - - status() << "Analyzing function " << function_name << eom; - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.bw_postcondition = postcondition; - - if(!options.get_bool_option("havoc")) - { - do_summary(function_name,SSA,old_summary,summary,context_sensitive); - } - - // store summary in db - summary_db.put(function_name,summary); - - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary_db.get(function_name).output(out,SSA.ns); - status() << out.str() << eom; - } - -} - -/*******************************************************************\ - -Function: summarizer_bwt::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::do_summary(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive) -{ - bool sufficient = options.get_bool_option("sufficient"); - status() << "Computing preconditions" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - template_generator_summaryt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,false); - - exprt::operandst c; - c.push_back(old_summary.fw_precondition); - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,c); //backward summaries - collect_postconditions(function_name, SSA, summary, postcond,sufficient); - if(!sufficient) - { - c.push_back(conjunction(postcond)); - } - else //sufficient - { - c.push_back(not_exprt(conjunction(postcond))); - } - - if(!template_generator.out_vars().empty()) - { - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - analyzer(solver,SSA,conjunction(c),template_generator); - analyzer.get_result(summary.bw_transformer,template_generator.inout_vars()); - analyzer.get_result(summary.bw_invariant,template_generator.loop_vars()); - analyzer.get_result(summary.bw_precondition,template_generator.out_vars()); - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - } - else // TODO: yet another workaround for ssa_analyzer not being able to handle empty templates properly - { - solver << SSA; - solver.new_context(); - solver << SSA.get_enabling_exprs(); - solver << conjunction(c); - exprt result = true_exprt(); - if(solver()==decision_proceduret::D_UNSATISFIABLE) result = false_exprt(); - solver.pop_context(); - summary.bw_transformer = result; - summary.bw_invariant = result; - summary.bw_precondition = result; - } - - if(sufficient) - { - summary.bw_transformer = not_exprt(summary.bw_transformer); - summary.bw_invariant = not_exprt(summary.bw_invariant); - summary.bw_precondition = not_exprt(summary.bw_precondition); - } - - if(context_sensitive && !summary.bw_postcondition.is_true()) - { - summary.bw_transformer = - implies_exprt(summary.bw_postcondition,summary.bw_transformer); - summary.bw_invariant = - implies_exprt(summary.bw_postcondition,summary.bw_invariant); - summary.bw_precondition = - implies_exprt(summary.bw_postcondition,summary.bw_precondition); - } -} - -/*******************************************************************\ - -Function: summarizer_bwt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_bwt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.end(); - n_it != SSA.nodes.begin(); ) - { - n_it--; - - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!sufficient && - !check_call_reachable(function_name,SSA,n_it,f_it,postcondition,false)) - { - continue; - } - - if(!check_postcondition(function_name,SSA,n_it,f_it, - postcondition,context_sensitive)) - { - exprt postcondition_call = true_exprt(); - if(context_sensitive) - postcondition_call = compute_calling_context2( - function_name,SSA,old_summary,n_it,f_it,postcondition,sufficient); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,postcondition_call,context_sensitive); - summaries_used++; - } - } - } -} - - -/*******************************************************************\ - -Function: summarizer_bwt::collect_postconditions() - - Inputs: - - Outputs: - - Purpose: collects postconditions where precondition inference starts from - -\*******************************************************************/ - -void summarizer_bwt::collect_postconditions( - const function_namet &function_name, - const local_SSAt &SSA, - const summaryt &summary, - exprt::operandst &postconditions, - bool sufficient) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::assertionst::const_iterator - a_it = n_it->assertions.begin(); - a_it != n_it->assertions.end(); a_it++) - { - postconditions.push_back(*a_it); - } - } - /* if(termination) - { - if(!summary.termination_argument.is_nil()) - postconditions.push_back(summary.termination_argument); - }*/ - - exprt guard = SSA.guard_symbol(--SSA.goto_function.body.instructions.end()); - if(!sufficient) - postconditions.push_back(and_exprt(guard,summary.bw_postcondition)); - else - postconditions.push_back(implies_exprt(guard,summary.bw_postcondition)); -} - -/*******************************************************************\ - -Function: summarizer_bwt::check_postcondition() - - Inputs: - - Outputs: - - Purpose: returns false if the summary needs to be recomputed - -\******************************************************************/ - -bool summarizer_bwt::check_postcondition( - const function_namet &function_name, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &precondition, - bool context_sensitive) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Checking precondition of " << fname << eom; - - bool precondition_holds = false; - exprt assertion; - - if(!summary_db.exists(fname)) return true; //nothing to do - - summaryt summary = summary_db.get(fname); - - if(summary.bw_precondition.is_nil()) return false; //there is work to do - - if(!context_sensitive || - summary.fw_precondition.is_true()) //precondition trivially holds - { - status() << "Precondition trivially holds, replacing by summary." - << eom; - summaries_used++; - precondition_holds = true; - } - else - { - assertion = summary.bw_precondition; - - //getting globals at call site - local_SSAt::var_sett cs_globals_in; - SSA.get_globals(n_it->location,cs_globals_in); - - ssa_inliner.rename_to_caller(f_it,summary.params, - cs_globals_in,summary.globals_in,assertion); - - debug() << "precondition assertion: " << - from_expr(SSA.ns,"",assertion) << eom; - - precondition_holds = false; - } - - if(precondition_holds) return true; - - assert(!assertion.is_nil()); - - // postcondition check - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - solver << SSA; - - solver.new_context(); - solver << SSA.get_enabling_exprs(); - - solver << precondition; - solver << ssa_inliner.get_summaries(SSA); - - //add postcondition - solver << not_exprt(assertion); - - switch(solver()) - { - case decision_proceduret::D_SATISFIABLE: { - precondition_holds = false; - - status() << "Precondition does not hold, need to recompute summary." << eom; - break; } - case decision_proceduret::D_UNSATISFIABLE: { - precondition_holds = true; - - status() << "Precondition holds, replacing by summary." << eom; - summaries_used++; - - break; } - default: assert(false); break; - } - - return precondition_holds; -} - -/*******************************************************************\ - -Function: summarizer_bwt::compute_calling_context2() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -exprt summarizer_bwt::compute_calling_context2( - const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient) -{ - assert(f_it->function().id()==ID_symbol); //no function pointers - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - - status() << "Computing calling context for function " << fname << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //analyze - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - - template_generator_callingcontextt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,n_it,f_it,false); - - // collect globals at call site - std::map - cs_globals_out; - SSA.get_globals(n_it->location,cs_globals_out[f_it],false); - - exprt::operandst c; - c.push_back(old_summary.fw_precondition); - c.push_back(old_summary.fw_invariant); - c.push_back(ssa_inliner.get_summaries(SSA)); //forward summaries - exprt::operandst postcond; - ssa_inliner.get_summaries(SSA,false,postcond,c); //backward summaries - old_summary.bw_postcondition = postcondition; //that's a bit awkward - collect_postconditions(function_name, SSA, old_summary, postcond,sufficient); - if(!sufficient) - { - c.push_back(conjunction(postcond)); - } - else //sufficient - { - c.push_back(not_exprt(conjunction(postcond))); - } - - analyzer(solver,SSA,conjunction(c),template_generator); - - // set preconditions - local_SSAt &fSSA = ssa_db.get(fname); - - exprt postcondition_call; - analyzer.get_result(postcondition_call, - template_generator.callingcontext_vars()); - - ssa_inliner.rename_to_callee(f_it, fSSA.params, - cs_globals_out[f_it],fSSA.globals_out, - postcondition_call); - - if(sufficient && - !postcondition_call.is_true()) //TODO: this should actually be handled by ssa_analyzer using a "guard-reachabiliity-only" analysis if template is empty - { - postcondition_call = not_exprt(postcondition_call); - } - - debug() << "Backward calling context for " << - from_expr(SSA.ns, "", *f_it) << ": " - << from_expr(SSA.ns, "", postcondition_call) << eom; - - //statistics - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); - - return postcondition_call; -} - - diff --git a/src/summarizer/summarizer_bw.h b/src/summarizer/summarizer_bw.h deleted file mode 100644 index 48600ede1..000000000 --- a/src/summarizer/summarizer_bw.h +++ /dev/null @@ -1,81 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Backward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_BW_H -#define CPROVER_SUMMARIZER_BW_H - -#include -#include -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -#include "summarizer_base.h" - -class summarizer_bwt : public summarizer_baset -{ - public: - explicit summarizer_bwt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - virtual void summarize(); - virtual void summarize(const function_namet &entry_function); - - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &postcondition, - bool context_sensitive); - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - const exprt &postcondition, - bool context_sensitive, - bool sufficient); - - virtual void do_summary(const function_namet &function_name, - local_SSAt &SSA, - const summaryt &old_summary, - summaryt &summary, - bool context_sensitive); - - virtual bool check_postcondition(const function_namet &function_name, - const local_SSAt &SSA, - local_SSAt::nodest::const_iterator node, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool context_sensitive); - - virtual void collect_postconditions(const function_namet &function_name, - const local_SSAt &SSA, - const summaryt &summary, - exprt::operandst &postconditions, - bool sufficient); - - virtual exprt compute_calling_context2(const function_namet &function_name, - local_SSAt &SSA, - summaryt old_summary, - local_SSAt::nodest::const_iterator n_it, - local_SSAt::nodet::function_callst::const_iterator f_it, - const exprt &postcondition, - bool sufficient); - -}; - - -#endif diff --git a/src/summarizer/summarizer_fw.cpp b/src/summarizer/summarizer_fw.cpp deleted file mode 100644 index dcf437019..000000000 --- a/src/summarizer/summarizer_fw.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#include - -#include -#include -#include -#include -#include - -#include "summarizer_fw.h" -#include "summary_db.h" - -#include "../domains/ssa_analyzer.h" -#include "../domains/template_generator_summary.h" -#include "../domains/template_generator_callingcontext.h" - -#include "../ssa/local_ssa.h" -#include "../ssa/simplify_ssa.h" - -//#define SHOW_WHOLE_RESULT - -/*******************************************************************\ - -Function: summarizert::compute_summary_rec() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive) -{ - local_SSAt &SSA = ssa_db.get(function_name); //TODO: make const - - // recursively compute summaries for function calls - inline_summaries(function_name,SSA,precondition,context_sensitive); - - status() << "Analyzing function " << function_name << eom; - -#if 0 - { - std::ostringstream out; - out << "Function body for " << function_name << - " to be analyzed: " << std::endl; - for(local_SSAt::nodest::iterator n = SSA.nodes.begin(); - n!=SSA.nodes.end(); n++) - { - if(!n->empty()) n->output(out,SSA.ns); - } - out << "(enable) " << from_expr(SSA.ns, "", SSA.get_enabling_exprs()) - << "\n"; - debug() << out.str() << eom; - } -#endif - - // create summary - summaryt summary; - summary.params = SSA.params; - summary.globals_in = SSA.globals_in; - summary.globals_out = SSA.globals_out; - summary.fw_precondition = precondition; - - if(!options.get_bool_option("havoc")) - { - do_summary(function_name,SSA,summary,true_exprt(),context_sensitive); - } - - if(!options.get_bool_option("competition-mode")) - { - std::ostringstream out; - out << std::endl << "Summary for function " << function_name << std::endl; - summary.output(out,SSA.ns); - status() << out.str() << eom; - } - - // store summary in db - summary_db.put(function_name,summary); -} - -/*******************************************************************\ - -Function: summarizer_fwt::do_summary() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::do_summary(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary, - exprt cond, - bool context_sensitive) -{ - status() << "Computing summary" << eom; - - // solver - incremental_solvert &solver = ssa_db.get_solver(function_name); - solver.set_message_handler(get_message_handler()); - - //analyze - ssa_analyzert analyzer; - analyzer.set_message_handler(get_message_handler()); - - template_generator_summaryt template_generator( - options,ssa_db,ssa_unwinder.get(function_name)); - template_generator.set_message_handler(get_message_handler()); - template_generator(solver.next_domain_number(),SSA,true); - - exprt::operandst conds; - conds.reserve(5); - conds.push_back(cond); - conds.push_back(summary.fw_precondition); - conds.push_back(ssa_inliner.get_summaries(SSA)); - -#ifdef REUSE_INVARIANTS - if(summary_db.exists(function_name)) //reuse existing invariants - { - const exprt &old_inv = summary_db.get(function_name).fw_invariant; - exprt inv = ssa_unwinder.get(function_name).rename_invariant(old_inv); - conds.push_back(inv); - -#if 0 - std::ostringstream out; - out << "(original inv)" << from_expr(SSA.ns,"",old_inv) << "\n"; - debug() << out.str() << eom; - out << "(renamed inv)" << from_expr(SSA.ns,"",inv)<<"\n"; - debug() << out.str() << eom; -#endif - } -#endif - - cond = conjunction(conds); - - bool assertions_check = false; //options.get_bool_option("k-induction"); - bool assertions_hold = analyzer(solver,SSA,cond,template_generator, - assertions_check); - if(assertions_hold) - { - analyzer.get_result(summary.fw_transformer,template_generator.inout_vars()); - analyzer.get_result(summary.fw_invariant,template_generator.loop_vars()); - -#ifdef SHOW_WHOLE_RESULT - // to see all the custom template values - exprt whole_result; - analyzer.get_result(whole_result,template_generator.all_vars()); - debug() << "whole result: " << from_expr(SSA.ns,"",whole_result) << eom; -#endif - - if(context_sensitive && !summary.fw_precondition.is_true()) - { - summary.fw_transformer = - implies_exprt(summary.fw_precondition,summary.fw_transformer); - summary.fw_invariant = - implies_exprt(summary.fw_precondition,summary.fw_invariant); - } - } - else //!assertions_hold - { - nonpassed_assertions = true; - summary.nonpassed_assertions = analyzer.get_nonpassed_assertions(); - } - - solver_instances += analyzer.get_number_of_solver_instances(); - solver_calls += analyzer.get_number_of_solver_calls(); -} - -/*******************************************************************\ - -Function: summarizer_fwt::inline_summaries() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summarizer_fwt::inline_summaries(const function_namet &function_name, - local_SSAt &SSA, const exprt &precondition, - bool context_sensitive) -{ - for(local_SSAt::nodest::const_iterator n_it = SSA.nodes.begin(); - n_it != SSA.nodes.end(); n_it++) - { - for(local_SSAt::nodet::function_callst::const_iterator f_it = - n_it->function_calls.begin(); - f_it != n_it->function_calls.end(); f_it++) - { - assert(f_it->function().id()==ID_symbol); //no function pointers - if(!check_call_reachable(function_name,SSA,n_it,f_it,precondition,true)) - { - continue; - } - - if(!check_precondition(function_name,SSA,n_it,f_it, - precondition,context_sensitive)) - { - exprt precondition_call = true_exprt(); - if(context_sensitive) - precondition_call = compute_calling_context( - function_name,SSA,n_it,f_it,precondition,true); - - irep_idt fname = to_symbol_expr(f_it->function()).get_identifier(); - status() << "Recursively summarizing function " << fname << eom; - compute_summary_rec(fname,precondition_call,context_sensitive); - summaries_used++; - } - } - } -} - - diff --git a/src/summarizer/summarizer_fw.h b/src/summarizer/summarizer_fw.h deleted file mode 100644 index 054ee113d..000000000 --- a/src/summarizer/summarizer_fw.h +++ /dev/null @@ -1,54 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer for Forward Analysis - -Author: Peter Schrammel - -\*******************************************************************/ - -#ifndef CPROVER_SUMMARIZER_FW_H -#define CPROVER_SUMMARIZER_FW_H - -#include -#include - -#include "summarizer_base.h" - -#include "../ssa/ssa_inliner.h" -#include "../ssa/ssa_unwinder.h" -#include "../ssa/local_ssa.h" -#include "ssa_db.h" - -#include - -class summarizer_fwt : public summarizer_baset -{ - public: - explicit summarizer_fwt(optionst &_options, - summary_dbt &_summary_db, - ssa_dbt &_ssa_db, - ssa_unwindert &_ssa_unwinder, - ssa_inlinert &_ssa_inliner) : - summarizer_baset(_options,_summary_db,_ssa_db,_ssa_unwinder,_ssa_inliner) - {} - - protected: - - virtual void compute_summary_rec(const function_namet &function_name, - const exprt &precondition, - bool context_sensitive); - - virtual void inline_summaries(const function_namet &function_name, - local_SSAt &SSA, - const exprt &precondition, - bool context_sensitive); - - void do_summary(const function_namet &function_name, - local_SSAt &SSA, - summaryt &summary, - exprt cond, //additional constraints - bool forward); -}; - - -#endif diff --git a/src/summarizer/summarizer_main.cpp b/src/summarizer/summarizer_main.cpp deleted file mode 100644 index 6bafefc6a..000000000 --- a/src/summarizer/summarizer_main.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/*******************************************************************\ - -Module: Summarizer Main Module - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include "summarizer_parse_options.h" - -/*******************************************************************\ - -Function: main - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -#ifdef _MSC_VER -int wmain(int argc, const wchar_t **argv_wide) -{ - const char **argv=narrow_argv(argc, argv_wide); - summarizer_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#else -int main(int argc, const char **argv) -{ - summarizer_parse_optionst parse_options(argc, argv); - return parse_options.main(); -} -#endif diff --git a/src/summarizer/summary.cpp b/src/summarizer/summary.cpp deleted file mode 100644 index 6904fa878..000000000 --- a/src/summarizer/summary.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/*******************************************************************\ - -Module: Summary - -Author: Peter Schrammel - -\*******************************************************************/ - -#include "summary.h" -#include "../domains/util.h" - -#include - -const summaryt::call_sitet summaryt::entry_call_site; - -/*******************************************************************\ - -Function: summaryt::output() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summaryt::output(std::ostream &out, const namespacet &ns) const -{ - out << "params: "; - for(summaryt::var_listt::const_iterator it = params.begin(); - it != params.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "globals_in: "; - for(summaryt::var_sett::const_iterator it = globals_in.begin(); - it != globals_in.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "globals_out: "; - for(summaryt::var_sett::const_iterator it = globals_out.begin(); - it != globals_out.end(); it++) - out << from_expr(ns,"",*it) << " "; - out << std::endl; - out << "forward precondition: " - << (fw_precondition.is_nil() ? "not computed" : - from_expr(ns,"",fw_precondition)) << std::endl; - out << "forward transformer: " - << (fw_transformer.is_nil() ? "not computed" : - from_expr(ns,"",fw_transformer)) << std::endl; - out << "forward invariant: " - << (fw_invariant.is_nil() ? "not computed" : - from_expr(ns,"",fw_invariant)) << std::endl; - out << "backward precondition: " - << (bw_precondition.is_nil() ? "not computed" : - from_expr(ns,"",bw_precondition)) << std::endl; - out << "backward postcondition: " - << (bw_postcondition.is_nil() ? "not computed" : - from_expr(ns,"",bw_postcondition)) << std::endl; - out << "backward transformer: " - << (bw_transformer.is_nil() ? "not computed" : - from_expr(ns,"",bw_transformer)) << std::endl; - out << "backward invariant: " - << (bw_invariant.is_nil() ? "not computed" : - from_expr(ns,"",bw_invariant)) << std::endl; - for(error_summariest::const_iterator - it = error_summaries.begin(); - it != error_summaries.end(); it++) - { - out << "error summary for "; - if(it->first == entry_call_site) - out << "entry point"; - else - out << "location " << it->first.location_number; - out << ": " << std::endl - << " " << from_expr(ns,"",it->second) << std::endl; - } -} - -/*******************************************************************\ - -Function: summaryt::join() - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summaryt::combine_and(exprt &olde, const exprt &newe) -{ - if(olde.is_nil()) - { - olde = newe; - } - else - { - if(newe.is_nil()) return; - olde = and_exprt(olde,newe); - } -} - -void summaryt::combine_or(exprt &olde, const exprt &newe) -{ - if(olde.is_nil()) - { - olde = newe; - } - else - { - if(newe.is_nil()) return; - olde = or_exprt(olde,newe); - } -} - -void summaryt::join(const summaryt &new_summary) -{ - assert(params == new_summary.params); - assert(globals_in == new_summary.globals_in); - assert(globals_out == new_summary.globals_out); - combine_or(fw_precondition,new_summary.fw_precondition); - combine_and(fw_transformer,new_summary.fw_transformer); - combine_and(fw_invariant,new_summary.fw_invariant); - combine_and(bw_precondition,new_summary.bw_precondition); - combine_or(bw_postcondition,new_summary.bw_postcondition); - combine_and(bw_transformer,new_summary.bw_transformer); - combine_and(bw_invariant,new_summary.bw_invariant); -} - diff --git a/src/summarizer/summary_db.cpp b/src/summarizer/summary_db.cpp deleted file mode 100644 index 4e7dde39e..000000000 --- a/src/summarizer/summary_db.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/*******************************************************************\ - -Module: Storage for Function Summaries - -Author: Daniel Kroening, kroening@kroening.com - -\*******************************************************************/ - -#include - -#include - -#include "summary_db.h" - -void summary_dbt::put(const function_namet &function_name, - const summaryt &summary) -{ - if(store.find(function_name)==store.end() || - store[function_name].mark_recompute) - store[function_name] = summary; - else - store[function_name].join(summary); -} - -void summary_dbt::mark_recompute_all() -{ - for(std::map::iterator it = store.begin(); - it != store.end(); it++) - it->second.mark_recompute = true; -} - -/*******************************************************************\ - -Function: summary_dbt::file_name - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -std::string summary_dbt::file_name(const std::string &id) -{ - return "summary."+id; -} - -/*******************************************************************\ - -Function: summary_dbt::read - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_dbt::read(const std::string &id) -{ - current=id; - - summary=jsont::json_object(); - - parse_json(file_name(id), get_message_handler(), summary); -} - -/*******************************************************************\ - -Function: summary_dbt::write - - Inputs: - - Outputs: - - Purpose: - -\*******************************************************************/ - -void summary_dbt::write() -{ - std::ofstream out(file_name(current).c_str()); - out << summary << '\n'; -} diff --git a/src/summarizer/version.h b/src/summarizer/version.h deleted file mode 100644 index 401faf272..000000000 --- a/src/summarizer/version.h +++ /dev/null @@ -1 +0,0 @@ -#define SUMMARIZER_VERSION "0.1.0" From c60433cc38be9be73bd1e95c128117d452d1bace Mon Sep 17 00:00:00 2001 From: Kumar Madhukar Date: Wed, 28 Jun 2017 20:52:08 +0530 Subject: [PATCH 90/90] fixed spurious-check options --- src/2ls/2ls_parse_options.cpp | 7 +++++++ src/2ls/2ls_parse_options.h | 1 + 2 files changed, 8 insertions(+) diff --git a/src/2ls/2ls_parse_options.cpp b/src/2ls/2ls_parse_options.cpp index c67380dc5..046249b2e 100644 --- a/src/2ls/2ls_parse_options.cpp +++ b/src/2ls/2ls_parse_options.cpp @@ -279,10 +279,17 @@ void twols_parse_optionst::get_command_line_options(optionst &options) } // check for spuriousness of assertion failures + /* if(cmdline.isset("no-spurious-check")) options.set_option("spurious-check", false); else options.set_option("spurious-check", true); + */ + + if(cmdline.isset("spurious-check")) + options.set_option("spurious-check", cmdline.get_value("spurious-check")); + else + options.set_option("spurious-check", "all"); // all properties (default) if(cmdline.isset("stop-on-fail")) diff --git a/src/2ls/2ls_parse_options.h b/src/2ls/2ls_parse_options.h index d564b1279..1a2037028 100644 --- a/src/2ls/2ls_parse_options.h +++ b/src/2ls/2ls_parse_options.h @@ -53,6 +53,7 @@ class optionst; "(no-simplify)(no-fixed-point)" \ "(graphml-witness):(json-cex):" \ "(no-spurious-check)(stop-on-fail)" \ + "(spurious-check):" \ "(competition-mode)(slice)(no-propagation)(independent-properties)" \ "(no-unwinding-assertions)" // the last line is for CBMC-regression testing only