diff --git a/Makefile b/Makefile index 93e4799cf..241bcdc6d 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,55 @@ +SECURITY_LEVELS=80 112 128 192 +SAGE_OBJECT_DIR=sage-object +SAGE_SECURITY_CURVES=$(SECURITY_LEVELS:%=$(SAGE_OBJECT_DIR)/%.sobj) +SAGE_VERIFED_CURVES=$(CURVES_SAGE_OBJECT_DIR)/verified_curves.sobj CURVES_JSON_PATH=json/curves.json CURVES_CPP_GEN_H=concrete-security-curves-cpp/include/concrete/curves.gen.h CURVES_RUST_GEN_TXT=concrete-security-curves-rust/src/curves.gen.rs -$(CURVES_JSON_PATH): verify_curves.py - sage verify_curves.py > $@ +generate-code: generate-cpp generate-rust + +# Generate CPP ######################## $(CURVES_CPP_GEN_H): concrete-security-curves-cpp/gen_header.py $(CURVES_JSON_PATH) cat $(CURVES_JSON_PATH) | python concrete-security-curves-cpp/gen_header.py > $(CURVES_CPP_GEN_H) generate-cpp: $(CURVES_CPP_GEN_H) -$(CURVES_RUST_GEN_TXT): rust/gen_table.py $(CURVES_JSON_PATH) - cat $(CURVES_JSON_PATH) | python concrete-security-curves-cpp/gen_table.py > $(CURVES_CPP_GEN_H) +# Generate RUST ######################## + +$(CURVES_RUST_GEN_TXT): concrete-security-curves-rust/gen_table.py $(CURVES_JSON_PATH) + cat $(CURVES_JSON_PATH) | python concrete-security-curves-rust/gen_table.py > $(CURVES_RUST_GEN_TXT) generate-rust: $(CURVES_RUST_GEN_TXT) -.PHONY: generate-cpp-header \ No newline at end of file +# Compare curves ####################### + +$(SAGE_OBJECT_DIR)/outdated_curves.timestamp: ./lattice-scripts/compare_curves_and_estimator.py + python ./lattice-scripts/compare_curves_and_estimator.py \ + --curves-dir $(SAGE_OBJECT_DIR) --security-levels $(SECURITY_LEVELS) --log-q 64 \ + || touch $(SAGE_OBJECT_DIR)/outdated_curves.timestamp + +compare-curves: $(SAGE_OBJECT_DIR)/outdated_curves.timestamp + +# Generate curves ###################### + +$(SAGE_OBJECT_DIR)/%.sobj: $(SAGE_OBJECT_DIR)/outdated_curves.timestamp ./lattice-scripts/generate_data.sh ./lattice-scripts/generate_data.py + PYTHONPATH=$(PWD)/lattice-estimator ./lattice-scripts/generate_data.sh \ + $* --output $(SAGE_OBJECT_DIR) --old-models $(SAGE_OBJECT_DIR)/verified_curves.sobj + +generate-curves: $(SAGE_SECURITY_CURVES) + +# Verify curves ####################### + +$(CURVES_JSON_PATH): ./lattice-scripts/verify_curves.py $(SAGE_SECURITY_CURVES) + python ./lattice-scripts/verify_curves.py \ + --curves-dir $(SAGE_OBJECT_DIR) --security-levels $(SECURITY_LEVELS) --log-q 64 > $(CURVES_JSON_PATH) + +verify-curves: $(CURVES_JSON_PATH) + +.PHONY: generate-cpp \ + generate-rust \ + generate-code \ + compare-curves \ + generate-curves \ + verify-curves diff --git a/concrete-security-curves-cpp/include/concrete/curves.gen.h b/concrete-security-curves-cpp/include/concrete/curves.gen.h index 05ff21944..e33c5d3f5 100644 --- a/concrete-security-curves-cpp/include/concrete/curves.gen.h +++ b/concrete-security-curves-cpp/include/concrete/curves.gen.h @@ -1,12 +1,7 @@ std::vector curves { SecurityCurve(80,-0.0404263311936459, 1.660978864143658, 450, KeyFormat::BINARY), - SecurityCurve(96,-0.03414780360867054, 2.0173102586603733, 450, KeyFormat::BINARY), SecurityCurve(112,-0.02967013708113588, 2.16246371408387, 450, KeyFormat::BINARY), SecurityCurve(128,-0.026405028765226296, 2.482642269104389, 450, KeyFormat::BINARY), - SecurityCurve(144,-0.023821437305989134, 2.7177789440636673, 450, KeyFormat::BINARY), - SecurityCurve(160,-0.021743582187160406, 2.9388105484933504, 498, KeyFormat::BINARY), - SecurityCurve(176,-0.019904056582117705, 2.8161252801542673, 551, KeyFormat::BINARY), SecurityCurve(192,-0.018610403247590064, 3.2996236848399008, 606, KeyFormat::BINARY), - SecurityCurve(256,-0.014606812351714961, 3.8493629234693145, 826, KeyFormat::BINARY), } diff --git a/concrete-security-curves-rust/src/curves.gen.rs b/concrete-security-curves-rust/src/curves.gen.rs index faca91268..d95a357b6 100644 --- a/concrete-security-curves-rust/src/curves.gen.rs +++ b/concrete-security-curves-rust/src/curves.gen.rs @@ -1,11 +1,6 @@ [ (80, SecurityWeights { slope: -0.0404263311936459, bias: 1.660978864143658, minimal_lwe_dimension: 450 }), - (96, SecurityWeights { slope: -0.03414780360867054, bias: 2.0173102586603733, minimal_lwe_dimension: 450 }), (112, SecurityWeights { slope: -0.02967013708113588, bias: 2.16246371408387, minimal_lwe_dimension: 450 }), (128, SecurityWeights { slope: -0.026405028765226296, bias: 2.482642269104389, minimal_lwe_dimension: 450 }), - (144, SecurityWeights { slope: -0.023821437305989134, bias: 2.7177789440636673, minimal_lwe_dimension: 450 }), - (160, SecurityWeights { slope: -0.021743582187160406, bias: 2.9388105484933504, minimal_lwe_dimension: 498 }), - (176, SecurityWeights { slope: -0.019904056582117705, bias: 2.8161252801542673, minimal_lwe_dimension: 551 }), (192, SecurityWeights { slope: -0.018610403247590064, bias: 3.2996236848399008, minimal_lwe_dimension: 606 }), - (256, SecurityWeights { slope: -0.014606812351714961, bias: 3.8493629234693145, minimal_lwe_dimension: 826 }), ] diff --git a/concrete-security-curves-rust/src/lib.rs b/concrete-security-curves-rust/src/lib.rs index 75890f2e7..d0c941f17 100644 --- a/concrete-security-curves-rust/src/lib.rs +++ b/concrete-security-curves-rust/src/lib.rs @@ -1,4 +1,4 @@ -const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 9] = include!("./curves.gen.rs"); +const SECURITY_WEIGHTS_ARRAY: [(u64, SecurityWeights); 4] = include!("./curves.gen.rs"); #[derive(Clone, Copy)] pub struct SecurityWeights { diff --git a/data/v0.1.sobj b/data/v0.1.sobj deleted file mode 100644 index 6b6d68553..000000000 Binary files a/data/v0.1.sobj and /dev/null differ diff --git a/data/v0.2/112.sobj b/data/v0.2/112.sobj deleted file mode 100644 index a50b2f74c..000000000 Binary files a/data/v0.2/112.sobj and /dev/null differ diff --git a/data/v0.2/128.sobj b/data/v0.2/128.sobj deleted file mode 100644 index 79a6242f3..000000000 Binary files a/data/v0.2/128.sobj and /dev/null differ diff --git a/data/v0.2/144.sobj b/data/v0.2/144.sobj deleted file mode 100644 index fb6e74386..000000000 Binary files a/data/v0.2/144.sobj and /dev/null differ diff --git a/data/v0.2/160.sobj b/data/v0.2/160.sobj deleted file mode 100644 index 005181dd1..000000000 Binary files a/data/v0.2/160.sobj and /dev/null differ diff --git a/data/v0.2/176.sobj b/data/v0.2/176.sobj deleted file mode 100644 index cfc0d5971..000000000 Binary files a/data/v0.2/176.sobj and /dev/null differ diff --git a/data/v0.2/192.sobj b/data/v0.2/192.sobj deleted file mode 100644 index 213644dde..000000000 Binary files a/data/v0.2/192.sobj and /dev/null differ diff --git a/data/v0.2/256.sobj b/data/v0.2/256.sobj deleted file mode 100644 index 168914b0a..000000000 Binary files a/data/v0.2/256.sobj and /dev/null differ diff --git a/data/v0.2/80.sobj b/data/v0.2/80.sobj deleted file mode 100644 index 14697f236..000000000 Binary files a/data/v0.2/80.sobj and /dev/null differ diff --git a/data/v0.2/96.sobj b/data/v0.2/96.sobj deleted file mode 100644 index aa776fb42..000000000 Binary files a/data/v0.2/96.sobj and /dev/null differ diff --git a/data/v0.sobj b/data/v0.sobj deleted file mode 100644 index 925b21485..000000000 Binary files a/data/v0.sobj and /dev/null differ diff --git a/job.sh b/job.sh deleted file mode 100755 index cfe8c2375..000000000 --- a/job.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh -# 80-bits -sage generate_data.py 80 2 12 0 -sage generate_data.py 80 12 22 0 -sage generate_data.py 80 22 32 0 -sage generate_data.py 80 32 42 0 -sage generate_data.py 80 42 52 0 -sage generate_data.py 80 52 59 0 -# 112-bits -sage generate_data.py 112 2 12 0 -sage generate_data.py 112 12 22 0 -sage generate_data.py 112 22 32 0 -sage generate_data.py 112 32 42 0 -sage generate_data.py 112 42 52 0 -sage generate_data.py 112 52 59 0 -# 128-bits -sage generate_data.py 128 2 12 0 -sage generate_data.py 128 12 22 0 -sage generate_data.py 128 22 32 0 -sage generate_data.py 128 32 42 0 -sage generate_data.py 128 42 52 0 -sage generate_data.py 128 52 59 0 -# 192-bits -sage generate_data.py 192 2 12 0 -sage generate_data.py 192 12 22 0 -sage generate_data.py 192 22 32 0 -sage generate_data.py 192 32 42 0 -sage generate_data.py 192 42 52 0 -sage generate_data.py 192 52 59 0 - - - -# 96-bits -#sage generate_data.py 96 2 12 0 -#sage generate_data.py 96 12 22 0 -#sage generate_data.py 96 22 32 0 -#sage generate_data.py 96 32 42 0 -#sage generate_data.py 96 42 52 0 -#sage generate_data.py 96 52 59 0 -# 144-bits -#sage generate_data.py 144 2 12 0 -#sage generate_data.py 144 12 22 0 -#sage generate_data.py 144 22 32 0 -#sage generate_data.py 144 32 42 0 -#sage generate_data.py 144 42 52 0 -#sage generate_data.py 144 52 59 0 -# 160-bits -#sage generate_data.py 160 2 12 0 -#sage generate_data.py 160 12 22 0 -#sage generate_data.py 160 22 32 0 -#sage generate_data.py 160 32 42 0 -#sage generate_data.py 160 42 52 0 -#sage generate_data.py 160 52 59 0 -# 176-bits -#sage generate_data.py 176 2 12 0 -#sage generate_data.py 176 12 22 0 -#sage generate_data.py 176 22 32 0 -#sage generate_data.py 176 32 42 0 -#sage generate_data.py 176 42 52 0 -#sage generate_data.py 176 52 59 0 -# 256-bits -#sage generate_data.py 256 2 12 0 -#sage generate_data.py 256 12 22 0 -#sage generate_data.py 256 22 32 0 -#sage generate_data.py 256 32 42 0 -#sage generate_data.py 256 42 52 0 -#sage generate_data.py 256 52 59 0 - - diff --git a/json/curves.json b/json/curves.json index 4c621f7ac..ea5bc7cc4 100644 --- a/json/curves.json +++ b/json/curves.json @@ -1 +1 @@ -[{"slope": -0.0404263311936459, "bias": 1.660978864143658, "security_level": 80, "minimal_lwe_dimension": 450}, {"slope": -0.03414780360867054, "bias": 2.0173102586603733, "security_level": 96, "minimal_lwe_dimension": 450}, {"slope": -0.02967013708113588, "bias": 2.16246371408387, "security_level": 112, "minimal_lwe_dimension": 450}, {"slope": -0.026405028765226296, "bias": 2.482642269104389, "security_level": 128, "minimal_lwe_dimension": 450}, {"slope": -0.023821437305989134, "bias": 2.7177789440636673, "security_level": 144, "minimal_lwe_dimension": 450}, {"slope": -0.021743582187160406, "bias": 2.9388105484933504, "security_level": 160, "minimal_lwe_dimension": 498}, {"slope": -0.019904056582117705, "bias": 2.8161252801542673, "security_level": 176, "minimal_lwe_dimension": 551}, {"slope": -0.018610403247590064, "bias": 3.2996236848399008, "security_level": 192, "minimal_lwe_dimension": 606}, {"slope": -0.014606812351714961, "bias": 3.8493629234693145, "security_level": 256, "minimal_lwe_dimension": 826}] +[{"slope": -0.0404263311936459, "bias": 1.660978864143658, "security_level": 80, "minimal_lwe_dimension": 450}, {"slope": -0.02967013708113588, "bias": 2.16246371408387, "security_level": 112, "minimal_lwe_dimension": 450}, {"slope": -0.026405028765226296, "bias": 2.482642269104389, "security_level": 128, "minimal_lwe_dimension": 450}, {"slope": -0.018610403247590064, "bias": 3.2996236848399008, "security_level": 192, "minimal_lwe_dimension": 606}] diff --git a/compare_curves_and_estimator.py b/lattice-scripts/compare_curves_and_estimator.py similarity index 79% rename from compare_curves_and_estimator.py rename to lattice-scripts/compare_curves_and_estimator.py index fd9f9bb6b..6e965f9f7 100644 --- a/compare_curves_and_estimator.py +++ b/lattice-scripts/compare_curves_and_estimator.py @@ -2,7 +2,9 @@ import sys sys.path.insert(1, 'lattice-estimator') from estimator import * from sage.all import oo, save, load, ceil, floor -from generate_data import estimate, get_security_level, old_models +from generate_data import estimate, get_security_level +import argparse +import os LOG_N_MAX = 17 + 1 @@ -67,7 +69,7 @@ def estimate_stddev_with_current_curve(curve, lwe_dimension, log_q): return stddev -def compare_curve_and_estimator(security_level, log_q): +def compare_curve_and_estimator(security_level, log_q, curves_dir): """ For a subset of every lwe dimension possibles, estimate the security of those lwe dimension associated with the stddev recommanded by our current curve. @@ -84,7 +86,7 @@ def compare_curve_and_estimator(security_level, log_q): print(f"Security Target: {security_level} bits") # step 0. loading the right curve - curves = load("verified_curves.sobj") + curves = load(os.path.join(curves_dir, "verified_curves.sobj")) j = get_index(security_level, curves) curve = curves[j] @@ -113,4 +115,27 @@ def compare_curve_and_estimator(security_level, log_q): return True if __name__ == "__main__": - compare_curve_and_estimator(128, 64) \ No newline at end of file + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--curves-dir", + help="The directory where curves has been saved (sage object)", + type=str, + required=True, + ) + CLI.add_argument( + "--security-levels", + help="The security levels to verify", + nargs="+", + type=int, + required=True + ) + CLI.add_argument( + "--log-q", + type=int, + required=True + ) + args = CLI.parse_args() + for security_level in args.security_levels: + if not(compare_curve_and_estimator(security_level, args.log_q, args.curves_dir)): + exit(1) + exit(0) \ No newline at end of file diff --git a/generate_data.py b/lattice-scripts/generate_data.py similarity index 87% rename from generate_data.py rename to lattice-scripts/generate_data.py index 808e1af58..735213300 100644 --- a/generate_data.py +++ b/lattice-scripts/generate_data.py @@ -1,10 +1,13 @@ import sys -sys.path.insert(1, '../lattice-estimator') +sys.path.insert(1, 'lattice-estimator') from estimator import * from sage.all import oo, save, load, ceil from math import log2 import multiprocessing +import argparse +import os +old_models_sobj = "" def old_models(security_level, sd, logq=32): """ @@ -22,7 +25,10 @@ def old_models(security_level, sd, logq=32): if curves[i][2] == sec: return i - curves = load("verified_curves.sobj") + if old_models_sobj is None or not(os.path.exists(old_models_sobj)): + return 450 + + curves = load(old_models_sobj) j = get_index(security_level, curves) a = curves[j][0] @@ -189,11 +195,44 @@ def generate_zama_curves64( return "done" if __name__ == "__main__": + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--security-level", + type=int, + required=True, + ) + CLI.add_argument( + "--output", + type=str, + required=True, + ) + CLI.add_argument( + "--old-models", + type=str, + ) + CLI.add_argument( + "--sd-min", + type=int, + required=True, + ) + CLI.add_argument( + "--sd-max", + type=int, + required=True, + ) + CLI.add_argument( + "--margin", + type=int, + default=0, + ) + args = CLI.parse_args() # The script runs the following commands # grab values of the command-line input arguments - security = int(sys.argv[1]) - sd_min = int(sys.argv[2]) - sd_max = int(sys.argv[3]) - margin = int(sys.argv[4]) + security = args.security_level + sd_min = args.sd_min + sd_max = args.sd_max + margin = args.margin + output = args.output + old_models_sobj = args.old_models # run the code generate_zama_curves64(sd_range=(sd_min, sd_max), target_security_levels=[security + margin], name="security_{}_margin_{} ".format(security, margin)) diff --git a/lattice-scripts/generate_data.sh b/lattice-scripts/generate_data.sh new file mode 100755 index 000000000..81640299e --- /dev/null +++ b/lattice-scripts/generate_data.sh @@ -0,0 +1,42 @@ +#!/bin/sh + +set -e + +output_dir="" +old_models="" +while : +do + case $1 in + --help) + echo "generate_data.sh -o [output_dir] [security_levels]" + exit 2 + ;; + --output) + output_dir="$2" + shift 2 + ;; + --old-models) + old_models="$2" + shift 2 + ;; + --) + break; + ;; + "") + break + ;; + *) + security_levels="$security_levels $1" + shift; + ;; + esac +done + +for security_level in $security_levels; do + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 2 --sd-max 12 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 12 --sd-max 22 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 22 --sd-max 32 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 32 --sd-max 42 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 42 --sd-max 52 --margin 0 + sage lattice-scripts/generate_data.py --output $output_dir/$security_level.sobj --old-models $old_models --security-level $security_level --sd-min 52 --sd-max 59 --margin 0 +done diff --git a/verify_curves.py b/lattice-scripts/verify_curves.py similarity index 55% rename from verify_curves.py rename to lattice-scripts/verify_curves.py index f6dd7e233..85af038c2 100644 --- a/verify_curves.py +++ b/lattice-scripts/verify_curves.py @@ -1,13 +1,15 @@ import numpy as np from sage.all import save, load, ceil import json +import os +import argparse -def sort_data(security_level): +def sort_data(security_level, curves_dir): from operator import itemgetter # step 1. load the data - X = load("{}.sobj".format(security_level)) + X = load(os.path.join(curves_dir, f"{security_level}.sobj")) # step 2. sort by SD x = sorted(X["{}".format(security_level)], key=itemgetter(2)) @@ -18,10 +20,10 @@ def sort_data(security_level): return X -def generate_curve(security_level): +def generate_curve(security_level, curves_dir): # step 1. get the data - X = sort_data(security_level) + X = sort_data(security_level, curves_dir) # step 2. group the n and sigma data into lists N = [] @@ -36,10 +38,10 @@ def generate_curve(security_level): return a, b -def verify_curve(security_level, a=None, b=None): +def verify_curve(security_level, a, b, curves_dir): # step 1. get the table and max values of n, sd - X = sort_data(security_level) + X = sort_data(security_level, curves_dir) n_max = X["{}".format(security_level)][0][0] # step 2. a function to get model values @@ -76,34 +78,55 @@ def verify_curve(security_level, a=None, b=None): return True, n_min -def generate_and_verify(security_levels, log_q, name="verified_curves"): - +def generate_and_verify(security_levels, log_q, curves_dir, name="verified_curves.sobj"): success = [] + json = [] fail = [] for sec in security_levels: #print("WE GO FOR {}".format(sec)) # generate the model for security level sec - (a_sec, b_sec) = generate_curve(sec) + (a_sec, b_sec) = generate_curve(sec, curves_dir) # verify the model for security level sec - (status, n_alpha) = verify_curve(sec, a_sec, b_sec) + (status, n_alpha) = verify_curve(sec, a_sec, b_sec, curves_dir) # append the information into a list - x = {"slope": a_sec, "bias": b_sec - log_q, "security_level": sec, "minimal_lwe_dimension": n_alpha} if status: - success.append(x) + json.append({"slope": a_sec, "bias": b_sec - log_q, "security_level": sec, "minimal_lwe_dimension": n_alpha}) + success.append((a_sec, b_sec - log_q, sec, a_sec, b_sec)) else: fail.append(x) - save(success, "{}.sobj".format(name)) + save(success, os.path.join(curves_dir, name)) - return success, fail + return json, fail -(success, fail) = generate_and_verify([80, 96, 112, 128, 144, 160, 176, 192, 256], log_q=64) -if (fail): - print("FAILURE: Fail to verify the following curves") - print(json.dumps(fail)) - exit(1) +if __name__ == "__main__": + CLI = argparse.ArgumentParser() + CLI.add_argument( + "--curves-dir", + help="The directory where curves has been saved (sage object)", + type=str, + required=True, + ) + CLI.add_argument( + "--security-levels", + help="The security levels to verify", + nargs="+", + type=int, + required=True + ) + CLI.add_argument( + "--log-q", + type=int, + required=True + ) + args = CLI.parse_args() + (success, fail) = generate_and_verify(args.security_levels, log_q=args.log_q, curves_dir=args.curves_dir) + if (fail): + print("FAILURE: Fail to verify the following curves") + print(json.dumps(fail)) + exit(1) -print(json.dumps(success)) + print(json.dumps(success)) diff --git a/112.sobj b/sage-object/112.sobj similarity index 100% rename from 112.sobj rename to sage-object/112.sobj diff --git a/128.sobj b/sage-object/128.sobj similarity index 100% rename from 128.sobj rename to sage-object/128.sobj diff --git a/144.sobj b/sage-object/144.sobj similarity index 100% rename from 144.sobj rename to sage-object/144.sobj diff --git a/160.sobj b/sage-object/160.sobj similarity index 100% rename from 160.sobj rename to sage-object/160.sobj diff --git a/176.sobj b/sage-object/176.sobj similarity index 100% rename from 176.sobj rename to sage-object/176.sobj diff --git a/192.sobj b/sage-object/192.sobj similarity index 100% rename from 192.sobj rename to sage-object/192.sobj diff --git a/256.sobj b/sage-object/256.sobj similarity index 100% rename from 256.sobj rename to sage-object/256.sobj diff --git a/80.sobj b/sage-object/80.sobj similarity index 100% rename from 80.sobj rename to sage-object/80.sobj diff --git a/96.sobj b/sage-object/96.sobj similarity index 100% rename from 96.sobj rename to sage-object/96.sobj diff --git a/sage-object/outdated_curves.timestamp b/sage-object/outdated_curves.timestamp new file mode 100644 index 000000000..e69de29bb diff --git a/sage-object/verified_curves.sobj b/sage-object/verified_curves.sobj new file mode 100644 index 000000000..8048bb4c0 Binary files /dev/null and b/sage-object/verified_curves.sobj differ diff --git a/verified_curves.sobj b/verified_curves.sobj deleted file mode 100644 index 331cd44ee..000000000 --- a/verified_curves.sobj +++ /dev/null @@ -1,2 +0,0 @@ -x]s QgF [}OۗږљiY,ԔQ*%ֲV`B)QGC0ë9{ow)tH;"tA(*$3ņx:4$ ~޳Yhog |1cT\pH4lUmlG4Gx,N8êVE3AR};1@` Z`aG]yvCܰ[@Up[K~jPWY x!19nFrKyIۏ'XK:Nad3 qC!P/Ǫ:M#00n[|%79`bM LDFӧM s?6ʻ6?T8P/!L/,[RǭA3n&alBdzsF|ՏH'0w$N&j"b%P%,+j- u`6 XEh0Ԭ.K SV%SW{חO oOkj=ؠ} 6;'ok2­f6VoK۸?|Cvh#v -".|o|>_ibV%XBԇ@ODͫ+^5wu*#.\c0ak]!. 8*JoG74KjK-țiYm$!%&d@P! No* \ No newline at end of file