From a7608b081e30f6fef5fd36935f8c8591381d4bde Mon Sep 17 00:00:00 2001 From: Jan Tattermusch Date: Tue, 3 Feb 2015 17:54:38 -0800 Subject: [PATCH] Prototype of gRPC C# library (Core and API examples) --- src/csharp/Grpc.sln | 38 + src/csharp/GrpcApi/.gitignore | 2 + src/csharp/GrpcApi/DummyMathServiceClient.cs | 74 + src/csharp/GrpcApi/Examples.cs | 97 ++ src/csharp/GrpcApi/GrpcApi.csproj | 64 + src/csharp/GrpcApi/IMathServiceClient.cs | 26 + src/csharp/GrpcApi/Math.cs | 1531 +++++++++++++++++ src/csharp/GrpcApi/MathServiceClientStub.cs | 75 + src/csharp/GrpcApi/Messages.cs | 35 + src/csharp/GrpcApi/Properties/AssemblyInfo.cs | 22 + src/csharp/GrpcApi/RecordingObserver.cs | 32 + src/csharp/GrpcApi/math.proto | 50 + src/csharp/GrpcCore/.gitignore | 1 + src/csharp/GrpcCore/Call.cs | 69 + src/csharp/GrpcCore/Calls.cs | 85 + src/csharp/GrpcCore/Channel.cs | 59 + .../GrpcCore/ClientStreamingAsyncResult.cs | 37 + src/csharp/GrpcCore/GrpcCore.csproj | 62 + src/csharp/GrpcCore/GrpcEnvironment.cs | 91 + src/csharp/GrpcCore/Internal/AsyncCall.cs | 485 ++++++ .../GrpcCore/Internal/CallSafeHandle.cs | 182 ++ .../GrpcCore/Internal/ChannelSafeHandle.cs | 34 + .../Internal/CompletionQueueSafeHandle.cs | 66 + src/csharp/GrpcCore/Internal/Enums.cs | 75 + src/csharp/GrpcCore/Internal/Event.cs | 191 ++ .../GrpcCore/Internal/GrpcThreadPool.cs | 129 ++ .../Internal/SafeHandleZeroIsInvalid.cs | 28 + .../GrpcCore/Internal/ServerSafeHandle.cs | 76 + .../Internal/StreamingInputObserver.cs | 33 + src/csharp/GrpcCore/Internal/Timespec.cs | 67 + .../GrpcCore/Properties/AssemblyInfo.cs | 24 + src/csharp/GrpcCore/RpcException.cs | 27 + src/csharp/GrpcCore/Server.cs | 141 ++ src/csharp/GrpcCore/Status.cs | 36 + src/csharp/GrpcCore/StatusCode.cs | 150 ++ src/csharp/GrpcCoreTests/.gitignore | 2 + src/csharp/GrpcCoreTests/ClientServerTest.cs | 48 + src/csharp/GrpcCoreTests/GrpcCoreTests.csproj | 53 + .../GrpcCoreTests/GrpcEnvironmentTest.cs | 18 + .../GrpcCoreTests/Properties/AssemblyInfo.cs | 22 + src/csharp/GrpcCoreTests/ServerTest.cs | 21 + src/csharp/GrpcCoreTests/TestResult.xml | 41 + src/csharp/GrpcCoreTests/TimespecTest.cs | 43 + src/csharp/GrpcCoreTests/Utils.cs | 51 + src/csharp/GrpcDemo/.gitignore | 1 + src/csharp/GrpcDemo/GrpcDemo.csproj | 52 + src/csharp/GrpcDemo/Program.cs | 28 + .../GrpcDemo/Properties/AssemblyInfo.cs | 22 + src/csharp/README.md | 4 + src/csharp/lib/Google.ProtocolBuffers.dll | Bin 0 -> 380416 bytes 50 files changed, 4600 insertions(+) create mode 100644 src/csharp/Grpc.sln create mode 100644 src/csharp/GrpcApi/.gitignore create mode 100644 src/csharp/GrpcApi/DummyMathServiceClient.cs create mode 100644 src/csharp/GrpcApi/Examples.cs create mode 100644 src/csharp/GrpcApi/GrpcApi.csproj create mode 100644 src/csharp/GrpcApi/IMathServiceClient.cs create mode 100644 src/csharp/GrpcApi/Math.cs create mode 100644 src/csharp/GrpcApi/MathServiceClientStub.cs create mode 100644 src/csharp/GrpcApi/Messages.cs create mode 100644 src/csharp/GrpcApi/Properties/AssemblyInfo.cs create mode 100644 src/csharp/GrpcApi/RecordingObserver.cs create mode 100755 src/csharp/GrpcApi/math.proto create mode 100644 src/csharp/GrpcCore/.gitignore create mode 100644 src/csharp/GrpcCore/Call.cs create mode 100644 src/csharp/GrpcCore/Calls.cs create mode 100644 src/csharp/GrpcCore/Channel.cs create mode 100644 src/csharp/GrpcCore/ClientStreamingAsyncResult.cs create mode 100644 src/csharp/GrpcCore/GrpcCore.csproj create mode 100644 src/csharp/GrpcCore/GrpcEnvironment.cs create mode 100644 src/csharp/GrpcCore/Internal/AsyncCall.cs create mode 100644 src/csharp/GrpcCore/Internal/CallSafeHandle.cs create mode 100644 src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs create mode 100644 src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs create mode 100644 src/csharp/GrpcCore/Internal/Enums.cs create mode 100644 src/csharp/GrpcCore/Internal/Event.cs create mode 100644 src/csharp/GrpcCore/Internal/GrpcThreadPool.cs create mode 100644 src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs create mode 100644 src/csharp/GrpcCore/Internal/ServerSafeHandle.cs create mode 100644 src/csharp/GrpcCore/Internal/StreamingInputObserver.cs create mode 100644 src/csharp/GrpcCore/Internal/Timespec.cs create mode 100644 src/csharp/GrpcCore/Properties/AssemblyInfo.cs create mode 100644 src/csharp/GrpcCore/RpcException.cs create mode 100644 src/csharp/GrpcCore/Server.cs create mode 100644 src/csharp/GrpcCore/Status.cs create mode 100644 src/csharp/GrpcCore/StatusCode.cs create mode 100644 src/csharp/GrpcCoreTests/.gitignore create mode 100644 src/csharp/GrpcCoreTests/ClientServerTest.cs create mode 100644 src/csharp/GrpcCoreTests/GrpcCoreTests.csproj create mode 100644 src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs create mode 100644 src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs create mode 100644 src/csharp/GrpcCoreTests/ServerTest.cs create mode 100644 src/csharp/GrpcCoreTests/TestResult.xml create mode 100644 src/csharp/GrpcCoreTests/TimespecTest.cs create mode 100644 src/csharp/GrpcCoreTests/Utils.cs create mode 100644 src/csharp/GrpcDemo/.gitignore create mode 100644 src/csharp/GrpcDemo/GrpcDemo.csproj create mode 100644 src/csharp/GrpcDemo/Program.cs create mode 100644 src/csharp/GrpcDemo/Properties/AssemblyInfo.cs create mode 100755 src/csharp/lib/Google.ProtocolBuffers.dll diff --git a/src/csharp/Grpc.sln b/src/csharp/Grpc.sln new file mode 100644 index 00000000000..5890617acf5 --- /dev/null +++ b/src/csharp/Grpc.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcDemo", "GrpcDemo\GrpcDemo.csproj", "{61ECB8EE-0C96-4F8E-B187-8E4D227417C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcApi", "GrpcApi\GrpcApi.csproj", "{7DC1433E-3225-42C7-B7EA-546D56E27A4B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCore", "GrpcCore\GrpcCore.csproj", "{CCC4440E-49F7-4790-B0AF-FEABB0837AE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GrpcCoreTests", "GrpcCoreTests\GrpcCoreTests.csproj", "{86EC5CB4-4EA2-40A2-8057-86542A0353BB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.ActiveCfg = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Debug|x86.Build.0 = Debug|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.ActiveCfg = Release|x86 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0}.Release|x86.Build.0 = Release|x86 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Debug|x86.Build.0 = Debug|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.ActiveCfg = Release|Any CPU + {7DC1433E-3225-42C7-B7EA-546D56E27A4B}.Release|x86.Build.0 = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.ActiveCfg = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Debug|x86.Build.0 = Debug|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.ActiveCfg = Release|Any CPU + {86EC5CB4-4EA2-40A2-8057-86542A0353BB}.Release|x86.Build.0 = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Debug|x86.Build.0 = Debug|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.ActiveCfg = Release|Any CPU + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = GrpcDemo\GrpcDemo.csproj + EndGlobalSection +EndGlobal diff --git a/src/csharp/GrpcApi/.gitignore b/src/csharp/GrpcApi/.gitignore new file mode 100644 index 00000000000..2cc8cca52d0 --- /dev/null +++ b/src/csharp/GrpcApi/.gitignore @@ -0,0 +1,2 @@ +test-results +bin diff --git a/src/csharp/GrpcApi/DummyMathServiceClient.cs b/src/csharp/GrpcApi/DummyMathServiceClient.cs new file mode 100644 index 00000000000..6799109be42 --- /dev/null +++ b/src/csharp/GrpcApi/DummyMathServiceClient.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace math +{ +// /// +// /// Dummy local implementation of math service. +// /// +// public class DummyMathServiceClient : IMathServiceClient +// { +// public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)) +// { +// // TODO: cancellation... +// return DivInternal(args); +// } +// +// public Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)) +// { +// return Task.Factory.StartNew(() => DivInternal(args), token); +// } +// +// public IObservable Fib(FibArgs args, CancellationToken token = default(CancellationToken)) +// { +// if (args.Limit > 0) +// { +// // TODO: cancellation +// return FibInternal(args.Limit).ToObservable(); +// } +// +// throw new NotImplementedException("Not implemented yet"); +// } +// +// public Task Sum(IObservable inputs, CancellationToken token = default(CancellationToken)) +// { +// // TODO: implement +// inputs = null; +// return Task.Factory.StartNew(() => Num.CreateBuilder().Build(), token); +// } +// +// public IObservable DivMany(IObservable inputs, CancellationToken token = default(CancellationToken)) +// { +// // TODO: implement +// inputs = null; +// return new List { }.ToObservable (); +// } +// +// +// DivReply DivInternal(DivArgs args) +// { +// long quotient = args.Dividend / args.Divisor; +// long remainder = args.Dividend % args.Divisor; +// return new DivReply.Builder{ Quotient = quotient, Remainder = remainder }.Build(); +// } +// +// IEnumerable FibInternal(long n) +// { +// long a = 0; +// yield return new Num.Builder{Num_=a}.Build(); +// +// long b = 1; +// for (long i = 0; i < n - 1; i++) +// { +// long temp = a; +// a = b; +// b = temp + b; +// yield return new Num.Builder{Num_=a}.Build(); +// } +// } +// } +} + diff --git a/src/csharp/GrpcApi/Examples.cs b/src/csharp/GrpcApi/Examples.cs new file mode 100644 index 00000000000..d45b702708e --- /dev/null +++ b/src/csharp/GrpcApi/Examples.cs @@ -0,0 +1,97 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; + +namespace math +{ + public class Examples + { + public static void DivExample(IMathServiceClient stub) + { + DivReply result = stub.Div(new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build()); + Console.WriteLine("Div Result: " + result); + } + + public static void DivAsyncExample(IMathServiceClient stub) + { + Task call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); + DivReply result = call.Result; + Console.WriteLine(result); + } + + public static void DivAsyncWithCancellationExample(IMathServiceClient stub) + { + Task call = stub.DivAsync(new DivArgs.Builder { Dividend = 4, Divisor = 5 }.Build()); + DivReply result = call.Result; + Console.WriteLine(result); + } + + public static void FibExample(IMathServiceClient stub) + { + var recorder = new RecordingObserver(); + stub.Fib(new FibArgs.Builder { Limit = 5 }.Build(), recorder); + + List numbers = recorder.ToList().Result; + Console.WriteLine("Fib Result: " + string.Join("|", recorder.ToList().Result)); + } + + public static void SumExample(IMathServiceClient stub) + { + List numbers = new List{new Num.Builder { Num_ = 1 }.Build(), + new Num.Builder { Num_ = 2 }.Build(), + new Num.Builder { Num_ = 3 }.Build()}; + + var res = stub.Sum(); + foreach (var num in numbers) { + res.Inputs.OnNext(num); + } + res.Inputs.OnCompleted(); + + Console.WriteLine("Sum Result: " + res.Task.Result); + } + + public static void DivManyExample(IMathServiceClient stub) + { + List divArgsList = new List{ + new DivArgs.Builder { Dividend = 10, Divisor = 3 }.Build(), + new DivArgs.Builder { Dividend = 100, Divisor = 21 }.Build(), + new DivArgs.Builder { Dividend = 7, Divisor = 2 }.Build() + }; + + var recorder = new RecordingObserver(); + + var inputs = stub.DivMany(recorder); + foreach (var input in divArgsList) + { + inputs.OnNext(input); + } + inputs.OnCompleted(); + + Console.WriteLine("DivMany Result: " + string.Join("|", recorder.ToList().Result)); + } + + public static void DependendRequestsExample(IMathServiceClient stub) + { + var numberList = new List + { new Num.Builder{ Num_ = 1 }.Build(), + new Num.Builder{ Num_ = 2 }.Build(), new Num.Builder{ Num_ = 3 }.Build() + }; + + numberList.ToObservable(); + + //IObserver numbers; + //Task call = stub.Sum(out numbers); + //foreach (var num in numberList) + //{ + // numbers.OnNext(num); + //} + //numbers.OnCompleted(); + + //Num sum = call.Result; + + //DivReply result = stub.Div(new DivArgs.Builder { Dividend = sum.Num_, Divisor = numberList.Count }.Build()); + } + } +} + diff --git a/src/csharp/GrpcApi/GrpcApi.csproj b/src/csharp/GrpcApi/GrpcApi.csproj new file mode 100644 index 00000000000..d0377828b54 --- /dev/null +++ b/src/csharp/GrpcApi/GrpcApi.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + Library + GrpcApi + GrpcApi + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + False + + + + False + + + False + + + ..\lib\Google.ProtocolBuffers.dll + + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcApi/IMathServiceClient.cs b/src/csharp/GrpcApi/IMathServiceClient.cs new file mode 100644 index 00000000000..51385a328f4 --- /dev/null +++ b/src/csharp/GrpcApi/IMathServiceClient.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; +using Google.GRPC.Core; + +namespace math +{ + /// + /// Hand-written stub for MathService defined in math.proto. + /// This code will be generated by gRPC codegen in the future. + /// + public interface IMathServiceClient + { + DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)); + + Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)); + + Task Fib(FibArgs args, IObserver outputs, CancellationToken token = default(CancellationToken)); + + ClientStreamingAsyncResult Sum(CancellationToken token = default(CancellationToken)); + + IObserver DivMany(IObserver outputs, CancellationToken token = default(CancellationToken)); + } +} \ No newline at end of file diff --git a/src/csharp/GrpcApi/Math.cs b/src/csharp/GrpcApi/Math.cs new file mode 100644 index 00000000000..2d700337ac7 --- /dev/null +++ b/src/csharp/GrpcApi/Math.cs @@ -0,0 +1,1531 @@ +// Generated by ProtoGen, Version=2.4.1.521, Culture=neutral, PublicKeyToken=17b3b1f090c3ea48. DO NOT EDIT! +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.ProtocolBuffers; +using pbc = global::Google.ProtocolBuffers.Collections; +using pbd = global::Google.ProtocolBuffers.Descriptors; +using scg = global::System.Collections.Generic; +namespace math { + + namespace Proto { + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public static partial class Math { + + #region Extension registration + public static void RegisterAllExtensions(pb::ExtensionRegistry registry) { + } + #endregion + #region Static variables + internal static pbd::MessageDescriptor internal__static_math_DivArgs__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_DivArgs__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_DivReply__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_DivReply__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_FibArgs__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_FibArgs__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_Num__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_Num__FieldAccessorTable; + internal static pbd::MessageDescriptor internal__static_math_FibReply__Descriptor; + internal static pb::FieldAccess.FieldAccessorTable internal__static_math_FibReply__FieldAccessorTable; + #endregion + #region Descriptor + public static pbd::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbd::FileDescriptor descriptor; + + static Math() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "CgptYXRoLnByb3RvEgRtYXRoIiwKB0RpdkFyZ3MSEAoIZGl2aWRlbmQYASAB", + "KAMSDwoHZGl2aXNvchgCIAEoAyIvCghEaXZSZXBseRIQCghxdW90aWVudBgB", + "IAEoAxIRCglyZW1haW5kZXIYAiABKAMiGAoHRmliQXJncxINCgVsaW1pdBgB", + "IAEoAyISCgNOdW0SCwoDbnVtGAEgASgDIhkKCEZpYlJlcGx5Eg0KBWNvdW50", + "GAEgASgDMqQBCgRNYXRoEiYKA0RpdhINLm1hdGguRGl2QXJncxoOLm1hdGgu", + "RGl2UmVwbHkiABIuCgdEaXZNYW55Eg0ubWF0aC5EaXZBcmdzGg4ubWF0aC5E", + "aXZSZXBseSIAKAEwARIjCgNGaWISDS5tYXRoLkZpYkFyZ3MaCS5tYXRoLk51", + "bSIAMAESHwoDU3VtEgkubWF0aC5OdW0aCS5tYXRoLk51bSIAKAE=")); + pbd::FileDescriptor.InternalDescriptorAssigner assigner = delegate(pbd::FileDescriptor root) { + descriptor = root; + internal__static_math_DivArgs__Descriptor = Descriptor.MessageTypes[0]; + internal__static_math_DivArgs__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_DivArgs__Descriptor, + new string[] { "Dividend", "Divisor", }); + internal__static_math_DivReply__Descriptor = Descriptor.MessageTypes[1]; + internal__static_math_DivReply__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_DivReply__Descriptor, + new string[] { "Quotient", "Remainder", }); + internal__static_math_FibArgs__Descriptor = Descriptor.MessageTypes[2]; + internal__static_math_FibArgs__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_FibArgs__Descriptor, + new string[] { "Limit", }); + internal__static_math_Num__Descriptor = Descriptor.MessageTypes[3]; + internal__static_math_Num__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_Num__Descriptor, + new string[] { "Num_", }); + internal__static_math_FibReply__Descriptor = Descriptor.MessageTypes[4]; + internal__static_math_FibReply__FieldAccessorTable = + new pb::FieldAccess.FieldAccessorTable(internal__static_math_FibReply__Descriptor, + new string[] { "Count", }); + pb::ExtensionRegistry registry = pb::ExtensionRegistry.CreateInstance(); + RegisterAllExtensions(registry); + return registry; + }; + pbd::FileDescriptor.InternalBuildGeneratedFileFrom(descriptorData, + new pbd::FileDescriptor[] { + }, assigner); + } + #endregion + + } + } + #region Messages + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivArgs : pb::GeneratedMessage { + private DivArgs() { } + private static readonly DivArgs defaultInstance = new DivArgs().MakeReadOnly(); + private static readonly string[] _divArgsFieldNames = new string[] { "dividend", "divisor" }; + private static readonly uint[] _divArgsFieldTags = new uint[] { 8, 16 }; + public static DivArgs DefaultInstance { + get { return defaultInstance; } + } + + public override DivArgs DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override DivArgs ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_DivArgs__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_DivArgs__FieldAccessorTable; } + } + + public const int DividendFieldNumber = 1; + private bool hasDividend; + private long dividend_; + public bool HasDividend { + get { return hasDividend; } + } + public long Dividend { + get { return dividend_; } + } + + public const int DivisorFieldNumber = 2; + private bool hasDivisor; + private long divisor_; + public bool HasDivisor { + get { return hasDivisor; } + } + public long Divisor { + get { return divisor_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _divArgsFieldNames; + if (hasDividend) { + output.WriteInt64(1, field_names[0], Dividend); + } + if (hasDivisor) { + output.WriteInt64(2, field_names[1], Divisor); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasDividend) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Dividend); + } + if (hasDivisor) { + size += pb::CodedOutputStream.ComputeInt64Size(2, Divisor); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static DivArgs ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static DivArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private DivArgs MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(DivArgs prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(DivArgs cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private DivArgs result; + + private DivArgs PrepareBuilder() { + if (resultIsReadOnly) { + DivArgs original = result; + result = new DivArgs(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override DivArgs MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.DivArgs.Descriptor; } + } + + public override DivArgs DefaultInstanceForType { + get { return global::math.DivArgs.DefaultInstance; } + } + + public override DivArgs BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is DivArgs) { + return MergeFrom((DivArgs) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(DivArgs other) { + if (other == global::math.DivArgs.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasDividend) { + Dividend = other.Dividend; + } + if (other.HasDivisor) { + Divisor = other.Divisor; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_divArgsFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _divArgsFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasDividend = input.ReadInt64(ref result.dividend_); + break; + } + case 16: { + result.hasDivisor = input.ReadInt64(ref result.divisor_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasDividend { + get { return result.hasDividend; } + } + public long Dividend { + get { return result.Dividend; } + set { SetDividend(value); } + } + public Builder SetDividend(long value) { + PrepareBuilder(); + result.hasDividend = true; + result.dividend_ = value; + return this; + } + public Builder ClearDividend() { + PrepareBuilder(); + result.hasDividend = false; + result.dividend_ = 0L; + return this; + } + + public bool HasDivisor { + get { return result.hasDivisor; } + } + public long Divisor { + get { return result.Divisor; } + set { SetDivisor(value); } + } + public Builder SetDivisor(long value) { + PrepareBuilder(); + result.hasDivisor = true; + result.divisor_ = value; + return this; + } + public Builder ClearDivisor() { + PrepareBuilder(); + result.hasDivisor = false; + result.divisor_ = 0L; + return this; + } + } + static DivArgs() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class DivReply : pb::GeneratedMessage { + private DivReply() { } + private static readonly DivReply defaultInstance = new DivReply().MakeReadOnly(); + private static readonly string[] _divReplyFieldNames = new string[] { "quotient", "remainder" }; + private static readonly uint[] _divReplyFieldTags = new uint[] { 8, 16 }; + public static DivReply DefaultInstance { + get { return defaultInstance; } + } + + public override DivReply DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override DivReply ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_DivReply__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_DivReply__FieldAccessorTable; } + } + + public const int QuotientFieldNumber = 1; + private bool hasQuotient; + private long quotient_; + public bool HasQuotient { + get { return hasQuotient; } + } + public long Quotient { + get { return quotient_; } + } + + public const int RemainderFieldNumber = 2; + private bool hasRemainder; + private long remainder_; + public bool HasRemainder { + get { return hasRemainder; } + } + public long Remainder { + get { return remainder_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _divReplyFieldNames; + if (hasQuotient) { + output.WriteInt64(1, field_names[0], Quotient); + } + if (hasRemainder) { + output.WriteInt64(2, field_names[1], Remainder); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasQuotient) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Quotient); + } + if (hasRemainder) { + size += pb::CodedOutputStream.ComputeInt64Size(2, Remainder); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static DivReply ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static DivReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static DivReply ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static DivReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static DivReply ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static DivReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private DivReply MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(DivReply prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(DivReply cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private DivReply result; + + private DivReply PrepareBuilder() { + if (resultIsReadOnly) { + DivReply original = result; + result = new DivReply(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override DivReply MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.DivReply.Descriptor; } + } + + public override DivReply DefaultInstanceForType { + get { return global::math.DivReply.DefaultInstance; } + } + + public override DivReply BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is DivReply) { + return MergeFrom((DivReply) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(DivReply other) { + if (other == global::math.DivReply.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasQuotient) { + Quotient = other.Quotient; + } + if (other.HasRemainder) { + Remainder = other.Remainder; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_divReplyFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _divReplyFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasQuotient = input.ReadInt64(ref result.quotient_); + break; + } + case 16: { + result.hasRemainder = input.ReadInt64(ref result.remainder_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasQuotient { + get { return result.hasQuotient; } + } + public long Quotient { + get { return result.Quotient; } + set { SetQuotient(value); } + } + public Builder SetQuotient(long value) { + PrepareBuilder(); + result.hasQuotient = true; + result.quotient_ = value; + return this; + } + public Builder ClearQuotient() { + PrepareBuilder(); + result.hasQuotient = false; + result.quotient_ = 0L; + return this; + } + + public bool HasRemainder { + get { return result.hasRemainder; } + } + public long Remainder { + get { return result.Remainder; } + set { SetRemainder(value); } + } + public Builder SetRemainder(long value) { + PrepareBuilder(); + result.hasRemainder = true; + result.remainder_ = value; + return this; + } + public Builder ClearRemainder() { + PrepareBuilder(); + result.hasRemainder = false; + result.remainder_ = 0L; + return this; + } + } + static DivReply() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibArgs : pb::GeneratedMessage { + private FibArgs() { } + private static readonly FibArgs defaultInstance = new FibArgs().MakeReadOnly(); + private static readonly string[] _fibArgsFieldNames = new string[] { "limit" }; + private static readonly uint[] _fibArgsFieldTags = new uint[] { 8 }; + public static FibArgs DefaultInstance { + get { return defaultInstance; } + } + + public override FibArgs DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override FibArgs ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_FibArgs__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_FibArgs__FieldAccessorTable; } + } + + public const int LimitFieldNumber = 1; + private bool hasLimit; + private long limit_; + public bool HasLimit { + get { return hasLimit; } + } + public long Limit { + get { return limit_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _fibArgsFieldNames; + if (hasLimit) { + output.WriteInt64(1, field_names[0], Limit); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasLimit) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Limit); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static FibArgs ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibArgs ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibArgs ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static FibArgs ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibArgs ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private FibArgs MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(FibArgs prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(FibArgs cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private FibArgs result; + + private FibArgs PrepareBuilder() { + if (resultIsReadOnly) { + FibArgs original = result; + result = new FibArgs(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override FibArgs MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.FibArgs.Descriptor; } + } + + public override FibArgs DefaultInstanceForType { + get { return global::math.FibArgs.DefaultInstance; } + } + + public override FibArgs BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is FibArgs) { + return MergeFrom((FibArgs) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(FibArgs other) { + if (other == global::math.FibArgs.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasLimit) { + Limit = other.Limit; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_fibArgsFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _fibArgsFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasLimit = input.ReadInt64(ref result.limit_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasLimit { + get { return result.hasLimit; } + } + public long Limit { + get { return result.Limit; } + set { SetLimit(value); } + } + public Builder SetLimit(long value) { + PrepareBuilder(); + result.hasLimit = true; + result.limit_ = value; + return this; + } + public Builder ClearLimit() { + PrepareBuilder(); + result.hasLimit = false; + result.limit_ = 0L; + return this; + } + } + static FibArgs() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Num : pb::GeneratedMessage { + private Num() { } + private static readonly Num defaultInstance = new Num().MakeReadOnly(); + private static readonly string[] _numFieldNames = new string[] { "num" }; + private static readonly uint[] _numFieldTags = new uint[] { 8 }; + public static Num DefaultInstance { + get { return defaultInstance; } + } + + public override Num DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override Num ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_Num__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_Num__FieldAccessorTable; } + } + + public const int Num_FieldNumber = 1; + private bool hasNum_; + private long num_; + public bool HasNum_ { + get { return hasNum_; } + } + public long Num_ { + get { return num_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _numFieldNames; + if (hasNum_) { + output.WriteInt64(1, field_names[0], Num_); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasNum_) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Num_); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static Num ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static Num ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static Num ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static Num ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static Num ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static Num ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static Num ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static Num ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static Num ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static Num ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private Num MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(Num prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(Num cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private Num result; + + private Num PrepareBuilder() { + if (resultIsReadOnly) { + Num original = result; + result = new Num(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override Num MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.Num.Descriptor; } + } + + public override Num DefaultInstanceForType { + get { return global::math.Num.DefaultInstance; } + } + + public override Num BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is Num) { + return MergeFrom((Num) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(Num other) { + if (other == global::math.Num.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasNum_) { + Num_ = other.Num_; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_numFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _numFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasNum_ = input.ReadInt64(ref result.num_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasNum_ { + get { return result.hasNum_; } + } + public long Num_ { + get { return result.Num_; } + set { SetNum_(value); } + } + public Builder SetNum_(long value) { + PrepareBuilder(); + result.hasNum_ = true; + result.num_ = value; + return this; + } + public Builder ClearNum_() { + PrepareBuilder(); + result.hasNum_ = false; + result.num_ = 0L; + return this; + } + } + static Num() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class FibReply : pb::GeneratedMessage { + private FibReply() { } + private static readonly FibReply defaultInstance = new FibReply().MakeReadOnly(); + private static readonly string[] _fibReplyFieldNames = new string[] { "count" }; + private static readonly uint[] _fibReplyFieldTags = new uint[] { 8 }; + public static FibReply DefaultInstance { + get { return defaultInstance; } + } + + public override FibReply DefaultInstanceForType { + get { return DefaultInstance; } + } + + protected override FibReply ThisMessage { + get { return this; } + } + + public static pbd::MessageDescriptor Descriptor { + get { return global::math.Proto.Math.internal__static_math_FibReply__Descriptor; } + } + + protected override pb::FieldAccess.FieldAccessorTable InternalFieldAccessors { + get { return global::math.Proto.Math.internal__static_math_FibReply__FieldAccessorTable; } + } + + public const int CountFieldNumber = 1; + private bool hasCount; + private long count_; + public bool HasCount { + get { return hasCount; } + } + public long Count { + get { return count_; } + } + + public override bool IsInitialized { + get { + return true; + } + } + + public override void WriteTo(pb::ICodedOutputStream output) { + int size = SerializedSize; + string[] field_names = _fibReplyFieldNames; + if (hasCount) { + output.WriteInt64(1, field_names[0], Count); + } + UnknownFields.WriteTo(output); + } + + private int memoizedSerializedSize = -1; + public override int SerializedSize { + get { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (hasCount) { + size += pb::CodedOutputStream.ComputeInt64Size(1, Count); + } + size += UnknownFields.SerializedSize; + memoizedSerializedSize = size; + return size; + } + } + + public static FibReply ParseFrom(pb::ByteString data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibReply ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseFrom(byte[] data) { + return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed(); + } + public static FibReply ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseFrom(global::System.IO.Stream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibReply ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + public static FibReply ParseDelimitedFrom(global::System.IO.Stream input) { + return CreateBuilder().MergeDelimitedFrom(input).BuildParsed(); + } + public static FibReply ParseDelimitedFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) { + return CreateBuilder().MergeDelimitedFrom(input, extensionRegistry).BuildParsed(); + } + public static FibReply ParseFrom(pb::ICodedInputStream input) { + return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed(); + } + public static FibReply ParseFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed(); + } + private FibReply MakeReadOnly() { + return this; + } + + public static Builder CreateBuilder() { return new Builder(); } + public override Builder ToBuilder() { return CreateBuilder(this); } + public override Builder CreateBuilderForType() { return new Builder(); } + public static Builder CreateBuilder(FibReply prototype) { + return new Builder(prototype); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public sealed partial class Builder : pb::GeneratedBuilder { + protected override Builder ThisBuilder { + get { return this; } + } + public Builder() { + result = DefaultInstance; + resultIsReadOnly = true; + } + internal Builder(FibReply cloneFrom) { + result = cloneFrom; + resultIsReadOnly = true; + } + + private bool resultIsReadOnly; + private FibReply result; + + private FibReply PrepareBuilder() { + if (resultIsReadOnly) { + FibReply original = result; + result = new FibReply(); + resultIsReadOnly = false; + MergeFrom(original); + } + return result; + } + + public override bool IsInitialized { + get { return result.IsInitialized; } + } + + protected override FibReply MessageBeingBuilt { + get { return PrepareBuilder(); } + } + + public override Builder Clear() { + result = DefaultInstance; + resultIsReadOnly = true; + return this; + } + + public override Builder Clone() { + if (resultIsReadOnly) { + return new Builder(result); + } else { + return new Builder().MergeFrom(result); + } + } + + public override pbd::MessageDescriptor DescriptorForType { + get { return global::math.FibReply.Descriptor; } + } + + public override FibReply DefaultInstanceForType { + get { return global::math.FibReply.DefaultInstance; } + } + + public override FibReply BuildPartial() { + if (resultIsReadOnly) { + return result; + } + resultIsReadOnly = true; + return result.MakeReadOnly(); + } + + public override Builder MergeFrom(pb::IMessage other) { + if (other is FibReply) { + return MergeFrom((FibReply) other); + } else { + base.MergeFrom(other); + return this; + } + } + + public override Builder MergeFrom(FibReply other) { + if (other == global::math.FibReply.DefaultInstance) return this; + PrepareBuilder(); + if (other.HasCount) { + Count = other.Count; + } + this.MergeUnknownFields(other.UnknownFields); + return this; + } + + public override Builder MergeFrom(pb::ICodedInputStream input) { + return MergeFrom(input, pb::ExtensionRegistry.Empty); + } + + public override Builder MergeFrom(pb::ICodedInputStream input, pb::ExtensionRegistry extensionRegistry) { + PrepareBuilder(); + pb::UnknownFieldSet.Builder unknownFields = null; + uint tag; + string field_name; + while (input.ReadTag(out tag, out field_name)) { + if(tag == 0 && field_name != null) { + int field_ordinal = global::System.Array.BinarySearch(_fibReplyFieldNames, field_name, global::System.StringComparer.Ordinal); + if(field_ordinal >= 0) + tag = _fibReplyFieldTags[field_ordinal]; + else { + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + continue; + } + } + switch (tag) { + case 0: { + throw pb::InvalidProtocolBufferException.InvalidTag(); + } + default: { + if (pb::WireFormat.IsEndGroupTag(tag)) { + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + if (unknownFields == null) { + unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields); + } + ParseUnknownField(input, unknownFields, extensionRegistry, tag, field_name); + break; + } + case 8: { + result.hasCount = input.ReadInt64(ref result.count_); + break; + } + } + } + + if (unknownFields != null) { + this.UnknownFields = unknownFields.Build(); + } + return this; + } + + + public bool HasCount { + get { return result.hasCount; } + } + public long Count { + get { return result.Count; } + set { SetCount(value); } + } + public Builder SetCount(long value) { + PrepareBuilder(); + result.hasCount = true; + result.count_ = value; + return this; + } + public Builder ClearCount() { + PrepareBuilder(); + result.hasCount = false; + result.count_ = 0L; + return this; + } + } + static FibReply() { + object.ReferenceEquals(global::math.Proto.Math.Descriptor, null); + } + } + + #endregion + + #region Services + /* + * Service generation is now disabled by default, use the following option to enable: + * option (google.protobuf.csharp_file_options).service_generator_type = GENERIC; + */ + #endregion + +} + +#endregion Designer generated code diff --git a/src/csharp/GrpcApi/MathServiceClientStub.cs b/src/csharp/GrpcApi/MathServiceClientStub.cs new file mode 100644 index 00000000000..493c186b8e5 --- /dev/null +++ b/src/csharp/GrpcApi/MathServiceClientStub.cs @@ -0,0 +1,75 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; +using System.Reactive.Linq; +using Google.GRPC.Core; + +namespace math +{ + /// + /// Implementation of math service stub (this is handwritten version of code + /// that will normally be generated). + /// + public class MathServiceClientStub : IMathServiceClient + { + readonly Channel channel; + readonly TimeSpan methodTimeout; + + public MathServiceClientStub(Channel channel, TimeSpan methodTimeout) + { + this.channel = channel; + this.methodTimeout = methodTimeout; + } + + public DivReply Div(DivArgs args, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.BlockingUnaryCall(call, args, token); + } + + public Task DivAsync(DivArgs args, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Div", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.AsyncUnaryCall(call, args, token); + } + + public Task Fib(FibArgs args, IObserver outputs, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Fib", Serialize_FibArgs, Deserialize_Num, methodTimeout, channel); + return Calls.AsyncServerStreamingCall(call, args, outputs, token); + } + + public ClientStreamingAsyncResult Sum(CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/Sum", Serialize_Num, Deserialize_Num, methodTimeout, channel); + return Calls.AsyncClientStreamingCall(call, token); + } + + public IObserver DivMany(IObserver outputs, CancellationToken token = default(CancellationToken)) + { + var call = new Google.GRPC.Core.Call("/math.Math/DivMany", Serialize_DivArgs, Deserialize_DivReply, methodTimeout, channel); + return Calls.DuplexStreamingCall(call, outputs, token); + } + + private static byte[] Serialize_DivArgs(DivArgs arg) { + return arg.ToByteArray(); + } + + private static byte[] Serialize_FibArgs(FibArgs arg) { + return arg.ToByteArray(); + } + + private static byte[] Serialize_Num(Num arg) { + return arg.ToByteArray(); + } + + private static DivReply Deserialize_DivReply(byte[] payload) { + return DivReply.CreateBuilder().MergeFrom(payload).Build(); + } + + private static Num Deserialize_Num(byte[] payload) { + return Num.CreateBuilder().MergeFrom(payload).Build(); + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcApi/Messages.cs b/src/csharp/GrpcApi/Messages.cs new file mode 100644 index 00000000000..b08816bdb7f --- /dev/null +++ b/src/csharp/GrpcApi/Messages.cs @@ -0,0 +1,35 @@ +//using System; + +//namespace Google.GRPC.Examples.Math +//{ +// // Messages in this file are placeholders for actual protobuf message classes +// // that will be generated from math.proto file. +// +// public class DivArgs +// { +// public long Dividend{ get; set; } +// public long Divisor { get; set; } +// } +// +// public class DivReply +// { +// public long Quotient { get; set; } +// public long Remainder { get; set; } +// } +// +// public class FibArgs +// { +// public long Limit { get; set; } +// } +// +// public class Number +// { +// public long Num { get; set; } +// } +// +// public class FibReply +// { +// public long Count { get; set; } +// } +//} + diff --git a/src/csharp/GrpcApi/Properties/AssemblyInfo.cs b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..725f12c4860 --- /dev/null +++ b/src/csharp/GrpcApi/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcApi")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/GrpcApi/RecordingObserver.cs b/src/csharp/GrpcApi/RecordingObserver.cs new file mode 100644 index 00000000000..8ba3787905a --- /dev/null +++ b/src/csharp/GrpcApi/RecordingObserver.cs @@ -0,0 +1,32 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace math +{ + public class RecordingObserver : IObserver + { + TaskCompletionSource> tcs = new TaskCompletionSource>(); + List data = new List(); + + public void OnCompleted() + { + tcs.SetResult(data); + } + + public void OnError(Exception error) + { + tcs.SetException(error); + } + + public void OnNext(T value) + { + data.Add(value); + } + + public Task> ToList() { + return tcs.Task; + } + } +} + diff --git a/src/csharp/GrpcApi/math.proto b/src/csharp/GrpcApi/math.proto new file mode 100755 index 00000000000..e98b99e002a --- /dev/null +++ b/src/csharp/GrpcApi/math.proto @@ -0,0 +1,50 @@ +syntax = "proto2"; + +package math; + +message DivArgs { + optional int64 dividend = 1; + optional int64 divisor = 2; +} + +message DivReply { + optional int64 quotient = 1; + optional int64 remainder = 2; +} + +message FibArgs { + optional int64 limit = 1; +} + +message Num { + optional int64 num = 1; +} + +message FibReply { + optional int64 count = 1; +} + +service Math { + // Div divides args.dividend by args.divisor and returns the quotient and + // remainder. + rpc Div (DivArgs) returns (DivReply) { + } + + // DivMany accepts an arbitrary number of division args from the client stream + // and sends back the results in the reply stream. The stream continues until + // the client closes its end; the server does the same after sending all the + // replies. The stream ends immediately if either end aborts. + rpc DivMany (stream DivArgs) returns (stream DivReply) { + } + + // Fib generates numbers in the Fibonacci sequence. If args.limit > 0, Fib + // generates up to limit numbers; otherwise it continues until the call is + // canceled. Unlike Fib above, Fib has no final FibReply. + rpc Fib (FibArgs) returns (stream Num) { + } + + // Sum sums a stream of numbers, returning the final result once the stream + // is closed. + rpc Sum (stream Num) returns (Num) { + } +} diff --git a/src/csharp/GrpcCore/.gitignore b/src/csharp/GrpcCore/.gitignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/src/csharp/GrpcCore/.gitignore @@ -0,0 +1 @@ +bin diff --git a/src/csharp/GrpcCore/Call.cs b/src/csharp/GrpcCore/Call.cs new file mode 100644 index 00000000000..bf257e5d598 --- /dev/null +++ b/src/csharp/GrpcCore/Call.cs @@ -0,0 +1,69 @@ +using System; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + public class Call + { + readonly string methodName; + readonly Func requestSerializer; + readonly Func responseDeserializer; + readonly TimeSpan timeout; + readonly Channel channel; + + // TODO: channel param should be removed in the future. + public Call(string methodName, + Func requestSerializer, + Func responseDeserializer, + TimeSpan timeout, + Channel channel) { + this.methodName = methodName; + this.requestSerializer = requestSerializer; + this.responseDeserializer = responseDeserializer; + this.timeout = timeout; + this.channel = channel; + } + + + public Channel Channel + { + get + { + return this.channel; + } + } + + public TimeSpan Timeout + { + get + { + return this.timeout; + } + } + + public string MethodName + { + get + { + return this.methodName; + } + } + + public Func RequestSerializer + { + get + { + return this.requestSerializer; + } + } + + public Func ResponseDeserializer + { + get + { + return this.responseDeserializer; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Calls.cs b/src/csharp/GrpcCore/Calls.cs new file mode 100644 index 00000000000..c3e51cb4781 --- /dev/null +++ b/src/csharp/GrpcCore/Calls.cs @@ -0,0 +1,85 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + // NOTE: this class is work-in-progress + + /// + /// Helper methods for generated stubs to make RPC calls. + /// + public static class Calls + { + public static TResponse BlockingUnaryCall(Call call, TRequest req, CancellationToken token) + { + //TODO: implement this in real synchronous style once new GRPC C core API is available. + return AsyncUnaryCall(call, req, token).Result; + } + + public static async Task AsyncUnaryCall(Call call, TRequest req, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + await asyncCall.WriteAsync(req); + await asyncCall.WritesCompletedAsync(); + + TResponse response = await asyncCall.ReadAsync(); + + Status status = await asyncCall.Finished; + + if (status.StatusCode != StatusCode.GRPC_STATUS_OK) + { + throw new RpcException(status); + } + return response; + } + + public static async Task AsyncServerStreamingCall(Call call, TRequest req, IObserver outputs, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + asyncCall.StartReadingToStream(outputs); + + await asyncCall.WriteAsync(req); + await asyncCall.WritesCompletedAsync(); + } + + public static ClientStreamingAsyncResult AsyncClientStreamingCall(Call call, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + var task = asyncCall.ReadAsync(); + var inputs = new StreamingInputObserver(asyncCall); + return new ClientStreamingAsyncResult(task, inputs); + } + + public static TResponse BlockingClientStreamingCall(Call call, IObservable inputs, CancellationToken token) + { + throw new NotImplementedException(); + } + + public static IObserver DuplexStreamingCall(Call call, IObserver outputs, CancellationToken token) + { + var asyncCall = new AsyncCall(call.RequestSerializer, call.ResponseDeserializer); + asyncCall.Initialize(call.Channel, call.MethodName); + asyncCall.Start(false, GetCompletionQueue()); + + asyncCall.StartReadingToStream(outputs); + var inputs = new StreamingInputObserver(asyncCall); + return inputs; + } + + private static CompletionQueueSafeHandle GetCompletionQueue() { + return GrpcEnvironment.ThreadPool.CompletionQueue; + } + } +} + diff --git a/src/csharp/GrpcCore/Channel.cs b/src/csharp/GrpcCore/Channel.cs new file mode 100644 index 00000000000..b0d8beeb7bf --- /dev/null +++ b/src/csharp/GrpcCore/Channel.cs @@ -0,0 +1,59 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + public class Channel : IDisposable + { + /// + /// Make sure GPRC environment is initialized before any channels get used. + /// + static Channel() { + GrpcEnvironment.EnsureInitialized(); + } + + readonly ChannelSafeHandle handle; + readonly String target; + + // TODO: add way how to create grpc_secure_channel.... + // TODO: add support for channel args... + public Channel(string target) + { + this.handle = ChannelSafeHandle.Create(target, IntPtr.Zero); + this.target = target; + } + + internal ChannelSafeHandle Handle + { + get + { + return this.handle; + } + } + + public string Target + { + get + { + return this.target; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (handle != null && !handle.IsInvalid) + { + handle.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs new file mode 100644 index 00000000000..9e7312c1fa0 --- /dev/null +++ b/src/csharp/GrpcCore/ClientStreamingAsyncResult.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading.Tasks; + +namespace Google.GRPC.Core +{ + /// + /// Return type for client streaming async method. + /// + public struct ClientStreamingAsyncResult + { + readonly Task task; + readonly IObserver inputs; + + public ClientStreamingAsyncResult(Task task, IObserver inputs) + { + this.task = task; + this.inputs = inputs; + } + + public Task Task + { + get + { + return this.task; + } + } + + public IObserver Inputs + { + get + { + return this.inputs; + } + } + } +} + diff --git a/src/csharp/GrpcCore/GrpcCore.csproj b/src/csharp/GrpcCore/GrpcCore.csproj new file mode 100644 index 00000000000..f0c84e78ea6 --- /dev/null +++ b/src/csharp/GrpcCore/GrpcCore.csproj @@ -0,0 +1,62 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + Library + GrpcCore + GrpcCore + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/GrpcCore/GrpcEnvironment.cs b/src/csharp/GrpcCore/GrpcEnvironment.cs new file mode 100644 index 00000000000..7a644f49619 --- /dev/null +++ b/src/csharp/GrpcCore/GrpcEnvironment.cs @@ -0,0 +1,91 @@ +using System; +using Google.GRPC.Core.Internal; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core +{ + /// + /// Encapsulates initialization and shutdown of GRPC C core library. + /// You should not need to initialize it manually, as static constructors + /// should load the library when needed. + /// + public static class GrpcEnvironment + { + const int THREAD_POOL_SIZE = 1; + + [DllImport("libgrpc.so")] + static extern void grpc_init(); + + [DllImport("libgrpc.so")] + static extern void grpc_shutdown(); + + static object staticLock = new object(); + static bool initCalled = false; + static bool shutdownCalled = false; + + static GrpcThreadPool threadPool = new GrpcThreadPool(THREAD_POOL_SIZE); + + /// + /// Makes sure GRPC environment is initialized. + /// + public static void EnsureInitialized() { + lock(staticLock) + { + if (!initCalled) + { + initCalled = true; + GrpcInit(); + } + } + } + + /// + /// Shuts down the GRPC environment if it was initialized before. + /// Repeated invocations have no effect. + /// + public static void Shutdown() + { + lock(staticLock) + { + if (initCalled && !shutdownCalled) + { + shutdownCalled = true; + GrpcShutdown(); + } + } + + } + + /// + /// Initializes GRPC C Core library. + /// + private static void GrpcInit() + { + grpc_init(); + threadPool.Start(); + // TODO: use proper logging here + Console.WriteLine("GRPC initialized."); + } + + /// + /// Shutdown GRPC C Core library. + /// + private static void GrpcShutdown() + { + threadPool.Stop(); + grpc_shutdown(); + + // TODO: use proper logging here + Console.WriteLine("GRPC shutdown."); + } + + internal static GrpcThreadPool ThreadPool + { + get + { + return threadPool; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/AsyncCall.cs b/src/csharp/GrpcCore/Internal/AsyncCall.cs new file mode 100644 index 00000000000..e83ca0eaa96 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/AsyncCall.cs @@ -0,0 +1,485 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Runtime.CompilerServices; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Listener for call events that can be delivered from a completion queue. + /// + internal interface ICallEventListener { + + void OnClientMetadata(); + + void OnRead(byte[] payload); + + void OnWriteAccepted(GRPCOpError error); + + void OnFinishAccepted(GRPCOpError error); + + // ignore the status on server + void OnFinished(Status status); + } + + /// + /// Handle native call lifecycle and provides convenience methods. + /// + internal class AsyncCall: ICallEventListener, IDisposable + { + readonly Func serializer; + readonly Func deserializer; + + // TODO: make sure the delegate doesn't get garbage collected while + // native callbacks are in the completion queue. + readonly EventCallbackDelegate callbackHandler; + + object myLock = new object(); + bool disposed; + CallSafeHandle call; + + bool started; + bool errorOccured; + + bool cancelRequested; + bool halfcloseRequested; + bool halfclosed; + bool doneWithReading; + Nullable finishedStatus; + + TaskCompletionSource writeTcs; + TaskCompletionSource readTcs; + TaskCompletionSource halfcloseTcs = new TaskCompletionSource(); + TaskCompletionSource finishedTcs = new TaskCompletionSource(); + + IObserver readObserver; + + public AsyncCall(Func serializer, Func deserializer) + { + this.serializer = serializer; + this.deserializer = deserializer; + this.callbackHandler = HandleEvent; + } + + public Task WriteAsync(TWrite msg) + { + return StartWrite(msg, false).Task; + } + + public Task WritesCompletedAsync() + { + WritesDone(); + return halfcloseTcs.Task; + } + + public Task WriteStatusAsync(Status status) + { + WriteStatus(status); + return halfcloseTcs.Task; + } + + public Task ReadAsync() + { + return StartRead().Task; + } + + public Task Finished + { + get + { + return finishedTcs.Task; + } + } + + /// + /// Initiates reading to given observer. + /// + public void StartReadingToStream(IObserver readObserver) { + lock (myLock) + { + CheckStarted(); + if (this.readObserver != null) + { + throw new InvalidOperationException("Already registered an observer."); + } + this.readObserver = readObserver; + StartRead(); + } + } + + public void Initialize(Channel channel, String methodName) { + lock (myLock) + { + this.call = CallSafeHandle.Create(channel.Handle, methodName, channel.Target, Timespec.InfFuture); + } + } + + public void InitializeServer(CallSafeHandle call) + { + lock(myLock) + { + this.call = call; + } + } + + // Client only + public void Start(bool buffered, CompletionQueueSafeHandle cq) + { + lock (myLock) + { + if (started) + { + throw new InvalidOperationException("Already started."); + } + + call.Invoke(cq, buffered, callbackHandler, callbackHandler); + started = true; + } + } + + // Server only + public void Accept(CompletionQueueSafeHandle cq) + { + lock (myLock) + { + if (started) + { + throw new InvalidOperationException("Already started."); + } + + call.ServerAccept(cq, callbackHandler); + call.ServerEndInitialMetadata(0); + started = true; + } + } + + public TaskCompletionSource StartWrite(TWrite msg, bool buffered) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + if (writeTcs != null) + { + throw new InvalidOperationException("Only one write can be pending at a time"); + } + + // TODO: wrap serialization... + byte[] payload = serializer(msg); + + call.StartWrite(payload, buffered, callbackHandler); + writeTcs = new TaskCompletionSource(); + return writeTcs; + } + } + + // client only + public void WritesDone() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + call.WritesDone(callbackHandler); + halfcloseRequested = true; + } + } + + // server only + public void WriteStatus(Status status) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + CheckCancelNotRequested(); + + if (halfcloseRequested || halfclosed) + { + throw new InvalidOperationException("Already halfclosed."); + } + + call.StartWriteStatus(status, callbackHandler); + halfcloseRequested = true; + } + } + + public TaskCompletionSource StartRead() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + CheckNoError(); + + // TODO: add check for not cancelled? + + if (doneWithReading) + { + throw new InvalidOperationException("Already read the last message."); + } + + if (readTcs != null) + { + throw new InvalidOperationException("Only one read can be pending at a time"); + } + + call.StartRead(callbackHandler); + + readTcs = new TaskCompletionSource(); + return readTcs; + } + } + + public void Cancel() + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + + cancelRequested = true; + } + // grpc_call_cancel is threadsafe + call.Cancel(); + } + + public void CancelWithStatus(Status status) + { + lock (myLock) + { + CheckStarted(); + CheckNotFinished(); + + cancelRequested = true; + } + // grpc_call_cancel_with_status is threadsafe + call.CancelWithStatus(status); + } + + public void OnClientMetadata() + { + // TODO: implement.... + } + + public void OnRead(byte[] payload) + { + TaskCompletionSource oldTcs = null; + IObserver observer = null; + lock (myLock) + { + oldTcs = readTcs; + readTcs = null; + if (payload == null) + { + doneWithReading = true; + } + observer = readObserver; + } + + // TODO: wrap deserialization... + TRead msg = payload != null ? deserializer(payload) : default(TRead); + + oldTcs.SetResult(msg); + + // TODO: make sure we deliver reads in the right order. + + if (observer != null) + { + if (payload != null) + { + // TODO: wrap to handle exceptions + observer.OnNext(msg); + + // start a new read + StartRead(); + } + else + { + // TODO: wrap to handle exceptions; + observer.OnCompleted(); + } + + } + } + + public void OnWriteAccepted(GRPCOpError error) + { + TaskCompletionSource oldTcs = null; + lock (myLock) + { + UpdateErrorOccured(error); + oldTcs = writeTcs; + writeTcs = null; + } + + if (errorOccured) + { + // TODO: use the right type of exception... + oldTcs.SetException(new Exception("Write failed")); + } + else + { + // TODO: where does the continuation run? + oldTcs.SetResult(null); + } + } + + public void OnFinishAccepted(GRPCOpError error) + { + lock (myLock) + { + UpdateErrorOccured(error); + halfclosed = true; + } + + if (errorOccured) + { + halfcloseTcs.SetException(new Exception("Halfclose failed")); + + } + else + { + halfcloseTcs.SetResult(null); + } + + } + + public void OnFinished(Status status) + { + lock (myLock) + { + finishedStatus = status; + + DisposeResourcesIfNeeded(); + } + finishedTcs.SetResult(status); + + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + if (call != null) + { + call.Dispose(); + } + } + disposed = true; + } + } + + private void UpdateErrorOccured(GRPCOpError error) + { + if (error == GRPCOpError.GRPC_OP_ERROR) + { + errorOccured = true; + } + } + + private void CheckStarted() + { + if (!started) + { + throw new InvalidOperationException("Call not started"); + } + } + + private void CheckNoError() + { + if (errorOccured) + { + throw new InvalidOperationException("Error occured when processing call."); + } + } + + private void CheckNotFinished() + { + if (finishedStatus.HasValue) + { + throw new InvalidOperationException("Already finished."); + } + } + + private void CheckCancelNotRequested() + { + if (cancelRequested) + { + throw new InvalidOperationException("Cancel has been requested."); + } + } + + private void DisposeResourcesIfNeeded() + { + if (call != null && started && finishedStatus.HasValue) + { + // TODO: should we also wait for all the pending events to finish? + + call.Dispose(); + } + } + + private void HandleEvent(IntPtr eventPtr) { + try { + var ev = new EventSafeHandleNotOwned(eventPtr); + switch (ev.GetCompletionType()) + { + case GRPCCompletionType.GRPC_CLIENT_METADATA_READ: + OnClientMetadata(); + break; + + case GRPCCompletionType.GRPC_READ: + byte[] payload = ev.GetReadData(); + OnRead(payload); + break; + + case GRPCCompletionType.GRPC_WRITE_ACCEPTED: + OnWriteAccepted(ev.GetWriteAccepted()); + break; + + case GRPCCompletionType.GRPC_FINISH_ACCEPTED: + OnFinishAccepted(ev.GetFinishAccepted()); + break; + + case GRPCCompletionType.GRPC_FINISHED: + OnFinished(ev.GetFinished()); + break; + + default: + throw new ArgumentException("Unexpected completion type"); + } + } catch(Exception e) { + Console.WriteLine("Caught exception in a native handler: " + e); + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/CallSafeHandle.cs b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs new file mode 100644 index 00000000000..6c9c58a4c3f --- /dev/null +++ b/src/csharp/GrpcCore/Internal/CallSafeHandle.cs @@ -0,0 +1,182 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using Google.GRPC.Core; + +namespace Google.GRPC.Core.Internal +{ + // TODO: we need to make sure that the delegates are not collected before invoked. + internal delegate void EventCallbackDelegate(IntPtr eventPtr); + + /// + /// grpc_call from + /// + internal class CallSafeHandle : SafeHandleZeroIsInvalid + { + const UInt32 GRPC_WRITE_BUFFER_HINT = 1; + + [DllImport("libgrpc.so")] + static extern CallSafeHandle grpc_channel_create_call_old(ChannelSafeHandle channel, string method, string host, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_add_metadata(CallSafeHandle call, IntPtr metadata, UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_invoke_old(CallSafeHandle call, CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, UInt32 flags); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_invoke_old")] + static extern GRPCCallError grpc_call_invoke_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle cq, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate metadataReadCallback, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback, + UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_server_accept_old(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, IntPtr finishedTag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_server_accept_old")] + static extern GRPCCallError grpc_call_server_accept_old_CALLBACK(CallSafeHandle call, CompletionQueueSafeHandle completionQueue, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate finishedCallback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_server_end_initial_metadata_old(CallSafeHandle call, UInt32 flags); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_cancel(CallSafeHandle call); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_cancel_with_status(CallSafeHandle call, StatusCode status, string description); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_start_write_status_old(CallSafeHandle call, StatusCode statusCode, string statusMessage, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_write_status_old")] + static extern GRPCCallError grpc_call_start_write_status_old_CALLBACK(CallSafeHandle call, StatusCode statusCode, string statusMessage, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_writes_done_old(CallSafeHandle call, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_writes_done_old")] + static extern GRPCCallError grpc_call_writes_done_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern GRPCCallError grpc_call_start_read_old(CallSafeHandle call, IntPtr tag); + + [DllImport("libgrpc.so", EntryPoint = "grpc_call_start_read_old")] + static extern GRPCCallError grpc_call_start_read_old_CALLBACK(CallSafeHandle call, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_call_start_write_from_copied_buffer(CallSafeHandle call, + byte[] buffer, UIntPtr length, + IntPtr tag, UInt32 flags); + + [DllImport("libgrpc_csharp_ext.so", EntryPoint = "grpc_call_start_write_from_copied_buffer")] + static extern void grpc_call_start_write_from_copied_buffer_CALLBACK(CallSafeHandle call, + byte[] buffer, UIntPtr length, + [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback, + UInt32 flags); + + [DllImport("libgrpc.so")] + static extern void grpc_call_destroy(IntPtr call); + + private CallSafeHandle() + { + } + + /// + /// Creates a client call. + /// + public static CallSafeHandle Create(ChannelSafeHandle channel, string method, string host, Timespec deadline) + { + return grpc_channel_create_call_old(channel, method, host, deadline); + } + + public void Invoke(CompletionQueueSafeHandle cq, IntPtr metadataReadTag, IntPtr finishedTag, bool buffered) + { + AssertCallOk(grpc_call_invoke_old(this, cq, metadataReadTag, finishedTag, GetFlags(buffered))); + } + + public void Invoke(CompletionQueueSafeHandle cq, bool buffered, EventCallbackDelegate metadataReadCallback, EventCallbackDelegate finishedCallback) + { + AssertCallOk(grpc_call_invoke_old_CALLBACK(this, cq, metadataReadCallback, finishedCallback, GetFlags(buffered))); + } + + public void ServerAccept(CompletionQueueSafeHandle cq, IntPtr finishedTag) + { + AssertCallOk(grpc_call_server_accept_old(this, cq, finishedTag)); + } + + public void ServerAccept(CompletionQueueSafeHandle cq, EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_server_accept_old_CALLBACK(this, cq, callback)); + } + + public void ServerEndInitialMetadata(UInt32 flags) + { + AssertCallOk(grpc_call_server_end_initial_metadata_old(this, flags)); + } + + public void StartWrite(byte[] payload, IntPtr tag, bool buffered) + { + grpc_call_start_write_from_copied_buffer(this, payload, new UIntPtr((ulong) payload.Length), tag, GetFlags(buffered)); + } + + public void StartWrite(byte[] payload, bool buffered, EventCallbackDelegate callback) + { + grpc_call_start_write_from_copied_buffer_CALLBACK(this, payload, new UIntPtr((ulong) payload.Length), callback, GetFlags(buffered)); + } + + public void StartWriteStatus(Status status, IntPtr tag) + { + AssertCallOk(grpc_call_start_write_status_old(this, status.StatusCode, status.Detail, tag)); + } + + public void StartWriteStatus(Status status, EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_start_write_status_old_CALLBACK(this, status.StatusCode, status.Detail, callback)); + } + + public void WritesDone(IntPtr tag) + { + AssertCallOk(grpc_call_writes_done_old(this, tag)); + } + + public void WritesDone(EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_writes_done_old_CALLBACK(this, callback)); + } + + public void StartRead(IntPtr tag) + { + AssertCallOk(grpc_call_start_read_old(this, tag)); + } + + public void StartRead(EventCallbackDelegate callback) + { + AssertCallOk(grpc_call_start_read_old_CALLBACK(this, callback)); + } + + public void Cancel() + { + AssertCallOk(grpc_call_cancel(this)); + } + + public void CancelWithStatus(Status status) + { + AssertCallOk(grpc_call_cancel_with_status(this, status.StatusCode, status.Detail)); + } + + protected override bool ReleaseHandle() + { + grpc_call_destroy(handle); + return true; + } + + private static void AssertCallOk(GRPCCallError callError) + { + Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); + } + + private static UInt32 GetFlags(bool buffered) { + return buffered ? 0 : GRPC_WRITE_BUFFER_HINT; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs new file mode 100644 index 00000000000..3a09d8b1b64 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/ChannelSafeHandle.cs @@ -0,0 +1,34 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_channel from + /// + internal class ChannelSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern ChannelSafeHandle grpc_channel_create(string target, IntPtr channelArgs); + + [DllImport("libgrpc.so")] + static extern void grpc_channel_destroy(IntPtr channel); + + private ChannelSafeHandle() + { + } + + public static ChannelSafeHandle Create(string target, IntPtr channelArgs) + { + return grpc_channel_create(target, channelArgs); + } + + protected override bool ReleaseHandle() + { + grpc_channel_destroy(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs new file mode 100644 index 00000000000..73dd3edde33 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/CompletionQueueSafeHandle.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_completion_queue from + /// + internal class CompletionQueueSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern CompletionQueueSafeHandle grpc_completion_queue_create(); + + [DllImport("libgrpc.so")] + static extern EventSafeHandle grpc_completion_queue_pluck(CompletionQueueSafeHandle cq, IntPtr tag, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern EventSafeHandle grpc_completion_queue_next(CompletionQueueSafeHandle cq, Timespec deadline); + + [DllImport("libgrpc.so")] + static extern void grpc_completion_queue_shutdown(CompletionQueueSafeHandle cq); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_completion_queue_next_with_callback(CompletionQueueSafeHandle cq); + + [DllImport("libgrpc.so")] + static extern void grpc_completion_queue_destroy(IntPtr cq); + + private CompletionQueueSafeHandle() + { + } + + public static CompletionQueueSafeHandle Create() + { + return grpc_completion_queue_create(); + } + + public EventSafeHandle Next(Timespec deadline) + { + return grpc_completion_queue_next(this, deadline); + } + + public GRPCCompletionType NextWithCallback() + { + return grpc_completion_queue_next_with_callback(this); + } + + public EventSafeHandle Pluck(IntPtr tag, Timespec deadline) + { + return grpc_completion_queue_pluck(this, tag, deadline); + } + + public void Shutdown() + { + grpc_completion_queue_shutdown(this); + } + + protected override bool ReleaseHandle() + { + grpc_completion_queue_destroy(handle); + return true; + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/Enums.cs b/src/csharp/GrpcCore/Internal/Enums.cs new file mode 100644 index 00000000000..46e3bca6ebe --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Enums.cs @@ -0,0 +1,75 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core.Internal +{ + /// + /// from grpc/grpc.h + /// + internal enum GRPCCallError + { + /* everything went ok */ + GRPC_CALL_OK = 0, + /* something failed, we don't know what */ + GRPC_CALL_ERROR, + /* this method is not available on the server */ + GRPC_CALL_ERROR_NOT_ON_SERVER, + /* this method is not available on the client */ + GRPC_CALL_ERROR_NOT_ON_CLIENT, + /* this method must be called before server_accept */ + GRPC_CALL_ERROR_ALREADY_ACCEPTED, + /* this method must be called before invoke */ + GRPC_CALL_ERROR_ALREADY_INVOKED, + /* this method must be called after invoke */ + GRPC_CALL_ERROR_NOT_INVOKED, + /* this call is already finished + (writes_done or write_status has already been called) */ + GRPC_CALL_ERROR_ALREADY_FINISHED, + /* there is already an outstanding read/write operation on the call */ + GRPC_CALL_ERROR_TOO_MANY_OPERATIONS, + /* the flags value was illegal for this call */ + GRPC_CALL_ERROR_INVALID_FLAGS + } + + /// + /// grpc_completion_type from grpc/grpc.h + /// + internal enum GRPCCompletionType + { + GRPC_QUEUE_SHUTDOWN, + /* Shutting down */ + GRPC_READ, + /* A read has completed */ + GRPC_INVOKE_ACCEPTED, + /* An invoke call has been accepted by flow + control */ + GRPC_WRITE_ACCEPTED, + /* A write has been accepted by + flow control */ + GRPC_FINISH_ACCEPTED, + /* writes_done or write_status has been accepted */ + GRPC_CLIENT_METADATA_READ, + /* The metadata array sent by server received at + client */ + GRPC_FINISHED, + /* An RPC has finished. The event contains status. + On the server this will be OK or Cancelled. */ + GRPC_SERVER_RPC_NEW, + /* A new RPC has arrived at the server */ + GRPC_COMPLETION_DO_NOT_USE + /* must be last, forces users to include + a default: case */ + } + + /// + /// grpc_op_error from grpc/grpc.h + /// + internal enum GRPCOpError + { + /* everything went ok */ + GRPC_OP_OK = 0, + /* something failed, we don't know what */ + GRPC_OP_ERROR + } +} + diff --git a/src/csharp/GrpcCore/Internal/Event.cs b/src/csharp/GrpcCore/Internal/Event.cs new file mode 100644 index 00000000000..7056005ba65 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Event.cs @@ -0,0 +1,191 @@ +using System; +using System.Runtime.InteropServices; +using Google.GRPC.Core; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_event from grpc/grpc.h + /// + internal class EventSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern void grpc_event_finish(IntPtr ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_event_type(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern CallSafeHandle grpc_event_call(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_write_accepted(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern StatusCode grpc_event_finished_status(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_finished_details(EventSafeHandle ev); // returns const char* + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_read_length(EventSafeHandle ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_event_read_copy_to_buffer(EventSafeHandle ev, byte[] buffer, UIntPtr bufferLen); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandle ev); // returns const char* + + public GRPCCompletionType GetCompletionType() + { + return grpc_event_type(this); + } + + public GRPCOpError GetWriteAccepted() + { + return grpc_event_write_accepted(this); + } + + public GRPCOpError GetFinishAccepted() + { + return grpc_event_finish_accepted(this); + } + + public Status GetFinished() + { + // TODO: can the native method return string directly? + string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); + return new Status(grpc_event_finished_status(this), details); + } + + public byte[] GetReadData() + { + IntPtr len = grpc_event_read_length(this); + if (len == new IntPtr(-1)) + { + return null; + } + byte[] data = new byte[(int) len]; + grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + return data; + } + + public CallSafeHandle GetCall() { + return grpc_event_call(this); + } + + public string GetServerRpcNewMethod() { + // TODO: can the native method return string directly? + return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + } + + //TODO: client_metadata_read event type + + protected override bool ReleaseHandle() + { + grpc_event_finish(handle); + return true; + } + } + + // TODO: this is basically c&p of EventSafeHandle. Unify! + /// + /// Not owned version of + /// grpc_event from grpc/grpc.h + /// + internal class EventSafeHandleNotOwned : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so")] + static extern void grpc_event_finish(IntPtr ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCCompletionType grpc_event_type(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern CallSafeHandle grpc_event_call(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_write_accepted(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern GRPCOpError grpc_event_finish_accepted(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern StatusCode grpc_event_finished_status(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_finished_details(EventSafeHandleNotOwned ev); // returns const char* + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_read_length(EventSafeHandleNotOwned ev); + + [DllImport("libgrpc_csharp_ext.so")] + static extern void grpc_event_read_copy_to_buffer(EventSafeHandleNotOwned ev, byte[] buffer, UIntPtr bufferLen); + + [DllImport("libgrpc_csharp_ext.so")] + static extern IntPtr grpc_event_server_rpc_new_method(EventSafeHandleNotOwned ev); // returns const char* + + public EventSafeHandleNotOwned() : base(false) + { + } + + public EventSafeHandleNotOwned(IntPtr handle) : base(false) + { + SetHandle(handle); + } + + public GRPCCompletionType GetCompletionType() + { + return grpc_event_type(this); + } + + public GRPCOpError GetWriteAccepted() + { + return grpc_event_write_accepted(this); + } + + public GRPCOpError GetFinishAccepted() + { + return grpc_event_finish_accepted(this); + } + + public Status GetFinished() + { + // TODO: can the native method return string directly? + string details = Marshal.PtrToStringAnsi(grpc_event_finished_details(this)); + return new Status(grpc_event_finished_status(this), details); + } + + public byte[] GetReadData() + { + IntPtr len = grpc_event_read_length(this); + if (len == new IntPtr(-1)) + { + return null; + } + byte[] data = new byte[(int) len]; + grpc_event_read_copy_to_buffer(this, data, new UIntPtr((ulong)data.Length)); + return data; + } + + public CallSafeHandle GetCall() { + return grpc_event_call(this); + } + + public string GetServerRpcNewMethod() { + // TODO: can the native method return string directly? + return Marshal.PtrToStringAnsi(grpc_event_server_rpc_new_method(this)); + } + + //TODO: client_metadata_read event type + + protected override bool ReleaseHandle() + { + grpc_event_finish(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs new file mode 100644 index 00000000000..1139e54a1d7 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/GrpcThreadPool.cs @@ -0,0 +1,129 @@ +using System; +using Google.GRPC.Core.Internal; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Pool of threads polling on the same completion queue. + /// + internal class GrpcThreadPool + { + readonly object myLock = new object(); + readonly List threads = new List(); + readonly int poolSize; + readonly Action eventHandler; + + CompletionQueueSafeHandle cq; + + public GrpcThreadPool(int poolSize) { + this.poolSize = poolSize; + } + + internal GrpcThreadPool(int poolSize, Action eventHandler) { + this.poolSize = poolSize; + this.eventHandler = eventHandler; + } + + public void Start() { + + lock (myLock) + { + if (cq != null) + { + throw new InvalidOperationException("Already started."); + } + + cq = CompletionQueueSafeHandle.Create(); + + for (int i = 0; i < poolSize; i++) + { + threads.Add(CreateAndStartThread(i)); + } + } + } + + public void Stop() { + + lock (myLock) + { + cq.Shutdown(); + + Console.WriteLine("Waiting for GPRC threads to finish."); + foreach (var thread in threads) + { + thread.Join(); + } + + cq.Dispose(); + + } + } + + internal CompletionQueueSafeHandle CompletionQueue + { + get + { + return cq; + } + } + + private Thread CreateAndStartThread(int i) { + Action body; + if (eventHandler != null) + { + body = ThreadBodyWithHandler; + } + else + { + body = ThreadBodyNoHandler; + } + var thread = new Thread(new ThreadStart(body)); + thread.IsBackground = false; + thread.Start(); + if (eventHandler != null) + { + thread.Name = "grpc_server_newrpc " + i; + } + else + { + thread.Name = "grpc " + i; + } + return thread; + } + + /// + /// Body of the polling thread. + /// + private void ThreadBodyNoHandler() + { + GRPCCompletionType completionType; + do + { + completionType = cq.NextWithCallback(); + } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); + Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); + } + + /// + /// Body of the polling thread. + /// + private void ThreadBodyWithHandler() + { + GRPCCompletionType completionType; + do + { + using (EventSafeHandle ev = cq.Next(Timespec.InfFuture)) { + completionType = ev.GetCompletionType(); + eventHandler(ev); + } + } while(completionType != GRPCCompletionType.GRPC_QUEUE_SHUTDOWN); + Console.WriteLine("Completion queue has shutdown successfully, thread " + Thread.CurrentThread.Name + " exiting."); + } + } + +} + diff --git a/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs new file mode 100644 index 00000000000..5a1252b8814 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/SafeHandleZeroIsInvalid.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core.Internal +{ + /// + /// Safe handle to wrap native objects. + /// + internal abstract class SafeHandleZeroIsInvalid : SafeHandle + { + public SafeHandleZeroIsInvalid() : base(IntPtr.Zero, true) + { + } + + public SafeHandleZeroIsInvalid(bool ownsHandle) : base(IntPtr.Zero, ownsHandle) + { + } + + public override bool IsInvalid + { + get + { + return handle == IntPtr.Zero; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs new file mode 100644 index 00000000000..0d38bce63e3 --- /dev/null +++ b/src/csharp/GrpcCore/Internal/ServerSafeHandle.cs @@ -0,0 +1,76 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Collections.Concurrent; + +namespace Google.GRPC.Core.Internal +{ + /// + /// grpc_server from grpc/grpc.h + /// + internal sealed class ServerSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("libgrpc.so", EntryPoint = "grpc_server_request_call_old")] + static extern GRPCCallError grpc_server_request_call_old_CALLBACK(ServerSafeHandle server, [MarshalAs(UnmanagedType.FunctionPtr)] EventCallbackDelegate callback); + + [DllImport("libgrpc.so")] + static extern ServerSafeHandle grpc_server_create(CompletionQueueSafeHandle cq, IntPtr args); + + // TODO: check int representation size + [DllImport("libgrpc.so")] + static extern int grpc_server_add_http2_port(ServerSafeHandle server, string addr); + + // TODO: check int representation size + [DllImport("libgrpc.so")] + static extern int grpc_server_add_secure_http2_port(ServerSafeHandle server, string addr); + + [DllImport("libgrpc.so")] + static extern void grpc_server_start(ServerSafeHandle server); + + [DllImport("libgrpc.so")] + static extern void grpc_server_shutdown(ServerSafeHandle server); + + [DllImport("libgrpc.so")] + static extern void grpc_server_shutdown_and_notify(ServerSafeHandle server, IntPtr tag); + + [DllImport("libgrpc.so")] + static extern void grpc_server_destroy(IntPtr server); + + private ServerSafeHandle() + { + } + + public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) + { + // TODO: also grpc_secure_server_create... + return grpc_server_create(cq, args); + } + + public int AddPort(string addr) + { + // TODO: also grpc_server_add_secure_http2_port... + return grpc_server_add_http2_port(this, addr); + } + + public void Start() + { + grpc_server_start(this); + } + + public void Shutdown() + { + grpc_server_shutdown(this); + } + + public GRPCCallError RequestCall(EventCallbackDelegate callback) + { + return grpc_server_request_call_old_CALLBACK(this, callback); + } + + protected override bool ReleaseHandle() + { + grpc_server_destroy(handle); + return true; + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs new file mode 100644 index 00000000000..d483e53a2db --- /dev/null +++ b/src/csharp/GrpcCore/Internal/StreamingInputObserver.cs @@ -0,0 +1,33 @@ +using System; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + internal class StreamingInputObserver : IObserver + { + readonly AsyncCall call; + + public StreamingInputObserver(AsyncCall call) + { + this.call = call; + } + + public void OnCompleted() + { + // TODO: how bad is the Wait here? + call.WritesCompletedAsync().Wait(); + } + + public void OnError(Exception error) + { + throw new InvalidOperationException("This should never be called."); + } + + public void OnNext(TWrite value) + { + // TODO: how bad is the Wait here? + call.WriteAsync(value).Wait(); + } + } +} + diff --git a/src/csharp/GrpcCore/Internal/Timespec.cs b/src/csharp/GrpcCore/Internal/Timespec.cs new file mode 100644 index 00000000000..8ffaf70bbfb --- /dev/null +++ b/src/csharp/GrpcCore/Internal/Timespec.cs @@ -0,0 +1,67 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Google.GRPC.Core.Internal +{ + /// + /// gpr_timespec from grpc/support/time.h + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Timespec + { + const int nanosPerSecond = 1000 * 1000 * 1000; + const int nanosPerTick = 100; + + [DllImport("libgpr.so")] + static extern Timespec gpr_now(); + + // TODO: this only works on 64bit linux, can we autoselect the right size of ints? + // perhaps using IntPtr would work. + public System.Int64 tv_sec; + public System.Int64 tv_nsec; + + /// + /// Timespec a long time in the future. + /// + public static Timespec InfFuture + { + get + { + // TODO: set correct value based on the length of the struct + return new Timespec { tv_sec = Int32.MaxValue, tv_nsec = 0 }; + } + } + + public static Timespec Now + { + get + { + return gpr_now(); + } + } + + /// + /// Creates a GPR deadline from current instant and given timeout. + /// + /// The from timeout. + public static Timespec DeadlineFromTimeout(TimeSpan timeout) { + if (timeout == Timeout.InfiniteTimeSpan) + { + return Timespec.InfFuture; + } + return Timespec.Now.Add(timeout); + } + + public Timespec Add(TimeSpan timeSpan) { + long nanos = tv_nsec + (timeSpan.Ticks % TimeSpan.TicksPerSecond) * nanosPerTick; + long overflow_sec = (nanos > nanosPerSecond) ? 1 : 0; + + Timespec result; + result.tv_nsec = nanos % nanosPerSecond; + result.tv_sec = tv_sec + (timeSpan.Ticks / TimeSpan.TicksPerSecond) + overflow_sec; + return result; + } + } +} + diff --git a/src/csharp/GrpcCore/Properties/AssemblyInfo.cs b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..74aba257678 --- /dev/null +++ b/src/csharp/GrpcCore/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcCore")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + +[assembly: InternalsVisibleTo("GrpcCoreTests")] + diff --git a/src/csharp/GrpcCore/RpcException.cs b/src/csharp/GrpcCore/RpcException.cs new file mode 100644 index 00000000000..8811c3a7c75 --- /dev/null +++ b/src/csharp/GrpcCore/RpcException.cs @@ -0,0 +1,27 @@ +using System; + +namespace Google.GRPC.Core +{ + public class RpcException : Exception + { + private readonly Status status; + + public RpcException(Status status) + { + this.status = status; + } + + public RpcException(Status status, string message) : base(message) + { + this.status = status; + } + + public Status Status { + get + { + return status; + } + } + } +} + diff --git a/src/csharp/GrpcCore/Server.cs b/src/csharp/GrpcCore/Server.cs new file mode 100644 index 00000000000..68da1a83008 --- /dev/null +++ b/src/csharp/GrpcCore/Server.cs @@ -0,0 +1,141 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Collections.Concurrent; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core +{ + /// + /// Server is implemented only to be able to do + /// in-process testing. + /// + public class Server + { + // TODO: make sure the delegate doesn't get garbage collected while + // native callbacks are in the completion queue. + readonly EventCallbackDelegate newRpcHandler; + + readonly BlockingCollection newRpcQueue = new BlockingCollection(); + readonly ServerSafeHandle handle; + + static Server() { + GrpcEnvironment.EnsureInitialized(); + } + + public Server() + { + // TODO: what is the tag for server shutdown? + this.handle = ServerSafeHandle.NewServer(GetCompletionQueue(), IntPtr.Zero); + this.newRpcHandler = HandleNewRpc; + } + + public int AddPort(string addr) { + return handle.AddPort(addr); + } + + public void Start() + { + handle.Start(); + } + + public void RunRpc() + { + AllowOneRpc(); + + try { + var rpcInfo = newRpcQueue.Take(); + + Console.WriteLine("Server received RPC " + rpcInfo.Method); + + AsyncCall asyncCall = new AsyncCall( + (payload) => payload, (payload) => payload); + + asyncCall.InitializeServer(rpcInfo.Call); + + asyncCall.Accept(GetCompletionQueue()); + + while(true) { + byte[] payload = asyncCall.ReadAsync().Result; + if (payload == null) + { + break; + } + } + + asyncCall.WriteAsync(new byte[] { }).Wait(); + + // TODO: what should be the details? + asyncCall.WriteStatusAsync(new Status(StatusCode.GRPC_STATUS_OK, "")).Wait(); + + asyncCall.Finished.Wait(); + } catch(Exception e) { + Console.WriteLine("Exception while handling RPC: " + e); + } + } + + // TODO: implement disposal properly... + public void Shutdown() { + handle.Shutdown(); + + + //handle.Dispose(); + } + + private void AllowOneRpc() + { + AssertCallOk(handle.RequestCall(newRpcHandler)); + } + + private void HandleNewRpc(IntPtr eventPtr) + { + try + { + var ev = new EventSafeHandleNotOwned(eventPtr); + newRpcQueue.Add(new NewRpcInfo(ev.GetCall(), ev.GetServerRpcNewMethod())); + } + catch (Exception e) + { + Console.WriteLine("Caught exception in a native handler: " + e); + } + } + + private static void AssertCallOk(GRPCCallError callError) + { + Trace.Assert(callError == GRPCCallError.GRPC_CALL_OK, "Status not GRPC_CALL_OK"); + } + + private static CompletionQueueSafeHandle GetCompletionQueue() + { + return GrpcEnvironment.ThreadPool.CompletionQueue; + } + + private struct NewRpcInfo + { + private CallSafeHandle call; + private string method; + + public NewRpcInfo(CallSafeHandle call, string method) + { + this.call = call; + this.method = method; + } + + public CallSafeHandle Call + { + get + { + return this.call; + } + } + + public string Method + { + get + { + return this.method; + } + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/Status.cs b/src/csharp/GrpcCore/Status.cs new file mode 100644 index 00000000000..f1212f8d672 --- /dev/null +++ b/src/csharp/GrpcCore/Status.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; + +namespace Google.GRPC.Core +{ + /// + /// Represents RPC result. + /// + public struct Status + { + readonly StatusCode statusCode; + readonly string detail; + + public Status(StatusCode statusCode, string detail) + { + this.statusCode = statusCode; + this.detail = detail; + } + + public StatusCode StatusCode + { + get + { + return statusCode; + } + } + + public string Detail + { + get + { + return detail; + } + } + } +} \ No newline at end of file diff --git a/src/csharp/GrpcCore/StatusCode.cs b/src/csharp/GrpcCore/StatusCode.cs new file mode 100644 index 00000000000..80fc8bd5815 --- /dev/null +++ b/src/csharp/GrpcCore/StatusCode.cs @@ -0,0 +1,150 @@ +using System; + +namespace Google.GRPC.Core +{ + // TODO: element names should changed to comply with C# naming conventions. + /// + /// grpc_status_code from grpc/status.h + /// + public enum StatusCode + { + /* Not an error; returned on success + + HTTP Mapping: 200 OK */ + GRPC_STATUS_OK = 0, + /* The operation was cancelled (typically by the caller). + + HTTP Mapping: 499 Client Closed Request */ + GRPC_STATUS_CANCELLED = 1, + /* Unknown error. An example of where this error may be returned is + if a Status value received from another address space belongs to + an error-space that is not known in this address space. Also + errors raised by APIs that do not return enough error information + may be converted to this error. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_UNKNOWN = 2, + /* Client specified an invalid argument. Note that this differs + from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments + that are problematic regardless of the state of the system + (e.g., a malformed file name). + + HTTP Mapping: 400 Bad Request */ + GRPC_STATUS_INVALID_ARGUMENT = 3, + /* Deadline expired before operation could complete. For operations + that change the state of the system, this error may be returned + even if the operation has completed successfully. For example, a + successful response from a server could have been delayed long + enough for the deadline to expire. + + HTTP Mapping: 504 Gateway Timeout */ + GRPC_STATUS_DEADLINE_EXCEEDED = 4, + /* Some requested entity (e.g., file or directory) was not found. + + HTTP Mapping: 404 Not Found */ + GRPC_STATUS_NOT_FOUND = 5, + /* Some entity that we attempted to create (e.g., file or directory) + already exists. + + HTTP Mapping: 409 Conflict */ + GRPC_STATUS_ALREADY_EXISTS = 6, + /* The caller does not have permission to execute the specified + operation. PERMISSION_DENIED must not be used for rejections + caused by exhausting some resource (use RESOURCE_EXHAUSTED + instead for those errors). PERMISSION_DENIED must not be + used if the caller can not be identified (use UNAUTHENTICATED + instead for those errors). + + HTTP Mapping: 403 Forbidden */ + GRPC_STATUS_PERMISSION_DENIED = 7, + /* The request does not have valid authentication credentials for the + operation. + + HTTP Mapping: 401 Unauthorized */ + GRPC_STATUS_UNAUTHENTICATED = 16, + /* Some resource has been exhausted, perhaps a per-user quota, or + perhaps the entire file system is out of space. + + HTTP Mapping: 429 Too Many Requests */ + GRPC_STATUS_RESOURCE_EXHAUSTED = 8, + /* Operation was rejected because the system is not in a state + required for the operation's execution. For example, directory + to be deleted may be non-empty, an rmdir operation is applied to + a non-directory, etc. + + A litmus test that may help a service implementor in deciding + between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE: + (a) Use UNAVAILABLE if the client can retry just the failing call. + (b) Use ABORTED if the client should retry at a higher-level + (e.g., restarting a read-modify-write sequence). + (c) Use FAILED_PRECONDITION if the client should not retry until + the system state has been explicitly fixed. E.g., if an "rmdir" + fails because the directory is non-empty, FAILED_PRECONDITION + should be returned since the client should not retry unless + they have first fixed up the directory by deleting files from it. + (d) Use FAILED_PRECONDITION if the client performs conditional + REST Get/Update/Delete on a resource and the resource on the + server does not match the condition. E.g., conflicting + read-modify-write on the same resource. + + HTTP Mapping: 400 Bad Request + + NOTE: HTTP spec says 412 Precondition Failed should only be used if + the request contains Etag related headers. So if the server does see + Etag related headers in the request, it may choose to return 412 + instead of 400 for this error code. */ + GRPC_STATUS_FAILED_PRECONDITION = 9, + /* The operation was aborted, typically due to a concurrency issue + like sequencer check failures, transaction aborts, etc. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. + + HTTP Mapping: 409 Conflict */ + GRPC_STATUS_ABORTED = 10, + /* Operation was attempted past the valid range. E.g., seeking or + reading past end of file. + + Unlike INVALID_ARGUMENT, this error indicates a problem that may + be fixed if the system state changes. For example, a 32-bit file + system will generate INVALID_ARGUMENT if asked to read at an + offset that is not in the range [0,2^32-1], but it will generate + OUT_OF_RANGE if asked to read from an offset past the current + file size. + + There is a fair bit of overlap between FAILED_PRECONDITION and + OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific + error) when it applies so that callers who are iterating through + a space can easily look for an OUT_OF_RANGE error to detect when + they are done. + + HTTP Mapping: 400 Bad Request */ + GRPC_STATUS_OUT_OF_RANGE = 11, + /* Operation is not implemented or not supported/enabled in this service. + + HTTP Mapping: 501 Not Implemented */ + GRPC_STATUS_UNIMPLEMENTED = 12, + /* Internal errors. Means some invariants expected by underlying + system has been broken. If you see one of these errors, + something is very broken. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_INTERNAL = 13, + /* The service is currently unavailable. This is a most likely a + transient condition and may be corrected by retrying with + a backoff. + + See litmus test above for deciding between FAILED_PRECONDITION, + ABORTED, and UNAVAILABLE. + + HTTP Mapping: 503 Service Unavailable */ + GRPC_STATUS_UNAVAILABLE = 14, + /* Unrecoverable data loss or corruption. + + HTTP Mapping: 500 Internal Server Error */ + GRPC_STATUS_DATA_LOSS = 15, + /* Force users to include a default branch: */ + GRPC_STATUS__DO_NOT_USE = -1 + } +} + diff --git a/src/csharp/GrpcCoreTests/.gitignore b/src/csharp/GrpcCoreTests/.gitignore new file mode 100644 index 00000000000..2cc8cca52d0 --- /dev/null +++ b/src/csharp/GrpcCoreTests/.gitignore @@ -0,0 +1,2 @@ +test-results +bin diff --git a/src/csharp/GrpcCoreTests/ClientServerTest.cs b/src/csharp/GrpcCoreTests/ClientServerTest.cs new file mode 100644 index 00000000000..823ee942882 --- /dev/null +++ b/src/csharp/GrpcCoreTests/ClientServerTest.cs @@ -0,0 +1,48 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; +using System.Threading; +using System.Threading.Tasks; + +namespace Google.GRPC.Core.Tests +{ + public class ClientServerTest + { + string request = "REQUEST"; + string serverAddr = "localhost:" + Utils.PickUnusedPort(); + + [Test] + public void EmptyCall() + { + Server server = new Server(); + server.AddPort(serverAddr); + server.Start(); + + Task.Factory.StartNew( + () => { + server.RunRpc(); + } + ); + + using (Channel channel = new Channel(serverAddr)) + { + CreateCall(channel); + string response = Calls.BlockingUnaryCall(CreateCall(channel), request, default(CancellationToken)); + Console.WriteLine("Received response: " + response); + } + + server.Shutdown(); + + GrpcEnvironment.Shutdown(); + } + + private Call CreateCall(Channel channel) + { + return new Call("/tests.Test/EmptyCall", + (s) => System.Text.Encoding.ASCII.GetBytes(s), + (b) => System.Text.Encoding.ASCII.GetString(b), + Timeout.InfiniteTimeSpan, channel); + } + } +} + diff --git a/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj new file mode 100644 index 00000000000..3de0f585cda --- /dev/null +++ b/src/csharp/GrpcCoreTests/GrpcCoreTests.csproj @@ -0,0 +1,53 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {86EC5CB4-4EA2-40A2-8057-86542A0353BB} + Library + GrpcCoreTests + GrpcCoreTests + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + False + + + + + + + + + + + + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs new file mode 100644 index 00000000000..136878d76eb --- /dev/null +++ b/src/csharp/GrpcCoreTests/GrpcEnvironmentTest.cs @@ -0,0 +1,18 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core; +using System.Threading; + +namespace Google.GRPC.Core.Tests +{ + public class GrpcEnvironmentTest + { + [Test] + public void InitializeAndShutdownGrpcEnvironment() { + GrpcEnvironment.EnsureInitialized(); + Thread.Sleep(500); + Assert.IsNotNull(GrpcEnvironment.ThreadPool.CompletionQueue); + GrpcEnvironment.Shutdown(); + } + } +} diff --git a/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..565b1e2bd65 --- /dev/null +++ b/src/csharp/GrpcCoreTests/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle("GrpcCoreTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("jtattermusch")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/GrpcCoreTests/ServerTest.cs b/src/csharp/GrpcCoreTests/ServerTest.cs new file mode 100644 index 00000000000..b34101bbf59 --- /dev/null +++ b/src/csharp/GrpcCoreTests/ServerTest.cs @@ -0,0 +1,21 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Tests +{ + public class ServerTest + { + [Test] + public void StartAndShutdownServer() { + + Server server = new Server(); + server.AddPort("localhost:" + Utils.PickUnusedPort()); + server.Start(); + server.Shutdown(); + + GrpcEnvironment.Shutdown(); + } + + } +} diff --git a/src/csharp/GrpcCoreTests/TestResult.xml b/src/csharp/GrpcCoreTests/TestResult.xml new file mode 100644 index 00000000000..a5a6abd7b95 --- /dev/null +++ b/src/csharp/GrpcCoreTests/TestResult.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/csharp/GrpcCoreTests/TimespecTest.cs b/src/csharp/GrpcCoreTests/TimespecTest.cs new file mode 100644 index 00000000000..484bad7ca19 --- /dev/null +++ b/src/csharp/GrpcCoreTests/TimespecTest.cs @@ -0,0 +1,43 @@ +using System; +using NUnit.Framework; +using Google.GRPC.Core.Internal; + +namespace Google.GRPC.Core.Internal.Tests +{ + public class TimespecTest + { + [Test] + public void Now() + { + var timespec = Timespec.Now; + } + + [Test] + public void Add() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10)); + Assert.AreEqual(result.tv_sec, 12355); + Assert.AreEqual(result.tv_nsec, 123456789); + } + + [Test] + public void Add_Nanos() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 123456789 }; + var result = t.Add(TimeSpan.FromTicks(10)); + Assert.AreEqual(result.tv_sec, 12345); + Assert.AreEqual(result.tv_nsec, 123456789 + 1000); + } + + [Test] + public void Add_NanosOverflow() + { + var t = new Timespec { tv_sec = 12345, tv_nsec = 999999999 }; + var result = t.Add(TimeSpan.FromTicks(TimeSpan.TicksPerSecond * 10 + 10)); + Assert.AreEqual(result.tv_sec, 12356); + Assert.AreEqual(result.tv_nsec, 999); + } + } +} + diff --git a/src/csharp/GrpcCoreTests/Utils.cs b/src/csharp/GrpcCoreTests/Utils.cs new file mode 100644 index 00000000000..b0c0a7b6205 --- /dev/null +++ b/src/csharp/GrpcCoreTests/Utils.cs @@ -0,0 +1,51 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace Google.GRPC.Core.Tests +{ + /// + /// Testing utils. + /// + public class Utils + { + static Random random = new Random(); + // TODO: cleanup this code a bit + public static int PickUnusedPort() + { + int port; + do + { + port = random.Next(2000, 50000); + + } while(!IsPortAvailable(port)); + return port; + } + // TODO: cleanup this code a bit + public static bool IsPortAvailable(int port) + { + bool available = true; + + TcpListener server = null; + try + { + IPAddress ipAddress = Dns.GetHostEntry("localhost").AddressList[0]; + server = new TcpListener(ipAddress, port); + server.Start(); + } + catch (Exception ex) + { + available = false; + } + finally + { + if (server != null) + { + server.Stop(); + } + } + return available; + } + } +} + diff --git a/src/csharp/GrpcDemo/.gitignore b/src/csharp/GrpcDemo/.gitignore new file mode 100644 index 00000000000..ba077a4031a --- /dev/null +++ b/src/csharp/GrpcDemo/.gitignore @@ -0,0 +1 @@ +bin diff --git a/src/csharp/GrpcDemo/GrpcDemo.csproj b/src/csharp/GrpcDemo/GrpcDemo.csproj new file mode 100644 index 00000000000..31ce7f133b3 --- /dev/null +++ b/src/csharp/GrpcDemo/GrpcDemo.csproj @@ -0,0 +1,52 @@ + + + + Debug + x86 + 10.0.0 + 2.0 + {61ECB8EE-0C96-4F8E-B187-8E4D227417C0} + Exe + GrpcDemo + GrpcDemo + v4.5 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + x86 + + + full + true + bin\Release + prompt + 4 + true + x86 + + + + + + + + + + + + {7DC1433E-3225-42C7-B7EA-546D56E27A4B} + GrpcApi + + + {CCC4440E-49F7-4790-B0AF-FEABB0837AE7} + GrpcCore + + + \ No newline at end of file diff --git a/src/csharp/GrpcDemo/Program.cs b/src/csharp/GrpcDemo/Program.cs new file mode 100644 index 00000000000..258762dbb99 --- /dev/null +++ b/src/csharp/GrpcDemo/Program.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; +using Google.GRPC.Core; +using System.Threading; +using math; + +namespace Google.GRPC.Demo +{ + class MainClass + { + public static void Main (string[] args) + { + using (Channel channel = new Channel("127.0.0.1:23456")) + { + IMathServiceClient stub = new MathServiceClientStub(channel, Timeout.InfiniteTimeSpan); + Examples.DivExample(stub); + + Examples.FibExample(stub); + + Examples.SumExample(stub); + + Examples.DivManyExample(stub); + } + + GrpcEnvironment.Shutdown(); + } + } +} diff --git a/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..b8e1406da7c --- /dev/null +++ b/src/csharp/GrpcDemo/Properties/AssemblyInfo.cs @@ -0,0 +1,22 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. +[assembly: AssemblyTitle ("GrpcDemo")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("jtattermusch")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. +[assembly: AssemblyVersion ("1.0.*")] +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/src/csharp/README.md b/src/csharp/README.md index 5b56303c147..75bfb26252c 100755 --- a/src/csharp/README.md +++ b/src/csharp/README.md @@ -19,4 +19,8 @@ CONTENTS - ext: The extension library that wraps C API to be more digestible by C#. +- GrpcCore: + The main gRPC C# library. +- GrpcApi: + API examples for math.proto. diff --git a/src/csharp/lib/Google.ProtocolBuffers.dll b/src/csharp/lib/Google.ProtocolBuffers.dll new file mode 100755 index 0000000000000000000000000000000000000000..ce2f466b243da0e875764856cd99c61ca9d98628 GIT binary patch literal 380416 zcmeFad7K+g^E^9FXhr=GK(dg`gC zs=8~AIOzt*a~vm!zh|FyoV#)5zcKlo{9^;a(+2LH=G@i!;5K)69RA=o$DMoL_~3FOvs&S&$EgA4yHe**V|yer^uOz&OQo2ArW{;dWaU)KI7k9v*0+Hq#c z|MAs)HRykQJntOF`@w>9Aq~;|Z>->SHm(@*EpYwAQ8v`Oi|5Qn3EUZYo?k9IgD5yi#dN!+;ofVQl?CQuU3AG!4F*#{DK>ybJ;EA zM770*YA_oD++c6A7p}!2>ghU*A{xI!9o#|yc)1;k)Gd}{X`Yju>02Sk&}>PLwjn%& zZ8(g2Y(x}|+K}~S+bP@&Mw$@-Yr^qIGjTkp8uQ6XTcjkl@DZanp@=P$e5A3Oked8* zAF7k|%O?1;c5q{p<;~Thufo|Gq2V_V&4jsp^r>=i2tK^AXRy$^Guc23@D>( zX2&yC>Jc1DWQ;`Rp_5#H3|I7u#=f|^xdx(fS^TU6#g0>?j9T1LsC!gi-BS4iXMBI~ zyQKMAb|3<_%sVf@6~ivteu9##%XdKmzEB%4r-ga{0DKD$#7Eg(BzT2YqG+M34h5a6 z?H|o@?DyL>5}=x5oriiOlN|x3Yx%WtB<_ZwQ_gu6SRy7>oar6@Q7Bz^1vDzJKN`2) zGhJg*Mw_0Qp0VgL_y+sB6Xa4@G=ipa7W*CojX^&a<%^5rDC@RC$m-2ia>LVsaYC?ZN&ZrQRR(Y z0Na*AOk7Ar_8I!ID|lYefkHRHw?t6v2M&~SKo7TZ1H`>@fM8V);F*X?D0GSes)~S$ z7=)H6FGc~Hvpn*%XPG)`ka(5|cbUKCMlS#-zs90VLLv;kGZMipBr$UunfKFHLaokd8 zfC5w-MN;o4SnrzPti#yUv8XpcOc$->)dEia3VbUCuZEO?&at!c1zZB4me+2=ZEvB1 z|AJRS%Q4110F4#_zLuT(j#>zu>wtsHFX3cXQF4%i;CCG3guqA1lZM52_N!fR#drE8 z(98aA-59(I=;gr)xPu2Bic7*1ZLem$BTw-QH$m(L$C9Fn%(^;cdd@uRA~L<4*h6cx z5v~;V3F?L(7~$oEKB$+02%M(=H3x{P{zaMWSCLG*M@m}ELvez+%@}q(Gd#rYn^;I+ zp?_GM{$?hkYm+W8MyoD5z8XG0?9hxNc1+gEU(kxwWS0SApnE24tZ~WId9ay7bDbGX zTI<~5Fl6EnKIDH99%|cqA<{akKD5~Na-!c5ixx*kKJQqBfyZDW)M(L8Q4u^e5kuj= z^wkSdxN)0AUE;jbb+(5-=wHNpDe%VMqDfqEE3U!$C@BO8T^Of;=b-Pfo=^b<5zTem zB2JVG{;!DV7KN%99NOU&3N}(|Y-AfC?lAZKg%P6?4goHhlJ~+U97ICu6)Xn|dRP;F zD)+j8^&Cvp)3&L}LxN`bBS=0VuQmDbFQUl9@Zp8U4rU`E3&!y=^ouDB#cP3xp_-(@ zI@~v2MoQE#=RAt~p-VN7%@+ef#%Agtyd2+TY-Xzkte1?S6zr?;SywU8L>sj409DJL=5^XrC70mm4g z8tXBZlsBS_M}1DlZ#eNRaW^+Okxajgfya#B=o1(Qbo?#^6CC$|%AtHEFh?++A+_p$ ztQRcoaPeT~uny;6Mc%LjhsgVYkMR&)9Yf?yPX<<6#14A5kTJf>NDJ@8KVYF3B}dv~ zeLG?mWu!Gjql4k@a`gI0H8ix~K-KcdL($M%G(&Gmhu)GMdIyPaMQuV&)7;oE@y`zB z!{oLd+pOYG2i~bXG=hRQ3@JMoZ9f8iNbgSephlC87sS;dbcgu zQ48GC_-bG`*m|WX#5?;QF1dQT8hQ ziR0iqV>()te-#(gx!K4#`U{P7Akp<-j8BRCsd4cOiE(i;*E>Fo0{V78=3CF;3Yn-{ zqR)2K--zNisqjevr8423lDgI)0bWVkup}-}3V>Yh3{HkXr{3QdelX!qeVaB2HsRe< z(9DG5MRW!R+NKGGv>j5&6CpW>+fM9zlL(OA#~KdTwe)Cl$0gy=lpAj?@}t}o&Bea^JpK=*QvesHQ>ep=2)aKVg(jbcmI zqX(P0ea=~oa;cj)Ew{4l>4eeAm78E?&W$ z-o5_3+N$n4S_JHiH(7h9(&t!B1bx`jzXEltKGP8+u-!;MFQ)fyjKRX6{wK2C@eQDl zpp=dJalXN^fU*M^P1hhIEY*>NG5A4|4{mUUz=E*K^ z%V`xvKkA^gvG^u5;N_h`;9)_&U*)uswmvjfW;J+T2);Pc=&EB|fOPYZi z7EGk=yQ@7iJuh|Mh+wzijNNRxv*@&?m5?;;6>(64fI#C zm-ZtM^kKF=i!BdD;VGCc??0DM8GSL+z)#{#-Y7rlACmvsVs*rOP`dFA!32E;4`5f z#-bW@)1@WH`BAM$e5Dgc=s<8{lsfceNgr@ua;~_RAy0jAdt4W*eXx8}9)Xb@XB^$2 ze2F{35-+#`pVn@7A`YUfme+5@$FM~F+(z)|_d7VC8j>9MFmztR{$b*)@Q1HBRSikP zK6pcdJ7BgyLJzjt)d{GbZX=heH3-l)c4d5NmN8qBG+Tvh6Ktkh1w@hZ&@raImeKVr z{7|bAyjCLwuOse}DF(q!EMp9S&CR$XW{`#AYD#AWj>znxv8=8XM^?TWC>UkQj}59e zllHOtzA?c>@DQuGRzcgcJV zVrx4$eUu%Fu9?aZ9cM^$y)&IX-=w+T7mb4rcLUi8x53ysej6kh&mR%@cE{g}q6Uq{ zgoZX@mIvLf4Ut*m-}<*vw>mqrDiv%(!P=#$qwPFwC)%I=jVV0)jn-w>GaVb=4)p4E zEZcB9ZijBMVQy9f3mrM^%*@~=*FZSKCBL$P+#?sGd5V@(w5mb7{yasaY(^?nfqIkU zumpd-58exr?gUw{S*Z&d=9^U>wB*5D!}K-s7~%K`epE#XR+V*n6s~q*&IiF3EAvXr zTuEtVj<^7VNG)>`@@|69ZHCjf33|`}DTotDE9cu~pVqc8unk!2x;|1@80Wrh-Ici!^{R%5>52ZeRX`A$&WeC%Wpx_M-t_8)8))XjNQmA0@^s64Re{B8oKHVjY*<(x!wtYGZ2ju zTccA{hbZOKzKXmn;fJpJp&Bc8K;Wcx7vF1BuV$F~uJ|0L3Gi7gebX^w_UX2Wy-a2m zv5Pn9U~EIvNPB92Z*2Qev)!ZmH?>t%-qf~?7lv2lJ+{KMMFJjqO$_%qmr(ke^y$8n z`mlikrB=@I{Lk}hk%7lNe=__pwBWnWiI%@Ru;Db#H_@{DqtqRAkZPK;zH)tMEpr~X zWpm21R0BN6`J=&uRghK0LzwiBU$R8Tk%0|sPOGjNLnF!}39M_x#Kn+wj}}8xTnu5i zn_}mWaWUi}y~MM4_;CJ#fjnZ5QS_n+;`}E8nOCl})=J|z&%`wI%k^amh!aE5s@oiq2vTjv8YNb)fhS+YYK;Edw$2>hLfKdSgj{2?=Q-i<$2{cHGp z41a&XUkBK59eol0_QqfRTzq)B%}5hEd>|tzyi`keWXZi!a*~!{*y`{$OBQ{IZl>p(RqUuSm&-2^>fP z)r%51FeFM2M@eqo?od!uOYHMOSlR!QK*3BZa0yfie$M4+EoWX3V(NOKyD^}aXq@u+ zdo(*?oOuA2P@j(4mn5)*!}0$R{GEiqm*DSG{9T7XyPHy7L$O<-lq- z{ubk}emp)rZQieog3_3qrF9>a0&Lm=LyYiEDc~Bvw^`cu8&a^n6o}y-l7ecC0l~0I zUJAt6k4V8^p@*mK`YkDVk_FnV-$N3rl&|926@O=<}$K#$c5PmGnnET@T5dIjLPQl+laNmvV zdAQbZ!VM-Y(@a1mt(_BHyHNjfR3_cvKPlI9sT1jLM{gU??cmUmleSKQ>tXtf)-*v9;<)MP{J&ODPxC zD%9!bS`Udje_|PmoS_%MO={w2Zr(=-DM(4bEh*TNsx4WOUGgsQQJj2r#Wb=8KR#$+ z_FLTi8lR+168E*-B8g)xd0E8G&C4@mH3med>hDKr?=A)B75KVD`mNU|s>xg_Z{|t` z5uKWn7FO&*trV@A58j8k?@r7>x6nCj@O~65#v_I3?YY8AnL0c5*)a`sPd?l0A0X9; z`)j_bzfyVC$<0erE6Nxwsagz0J6Pz=xEJF`rl$GQC7re;ZLU-!ib+pO7|@p_RhXrU zB{!Ak#Bx|oV~^Vxo{s_2^Xb*@9C<=AFFn((nQ*ivWOmO8S>o^q*mnG<7WBPM>ukRGlZ-nb04eCO zKH%fFTweb{eCqt7zzwNFdJ$;uig6I2Ogfo%tVLZl?OMe`75jH>K(+RuzV@Iw*hG|7 zWjwV~o$DWnx;R6NSr2jY`0h#vi#{5oyi|X0Iskq!pOgaIavSZOjsHMcB)OyVp zVvtw$o^uS6m6b#cT8y0;$rXmETpVlCrDzzbB}yg6NR=cU$eqNLxSDb&RTmeCV~2c= zV+QKXX9y0$6|Pg^$Sa#6UD8MNQ5`-@p34{fKhQVK-u+LxGRxt}JebohHZ_ms44J+7 zvg7Y2(`b2KW`AI#6F^2j_z;7z4CBS%!?@pwF4r$|3O}na6EW|npAYZd4DY)Wyzhy5|Mh>!o5|Q#+XOBl z_P#fv_kFS696JA3yqO$sHSaChn+fGs^WMYQd`S9L?y*Uxs>bBg=*yR3%O&*AC^2g`HdvC-0vkBfv0-wPV=PVknYD1IQ~Hz$6e=4XSbVe zO!u63fd4S-QZ3t2wOdBj=n(_2sLyAX?E?hSsA?V{=t)X+b7sP!d4T1K7mH>=5*``A zaD-=dQH@H@$I4ZKWk`|BBagENH9;~DCKU9k%YxPgB#Fj^{+0av@##B1Ck_6ss6~iPk(!$J&C=T-~~_=+M$8SgtVR;T&gZ|JZhu z$@_9b-d89OPQtOhAd`iylXp1ukg+|Zes_wz8Ni6f`hw4iiWT*9>maFJYO_Z@$62&I zt{3IVIG-Wsu7n&j?iYN{Znjd+uJpYOIhq4#eM}Be@HuJPN;xtfWysNqQ zPNx4a%?l)pIWO|~>i7&iNZt_Dr^kn8HyEu!Ex4B8^?EH4U zU(Q5FcT!~efO`>GHPt?wEymz_4a0r1t2-0ICg3u6;X_OA#5k`FAw z;y$W@ zkq=z9MipO^)O<~=>i1H0S{^n^5~Y=2v)^OSTYK))yq02cZV5v1xhXu9{CqI`u zz}$mz&HVUwm-x0G)%O>|I<@Q=*Q&kR&#%7i>`TSlTMAWa4@BkK15w?`#)#cX8>^S- z!y2xSow`EXo^$KaP~=EMd*Qa~VRMtKs_g09D?|=xd9(YfC$7q`*nvjoL*a;JrZg{4 zhMV0LJhsSA@6=yGL{-TU%Q+7*SwYCK9W%qt7gpg#5k=PL8b$rQmMr6(ERtC@;_^lg zV=Cle?w7-QWN;-yBcFn_d$S{w@7B(62L{eOz3JH-#;DNVXi)j$yHi?>JpPHD5UluItO=1c?;$d)EUt;lS4ZV@_phB)4^wGFE?!P z#g4C2dA^YI7;c8JKC15!??WBOzkxDNXo7FzrVu=Y&n|fkwjlFCGi&47G5Q>A(V5k6 z=NP&Ux{Q4A0N}$UHS4U8DA!tH=ktkAE)&5h5kFF zUnk|_7qTv|JI}INw3p|iw5Yte&S%-fa&uaKS*eHseXA#0o%cWghd2gjl zHoA(z4}b>r`Kt~rLJVt4HP6T25%_E153T8(ioe3Z!qLrg-G1~sxt=n*4%hl7e0brB z)cuFx_So;_`>E0QAIbMKqwnxO;GY+LM~aT`I1R+me?Y#ki@x6~-zTE)8|C}u(f3Q` z`&H5Rm&*6+qVK2RdtqR31xMC`WDxtHH$Z0L;owJNY$V(GI&g}7^#*n+%sX?=X)6`V zuXE0vpZpqqk;kz(Gna*q5A%J{9&k#}#2shfd1ywIcjE74w8L5WBOd=9>H0X2q0sm- z6mk8ZP#x?|>$8R{BxX!Jj>0IS*k=RLT73SRu;6?!un3k`;oP`yrR`79UXY@79mFwb zjBsz=Dd?!>S@Oh7d1OAyjkp(sq@a>{>%(=I;4MV^G%@44zLS=eBbWtXT1CN+`zDA3 z&c2kF-6}HPC0s$cgu&YFoZj$8pM&eA-4^`68TtG!|S@%sm=~p>RJ}x?Y{F1+Vp%bXdBi~ z+QRd3#d`~nlX-yXR1PjejS{x9_-a$nhsQrm>@qYj%f^z&kgqZy21^)zW;~J2=!N2S+AGs=zY)^#nZSaCNSpNo=p5UW0T?Y>A z90-~6p7S|iT4?+UNcMdQPqc6RnMHaZtVIiF2eZ{;z_ySQix1SV@GQzP4?Lx7iUJzW_3!c z19}cc;kXL_XeUh97BQJ@v<84mBj7krcB2hjfbhYSSs=5#+^ zh;kfTFcSjTZURaVCaI|d94;rNJ#gU>_RE?Sn3d<|vy+w*9fGKr+RX^v#CoMTi~q4+ z66@z@cKPTe2aLk=zyafEeRJIRpK>}R>|6UDD)!~-P1(oT5QRqe3C4!Q1TJmk1=wxG z5klCfLQlgwX53nAOMSM#4SwJy&~3XPF1#nOq7!jC)b)q(gkuA%edr@tljVzXjM4W& zmm`Qj{tUf98@vmqh|d1#sgDDvQ_lY2Q4@5hlBnF9Gbdo}3GUeeaiwdQv zka`B#J18TmU`yj1D`}WX@XSiY6}CB2D-nhG*fW!>_B&UM_O{}(GPd*Z(T4`DI%G$7EY`!E*o{E6i2I!x+wp`^L@Irf{Z4h7AgpC3DnP@LpcZ(!jogM46)r}=8_DtNRbhf7^c>q57V3Y z6wC#o-5~mwwQ?!>yakrk;k))tQJoXab@o0ww)4@@lK622YJdhY z0nheFZC|xfb6&*cG0`qwfc2;5L7IL-yaXS-+)KRl;7q(R;OW>b^1J{GYtf)=tbpb4 z^5#pxFCLk(GHQ#!z$wc3vlw?WE@MDu1A75BAnbsQKwaK^DVW&WYP{Hs)QL8s-py^I zDTp=vJTPkIA$2u&&*Gsqw>&Uei+HR@J70lzc7s8bnxplGa`qU$L|-{d^c-K5AV$06 zj6Ukje_pT{up~FQ1FFooAUhUS!&iap?Tcy&v|xJ>+`gzUyh}iHR0&f$H^ei^7vAps zz_A$x+w2tZY|1CL0ZN=t(D;poEb`QgB%j!p1rz*C=Mz#A=M#9Wuw6bOEH5QOQ$7(D zHRlttG@DOkNo&a`L~v14<*Ov15<%yZHz?DcG*C zFMOHtcnEYO9J+D81os_YNl@@UFiBdffGZs z_|yolh*ykNryxuYa~y^aJ5z_N*oz7Q9tgz}Q$Bbp;N}dUumFeAcYehNo}9;%m9Up6W{J8T+bPR?oGHk zqXhDqSPAw?jB9+T-|{s!^LTjI5@WhxFEQZ8Ygh}syi^twxB(u|jw3tQddAxDLJ*I5 z;Y`SK&dYDyNM?8{xGc?*W;U5#NnWEM_i`JrC#!wPY8*|IW+iR1dmFuyb;L^;<=|zl z^!<45{8rC#PKa%UzH6*W&noBrGvMLfdH*b2dXO+-pG1AHGsuALUC}df%x3ki@NS-iW4A@nub8tP)RyGoFYN`Yp z58pXOs$k^o27-AG*yHzU{LYh9%X$#1_SqXKa2yW?mwUTnW-V*?fiwwvQEU=ML}ym-bZ_SLnbV-+>8yPPmCU;?)d%`ToENJGiO+m zKGwfd2@W8%SC#DLI)1r#rYJenDA~&w1(HhkK2yCZQqrm4LLuNvHyUBgg?6)Oc{<(9 zS;4%NZnLaz;+o@laJbw%+hWtgvxAywHaoyJM7g)7n$`F=RJpe*npKTvHUH4GX0>Ok zHzzcsE;J&qI42qJ+F<_H>n>UDif_8k-R_bn?fo&{l7=VcjJr|HGh+t3N7k5;iU$a@ zSkH*Z4LQpbzxseuiJyiSbGs~Fxm0j!Wk$WW0jU`eO^i>(8BfNs;1#gDj(r`n`gsxQraLEP)#3OA58!a#z;R9ViUnd^6Nz|T zERuLZmc)Fpk|HBpoSbEg(-T}orYnm9rcIhEc_#TGk$SP=>+yX+&CzqyaMIk72nSKR zB;yd{uNm)~cwtvGQeX3Z;+j0733pWsE+ZJ68-9>zqyR6RCK4|C7k!hFI)!H%6YJcj zU1OcOmT(rr6TFq@tr}CaVN_6nH~#EI;BY2XNL$L6#F%s~bJX_Z5Z&f{L9_(^nKS<; zKWh6_FnlgYZJ!6m(NWun;|o?OUIZS)W4Py+)9_{OCfxQGTn=!P9;y8lgYjxmw4A6d zg?6cFww-mSwiVpSesr?n41C~VZT6%1U~OH48h3WM)viG;cmB4!U4!yuhX->S^h;Uz z8D(CBzHj`4XZ=fGpa3~rJ8?X=0nukcL9w=ff(855_GFdq8@T{Z(Ci$HDZ7Hk^o_HD zVe^0LvyqyN7tsZ5{(lZiW7m>?xU`AhuO5V{fwafGl1}+ag}$AmTh=GnuU=GfvNNP;`RXWO^letpg48 zdN*0UjI4AXkYpuxW`D`jD_O^jNw4#OCi;TBoQUiiXCZodcUo>*J~*)@#$yd$WtcS< z`gpTV6aJVIrHPB@Po!+%Ifpj!+;MWAHq}u5qZPDE6I|6t6ZwYm591x$-2Bvh<8|)r z1_s|8&cZ|)x=jNq0gt4@=$&)(jRm+MDY5GXumSvNf;dr+Ck!6S!!j&P77Q(dg`=*7 zlW}k^JR*j>&TgQ8p};-=2Uu)Wv}i3-%;&{xk)V!)9Q!wznl<_qy+2FK3oh1Xos#mQ1~`{3o|V19FE zJz9Ek#E$i4UU&w5+T%~1v2Uc~`u>2MHlZOxKag?WRn4_Mh{0CVv2`~kbWmt2?AW4(s0+w^O(vV^H@9>B$H!?J3?EyMC z7sf<&BF{VwV4C#@(G*Yz;g%SXjA2Q?O+Gq9IUJ|Ochb(Pxl+_p+84;v#=9iXbiDIq zT|R@S$(1bAI1mlOFs*?|+jJdd@*f`Y#L`g?7tDfq@$~}#<2Q6GUUuST5R<~o8ar#r zF1!qkH86hy1dMVGq&5g^=zJ37;Bd^YgewpH+>YO8HPF`Pd}G)7AL}3wpjjk)ODkUd`JZ!DTEZqW;?(2l~q@va1rHsHvtFrGg(-kAV{R?nX@ zw7C=%Q)m3or;_Qd-Y68t2{~`Xvg{=~4&=Dk;hdxOpbzVDYq?oRW4oHJeH;!J+!H9u zq04pMOIe)rYfwVHz9?P6;=2=M6ID&>b}<-;t-x2|!yKY{?6}Bqy~AvGA~8fBM3Me9 z$S~)>RgbesXeJPW);|#7s8|)$hqe7Tu!iwrmuxT0Ayo$u9s)vt148AANgU5v3!E3z z7IN^(FkNLX_FJKnaja$HeNbEaiMWV=9Y&NB@ZdYWVD3uk$4(vIhziX0kAtL)V^nai zzS2208+557$3C$WbUX-@$H(f49iIzaj57&6Z7>=Cg8^sbvm%@*(a-u4`jJ0e9RD8^ zVR1d+GT@vm{v_>#rx-cA!ZXAJc)0y&RChJLArnekm1|3Sa8uH<1^E?#Zev=U*hC9^ zCGy}v;K@VZc(<1}t|~HN*>vRveKKg&>jh7LcA#Irs@1F39D?5?zJj=t@9} zePc)ClCnm(TM{Gm$d6%RPZP(i&&>JzQ$6u4>{aH1*TOv_t-p&5&g~SX&HID1dh^JH zXXevnc}te_cLckU?}HB63fq%LK;uK`D5hGAI=NolqhtJ}{x;dMkMZlku2m zc--_ncnnRA$Ay#WvB>av{p5N~%45OQcuZ;!f6H~*8#cYDxh|3pRM%+3^cyBOXI6&o zvSl7irom+O`pE-ulo7(ev6nro*tKb#ySx3+6(tzBJ2XCNgI{x$^k}wG4$9-54 zbWQ}=X?cG!%7&-0Bwq~POi6O^3>Y3Y!Vrxcd8*=QmK1EH(Xjdn1}Gfn!Xe#}(~_Sr zUlWl&t`SW0y&pn}m)Sb<#fEz@QkG0zdpa}fir5ORq_%lEyHsoI_hC0Es_QM!t*-Y- zU3Ca?>W9)Y&x@<6>D^jWj;kDD@~o#b9eZZ?p^~yWj0Gq()3z`%;x;#gHw)-3>J%Em zzX~q$oG+U;=JBbRhbPa9k<&H_PJ(c2lGEqONymb}xsG!&_0jV*!B)~EEK@mG;qMOo z{Q!Tt4!jVEKRi(2oDY(hByu+g6m|-OTH3gyK|Neik$Zq-O^=KBSs338Ugt)P^uiLu z8CfD;5D#vG5tso;e|HfB@W!1D=D(mdF#m!Je#W|?9M6^e@&%wwdc1Ko$ixTlJL0r>|7U}UBVJ&7))Fd}5=Cy)}U&Nd_&F5Hc z5vN5gF`XmJ(+$h%?#$^v2Ox_%Jz~jD08EZw;b{f?kVyEs_;%-FREj6mF7%oYYUiuV zlIJ~*>&w{(T>n_eDw~%>CHKS%C;h*GLnpcwyr?|50VVtN;KL~u=AW?|zu@^{{Q@k0 zPa{g)Efof)3FfOJOex4loh_)$G9dv>2Ut+DxC8U>O_f~12^4-BO!G}eQ1wK6{CKEL zL*%F5`d9Fv4*zD(M#(raf6Y7C>*hJguz}3D`mw&X9lHH;sio^5K)ckB0g{=e>BMYN zH|DdPp_OjzDkkI>%k{;;EkgC4b_9#Mlt3hO7Gbv@eIeWFBpttRiBv2LY*-ewAD}6O zRtly{4AX-6f|!^Eo$aY<{T{9C3VI*KCyah;x@2Z$>Wm-Egi31) z11q3YS4yX1j~q8wn7470HEQD}xpfV9c|1fIt;sWPX_VWi zoBYF`QoMVnH54sWhp{SgJUOz^@ueSf!QcTDVwEa^t{XFE??!hIMhd{H%8@==pwR`# zdT?#&TPsiAGt7{$e|{4Gwl@-ntgvAT~o(k*Pt zXgL+_l5_R}e7*Rmi~(w!v9fTl$wmYGL)W8C_~&^yvOssE9ZdLM!!5|Xur@1xobVf~ z8wpl=7+|Dnw-NUGMKM;H6>$GVc#c{cF2@?#rE%m?XbRaz;u}>Fq)_$j*)SC18Rf7H z1&z#7`t|4SvpFX3PBFFvj#R%??NnxpBK35Q8RRH*V~c7Dg9WuCcG=vo?7@rTEKHF( zUMDKY;$@#f7&Kl`>O`@M!i^56U2-738H^FDgb1Oky{G9;npeqJa;sML7RKL-aRZNl zUdOst&~wz&`~#q)7_PTe#c#GBMR3ANXPX}!+#WhsN|nwEelNDtxk?-qZ%UM_;zY*( zjW(6?O7VGMmsj?7;#b~DwPG`Bi1!L`3w7{o@TK5woP$&f)nzXMNShBYOi*EDnWA6? zFH2M_-C_*9QW7AI#t*31VKE0O{=;5rh+ffCW>&+LKuS2N-dY z>z%U%iwnSHL;3p4DjaGghH_u(a_VY+M!-KFxDsdVQ^c}V$E~vwo~xsDIp=$zlh}(f z=6}*RXF~uZVxj{&^|Pf=oY?f?@)ljW;O(=clF=E>Zkxni{8scTeQOEz;6I0#%MU%WLdgLeW;%cQ7)GP8-P!Cu~cat3kMlp`Ie ztBxw7s=}sNEt)XiT}t&4ue53gPN=Ic<4e;R{rNujlOTJg=of5;P)|OipHTjia1GuC zNO%1W_)-cmsVsLn9+qC;gS(kt@LpW>#}caL$A|;a`%t#=i)h_-7$rJ-bDfDV=wo`; zZhmA5*(cEzFZp}I<;V6XlAfN2E}8e=iYzsfC)Kv^f}h5&#uPsFE_ghyXg^!%y#GOm zSn%Br*bqMir1gJ4l$Mv#Tyb)D`~xVkX)({22!Q));tmX8Ej#!iz}@o8M5W+ExbI?B zK8%~*+)S*ee4Ty$4D@xBegvf(A4GwFDqQ9CoM{;p zt`xn+#P-w_S0r}TPXKseK6EIO%EFjsa*CsW9YS4{OPxy+L( z;yIeyaeHU!vHA?Z-Q&MX^zrXNWv$s{EQg-X)dKVMkpnczT9mnBZTqX@<@#!t1$Tnn zlh4Qv4+6mdD9XDKj?hz(I^e~IyNIZl;A6PAPNBY1s;$Hqg3X;ioCV><+2 zOpw~T4<_IhboSWJ8Ff27fzZZ&3>YyFeV%-JLdHW)aQfA2315F8Mi5KMw#hrA9r7bY z@bP)})PZ}D#pBe0aQ9AL`@#>(bxyc_ncwGzZcNJ>aUC3Oa>fi1;h1-k%M*&|vbT58CZ!aJPbFh-{ zu162g1#{&Vxl=BH=c5#Jm0WnzR<3B5ftpmm z93G@MUE!H}(;Z%=H$CCI^=4Z5u-;4$|IM3mEGd^aU@bGDaZNQ|L5=xQvkEWiBgNN9 zF_xm}Q;K~_!Hh$v1KiU&z?Kt%CAvEw&T=zo(CwvJku zkXj&+hX`R~#gIOQVD-Gbfo&H<<|yP5LfDkEY%gN`K!22ft~|)oI2v$sdmL|TjT7~9 zj$U3W@Np>KK(=5}L%E$`DfZ6zz~lrkvm2P1VpZZ>kj%vx#uWDF*GYdq95Aa%6U<)^ zk4nQBXN4|$syL=OSoqQ5(lnHX{1mZ+jh==f=GeG=R1!!X3Bw=92ihYdv@jFTF)L|> z89ExT9UXF)wlG?|9o?!)U?)uiJ7p5ssguA?n*_Fc5*S>nZ5_^>1cr%ATROU5JG-1a z3GBQ{V0bL1El>J+J9!sm!A!dcSuo=-7iGZ=9eYqac}OK&VTO*~tsULPlfYg+3G9+A z*z2`FuwS>6*O&x`rLHtxYu?2E>^U=Bvo7mSFnI7?E0~XOf&BxcP8G98c30h^D%4#6 zRfrV+13*Cf>Q1mr8W?_?`^prI-Oj+~xc=3I-EZmGwG3>o>tCCKvHKWUzw2*I!PrF% zY@X|1pMtR)7}$K*zaa&qs~Z^JK7U;bMt2^bi8>=i?{xjwr-1ZX3&gVf8&W_z<}edZ zr!e>!K3)G!gh$q)16ufx1&-Cj1e}g$;Xe`httmJi%EBKP_*+x*={Oetgupiu{v~T= zI*5h;RN%KK=<(v{qZlXxeIE|5!?FFN#cZxMfRX$gZ;n;bqf_wSLmGLKsTWD_F$RP zim!`*d2@>k0HnKHpg6md_7?}rfYRYDRNP%j=N0<^q{myJ_`3q<7v}&-m$yK1cm*yf z)&Qi>TcCKn3LWSyRsp5cTd26aftHK?fYR$NRD8ZFb`qa2&IJP9-Wo%kzNQFt`r^QJ zV9@a`hPb`KpxYM*x`08~w;1C1M#$sDm=6T{zSTlJe<2`_H%QMf4)g$n-mjkB^0cznxc(E=&y@_i zaXDfMQuqzH44q^_UaFY#XMvB&QAiOy2^N;4)m{9`^`C0ifw~_oK6iN%1bZfO{fvCR zNv4eINtuM397lBGIB(%Ye9#sTQ(^upa+-M^i%M8rTfZbP#!3qpUQf5@bwUQOq^%fP zQ@sA!o);sp#kG1Rc`*=Mxajro_PmlweHJfN3KlNBI#!~A>T7(ix0YXw zv>Cz@2cWaYQ3X)QI&ke!>fp(sFa~=&z`@=W1}%;*i*axTF8Wk4-Y1s{-e*wa;8o=$ zKxTKJRlrqAfK2Vapa47?8?%ym-Io>cnj}Cbbzf7!bxDBC=)R$V*CqiBWB$VmxG@Qk zx!kuEa8nY%0ONmG0XHWBGKc$t0^XPe$mH!Y1-v;4kXhT~3V2HrAk(!c6>wV;AoH|e zDBx{L0K1F-YX!VL36PoD?+8E}$h_?5spjNdol{NqN6+{0#96?6De@M!cft9C=QwYY zexj|5#m75Q8!4xW3E3E<()U1-CuLYrW^c< zc)Rf}At+bK6-&Xdsat)OFdjy?L;_wO{h3^6jy@yTJ~_P-PwI{C245=<_@jr*b(hg| z{J>Ah)Se>GOCe$BBT`@N{u3iWZ<$mUGP!*hImaBgpL|d%#*+6o%ui}}T z%VDldkH$yPg{ek^D<$>)TQQD{FqkRTw`s*Vkr>=0Y@9%hXHnNCDJMl3%xdaL(VH<& zjWC$2)Ult}jIlbxU{zLf`;0JSd60qVQAVsNof zF*vfkqbKaH&ptaBQu=zZHXva;nG0#JGkSwpF?C zHZvbAK<_XyS6aK);DG4JE`24xZWk;%3=fem%X11cH?8F5Cy&#6@um02Gt z!XZ*7nS3H*rW?5MiIElmdG^_MUQ5{(ImboMn$TDuUWOGIABPZPJ}g0A8?h!XY8q7c z9&GsFSg|8`X?#f%RTD&sO$$p18q)4zX}z|x>HJnUyF@fpO(r`gQrRg&BO7C3hTb*l zOI9FzSR`;#x@v;XY{xX!wVgcnOe>H5b*l1Wx@u$UbhQTixs}J>KUH}#U9~ZFx>|!# z!^&Iz9OT7x)yCB6Y7NF9E02+Bs`ZQMs*S1B)f$XwRvzQrROQ8VwZ_!xY7NFmE00lg zs`6sGYGdkjwFaZDmB$!7Re3R8t&v4Hg}%=)ZbdV=N1`X|`!P`%?|?S-#VN?<0AXcw z*qD;+m`LMJiqOQ5Dac-%kj(*UO0r|3Fy7;Bs&~5>#F5Namg8F_a0@XnrmHrlPN&16 zl_#6fQ_{04!>H1k&Fd-A)fyZitvn8!Q>|Z2S8b%|)|l=0tH^A2<7!^~-!Un~^Om}% z_GaeO>#qPLRx$%UhiK=B z{BzD2xbWZjY!c$fA`2U6!01_wM`iEkl~S}Tw{EHf<+pCYlao!GHsr;G$n4oX1-a$* zB*UJKXgHHnuxG<<3@T~%5JW?>sLvQP2UyY`{!$>h!5>(GKa!iITd6hu1fCcCsTDDc z-=AC2U71k?A48H;cHl< zxds=m=_|-f6;u8!@R5GzPLLwNbTx75wa`9^_>&Q1nq*3U zu(hx{nYbW2v3pp!to!YU0$B^z^|f0y9=NqzyZMG^YA$uF+M_w+*{P5Fffx#jgFLw+F|Ci4p_Y4Zze z*py#rTb&FfH^{RB1#)Z6FA{iOP;5n{ug$UT>np!fD;nBc^NSR}&Q`=Me&trQY<^v> zh*|u)ThX%l^|T^p@tf9)md$T^E28zwcKu@${dIgb+9_@v>*oS<{TC&HgmH}CGTr#; z&2;Ri%G1h1elY_&V|ZY%ZrlW4Lw@l(T$o=d$V(MdeqyXAMKBXAEJv%m*4M|cj>i?V zx@&%Ma1sQ2YQ5BdT4PL;Oz96vnS`6TAUd&oSh(aD7oi}LU%Z`mcPCg?n_rL)`2{<% zR=FycKz>m{1({zEBO||1o3@xRiiY$l<`;^mGEII#j;5hBzn~|mAdzaT0Q+cup>|as zCchB9ZGOQD8F~hy6g^^(>fo)z@Db-1{9>fp`~uq=+4+Uk!IQeC^9!~#@{2=piSi2x zr01f9`GthhS*?6Ezfjwl{DS=`(mc*DZkIL_pIV%qUoco^#t@*`lh)M4yRjJ>R2H6$aj zCa15#Cyc+Uy%n*VAkzFIMYtSE1r`mERP@mfNOhuq{SLO>`qu+!r8L3(wMlmsI>sxZ zi+WN_qj|;tnf%@2}RMWDVLBo@UI1u8{j0UaxjnF#5YrG6$w22RAMWl z9a|H4!Gcya=&rd$0?!L@_F=Q&EPjKnXxaR*^EnlM3tQ2$`EA>Zn5Ex#t!UZ&7PTT; zUlhA!^oh;%-|^R?o#Mu^zAZ4?>BetpremK_o>mreiN(+vxdeN10~2`W z5}2b%E}xz(jk{%FV-qo#S+LRwnqh-OAsR?moV+rY{ICu=~K)l z6mLRgnp}b$O+#reK~GRYBGpy__R(BI?W#O_z|&|ItyPe@p=TsY(KDA29lUirCvypY zG16=)%yRRDC9&$SAr7k5eL5^QPY60g7|$|WRtz7ZwNB_wp>d5dPgnoFo{OfJEG z6lqS`1!o(qRlHl;Oni#D#Lu8k(^>_CWmYc1Xxo}g;PAAjTw*EJt79Sv#&SShalvz1 zs@M_qt*JyiIVP2$4X=clblS z=?RbDMoOlI-_)Dw;UNp9WJdToy{UxDww02Z;V1N_H{5kQDVY_%UvFlIixx>qHGG@i z)WSK7rKB&sNpI$aJ=;sk-0&*B=?^=6DVZ1I@PDd0Km42CEC_=gq+}rcwcZSdXAVip zHsND>voJhyiIi*`KCCy}g@-Mbl11Sc^k#9mVn-?2KD(|WUec+u`svPbw^ zz1cH7dk-nuEBuMx>>aM!Q%dULxAbN>JYp{?8416nH>2UcdrQea;eC3uEZnm$CCkGP z>&=RAhhZrh3*VtP`-byJq-4MFje4_xSQ(X)1H#wn&4FQlA1Qf3c$wZD6#i3h4i3kc z3FwgUDZM!~eDQKAIV^laZw?PnULhq%gpcaYk>Lx+q~wL+SM=tnaQ}U!92YLxUrLS-H|xy_;lKe>a$@*q-Xz}%{~|h%yc50|QB&UuXVH3obQP-d zPPly++`OZkcixwe-tT7TlQ+hb?{{O3H@BW2QA+wG<&N)!Xte>8(*%WizZ$E2x%K=Q z=(IidDbcwWJ}uY2uy`QrFejWZ*SX=Ia_tWflIy(iM7honUn189;RLVaJ5%JQlt}Y} zHYWGE_57IMEb-04q0exf6K6ln#56!v|6v<|~H}k^?{HHrL`j!=WM9Ii@yyk<*7#oE9jj50eu= zVn@75NNeHy4XcL@tG~&0Zn(oiYy!MADA#%6*>as9UMJTD;T^n=?@f^(Nr@cjRGD{B zCO@M3e_u#zp?5G@_Zb#-vbZzNqO2@FN*4U+w+2`yq_yx6!{S7_&I!+#>s-Tfm@Mx~ zv+PoqA0tbCM4ebGGD4D%r%Ae%rzUlIco<`E+hQKU(_wO~yPQHRgHLnCEG^_J`9CVGGO)x94?yIYoThGE4UL z&#;gmQC~k$C~M&=p!2EY(lt{xk z#}im0v9oilgM}Xlf;0{1WQZ7$#lavAn+_PF1lL`uYy`CP!2TE(9DoW~gnrEDjsq>? zNCndi{{zr8cmWW);)=EAah>JX(K?!gBj_@ZT zYbIsT|qPszh#%AwFVUIC()cF;sHFQ41CtizQ!tZh&kZ(9`f=(4ZZP$_pJKi%`$b}CxPb$N3a2(RC!Yz6uhl)|9Y`t|J*g61{uC(`Xf~BN1Te8MIRLh*PSA zw{ExOIugGaX?7jyB-S{p0O%-xWkfG7V%L%Orxwx@i@i8r4fmngXeXrxcCz>wHm`6E zPDzPj3}IhUJDD{l_Nk~Ax~4SHAwS|^+n;MnKc%qryG~z*C9>9(7^7R)lum`1^#0U~ zsGGhVQrxQNUrqx9SyZ|Zx<${wv=fA2j^`PRC2c&hnEykxU@jO$n3usKxuP_^bww$X zM^5(Lki^(SU-N=Hl3qc3dd^P37n|pj`&aC)cs_JZ9;|`Il5pyKm(tVp z7KFCbam|b&gat3np#7NAvaQRrQ!y1WU;tGORL3R zRZ>PJ@hY@hp}$g$7@5|J8Kqk*xPGvr_A0T z`df5>*uyB!p2NdK>{#OkFM$!F=&SiLb1f{K5r<=j7o4As^C`vQ*x&_0HV#(D?D*gX z7iHsoMsXPHy=S{Toy@Ek98T6+eUbXf)V$%$F) zwH6rlGP@cvhwtCW=o=g+&$LL*Wwf*?b9qRa79-)pg2hBMC|Z<$9#y8r+bT*l_F0s9 zJPS;Q%V0Z2VYOw5vncauW3ch; z$V?^sRwolHMJI+A%V7wQl=wSC6M3d5wSGig$rvnd7B5C43m0Cyrg=Re@lNV0VaeiJ zUdVG9S4FR%CwN)-XA|@c;*-(;GK2ne33|HVWc0tzphso`{ZxPY?PT=7&7l86f}YMk z8U0fk^vQN*_-v*(a|E#;qp5{UTO5~eizF{bTZ?OLeUIp>ZNb=U;lgXB<)!O&UsPOn z2fJQJ9k5=t2t3#PvF2WUSX|zia7cQAl9C+B@o>6CzVa5#?w)a}5|8 zEsQ%+s;vV0kgnHh-%}nvq)_B&y^hQcJrkW2J=g2*XT803%-*AS)c8f}7{8--Giw}G z0Cbf9q7F+hb+zkthoD$J49|T%4(mnFeM$1(3pH>C)K8ND$=s^~(7~QlX}iKlD@&=T zur~nacyS_-!jr^EVQ*BtC>u!OvDDL);7y7br35KFmU@~Oyjk(+^^zB)@L1|;a&U{_ z1u}nP($9z7B>g>sE{CfBkyW=4$$d`}2=FBJeG3>QjcwO0rL7R&{UgC$Au69g9pyaT~k671iG1r+?onN&>k z(HShS;1?PfLXiMIfl!5?ILigops|a^g-vYIZ2_$BFD(9m^^;y~3P|040V%`E0%!az zB8#^h7EoJR6oT8yVli2eLvr2twf}8-X) zHdz#Y7<+cK^d{0MVl_de?=++cK||Up$Y!%!*>r{}$&QIsc8bu*<{U1OyLiFh5DZfN zSl7$%XFpD@mnUqa(GWkF8mYoSGKSh{C#WcE)hN#yeloh?iCojCD!YcbvLx{25l3Aluxy_ozMsGNsE@c z?PhJ~CHD$iWCtR;i2-G|vAqi9NH#Uw6F+!w8a=Z8r(|r9-sJ^PZ;kC`)XW9%OADBq z?G5ewLEFS&YPJ{p-~(yVQ?$L(is&W=lh|H~m>o>b_QVf9m`0CmZ`NDX@BgtiwwFvK z7knr!U~0BEv>yg-6N9PQUg(35q(x8B_DUWLxo0)3Fz5IT<^V~KF~ImpwnRfBi{7c-|em zTD*UKCN#18*U9F!&mFT#@Z53V)Sf#Ahqzhuy6>CU?OMp0SIOBeSF}lq4QBk+F;hl3T(cUedC1_$)R7S;!QQVN7%BWMfv=QFtR-$@U%mdZ2MxDyY zJW`FSy_Bu?D1D$y8FdBU;WxN{f2AB%t*cq1E@jkZccR%5O$*=`yyy#-78`Bf2FG}_ znQa(tHg}An2Vy74KTn_AcJT|$OFv@wnjdtQ{?p!9JC^Qe_nJ@ZSo)fHulZ&4e*7Ch zg<@Zcj}06Iq8A)=i@9dDnES$OWD9vt_+GijTg@Mn@AGuadBg971=epn7CrR$XlMB; zl%{uNDg9hq1UVj*dqYjJaFde`aiLyZ`*4_8G=dFL)SjV1p=h zm@Yqp#24=3=v;QOtJ%&ocKoWKiy(26E`-rO{v7yPglF^k`i zTG6ukJ=Tht#qY6`NtyU+ALW{bs#9=K_>tpyRksTP4Qz69D13PW9q({k5=+JrBm6 zpb7?t=hALWz|eI+Nso$t9g*Cl5l3U6Bsh>Vel-a<;bQ|EC3=f&os0#wu)*sF2Mqe` zS#&erx~PUzWI9lm9tjvzj|Wc^Q}2Tg<@F>*OqH5w9IDBW`mr+R3>mG80{i4JT-L@O zFvF_Yr&y@F#ufFIw*$`&KE=v@n$qf!&R@b)`!cR1@VwwNt%zCt?r%kl`fZBe1FeWz z{65=?md)>Tt%zCtKHrL#&F>4Xh*|u;*ov0T?@O(S>@$<~&xa)5ybJ9VH;(OR0(1R$ zCV@6iY?*HS-OY5=LwQ49iqe`O;U&Qk$ow)FM6a{&yV#=QdKBk|OBKRs; zSdLbAjYs~S%{nlSNlfxLCPA>L#!>a3))?0$Q~FC%CgCQ&h)(Q27Cyx1QSL=S>+>kY zmF{J6ZT*tG*xM{zczx3H(&rYiYs(l#v)Shs$QREoFnFl>s$x|y5#mc((2lyolG}xrOT?GC|w%$ZVT=JuIcXSH@s ze}K(p${%h~tbR^fRHofV%IWGJUHw_m4Y$|`Cyz~=uDqPC-YU|mf0y_#V+gP)uUB}y9Nee>ix-(X zxKDp(3H<{)e?>mS|8DZnDxv?PNB`71=+7>p$AJm-m5h%h8r|fdTSD)R|HQvbSC2xm;R4#$&PZ#x}^49Fz3wT-MXZB z6C%^DOO$DoA6=K2U0HLYRM$nYM_rfH#+8R%mqc&(PF6Cv^epBC^jw!jhh%(5xU=j$ z9luB$^Ye6%vBg;fKu7sY;qaAS+UnM&I&SNzVqH2FLbG*=6C(XG{@^&=lNhkzSQ^!q zyB4O$(lkXKgLrp*P4H;$iNY)%4Opt`0UqxrS}ozB1jwbKN(DW@<6R58QSl`6UDGa? zcN5Ku9~V4x9vjFkkV`79EbXCX@tc(DehDx4Ft+UdTaG|@-tBiBrtbmRAO}B)c)EC4E$dLCB8cF;+yNhTUixR% z=;jelP?6ylJ5N6+Ep}-(wMoB?qqnnI5$hh2o)-%UK|@-`j+BYh@Bh2AU|;HawGQ~D z=NAfnzw){Cy|~ur*!Pz9IrqKLKgYgT+GpAKN;LhxCw|Aiw`;N5{rGd~ds(hJo)r{Z z=<(0%`F3w<<-lHe*f5yW!I_c*eaEwc-R)jvcReduV#i9vWqEpbPyBVA71X}J@VWH8 zga94S3U=>%OWWnFpzS~J_elM|7y6E81-tjX(sny5Skm`O#878?eNX(3X9XY3bdm2gjGG}_wD-V`!u@uy`}vFXxkV}&+diZJQ0ePwQPI04hCmr{c%%T5#7dsvRNf4 z>3bz&_&B}3C%*Y(h%Wl=;P`i%)59%B9B!TG8IDXM_2YT9uC^7>n?FH?R=@=G?rfFi z)4{$a@YxOMs>=kqq&!FB&Q@{W`Y_Z?akh%VkzqI_VzaYVGRgIp(pWuRn8CKO9#ZW4 zy4N9dOWLCEh6brSTQ!?{4Y%g&1g z%d~Z!Uk~yGCgG!Ng~C{e)_5wsPa55?x&wV#ggwMLIr*LfjG2nfoAG!tkH< z3${CB>R^^`ZW+wh8s?S3EFJQMBF`VFJa%v=c?-*6KU6x7NGH0D%3!vA9KKF;o0P#U z9ml#8-R5O5OGm@#M7KDE>A9;PY5TSU%$>X10&^nZu`C1Dt57e?`U?{ zmj=jyD&7WiB!pwCBfI*g9-a)Y3X?Y%$oM) zb<;JsYp0_g%G3El>^sbW&L~y#A;s3Nm`_;gcfp1G4hl+w+mt^SeC&Eg3OS1EI6B>R zy-p8o*CAW8)0z*GHWQ|HMr|BNjRD$Z%A9u?PA9w0h)#3?4lcFsBXPsqckuKVPwsFX zFT0itFFFMW7hXqq=H)N&j+b5Eg%{U?fY-5|dHIXJ<7L-;;YB|a@H)OTFFz1)y!`&+ zS`+X(u`@3}I4I*q4;%10xic?6U?}57HyH3bwKFe2h;Y1Y3?cnRuNm+<-SN`>kB2qh zp!vJ~57J@(gNvU|gSyNP?0>|VH{AaqM#=t%oxAPRD#$%{%I4#Q$P^E+9PQ-Q{SSHv zy)9B*6EJ6V|3l}R^050KqPN@sAahI41w5eV{)gzAjE`ci%Jx6_#Y&q66rPAk*y5}K zxJUW_$mqp|xcv`2j02~_*^m8?L0q!^4+)|gxPyK9^?ZOtP@@W%g*Kz1`ETvZ=ust_#u8aR!U zbCEmTqSKBV+XHo|cl?Dop*^w~pG(`oSH;~ylojSsocUiIHGV@>CxW@y?25teL7NEr zax0>;E)g3HRYDP0P=t1p6U5|A`-+~u@roh_(=ElIkG1a&*d6)PoT{>#kg!r__fR!X z_lJ?2J{k|UVyf=F0eD1!7IpxD4a)GhVsRCk_(H5&CX{$7kY2i0V_#R0bs zY)QSrO&$6e?!{Pt>JimFBHfD#2th+y)Jb&syznm(PHn3E@! z6xYD6de+4>T_;VSZn#BzbMmBQGfjCp-EganZW?1rgLI;$MC3$w8_S$X$1)(aeKZZ( zbPG;4okcdR-DKxPDmx&YzHCl2CtHeO(`k23q_P9T>C5I+cCuM73EDjZK6d_jdv@Mb z8g6;?riKaM3Ydr<`b=)ud8{5~H=wI76XcSzX5(s{^z%M!FR5|T>xiEbv006i$cf%k z8mp(@ubt*;-k`Z*TW+vVjP6ZMngeVrUVg-iCDsa5EC+GNJ715I4r++I0pz4=KEhl@ zV6s^r!kA|XEE|QKM(dUDEz~5*Z>`9BUjfEg#O5n|`HC>cCCtY0Y*D6&gW+%hYng0f z0AoI7a~#6VOf=geI9e-nzmQgIb3awS`vc}`b1VJY9G{7NHDvsZcnAg0=jyUPgt%uS zXHg*T4dH5^@LT(G5Oja1Z=8;Sk*o4G$#izkpzRUCvfmZC8xnr5x^uj}n%|9p@EHMi zv8|U4qF{>2mQOv@crLpH#x#?1w@yc$RSvTB^RC4a7s&juXH z?!v$3E6L^({F$PmWt9#nc|STdcY1S%yb* z8$!$u9i5>-Huc~*+q&EwLMJH#bvfxtEn5vsO^coBYFe4qxo55B%i~ybdzn6tC4Hsz z3#4=><88}c?5nnCt#m$K3L&wX$F8im>#8*`4=*vpMMSO@ta*8OW_}S3Z7<=spomz; zZ($LwoZkjT#4>&x7SYQ2ZB#@ot6~=g4>i& z-)Vg#Ddbh+j-%6E+n&C)T?hJkSp(8@d{EXOYID@aan!Y=O{UB(hv9Ux>wxIQspjBP zvwA0Pc=azN=sb5y{es28@v>`!@Vda`<=|fX#iu!04sO>6q35)9aM9&`P8Y4aJf{AI z-R|lxq(j|>j!nD2E~A3F%Vroa)?J8EQg^X7(>|P;>Cx6%cTv0vktr@lIa;I9x(lsR zZ;Mpd1dNr|U95cz4_kK;yMs0ZrAcOny36Klan=CP zQU28#y_l}6yIhFdI;yCv0F?KQrI!&VN#Exocrta8e{j)>=jwtc1u~ zbtf1|B}A60Ekn)RxlZj{TL+l-N;N`2$|Aj;|+odi>YHB;p5 z?BG=l&@?0G%IKY)%(_4Q5c;>rY|EpSyOTkBH4}EcD^O5mLDYmDXD8nRT=N39GCVuE zgk5n1fQ5wprs4w_S)(Y;J3Fbi>F4Wo6*xOdMTT2!fPPL|5=pxurfo7`R~r!jVu|V= zk>)x9A!ta;b;HIRl!UW15bKx=j-H(q{=wNvPd_zf98pD2coT>A`68!VaE7dec5BeK zF$mfgoHr@Zn{7hT(l!>DY`=PWSdmslw=tmX%^ba*;npfRn-jw+8?;r=P8P>OXaBMg zUG!sqZT0icuz30nw`hTG+!_045~&~a%&zSV=*_lJp%pLzy*o?ORS%7#HP4iZK`trt z50_{1uI(OyT`PGeCyU|q5V2XFDTb!El*a1m%#Lkio>}b6y4N9d!?vujdFBz+WM1od zUQ!q!l1r|Uj042ZCl3b1Nn-mwn7D%w#u!HDM|eMrE9CcLi?CnP-%j4yEw?<}SA=@d z^V^t$t;m=s;{FUAd@8Gh* zPvEoJ4n5rM%*R!r?Nb>X5-yCKj2?g7*Ees^|*O|>wb!T2~~2m|R96%g$L)a+cX~%)MOhgCzW4{F$O*6mmH|Pch%VQ{)gq(OeF@@-~+{6nBfc zTu;On+B|K0=u?{{Ir21Vu>?UlKY`zFNZDwYr%96)sOKlxYIj6J+N{6@azsMLz-DM{ zb9OL+bBH;W*lzk|@QBS0?35Q%6IrP$O8|E{m4}y@9gB!%{B|m$mGf&95zF|E6w%80 zEiWRL@f$6omGgT^5wVQlON(gb{B|xPavpW_CBNnzX?_Faln;)Z_X3O4ulYd2=!4cx z*L<*@j(R9hCkuJm%b+ttSq4O{6Je*w%TB zFW=cZUe?zNFHYQm*DoC}&C!0NK9fV>ax~H*N29~ip{UCUAxGNGR4U#M{7WuqtQb3wn%kNz_@9SrsJ(VY>p;+yBv+oEj^uXK+haabV$Z` zLoA==X#8TOxg2d*wm545=qP_xMlWXUaY+@q)e8XUjraXnXEFq1**85O#9O2Wb_AFOQ~xmdMgqzyyS{C0nrRO8BC@< zC*y)qmXmSCDduFmLrj>H?LpldGlX$lPBtsc$!>wz>E>j6f}!MOKLk;hlXVh&1(2*+ zA}6CAtYToJIT=?$w~tDY!(a%bJIb7oHZA*tP0NTlA**1Pmi-8$VfJD}!?bK~_QsC^ zETm;`Ev98zyQs0hkE-_Q=VdZn+M3}O8=;?*mPDRzm}!s9%hV2Zj1{r&5oul)5Q2uZ zFDRuQZn00CK=xGo94M!&dvx_y869(xb+nb;?nE!85o7Z039v=5CX z1eT40-p|(6$qy8A55n;DE@a_YN}DV?IJ*{}<|{&3gxNKfEy@&eu-}Uxn_v?I7}sLE zeiCN(L$e)%qqQ<$8PaNQ@(1O67GN$ndBo36+@9AH#DgRKGd;lp5aRZ{4x~WbI|$cj zv*-0!FmhSW-^ldfoWV5ry#A=VbG*Hr=SK7ha~^hap7T5j5iaNPo8k7n*okgtf_(x#cjNPIes-ojBDTTym?; zpoy1T{aNO;=oc&wj+b282Er%&1;WAY`XKb2wt+57oi3VB{YCu?yWQne zq(eSM$EMw1mr+4Jbuz|_`4llq@+oUG?ZcUw9&MfZl;TZ@%-Q&EIa;I9e2P}7f<&ro z0&G|FDQn-t!{$?>x67vpu=I4!0X_36(IFY%1H!X>ieIcWmrtF-7H16r9p!V+OX%63-JTcKmb(^qDklYg+lLGoVKBXtwQ3u3<~LcY zP75`2`IYvq&9CSmvX)bK-ShfuZhsy2yts&zep$}qWl zCA(q)fQ96$WX~&W6i3rMRXej%HGV;$9}(RV-2Tl&{o~^DvpEB{&PZf(QgOG zzf;?WTeLto?u`92iPVpIrpW8mzJT7G3l&-c6VP*vUZ3;v_TKz{l8K;src4ZSNtu7R zJd<}-&k2G3P4Y}m7Q^WwVzWF`3{7t-jn&h)Y37;5zN~v4GB<3?19s1AjGCyukLMp> z&5!e1t9VJzgGPKXoNFiJ1F`?fa{+T2w86wpgfPZ7I&Z@JS-dg%p+a6s*x%@fC+{l2 z==JRye}S(EbtKHj8f;xA(7`O-$}*UxV^?&ddsP|C(y`w<(One6G*A7z#ugKRxjc0i zUgRI}SeCUl9fjL;+2u>MS=RxX7-E&O1~&@fjr^Nu5m!=7zOHc$})&HWn{;=O*r~1=6Et^ z(*40AHe>9#Cbg86GuVUk@Dj7Kh*-vNyoe_LZDA-kF&7pQ%lN&jh*r+;)kVZIey=H_ zmGir(h*-vNqKH<`Z&eYIzN4EDdy4b8c?iZS9~|dz1Qw?U`9Q+x-_}jnJhYvTdMHmP z3wdG-IwMbHfYzFX4I@u%;leyoK}m3%^1Ymm6jJJP9G&j!v(wYsb)fG1u+8afplb+|ImwCtt>k zlRDsKI`i@!zvE^7zO<1(AmDX@U#v8jf4-J2&KdwZ%7;(aHY8$%F8`du?O3cC`R9uvG|N9_;X4L*kXP>H z17y`ZUI8OMK$g6d6d>y|Y)vvzS?`(vNG2-F-BeLWeGXOo+2*43J6SKV9hK;Q?7#g} z#$UC@Uf-p~y*@5lWw|I%vin64gbU zjoydrbocx&0b|KY(T04_uag4r2a+{im$FO3#Pu@v+o=FHsD=M@<+;UO!K~#(<*uOG zC36@V^B{+zdqo;bU4~n1sD4gb5}iAfpj|U}Rhv^k;phnhEvU6M=6e8A<6ysLUn2y6*piP);4b&A+5ZzOV}x0GhMC3^b(%Dl16 zp7I9G4cqb)n=HZ$xxDcTejIMSftThLRO9XZIIr~%Ueed&PVio_Ptu=Fz7{~I3Fn5! z4FZ#&1`T11c?6b?Oim+l$mE9$IU-|}e~GNS3o!a>8#`R$D?&X8!+$=D$5v*FIGClo zybNaP*d?9luIvK4stas&7uYpjVAqww@P<3Kk7L$J&o_2~y{QZAEnQ%EXF*4KH+F%& ztqk@bZ3B%hXai$NyFWmfxe^BG5V-Gq2H;PV7NNk^^z~N*!R`C5CNl0_gR9&3Wk9s9 zXC1H2@nvxw9&C`R501a=*+$gfqi0lq&Jr&#KMWA&c^Sqp7MPG1)QX($fZAE%cR_+>{)YjD6>ae8?`$su>ZAVR*>QT62lNMEW`j{>zu^F2$9j`rCg?p^Ht>JUkfCHhA~7QO<3|@C!3(_J1frvB z$dTWO4|#EcU7HsdbQ}g|Y`xPA2}fCLU@edTn&VXK9r!av$F!*56&s8#HoR4?^T)m` z*9BwG$aUe^R{QHq(+-jAhGS>Tb)&J%cbe5b{ zrpY*{7>tDu$!TQTjDw2BSm^BPM$n!{07CKjIGc;od&1QUk=ep=7hE&}cd-np` zo_qIwn*g*)Vy;JLsm-+4U349~8H{7|W?-4Ou$N^@2Xz+@FEKY15jmc$<9K-Htwl79 zsqWQ#c!{~Oh*-w&ZAG+lem4~n%lN&$h*r+;9Yw@4em57<%K6<=MC6*w`5^sbhwz<1 z^TGh|(%2#9d}!W|G0O+at=R&L(_4Mu^y!-KYNum7qCA}})L?FfVsL2}0=acN<|Bfl z+i+nGMnOq%oASMS2r0}I-e2c9I^DJH=>zRLf%6QS!1iZe{nU^0ZIbJsQ(!Ofv_07(_{NSmK7hOo8 z*P70}{D8{wvhkGYMSmIa`c7wFevsvO`E8`r40wI7GcP~zD&s{L8}NFlGcP|FE8|6n z9Ps*K$SaYxNo-d9uETY_Yn%6rq zX;||jMoG;pR$bbsW#&RClOdksO^8fc9hIY<{95y(chTD-)in!qPHSG)PYDlO^Af#X z&5Hm_&tM~an$6oX5~z7iKxkI; z;>1qx#2?hWT0TG`$`2{vwLUB|bo+$a@uVnGcW%@>2@9!Ust7 z_*n&9=>uqU=@$f`YdHgZcSMA}EO!%y-7da3N#BJl&Vxu?`0lXR+<6e`GrlyBaX;6f zY*gqkE75&%6!qrz%<~|Z;8xo+&moNfp9B7!Y$zA@F&MIM@n}OfG9hz-$`#2tzcy`15tM4V60;9tYtT*G7UzoZ5Ygw6@$x0V;f>P>NnsH zUI6zZglp~vaO67NqW6s&xSzcMZYz8))v&Utzx@qxUnOp+Cv$F7L)6;hg41|!5rf&T zV$fYTE-PX%uT=~>;l^o23}LD$;YwTQtSUNN{>H85W2jcT7ND@L8}fpSleK@D+f zgHXQ&d0z1aZ`lYE)jK;Q_8!z98CszJs2$AFvOUz2Dvz5E^l_{|3cd9xidb*uL|TUn z2th+y=xzOze{Ls`O*?S1X&r%&?j$=WQrQ8amHh%IJJ5SrhTI9HA!Gu(($zh>dTaW0 z!z~W2lg9y{uDqPC-kLt0&K4(+Q);^MILnmI7WSMGbnTbn7AJ_4$7wTNc{yFZHGR6_ z7N?t&$4NL{c{yFZHGR6_7ALQh$0IbQ=T8-ptVl zbJq&wIv=!E&y^K@l;E2W_~v;?0sEecXok&pbTbftL&Mq zac!|XVO?vhzl90^S8A&t!jRZn>~w0YpF@Ay+A4?CtF7Jv2y3hC#=N%rE9jZt=L((d zDhtDzr{8!mMA2E3o>#Tzheo zzEe`a)3&Fx+I7g*gY+>-%#G8vOKpzYgfOpuN0~C$97dT=oO%u}^}G4dq*%WbUj9N+ z#)~sG;I%O1m1v&Kw#r~TT*u4SeWb5`=kcQ5vF_6^^b_oMSN9<<>OORM+WmDI71VwB zdt0pg5Tm5-V{N8=II|I_OxArAPi2Z%QjXSWwC+Qz)Y~G}H4FBsbsuZrqPMO4h~BR5 zLx82H(+=oa_YobE@%<4gWzX91i6l|l{{`=>E1~U@V+nnbV-@H- z=2({2%dv{I-Eu6Uw;sc9tm%+rb-)jEtdGX^*1a4=t<8LaAN7#X{rotu^#CvFmpF}< za13-!|B>Wf9|0_P6ah?LRUX1>7(1c!493x*hK}6oo{DG}pXmboY!}$)%V5xzXMNc91wCz^$FT@tj4|w5O<31F zZ#&G6|4N?szc7Mlk>}yrAeZOSh`c=SCx9@|@9iW@YrECKRipW3&!3g*M&A;{HTy%DCBMBmHvZghS^}y<|onW}OR>bqXlRI=>2?W5cm=$3|ow zWS%JgC*@DK#O517Fc0ESHnVh%;qx^fUShskMC7{K&hJ}AG|1IFHsH6Wh*-w&+eNf; ze%~n~mht;;5v`ox_lk&R{JvjAE9du65s`kF>%H`g9m7s}=X$iu<~|s+e4v~k7g(I~ zw=XgvVO%%XP1k&RI~~_3<>}ZV@B9JuhwtEzZ?wLGd4at1%eXM_R8SJ!rhG34B!&Eb zs^jQ%*S4p}x9dRNWj#!{@DaVPwR zvaEfa<^iuWI`i@u&oW;03IVTIcIM?TsE(IiPo<6YApx%yAupQ`a}30hI9$ieuCc;v zdyg0A1M}haHO8PhxO|wn$cO3R)hy~_Ey#!aU`Nb{iBXadTU%~7vn*PoOyr2IRvpfY2--rlF<>;}7!Tr9MESg2NQBjSrBJ;7A2* z>jNYlI7R@vaQGQCdqD0c3Ue9skI)Ka(6ZS7IBbzSqo8AA^Jn_ItjE~q3h1L~me`86 z5c%_i#r&CzZdv}!mA#lhKMXNp{`?4)Yv6eg=Fi*m@5ZAoUXVZkP_FYwpOovqqwoYS zpWYlFr<+f&g$T)~M?jS2)16fL6@>A~0DE>7192P~c(He6fcJ-63@*C;W}L^pGTeS* z!0z^wlM}?qT@!M;t%gnNeGJCvia}@Ol6oJ5(YT&{po{U6dLoH=bW7^DC?@q;$FWP~ zfg{cZ=H_ZUY8Tp7+9moq(ex~^r%rTr=}M)uY2CEwhFk0?Cy(9NK4;7Itb26z*7WIy zTkLixkApE?c{yFZRYtdi>(3xZr+Y_zfI~Oj;&?jIlJ2@L;&^Q1=&dJG#JWf1<;ji^ zG^F+F7h*|fM~Dq@bdOlZJE&iH`l-dljN9$|BNYMKdai!%u8n#;ciCiP#*<$sDUdKS4nz3eku zr(hR*xjgH$b%w{8YR@aR{y*p?sr4`LAy2I-)Jv`HGdo*GTOX3=*1JO!{%bbTN$FMi zGsQt4cDc1a>hU$XCAo85%E`A(eI2*j66d!0%yB8noAq7uvt?a3KZC-xNwR0&{BwLm z-kc3+n78s(g#rrl)<;1T8#cU);tp0|2<*i)rDWaA0`)v`ijNk8yXyyEVUqme9_lF`{Ilm{0h@7wV^U^PN z3_Inmb7&XMH(<>2fg(M9fxzPQS|8~A%ev{B*SFKrUnx%~3wi4wp+6=jf6Sw`26l5c6=(En-P`YIoUzU}U7nU~r;j67vx_%aE% zd6?*Qk;k3m%RFqZ%)tpZ&*fpnMIJ_Hpv|mH>yd}O0R6%|j2I<(m>rw;Zp&5z%48m< zcq-H8VdQ8BNAoa_z6uhlu36|4&BLr6iQYC36TMv?Mu4TKLksAchlvi!_@RCt#xGWy z%ftS}7H1709ps;s(Tj<=JZxXwj%9QgC%OM}=eVhz+_f;5BRvTT$dSZr|2dTAawP39 zn}Ud)mH0x@BZ^j9j^;7b#bBe8$% zrsA>qINc=ZZ{Q(G(D@+BlAunq76D053TK_yEq54v=9y<)?prbY;#o_R$s?yMcktp( zGEU5-Zz1ecjW?y=gLsqE4DoQLPUrnHOY6maCA3}QO|?(|JaK`(W4vi;yUFevZwkG& zWWTYU_RBipm&cpmM~@@ke2ACy@3e<3-bB)i81UPG6%|^r=!18o=z6x&3@>Q(1AbL(E1_l z67l8_aACZupd`5Me36YeNg=;E<~Tatwe9IQ+I7g|%@6n>^sUC5YID@aaira<|FiKX zdCHt|7!GdZP0{Htj|0b-@#X@VgA;6?i#Lgjc$3aRn^~9EBi`H#bBFOJF-qdi`NFHv zZP`jdnT$6TPi5M8lN{~fXuQeMS3x4xH4E+5c+=XE=xyUo@~F9ZlK@LkhZfK?-V_~@ z@x%RilV7Yf7jM>J38nF-(o0*zc#|W6cr)`~F5aYea@WFKyg2{~h&RP+4~EiQyeYEW zbAy~)S!?ombD{RVNH32!>0ry^O*-*nyg3tM!gzB%D)&6&&Go^f7;koxmB*V|=RM1K zGn0oAFO4_J7(ck=TSy*nO1}s3rgX2Gm5yb{`lhAzV!jgEF7c+&2ldSYeaCpy(sq;G zHQp5ZSo{=%>mO!@+bQ1cfL|VOt_3^9n?K|xuy!j(QaszihP+;g1;iF(qLeaK21_x&ZUyO=_A6YxAdsb&WTF4wL$?#G6CtqURQGvWv^& z%}llD6>rv|m&BX%@F9;kDRkQL=ELZu^hx}g;-I(1n?IIYLA?1eZgr10IW8UJ%?-sz zn%PjeHc9r3H|O9R;>~P8i}9v_f_QT-Xkx>L7vfE|Q@*V}Hp4(L^YJIMBK0kd4?VoZ zEGQzn_{+mH3yWx3{N>>#W`iPP8NUsSXyyDiDk7Hg+qj5U&Tml>v5em)MYM8$n-&o{ zUm0&ozu4jJ6mM=syJ$XzG0O*v^z;P+i&MTgJp($Qv~IfQKicW&uau`_hj?={=#O~w z5g@mI2D?PO`BPjNZz?DWZaZIO<4sb?uRl4CPIqm4iuZCCb>D}+HvOru9{N_}O|>Oz z<2b5MWxj3WP4bjE<1m~~Hr^DS9`!hId>L(t5<3XWDp^ z7$xzh9h>%U%T@x)WW1?(D$~ZBCggt#+#x;GLFaU^5-u2McPEW3d711zfbwAz1 zgHA=nGJY>EqLuU8v4~j4Z>J(!Ilo2`v5enH5v`ox@**PVE8|V+7dyP2;!W%iBHr8@ zW0nsT>FEmuhUXD{pz}%VrfY7~PDg*GJe@4Wo1@Sl@g`sR-1-&l67l9Qabdivpd`5M ze36YeNg=-n<2X9qwe9H+?K;r^X}med2cd7(Z>lW?KSv`lFWw|inKQmj!fm`MI@LYy z9ACzpo68)WVDntONnFI6bOzeYy0jkg=A|~?Bt}WRX~(9$+p?8_GIz@QTV>jKlN{~f z3cvJLt@g^H&>FLk{dd8cgYchVcA8+!Dv@wr2Ujj=g zjW?BEOeBmqITDCBGymn{O=>51EzHH6FNFleo8q;14yCzxQ~TA%o19x&Yw~z=3mHeT ztq$?#qfnqM-lP*R#+xsLm@wYlg~~n8cym|qD8`$eWaaT@)_Kn|-pu6Drpory$v80! zeGAFsP3iX_-ju;rv(mBb7;joyFXk(u?GkSaeNf*l(07bCEp0d1UF(}dAFD0-jqMa~ zcEAtg%|FD=-7xyC-=oLvGpK*$$Mj;HtZ4l^zxxb7&TB<@+$~Obr@<`2;5lCy(atA- z4V-)u1Te9X5Jn#_beSj0`Dsl0xkB7YpS-2W`g{S#b!f5rO|~WzMm;>AOPDzp4R8pA zlh&>iPqfn#hW~sl7+?0jc;qS33$*RDP%obE14PcpW@@C}Al4D~tnWVH?Hi;e>t3 z!8jNehBw?FNto3I0rg2}Se|OIKeALq`)}~dm+0X|C+m#%^CPJ#fiMpHCthN z;`ijfWuJ+C!aft*e<0tTw7~u@`NFZK@_mD`L*%;Q*sFM*`i-!VykyNY*)y`g#oe~l zL}CZVpiA<(o0#KhBMqu$-zVM^{SIH=B6ey{By^G@@LP1*gwB7898bRL?H&0xqe#x?5IE) zJ_T+h?}_IKnq3trgjFTo6x?le+_?XgCuows0P9_34_(4)xX(RKMlj31n$K z@Nk@tq4%5zR^}FlcjT=k>^}9}n7(__su~2bt&*-vo{;O52gA3T>XoOmzb9q5zF}PTSSvF zW4$Qgw_g#ljNkr6v~qq26cNk#9auyw=XX#Mv5eorMYM8$hZGSxf4a@%ZNx`iiE+vY z$IUr`#pxA3kTA~ib<;JkYNw+f%G1e0P3KVPj2MyLr!@#WLrrG@7uIwXlmxdapRv51 z*QAgz(s6XUtKUlB(yjw_*P4#;LFi9ym)cL!7q-ci`QpnY-1;xkiPOiymqa)Obu(@h zpY12ExFUyJ);`WQ2Nz!NbiA}yv`phBcAKjekuPdRbRgP&b?H^qiVj16^DG=ON@_)R z%-e?}Ti$4&tQ9HVgvb=Pp&ad?YpsY@rME?@YXbUOYehOn%EQ)*L~mCsB6CYmCm7JP zRwOzk<9HW*zBkD)R+`(JJc2FG8UQ-VKQ*Hl({r^Vy53l<2G77>0HN74@Z#XF#~nNa z-}C|E-QP?A%0=Sak8Bg@>P6aTwqC@!nze{JRigcoscb7_sTyE^a(&!td;WqByR2SB zmtU+G9R)FAz36Ca*SHsa@%#lt8NNrK-NA>{SK->=au}sIAW*$1s-~wQMH<4TDR%J|@m3NLCO3o$iko zpYqO>A~Wxu?rJOkJ|SHLj)_r|;T9FJ%C#hpazjH~*#@k1>~vfdvF;J+GsyuVXh_TF zwu`jGEw;xAWGmb4vz)H((bZc;I`#D$Te=p&$DSi2B^8*~dK5O?nu!a2tb&r@cAF0( zh5X!`>#=z-k(UA^>Hme_1AG@~?%3zwK@Ll~GnZW2nL5@0v@RZ=%VK|*^-CT6q z*W=*ec3mJ`lD>9&>!XE!e~(`L>eq3j=%a;~U)*uBY;8w)F=ceJgx7y^OS3v;!TK5 zyQYz&O}2DRV>YLPM5=3o%ys_ehRq9whh5V|Z?~oqVCh+C3Fx_|i4Mv5vG9c1drtYq zN^@)4d2Dgk0MM}wf5_-%s=GDqN4Tvs3i}_wN6AErpKkwy+R0rDGxQaS*$>KPYab^{ z`a%2w-~b;Wb*>L9;2<9$MXrx3;1C}`drUvBfWv$MjVt}60*>$jQrr3r0a#z9vUPr2 ziW6?-T||H?q9ec^ONHG`Wy66+4}lh+%K-L7eGw7zK&75#%02I zgj;#lMYBh*lIz0J+vK{*C~AYapN@WCuG@`1A=l-j{UGD-*c;ewCBDzs`!_frTL<`lz) ztna8YH=;O?7!=vqRJfaCVSRxu>2sZ8Z^1qkmqNr8q|!OnV^(5xyopL%rJu00-d<1% z?Ko)L7*IdPBSIhSLlx-Fg`sF^+x9taH`zu+w=tk>25BPOdKAC0hQ1?ctNzwR2YmCY z5MA`!!SV0ZwqdbCYdbC4W+pM{n?S$1N2IY@KnR(nWz1F7by$qr38blK0xccZ?h?AX zM^|6bY9s6>k5k0Svfl9`u6dIW6kv$Q zq8&8OqNbT5E?%>-Heu#QXzvi5E)r{db<+*t+g=TxFIJvxwpTYj84%frTZ@L7cYvSU zhr1b{kz@|H|C;fmco2UrgQ&J&Gd6DrL0&w#(F7s)v`DTZ@Qg{BA3vp$#ScZZ9I1 z@q1Sht(@Pxi-=|X?kJ*_^LtMbv5en)i)iKi-d9AV-E;h?6Xh?;+f* zLL=JYmY$96Mo)Nwp6w~~T{tfjEmytRd!FyA0RrDuA4DSc5A=`uBz}!yPEUicOCIVm zFo@dBsINa=!%dz+-s;1xv}mWFg*5l@x65cWxf}CShH`L8m1m8&f`_)rawowp)#XuI_IEgANXxn&p?VeY;Orp zZ*(qjt)HGGhe@gdKb;LsedqZ=v2Wa-;D}h@3CKc_C1pM75arjJCfI=%sEE>)nOn!4 zQopYAZ9Vi&Y^K=R7q_=V{ji6ZnAa5%%lKVVM1$NCewP*z%lKVZL@Vcac@eRU-xWo) za(=HbB9`&HvWQmB@2VmqeaiZ>xLZ7q=; z?ci!Hk=CPvM5=2R>{j>3tUU@3St1lUdshSjmYxnapl8k|Iwa$#z*A?nM1HZ-TrKfB zwm545=qP_GqZbo%wZyY=TSpSL#LQ2+S|VF0cP;DjFo|qZaZlf6UvuLB>KZ`ah{`A`@ z172*S)(|xRa1#h?$(`s7OlwvgTAQKeSW7m=g=>j|lHj)U%XTdxh4}zjIF5vKEzvwF zZTP)NSiY7#?0M^aEfKns`UlsNm+2fQPf478nO@!?I{n1sPJ3f9eivN}*gUru5Ep9! ziv!xsy0ji^!EN^U^N3Nh7T7$lz1uP?q)e^_iZ>xL?OH&NHj&e{fSF=EA!<{qYnIq0 z>&4oa%EPV&qPN>uB6Ca60!Kj4wLo-8#!vFs0)CM;=Fio95W`ql3y#j{Wl-E&usd$Y zGP+m`vf768@zhT4TG)rg#uBaF+!cs%K3@CD`grEwSu51XFM>ngH6MTa_(!2bn~$Fb z!DT+4^_-%Q|1iXaKK^d%*0@p_OJ>IN5!iy8whR9V7!)6-=_DwBm?rBnS#f*Ja-_vb zjqxv;J!5ukVmS8Y>la^6naLxn@2_qAK)#82)VB)t*UNw}eID##aB9JIXWGDZe-tgs ztXgSlla|)oM<}7a2efSrI4`zw^g*4!KyN-4ikA7nRZs1Cteb2jqT3j_bw_PYt=Dg? zp`8Y8)peu;zWI2FF6$2c5Mp*dPjN56tM8i;ZQxA#!Z^jxR>D5;vjQaTX?=p1-qt5^ zL4xai=i;2+kqT@fc*g=;Mf#`fWNtoktZT5v7`I-4n{`BjV1SO`w@OmE z$$Suc+>U}Xp36?IJ?mvG(?rQ&U7z#r>%VCrimRe*H9Z`tQUCDKGyD61`7>a0k1%1~ zAlhBm66%x8j?5RJ#^TEOmoOlA@XtVXH7_Hm$-qYqHcsbNwV2a`!cOBQ_&(G#)O*v+ zsDEO*Ts9_`q2A%6=k%iZ(*vbG1*PVS#6fWveL60^(R+cp2X#yqEi?B)J65zdA$^?G zE9QRO>5&E|=nC@yz79kU6rJF3&;wSZSlTtRFK_pazZ$g5xsG~2nYJ|H;~>LT{+#wF zq^z@`&)m!5e3*z|glzU}CHg&B?7Zdxk25N52pp+eQs0BCINZcR7uqzInUM=XH##RmOHcC^}#4aAwpH-LqeN zv#MA6Z2Q@S6~M;REtNVN0kuN?vGg{ilM5_JdW({(DkZI-872MN1GYhlZMoVxPnOF+ z^f_8B8%yaFri&H`WpQ5MbGUmeE@xMc`@ikDz0oz`yAN?^&B*4%vXWGoIxvWQ|{pBhH%T)&hZJc$G+r|h`Tah z$1OQMiMcc^b0-~T89QoxnoKz3z5$eh>bzYl{NA7!Na`>R+#T!@vu-|!&#@oy!JVAWi;pOQWOJ~4alfQlDD~?ip5!z(@OrX0u z#vS~Uo`_MbNoJjqQ65`wb5#m5r=oW^LEEBZRou~mjNWK3;O{4PZoY%YT=P|U?OI(Q zR*Wju1dG9UaldN5hrjP8G^!3CJy5N{`96Y9tEArtAa}lQ4c9exVjJj?Np!H9uB*$+ zV3uxM5B4akBlLu0e?gz?qMAMgp)e4+-=YwFegHTvGQ~)ce#zrS!rt@;)1f>tPI`LN zg}oHAUQcTSd=MX^xt;A_-u2p6i7rK7;vDl(a|L#EYn7f>lu)tC1h4wHH!-FcuHlE! zV!fX9N4Qkt)(pTS#6cK8M5`A7VL7G-MtSU?XtJH%i8&vD%F~sz!M*uoP^MT!m$tTJ zi_?ekUDo#49D#n2_vO&W_tCy4XHQpc#{n|#6J+4Q;)suh_@qQ8s=*4E?WU>E ze;MTIcqQptU}LgK6cJ0aE9NJ-msHHF(6#2L_^gqULe0-`<7MAw+=JM<9=9)r;14WRpcESRb<@%IMP zM?q#j4nD4P-P}4@$CT_G2)h3Q=+s@u{SY0eD?@!}$bP$wdz^9=GsmOFhQ9~zIH4_! zQ=X7kp38@e<%3a zJP!2M2tL8LaW$;J)J}nY4slapz1|V#-C$Y=A2&sA%HZ+b){bD%YtL@>wq8Vb$g3!P zu6~Oe^s1JHi!Ejy>O^29$iiW}QIIKxw#N}G7PgOm4UNqZG@1YOjI4(`;S5g$XF-4y zjEBgA!>O39+uuXxKNFM+-hALWg{Ti(yr1}i^fL79qW)b5*#~2{9EN5;h#|Zvp4bVk z&S{D~+2cOk^DXQ!2Z}a;ynVs^P_;wa29^`VKj|N65Anou+*z*cdz@ZPQ{bH_T8mX7 zs%~AWq)!0x5k<_`6+PQhi_-1E1^;Nv$P=x9OD_ha<=U#v56@dg1ISd%>>u@V=7u!3 z4Cv;@;R>TkdayWRzM|tUbn{0kTzM=QWl|ZZjLXtbp-?e)4mwt%q4b=~%5F z^sA1Q&S4#^+Nic_*%5iGcJP#QJ& z*s$M0w?;+2bChQG%Q!LCw62Z_sw=6GnaUWZzsK!4Jy9P5MQRK=+DhV7vBt*HZ}3TN zX84#rS>WV`2ZxNuBWM>4$T$JF#s3}gpGnvr_`g5?ACCWUveD`Ie-8ey#Q#Mkw293SeE?bI5V@$-t+ z2;SjCUo8NXZ1I`>0RXxUUrxdo?tnzAw~_A$;Ug86f2+5adu&*JHR@x)XJ<qr8F0>J+z*>==cEfsKDXy#}wyvt$rP6oi zL9kj*6N%ytTsmw^60zXf#CQH!z25F3IE}QqHo#%sOm`8LYzJE-(4@p{l-K1gO@mpbu2)KrQgOHEq2sm)U)>9cK8~? z>4g-2Lp%Iigwtaw{K9tlHwlLevhX9@;ol*gj!fxyY=?iBa5^=G*9p(nAKMj$y>trR z!WIOE#pwxd)EGhQedPVxkkvz47Yi?0eK# zYpH9w)?k5-$L1c>fpn;+xdPHyF}^5jOadSMBlgF20H6O3m2H1;nZX^-&^eW%3iiI% z!wr1sm#UJa4z@MsTp-mYeH3Du|418XQBre9HKM8tjqjvfoiSBUDz}~tqzlC1gbq8{YqAhw5 zG~l``t{lq!V0OI!dQ4QbB|>3e7IfZUmc9Yr?1JZkPWPR~GHw3Z%Fv zeH$*CbFvc|bH@`e1*uyuH$$b2-*cR!1!w0r9DXNG5;5vIM%K-LK~#rzlY#nkSU3Mo z<+Htx&&ZOwYq(@W2R*we`NQPPpjT}E13FoBU9%vbcw6Gi_C(1D3Ms$y( zS(e_!jwMGiXt=iZL4^N2J9dYM3Ep~+;W~ji1{#x*rlvFu=QAK{7M}!+|7^~F9F?~_ zM!l_L_$_H2#!HXE9I37RF)Dq{r_s39(YV_ir`*4a4Y!1l$KwZJQ3k5qi*LP*tyenpdHX%&d4~Oj4^BP zf(k9(E=f#zJ4;MmUb@Xr?0Ovs=6E`GsteO9r>x5XeK>x%GJta&k5gUQrH)j_p0MK; z#vU!ePEZ&-v;aF%VeHQW>?DP8unRCmQ|vcdiG%G7ub$++#CQ^KL`|aT6vdS>V7taP zJJzjM>(jB=;bE6d9uI~lVHLGfe~Dykd4Ea%;UQ~!2V zU%=MwU)Rf5co{Zd;w3Tfz{UJRKi|U7kMpzXfi5!}d&2Ei#~xXyl*)0> zN6Jsx8;R+K$T_HiWkx%>BlBMP`lTa_*gT}D?Lb7$OUr^u=~TFrD%&?$?0RsP{K?IaOIG5!tsp_)<{d!0rq`3bG zxW1oUYvePaTnEemztzaKR?c;xo$G*d9mu%8k6Z`IXQpzU$x>x)4jxyi4h@!bo!QQH zrgELBT!*S!W<;rAGaACr&`{|oH6nI;Kb-lD6rCUaX~xKsm{S*@_l`%V+~=F)k%#m% z*SAXmKb7;x?V!!m<$UfPcw;*9{8x}(NBB;3SAFMs!cp#cRq$Ce>|{+0a>}LHiYNI9 zveIbwjHm1TBKyVhE*K1$dac2@$_e2uS+|quI($4$^jR*xt$xkL zybT`)aF9TL8N8zUR>zj1M>PDX4>k}e_56a=)?zd2;Bh=q)0tvN2e#BJg$1PGFas8iVEosU z1MY~?%M*RHfuUYCq+Vy9c?0HDm5DZi&$v{~cZG?4yd`H6KHjoq2I9FGrwg~(Uf*pQ zqrkMq`R?=8QF@Pr6u?;%rBCT+#s%9zA&fKpdophnq)GUph^ zuD5=)-n-Z>h;Y~6I@)-Jd_FFkb#^q1_c`bO3Z`eU4L1Hk#cgxkvQ~w?akyD^!17DT z6&C!nO(HQoln`n!`v$BFyQY?uD|3~b9hS6e@FTqy!~G}SJ&h-bVo*4Sr| z{#(*M^At%@nyJ}&p$3auizQW$W=*l*d^1zHpTE*tF{lyP$ktjba$qH0a_szT4njw5 zSzom6&3NwU_?xG=BqP>>^`Z~pJ%*BaZcp=n!?AbpgVApSKf4{C$X?!=0`F=5%mdFs z-HD0UD=qDC^CAzb@e?;}u7_+C>F~+ze^sI{;>%f}Yo25GS7W=s%4=nUf3XL38Xn+E zH$)q;yS5Q7*k4Tm;U?SJXdR;0R{{h3sONGYHIH7;QQ!&%o=0FFy`HPU(-mk4%mdW( z6nKUL&kunHK-JCL-OKml*vA+N@F(Ms>jn~M&6O7yoFdI9vK?RtN9oe1&tg5=4 z@58vC!vgLK^gCunx-ojxoP(B{@8hWMK!cT_s(FaVc5&}I0Fsw7{>7f^_4x7b6*2#V$x!_267GK8YnB+y4FA%;qzMq zG3F_q^SakBjw!>QbJ_0ZCNPCs99PosVPsk~T-piOrjOuy9&wV`YzizLD|q*t07&Ax z*$g+WIiCwdRX8?UE&?3JlG(tZ)PDQK1_odY6OiPP*B$`es$rc6o2m@3@QoWziJ?Ln z+G0Y`o0tr>^}ao?^}DVHGNb2)|U;K=Jb zg*i9QndvW4Vi&SRG3dq;WvS=@bcsr;d|67kIuWX?lTZyhD%we(EHO!+X_87-7Y*1Z z#EN8f5vrMviuR!kQ_{~@x`b3cI){O{0L{!s(ZQ%YU}IfiO%L{voE6y|1FQkP*%8Mt z+*y$=aGT2*1%)RQw6E?$+l=UvZV6QDBNy`{Hb1*<-()zQwr{9^T;RHV8eECfi7Q*a zBs&$}+q#fadRpVWB(0T%x_U`X`;i#3SM565^)uE>_A_Z5D65xP7}*wJ*X4XYSVgvE z&qrT2FTirnvs^u}JD%m@RsYUS40g~e=~n3A-ZaHugc`jPY9!{*fFQ;gV=(XnfYN>8 zzFV(?3QFt;1Fj7hs2vf>(?!?5F&)<>986vNaw8G@FsRVBtq;4v9-5>>$B?O63Tl@l zZH>=d(|!y`1+OKpDa&$#9BGMLvs`XLAB@d+yU&@V3{G=@ByBX?;2!+ZKhX_l89uvn zRiekXxY6NU72AoqF1M^D#@D@SRa z0`jB++2pY%Hmblo{ioNTBA4-+$`zO#Ms_0I{DXBlS~=qE!x^ixMg-pfZs*N}kp4EF zc&%sFto`$QUi&9~JTcXV!|zL$#L3+B88D8x%dkKCgS!m>!VT;){0A4e=8r+vtJp?# z;3c3mJD~aI#rQMO*d8z(LfR2uaEJ&skXpsOp7wt-{v>86+}91s%J{{gA6(1Gj}#3+ zmOsEXn@wQ>C63Y6$WdyKWKo@4v*$$n9ETtV|7cTN;QDGEC#r)pc%@8_IAPs1Xe@T zxSotXkp`+_)%lUfk)y!8YIIj^7dUTxk=-^rzN)ztG#cvdvJ5w1a2&OYxts$Wo0p(t zMx;0T6iW^>B<7{Kuls4V56*WP&*skT$d}W)Rb9zl6++MxjQ;&6)}vkwEbTQf&4F@OHBo{ti^<4%;I&@$F`B+*w%#YBrM8k?w5@GMf94 zCtN8V>(bWC@eN_izPL2^!ynIYRH73>gA)~jUQ;D{)dIct2b=m$<^WtesyE=PhdxmK zUPt{71fA%ITx)6TAbf*<2jkK_1b+hk&MeXI-dw*0uHV+{sNa_J*U^vb`-(a}FQJ}? zIz11ho`2eTtIZAaonz|el z>XOKOxLkCZ>w0A_w)V-OvXZy59@ce+^XCe|pI5}^trGm#3x2(b&s!zp?$KTZpLF^o&3nLGO=Q)8WJ$GyfusIQ*GiFBu zc#;Aa5qL5_FV3Kl!8Mr+BGzTbY^MNjPQ{J_xi7NWc1L zz^(?&1~_ZB1q+R6=T5STrb&u5XkGF4K#o2371&WCCveP4Pz>f2V{38{9m!0*T525E z7l%F5pSS`LhQqMV--Fz2Gn;8TAxW4cxM&bh;ji;PNyOFrML)60OroE%G*l+|@FUN%{)hz2k-O z$5`BmQ85S&^G6ARpU63Nd3MEn*1Siar{NZw;q!Q!5u70+ga+LcT|;|F(lc=v%fKsf z8QTjZp!RSUzSb~=GCY#%jGYRg;+eAn)pJ+_jCK&~EQAwnorBxj2Ar&jkR_o$=Kn4L zWhCZY{M8^J(JE82@>Ur0Ky1zfq-uT%R_1s34f{@8ex0g#;=6wC*k zvLSUjubmGUdmyyG8a+HCimvnK0}kX{!yjv$f=wfy%oYo;tr_VA8d&2|?G{iC=2YIi zn0y`8WY5lwbK0>3L8^1gx#l$-ab0=^y>$s_a59V+S%_O~kSF8F0*g4k6eza~ISv4L zxF=?~*-D>_!%FWFwk`t?H;-vDZXWNA&u$)%;RE{Zc7}oa2ng7SszH2)uz}O-8e-T2$Nw>pqghtR@ps0S zgMJCt);Z=yi$~=#4ZO<~k(wxRE&eW+_>ubA_>pZ+%yszd>Bsnu6$9G_8bq?1X6Pca z9+~PImN~kwMZMlElmSRq)2!#)FwPQfIozv+iJr6 zDsIrc6(3#Q`}?l%`?T+8O5fX)AC(kA?^3)~68vDGZVzZ-^x}hbEa*pBPqBWtC%qA@ zo#Um%(DA-8=$=Hqt>?YQfD5;of_k8sc? zvf@OGC|EaS6JY^9U$ZX;v-J*qLh8Wf18n#W$qaESQc~}!+?3voo431K88Psf?HY8P z*dF7?>&0hFvZ|WcJb6ivTbdI|c2>7*(b3lgL{O?*I^M;d^k=weY?8BtEosEhJv2weWp_bz2Kb+ifj; zKj0;6;a+GD>Ne?}0H+_YGW2|+T?;8VSPSj)DHI1U-OTV7ec|dqC_kY7gY#=a z{YUw~Nyc!3BOi@h}0^7VE^GOaS)|px8O1z0-tK(g5P-_I@pS6kh*~QhFX7IIVLHNpJ z;5hJ-T`+Q^Nn1!FF7VyxlU~s!>}mPU$EbN7hV&xKy4QSKl;j@xli+y+`_2xKSjRx) z6S!R!>!{)Ah8V|SpBtvP0SFH-eSJ1R6NNUDuM+U;^xQZP#YK7LtzeIrzL<+)Ys+CY zJWtrW_Er1<6x3Jj`M_bhp=-sQiXNG&XqQI~rlFanTfwo7T}n9B7JfTBlMVJYhV%D1 zV2lt#$9S91$W`))FFvmqedEV!yBi=sy9x2=TiG{!jk`a0aNrj0*STY-&P+RP=3v+J zc-rq)P^I6Tz>gBCSNaVf3mfW@&Paa>_lg%EH-^9pc+!`@qxwK#U^Az;-yBC`>7=^6 z`dEUUIeiW81>m$LdzJ?*!TD5ju5y?vM-$aCCC zjtx6k_}qw>HI}#?9Ox9~W@4W0iV{|gZ?JE9j_(0nDVhIxKSBTQgzIu#{ri}n5%!%T z)SGm`@tyv54zA*Fn;*g)X}$t&%_bpzY8Ci5hBk2*5Y312XHZVY^qITy8UAIQ^LS>z zJh9VnK7yNCNgS@pLoKe6*~ysDw~kx}O(M~P54+^%hf{C60)kV5G{8AHTGcC)PsGis z4)x$Wo%v8z9E^oZVHYNHG(5KXD0CqM>EwH8BXqldC|^?X*b4rnDwady0eCHv^DUmKn>jb*j-+TBFlpL7*ui{y`yXH$8i&5Oy7r$ zqr~@ZdYbd<)(^M&K1S}5EFwtNMG#(P&c-_@i*&PwI7`>kw{cv(o_6}sPhw@HKfr8A zY|gO_?r6@j@*3D0c$#DJN45ZvdtZRhwsJhUc01}}9n)9X-(Efv#KduWF6gqVty@R> zquut)^Aqi{&!8QepOCxcC&9XMebzVrx?(=-bu{VFw~XT6ZrgS?JhZ_|k5!j$rfRVG zF%5u+hmBy{F~!WOr58a<*PtJkdfIFaTrb*w7w1z|);m;IZqiN`4O(T5 z&snTPr&Jjc!>Fvh6$Zqo)y%c%eqAB*ipv~4eFN9Lf?eLIfsE0nesww1e$kfaM-+|m z>|wkWX&-yWtfzq7n$Gd``Z!5fc>^lT25cZaHtl4s7rkg#9Z%*#_W+ZNgii=F;Z#sF>q~90uP3 zz7OG%Jk(yspvSye5SRz_L=VEBzf<(jv+;T@mQZJ)nmy;v`MNp&Z;$`EUq2~d;a3#;e)$Rq zs$XxCuTVh0UM*h{W$0J-W-<l6Y>5Znrk znf-I0o`JVk)YKgN$8W~Xk;Q<=23*sc)QOobk}o+p^RH{FLT)r6DqCND7+UJenJPnjYD!kTo&h z1V=cc^tCkc^b-7aD-PGZ9yv3ucziR}{hq$d(g*Nu3Xd@{QDi-QUv@M8Dyrkt@p^6O6%Qnt+wuvYj5klxLUqT!Sh{gUpzsEs14|=6$h&S zPuiEjM^R+|S9i^s0}>JvE)5|ELlQ^=0VW3|T#9mqJKW*E9Z0wY0*N4kBCCjafC>u8 zDk7_>Ac%+^sYt*8ArU?c9Ryb8jR2dLYrE3<&Y$ck)kxu4e(F9)J=;7C=!;qC0vr3qt zZ!=Y)-#{0Sm*y}FkSYKjCrBNp=WP^lpxSWVrA|oc@q7U8;-Q#d`jssY6#RSOp~T;(i>+)gT~P4* zaOE-$74@Nqex}V{%_ghat2wGU?Td?{EheUP5kGW|X>6+oM7idEimJ%EkR-G-T@=JOS z1~I9<(D19vg!L{%vg1MuXjC!Tdf!q|B(FC5zC&a2A{K!j9WGT$(h&i#U;l^K=$`@J z8m>p4kH~IX;i$5(mIDEA7dUb%<-C6O63TOXRECc!5T15ABu@?kvw=`AjjM8s>e~Zb z%hX;p>8~jgO1Wyh3|O>NbveEJ?~#jcB@%4BBHxd zp7MtAJMQYflSTojr;?21DUiNl`^rDJz2SOq&{D6pR+O5r=Cw2r9geuyaol7b4iXEF zn@T1)?sp-wb}(|zw}*_^1|0dT2B`I!Xpzz0zt(IMcOi;r5x;IqYEf0M()?|t|4suN zOuj^n*5;;e2!)krIZ8~zQ7&JR<@)JNc70-%B!F`eyQ|f+tm~I1qoaMt5sJ&X>;xr- z+o@_#w2uxol41<1d+L5gwF}Ql@UGjxN9|3w3sM;r*5q&2t!KR|EbY?zqq<3UUt2f7 zrU?J6ZcacQ!*Y?esD4e|q_U>j4A(Q2Hnj*no$^Z)cH2R3>VNo4?;+3OzX1{RTZ2uG(+lkjaC?{Z+y92{fRv zsUM@c$KSb3I4g9m8;{wJUdk{16v9?9@_pNb2=kmO$ImQV+__j6LI)C{gcKBmB1#XF zfK8Dmx6QNl-C3IBq$^L;m^`}sh+ z^&^bSLFqn9X0vA6^m%Q+dn|~Xj56GZNt(a?&|1z0(p_>NnqcWtbXt}o%JTrf!I(ei zfBj$h9mf87_`m-be%!b}4?q3C@RyALFX1O#!@s2axH+ehe~Ym}yRQ1?n9Bb--+)^B zEu5L??atteiNtqwv6p?13m#TTTYcoN&2tuz@|*5EyMyuq<+nIU8r3E?>K6l0ZQ$XJ zExOxVOQIpXN{KbIa*DZzd8H;%T_MMyQSJ4m`b0fiORtEn1`n+X(U7o_>&`5)5nIT~ zxKc7HsGP5pGiOm!qj153~P1r%OPF=t(bwV=`KW2mfsv(Y-sE`jZjM)^51DlcCGB7;pt_PvAR;fhm6IcCnf47afp zpK+l?$A%u`F_~{5g=Ym6pe~RLA+bkWZ9XBu+nCBW#W^@0i z$OcSn`ar_;*+3}yf{L0Bw^d`+L0T!`)^Gak6jlZJ>JI~$YWC!fdL2WTJM{h^ z6@%J|W_lcCPO#hy*U0(jR%DTXYRqFuCnVt|PJ0fOi6{y6A=Nj*(W&-0p_^uMoHI10 zijpp1n?YNw9G+KEx%_&q_DJ{cZ6N-TJ#P5rkGfJzH0wXAI)_)lYMhu!!4;fXA4|D2jERuTUs5#%ZbEv zUj$&toL7Mzb0XpQ)gXfUv_+1}n(3T1Snkqwz@J_`jSAtzq($72#b}m>!}BbJ(0={d zrp9!t|L8j-;goEJF3%gVLmhDf%7=am-f;1Ee-zHn_i<`npiugvDYE*|gwCrl#w_$b zho~gG{rDjI?pPt9re!S%%qjT_s!MAkU^%6)5GA!FBXnv1dR&~ zz<&jKSz3!Y7#C{eDzIg_4z8tj;Z#j)Bgnr>^7U9=mfeIag58V@1&hPAv_2evFn`Uu z-JBHt*xnKM<3{YjmlI`T?~P>{@{5?)yE;y^F9>) z`STELq=bz0=me_yan)xd!STL`ns9^XF#k)uuRztG#yb+}#i}#zGu3TaS72IVu{S$zs6o>QLh{`CT28SE$vOQE+M-9|}w>&#X^)Y1W0FkJp+N$yQR=AD!^h zVD68=T~!Gk9DI!u6rCm^jDG8h?=Nj7aekdP-$}|fb+)FBzigni(BqtaoJJjzIvi~O z{u(q}No}0w=(mrW9Q$$aWTUkTv6b4Q%7+jKpP}hNSeozA2zTv&6lnM1e-lE||Fs*) zP#8QK65dV_u*tQk83y`^l7@(zm#RID&>gT9UMe++(2_U$jv$c~OT^A1Q*t4~p?#nG zv&#EDVnJwnq>&v0Kc8~xCEH8r2ul^kJ0!%XLNd0=DGf>0=g1pH2=KhTF{P?7JrVI7 zxsfmyDQNL+CIom2A*X_3>KpG=B(@ZjhLD+vkmx=-PG4EnjKi%8grmuY4mZ^bC;54a zjXOrZdsXM9J&x)4>r`e?nTp#0Un+7c)H@brr!NhD-fzZD@_48^tp2-8#jHwoDV-qxpvBWu{N#H9PxC=z$w?6r0Q?52e?400e; z*#$uRb0R2?*Ga3(ODzrCOSTbmT5*)ThIB_d`2bbVH{e|#k6xhLX~pAG@#wPo%oc%@dxC8OLNAbA%pw&N!Cq(UP2*eYj;tA1PPiRXL#j}|-hk0+O^o03dArva` zh}c7U9~E9BJSg*ZYK|dt<&6vt)G1l=jh{}MP~k#yp0iSU&BhnRjswUXzI2MDy0<&U zUfqZN!y!@C0s~^F)?NF|f)l{D@xtowBX9Gr5S4KQz8BtwjL3N!4dLzQ( z-%;e@u%OBgDRj_nmZ5fn-V^^@@TX=)RfB#+r(T2d%|O!*rpEyjee5;#dw`2U|MnWX z9m+<7J^|NYJ=8PL0S(iXpuIWjT}SjI=NmpJu8#Dtruw zpBPX;;faqm1q!eDW2^Eng1?RX+w3pN#I2Yi@?+1qkkU*PAfJI3;cLuPGfXYvd>?Vw~x63ASNu!b*N6h_cltaa#KD-nhhkGOrs(Y9; zNWOp7k)e;F-&_?L`Nr{jx=LEjah$Z8cSO0|k*;<;vHV|B?p9X0uS>ZbMv+aCv~OIk z5l#^emWysf0R8s{xZ{fyaT#W-5J;MFEw7l@ext+Wvn?z+^x=@pZcTM1Zm8Kx;Zc0{ zVq6=)o?HBSQjNfaXNr^hK<-B)z^_5Bf>8JwA-vw*)MIou{?JXvk{*Vz79K^7H-sS>NC`;X~VXl%mY& zCeQabfG}@wv>2D~2r3%ZVQ=c!0lC3Xr<>qff`1|Y^YG8de<=RWxCXf!`Pwj->V-3I zVD4;Oz0bjMl=P(;+(As5l8;?t!k_@*J0=Vd5Dqh8Y=H1C6MO-}t4x?0AgpD=tN;On z7NV^P5Xe|Z$vpuArNU8ie}JHpQ9qx0MrZwUxO^ln_B4Fx!<#Dj$O!LG$CKfK3OnL5 zob`s+9#W+FgliAKsR|AtaSR$MQ+ZCX>c*Q{d3G1rL+@W>tcif*P$F~jr!ks-H0M8p zu40EOL*#SF{pbx1^l&&x4ciV-oe&vyGw!&#Tx;EN_4Cjnp#=M)*{j~dje_Fg*5ORT zXWiT%t@MsUSRCkN@={5@3b57+Ku0|V6ldg9X}jZEwz{7byFH&_B#yFK!Skqkn}BM; z>l1LZYm+3YiL~9^U#sG9MVQ6@?;%wJB5fb76kcq?A*)JM#ifc!zkAh1JgTn;ado4m zb#TB#&AX{)_%Bqu-Kg0#=W)k1%%w_188(rxcjVIeD?OOHCX9=9_Wwq z@6#$dADrvBJKzOqQ6fwIwIZN}k{BpI)=~!~Retukh-(TEr~R;Tk#%nnQA*$*rB{=7m>TZ5lOh6qmP7ewRXYZ?i+>w%l?u{P5NY7VDKoIv?uD69e^5lE;^x5m9I~ z26|p-_$SG|PiZxMKf$j)SgCdry480uxkqy=w(8z6$DuF(`Y3P?z3?WT>LZmtJCujH z)CVTblE)}lO8()`5Z`6U&vdEdN@!P{GnaE9@MRPVfKwja^m)+V*rI(Y2TSI51AQdcVg3>;__(~IMf6d z;UzAf(nN=#fU-RZ;G$o7jna(J$Rv@}HjgpZ!m?mzlwuJwVJ z{3%4J9ciojy;+qQ5gmnPKD`*SkE}IICL#dmak5!5+3#1!F_%mQ#@(nsi>BmGVpC4= z+6fpgegs8lwQNU{aauLcd(kn|(M7zIVd`?k5#@?>U3VWZ_`^cTMy^EMk(niBh#}Ic zHXqV+58rQ8nAoQ4QdQ23bh544bX$$vWqMwPm2mGD)Jf^Ikbx>cquh~hUb?}gMRdWR zo@nIaZ`W5^)0&Jwbw)V+k^c|CPjr$YdH)rQ5yDGvHkiJj5d+UD(YT74a)Brq&<#-V zb~jo- zmlQYxfE=eS5>eP>dmhy6VxlQyqe3G?@D2%Wo1kxTpcs2UI3(VM5B&cvQI^w`^uT2dH^}Qh<*IBO zAzRt&xTJK&8a#1JG7*dFZ&E5X&DR=Xb$xAV8KLBpm|3eIf9o~{M_xz|ucGqG4|}O6 zMGsj39iCIj8+tydzQD|g`pKb42Bt3b{u2(7Y(=TU!I3n-!2J!?J!o(^9qQ4F=Q}9D z^I0;R{rF^Th)yPESM>c@sk;5w&?e|5ZQ3@h7tVuXmN8E zy6jSyx7B60y1YXd zt59|@`hn8uITvIjH9aTK$!EO@5l$iPrRKgIDWUNx6D{N|9-quv6jd3=V9uiW$|xFd zFE(oyM>30y^5-($lnXP7gT)q=1sN*%RRrRZ8t~{skb(o!1zEr;oJGmaUqSiKUXx>D zTMKG%3|#VMlmg+sn<#VMg7 zu8kk}Z(W~oh976QjsSMV4auc@T5RSnplH#m!pSvDeCS71s(8AGlgyr@nc7mWI#fNv zRrPlVZ}y_S4-FCgogf?egIhRkm)eRW^jHYkVVG5Veg~e3n|1$wo;r0!2%b0?ejSy; zI_RTZ|AUTqgLTk{yZ#3qdxCWw_`m9qVlxu_9_g5auaKZ2ap$lbJzh2q?7AQW%Tj&| zc|t?;S^Uml<n=P89ug2 z1>y?j_muDnFSq9k@JxEckPh7^zcYp2>7&^LX`WuLI@Xe7bd89fewiRMKk(@CB#gEZY$dCGO29fqD zQRc?_kF_85gzpw4Rdr_Cn4q=Hzmx+=DCfa5B5s#I58&UY@_@#Y-iSGz>MCsvd}3Tow3t|~$hIkq1UN&0IaX2~yBSS=eHB#e2-Td3-x zPF|<-SGw?HWSXjK>Z5VoeY*J!7E}U}{(^|4zJTT)T_C!T(&qnAq#8P7i}Xw{mTw3! z2UNqTMX$|EgqW^L{h>%&*R@7t8c%Mhn?__yN04qBoh|x**DZOM9ovnS?`ia^lJ#{w z?p7^JXkHNKPor5f{Lf|SEp_!Pc?goVNS#XmAehX^;#kdhle}UnMf7!T7 zMM&|Vdk_W5+wdQVKX>c6n)tts|4964hX&bBKcZ88MKhuIY%F;i&t`)DA=L#_)nR?2 z5Jq1bGCaKeE!AEbs;6;{{0rpN;ILT7xu>hJt!6IImmP5fSjy(>inQ1g#120Gr8b&`No9FBY$=}oi{lLeZo>`>L zV*2}tLyAXHT*uSlDBkOMpQvzT%XPd@6_4x-;+cZx@1HSGdQPBntM_Rz$)5{`oQmGT z40UFY2Qb-KC%c{jSgp&Zd4@TQ%yhErK|!Zx=QEWIbj~7I$m0f(lL!C>2hmXGmNzlA!0P61N6jtY#uN$gYC?C)3 z# zuDS^mrwVwc`~6Er>KkgyhKaj@6q~`l08UyqQaUL=?W3es$Rkuve0~l!L(Zl!&S-os z5Q&2^(&^cTdRSz=Ze(S2l>TYH6y#TuPBk6_^tS;i#2&v?BfdFQDj}olmN9sv-+f zib7P8VI5RPx;^O2Ica)pQ$SJGtxXOb(}QY*Q+O~LQsoyRJYIj9^%N;({xW-9q*eO; zUyHQr{_;yR(u(}$XX7~^mJ_(0S(P)CEmT#L+F8j3Oky0qtf02Wb^N-v7C1j5RnPgT zj&y~=SQXcG#EQjVN2md)I&xfPg3inv*AYkHIjWi$1=8Qv6N6sFhus;DavCkHjnjyq z?g={J%43N+`(v;@!_jG(vk#GX1baUTP(LLv23H7tM&42ImWBs)Qmj+*!4TY!;TDyY zN+0dk^Nfag8DWqq0?8ZfJY$GV7-WhJ=ge5rgh8f=C2wEzj3sHpAd?Z2_mz3Z5t(q% zHhJz-Y1QE<<+JqlMBkvM{18e4-BkPRWoa*PF#F~JF5B?A{y1O+t1|0(UISLW&nC2@qR(7U#IOi~kev1{4`oS+*|11jO@xfxh1mz`_ z?^gjnoq;m|v8wX-RHI58{3-^&ONKeUI8I@>-zK%ZOErw&Ja_RfRkTSQct+b$aiJZP zzS>X~=)*syUX!|jDt-73+GkdK)j#?7-T$BCt^5BMFO?Au+IlUA601)`@vq8+HXRzT8Cl11m`9J1So&PP5Oq>xc#Mj)Wu9?qMrpT!nvuHlA=G|0YPSrFgp=n+- z?+$*CUkOdF(tg#q=eg~BGGVUi>jKwzI~;$>AWF(3dN3USzgyxR9-MoRrAu0P~=Z$+tOiZ7^RIsDyLr1Uk`)YF1AMf zm$vIk3DVfrf4^Be5Sq&hKq`4pG=tfP?3ZiaN@n}V~x04Si24sDYd zAzlpcv?fgqMdC?^rxT)1#>P`j-vxb!s({3JQc_0Y%vodreVm25=WI2fW{plC-TzPn zEDPxLXA75oK2&|fw#9gEBUz=)HyE1KphM~Ar`3wX*@%IM6CK<}C9i99eCmyW(gO@eH!H5oVO)xeRS5zrMXJ;QJbbMdY*)KGrYt{m6DemT6d`W&m-j>5%Nt6>?f}Fl^Nc*Ek=Li?FNjp4jxgCTMa^?4PiQXA^Nl}fW4c?(rFn&JNIWeFelSdZQWx<5 zOCI!st=ZH^()g`1MD-@z8-Qrj9aA8OsEi%rNd)5j$p=I0BVYf(1?b>Xw!xmDe|8>Q=loWrksPhtty+I{%<0 zk3~yHeWZ2ldu>9TC0nc6a(WN|ryh1s;QTg^7-JbLW{)qv@cwjvlD8}7X%iOIc2lnoy0MWXD{ zQBD`uf%YR8vRiWG1P#Q?JI_Lhghs6Qv0<%-@(UZ4)IK%rW79i{DAWhc!rNMEYa7-_ zRlgVRorAb(soOIek>+yGfE5nf&*vFKX1Kw07eE<)-nMYS<)wAXk4Rgs0;>CwZtlGz z-F11RK%_g?A3}9%f#|9uI#NLqM1@3#VACL#D?A}wf#HeFjYnVVZEF(U$w!Q!=eZwU zikC)1J3{lmyijdP&Hzxcj6sY|$ek&xP$BpPtYjS#&I(tR2Y4YOjtsT=!k|y6YAp#@ zBMcSTP)6S^@%;mi@Gd3QrTKUb6q~`6)lgQKsFIGuiVTDNHxR{ZZjwptc1yhZk+A8PuqDB?djAK1dvO1y7HJcNZLgLF5#Y zHPKNKvPMLW>M)YF%lRUa95qr(d>C?6jF;wUkuiK6a#T$bS@Rk*YgRQAlBf+KIq%c0BelZF81>p(jDBrRcyV(P z$R*N_T#BW&QL213Vtw@}kHUyfc@(SiNO+5h!e>ZD)$&VoS=aL6Q5{lWi;sejs-se0 zhtGnKs?ADj`%_;>9S9#N$2!3W!mC3tQJNH$8>SgqsI%fuGi|NOou4V%$uytT{=dnB z4c@{uM29@V(9Sin80 z8{Z(u_~82<%`iC{<*5c0mpXU^vlUv(bMd4`&8>MvCP9dfg9Do>fdxDYxK)=f-MUNg zrb9yFgN-~I#u>nsxaue5vKO=;4svfauj`-EcSEM+?Cpc;_<*?@+ip%G6l;Syl$rK@y$W! z!nYZ4fwJA;1e%=+osp; z|Ng9CL*rgON)A2v!%t5vzN^;Y1CjUie5zl{fm+2aei)I~D($lop%Yfto_6Kc<=y%X z8hoz%-sx}sls013?n~n`cQ5+s_Leg?r?zaAdG7a~Sq;M8{;B`2qVnkXRu#V2*SYwK z+rmfeFK#t?ZAS0a&m8@GNN3-Z8qA!gOwoQ|s>pt#?nc2d`04yGgjU_ia=lhk>_` z0&Y9OH;jbwGqLp5_o=5--856WWHh4%DvEs?{`>H+i7C@M;WrE&IG%^YH2Hqqy@yDWPnTFH*=eQ>QFA!|n{GY+?6nrj+FpcN@F=*!_sz_cDoIJ&){pERXzVD{_1NY0eB=2ZP^zwE@d8!>ze(dlIRtutTp^{iu z)9jkJP6|yG({2@_ouOj<>Q)My*^%5IJ5s2E+sOakZJ%HRR?XYVZE!o$pSqpsAKgw? z&FDmyyE{+I2oc$E9b#fkc(t92on23<(zU?$1rfZ`Bw-H6+!7x@PaA$JVB zit8qJbJ*?2ZV9{hvAd4lx7hu171avc>QQY!HfpV=oNT!InYL7qRc)qr zTTwp(oni={bNEpQpfaGwLZUU&ywVQTi6!pT`x=&{{+G;PCNw-_!$bVVx6HtB)OVV z9ZY-5aAW_9kns^B4M{4)ihpM#fMP|VLFsD;$sR^+(Th=kpiw~m8I=I-GV0))=_GkK z&;g^an8xTaplW74e1DaAn}F&7En&0=Xd2KHjJ^Px1N4$%z?L6@HUhn6m_S!x**dd6 zp0pb9Im{58y_z8YP1!AKYV<3kM2&uDl&XVZz{1irx1 z&!qeuWl}CR6a5*LYBZS9of-{gEftKi#0c<&c;9^5EW z6nN|GLyWT)Z-;%n5$YhFzk&CWeWH=>@az22USYEp>cnv~vaQG|cgk#CK$1=W>Ta%9eF_0JhN5^1373 zT+LdZftE@~b@Q_zEj7)5v6h#h<$yyWrR7UUoLR%|x8*;Mcr)F-8Xed+Xu%A|ywzQS z-t>~~GaK5id7GPF!l+>Ednn?v1pSlt%9wMJuen44Pv z2=KB+V{;UvyMS_>8RmFK4+3>{HZ`X(S_{;}+02~5=oJcQv@#zC5@IjVeoI!tkIobS_(>?^KOiVGqV9CiqC!N#H;~c|0tYu>6Wq!lxevQ6mv{IupZUg!Gcc3yz zp5+{T19QDkfPQq_z{487UUL1?ylf#ds(!_77S!{{4E6(;tfA$NaaEq9v{Ayf{j zPM5Px++*TBT%a7F$6X7}ct+iTo^vfS6Jc(I7|oLRnjS_KjQ(b3vE&m#A?`=bHX*B! zvdus^*~h#kgi7-rmVC@CWXU5yUEM3pULjO_E;uPCR+{)k86@2BwJR4(Uay} zmTU+l#47Xd5XzT)=B+XpG3pC6&Ar}ygweyyTW>zjv2O%g>VCmo&*%%Fr-7blorarq zzF=--6r$0d5UR;Ffy%%;%Dg7bd(k|_C|9E&8QreYRYn6fY8*=G^Jz4W(F~2|Fq)^) zTt-VYs$jH2qrWkFPNN4xDL=OYoe(dYOPP0A^HwrCrO^|N{-x0xMke;s3xr$CsJce$ zLMcCM0hNLG9LJEs=oRzjP#SMCTqKZzF)hQ`Bv6#h){RA?034WUVpeG!)#nthp0w3#K#hA;nC_P+ z3k^}mF)n2H36A&KFiPVKVK%t0u=_?>l;~N$2i%_JU&8HE{v*p@VmBO8~f{2yC2IlO{x{aomlM=xSQF3 zq1qbw^QselPW6Al-NSx Su^@!LD_s+Jn!yI_2f-D>qaS-k>cE_CwnJEXDa)K#8GbIvF01V-Q+o!x$ z6zS>1l+jUS;WVZ^#FX_+*~XMlnerV|E-|G}H0fyyw@-P;Xwox=Ddo|WW6PPcl_`hV z{edaAniQ&LP0GEBh;QK5t4Vp7Q~9up7*S$& zEYY{dR)hZv`){vR7yc(}5&d{A5Bw>$$v?h!bNDy2|5EL?@OP|3^u=|$!GD1AAQ;L}~HgOPap2U=LrYuYx2FenqJk6AiiBxu5n6i&4hnaGcDVLZMl0-69OJuZ8 zyT323l|-5o*-d9RhuvG)?auCCcE__jmE8(<7qj~qyKC6p#O~|t?qT;+c2C0f$N0}A zO5N}5h9wUQ%00@riiint>n3ymlYHacE5cSHLyS$H3_Vp-H&drBP$HzQB4RGwNvw0( zALzWkM5=0@f1PI1SrM@a?rW_1^FPphiLDK5xa2x(AGuB^X{(4>2{*bSH_n0H z4Jil4HKh78y&;wIT}*kDDeIV0)&AAj#YM4HM6`hW636u($MqHK`48){dq{H)Jft*4 zhKJ(1g(>}+GMy=l;r1ziiYdF9a)K$BnG)5ALN#c#4rxzsM0z?iWhmS}h%9JZixjB_6*TxmlN!ltRM#IfZC7r!eN$2=fvTZiP8shHM&9G-NQ=VkXde*s# zDV0n)#FXPqxx$p_G|J&?lo;RD2Js+{;w=8)9l6}grFr{WX=}%<0>R50vsEjCe z6%n~`Gt)_Dr*zUeBAqOpk^YIU&m{9DWbR^_1OLN#_aPoL z?K<$UWckpHIQUny|K$w2@7R@*2+D^Ujo}^##SlNjZ-|TRCmGn2*%Xw>%;s?GX6C?6 z&in>pvq5hmI%MX9GMMQzGdm&n4w;40U)z@3J|#Sj~tu7b|@o32G!+#UKH!k%sVB3yT~9dPM>HeS?g))aoKAcR zs`|~W+MxG{pp;Tv)KchsD;+W_2U=w{gJ0F;$zjbA`)yfde=l|i zWl=s;{e*2oo#p;-kj{2*}M=sSAcGa zRqVddoce@U*?pbeH`%RZ_hY!t#7WkFh278=6jvQ~J#Z0!i(a-q<@qg6!jg)J0nlIA zq8RRg7Ng;gW`AjmJK(>k#WYw_*kUf+0WB859nJpI7Wcz{Pm70D{u;Y)vwM);W9)v*?$2<$m;c7Dv!#t&iVdNnT6RLs-;B18`(^g0 zu-ly7?V$H8Z{3pCBHFS0X(pxpc4%lOda>J%-A^-ZBFdQF?&a_(<5;_fkx|BlcAvuy zZQs8}l+mF5ak#1MX0h7|-=2&zo^4N(o0#%$`>2{x#=-WVN8&Vd^pjFN-=VP#_=`aC z)0cu?v*Yf%Qq1Z2BfFvX$e#t*(FtesiyECizFCT!I@#b)?sO7vMyJD|Jm29M+*X~w zH>J3xQxxdkI~j1>biO@KBA%61sh9?1^KNwq(2L%C>`pPCW1nAZ zfVVET=4wg%H~BB|iYehr`{kUG4%)&(ze@Y7-R_9TwhR7M+JA0Y-A-HB=~rog*gM!> zO)O@er;XDE)9iSf%BWI&?ft+ODe5PaX5rDuSGYg^X3S0%&sX?qe4LnMp|9fWi)9u%8{a@|vCzNb6U0%Co+um^ zpCm48bO13ViFzqY=f%Q1z$;)>DeAVKfgPg*6i+tnzRsR3Mp)GL5=-KWa}GPgv+{dxj{i&$hUibXn_eN?!bShdXW3ZAr{Y0r5QkB*}>?Tm%^!ErTuCZMCBgfQQ0;VGwPEpmHR{X z=3*tI(?O~+FNL^Jx|QA&`0)GVuOW_ z+VjP63w>=b5YY`t%L(JdF2B}qE3!1Y+QlBr-M0x>f{I}XBacSQy8c@KHqlX|U0vfHw~2=s%|mOvYVRaAFsgL@)-~4N zMeNq-A8iO7)yUb6(9ar8Z?(qRMO@OTFC#ONVy|?)(PmO;7ZIY6i0)R`-c>|sv@ko` z(Nzr7s7|+A;=7A+8l`lb6xvgiXw)gsEqV-#?@M0%D#NL_2_WjF0*HF4 zV6@duJM8GE`nvvN66>sVQ620rE;eDFYh3OsM}Lu(Nl4Yf{^Z3^^-BRnebxX`#FD4E zjSUo~8b!74;u|nRgH%A%m-SpTBCb@_qFb!oB~QawXp!A zv*Cm$$J zqxQLLfQmKh3^YPK!03eWZuej7j})6Uvh^TzP$R0lBgGC*?Q*fC=k^Ap#W{^00-7M| zW1lvj#*39b-)%5i^k-CoTK;i^5-~yXT)taAZ%`^0GFsr8-fESjRIJx%P0v_+so0~@ zI`6lTJj3WAv8m_z22+HsImNI*P>QCCG>x|RoB`CC(Q@%l&&v&_iiH{-M7U{UlZDKL z>Eft`Y9!25`$l2)=Lk1j)Ni3;sN1@3LYe5S(P_jmM@(RJ+Bn>@0k%IbW^}^%SI^$| zIbyw*tlz68czYPBS~o{r)`)7|Tv5=HVmNIy%uo_X9a@)Z&U5)9Y-)Dp91-FTH+Bcv{TX$kn@=V~yCNQSIJm6V{2t8m0CAC1Jg|s8N3J z-xHn_wenRA-FnN!4Wgq)LwmauH;M@wl>luL4`_5R&`V;wMo;vPOx!HaX|xeByec9J zl+K;7WsAtt=pfMRqL)UedjFKLT};vFB6vH*N{vGL{FJa$?AEAWpIV8##8Hi!^|>kW zZE-=P+xj#}+$|#72>h0d;yw)%-w|mVP3x1ESSgA$S`5i|#Uzc^^vO)zBNl43y-$n8 z_rxlVKJ1g5_`cYu(V0F4iF?HvjjjOg6SlT0zPf!2688&_M$P)(ns`7IYIIxQ%)}4G z1dWROrX?Q4ZdMg;D$s{wl}1bYHcb3TY|-dh@IDrYHQE8*A#qWoPx`tOKM|gGO3Q`5 zk%^y)B8_5-G7~=++cj#8$1_L7ruIs*eNmUhFT?}4DAc>C51vgFcT{M6QCi}0F<+w} zat0@!5Nj+n5>FQPSZHG6DRIU^Qxd-s_S;Cy31enaUHci~(&$x2k&I3o=Zne`&xllw z%zk$#elNTlMfY2rcup+SD7oLmi9d>kok-_tBeUOAiT@IhYt*XWy2Ss84H|Xq_d?>Y zVuwb(`fX0UEDmZkq~Es0tKyPI;~;4oVR+h4I;Z#Bm1s9^)@Xje_Yz%3o<>Xh9ZU>0 z-qPste#a848DD6$7AVsARil^solcB4V!J4vZ$M|P(MY5B`u#Jpj*+X;$FSvQql-o- zU}wBBM57(J4eLKCw7D@`qmura&KAa#8r=oSmd1LGmi3QH z@)|E`w66amXST6NqgVPjwC5Ndx~teL``1d!HM(i^DNvp)2J3uf$^9|sRJg3wlSX3s5N+Pjh8g)23|X3H>1-=@qnbH_J*g2Du?j{ zyh$C6g&NHQ>SUbJ=>7rSle!x1dn(B%2aHVWZmiO1@`fu^3f=)jj8N!a@IEj`j8tg#;J+sy zG}c%sD(OSxMU6HN{L-w&KCjNRi1DJ^G>!;I#+x)#(AIcr={ zJlDn6vG%h@^mvj~_kU-N3`X-^Q`@g{oHYs+kMGsa8jos3v*vTgPL1e3=bW*90%=(w z8WbFJ|6rWcD81mU`=3U=iHg^z;CJ_r#smw6g`79mGny}YwNGgHv$31eJh8a-R@c9( zQ+Uo@66nkP;d zylMN@c#@IQa>>}Ncw7#bjDw2D<#5S3&q$TSC8J(x!0JoJ8jZ-#ONMQV-|DUI-;9oo zREhm=jA2COups1j;{lD*3zmjlF}7&brr_z2tA=eVX;~oJ6yR&jrk7DAr^qyCYeXrM z<_<>l#KHXM-8S<$BPy|0(RTB)<}Ggh7C!P#8mT6S6@=KG=DpJiskA%IWsE9$1=6V{ z)e5A`#Iep4&ZyBY*5fw!Fd_a+{|dVpftpp^XDkZpIgdC3FbkK9`=SdN;K=uCEk3ow0(7Va+N%5 zXm)1a-7c!v4b2&hRNZZ8uF;5UK|}N38qvzL$BZZ^ovH<-n0XpeEl)8=YeXy0Y36*5 zsFtUjn>3=8?sW5GMk;qR%%hB`RGtjaFfS`fL3N_J>8TL-sru5w?4=Q{QMWW_YeZ|* zUUQR1wAP$$=FL;#XgxW{d{iS^zs@!HF;eB;$_$&YB&pn6ne7=V%UYR*iidK)2fURQ zy2D;zo?)a~KwC5VZqh>KzBHtrS)dWMfcEAD3%wZ9!F*JsUkCSXbgOyFLZyvvGcy(_ zEsi1gH0o^5)+lPo0KN;T5JkF?6P}+N$S@)LR;Mf&Gi~l+2V}>MhjdG zhQtD$*F0L?8f;EjL^_v?rbBi#8f-4psKb!TMnlY<7CO>ssCmvpXBv$#Yu&5Dbr|w< zqfusi3teqA)|_M^cglEkrG;Wr?lAXQsD8>M^MXc)3mT`Cni2OYoreo@Ql^+*Myh5_ zHH$T(S~tyHs1dESO*ao~^h{3Mlo{qljdF4c)m}_GE4a^^VX8OzY5nEalsnC0=IwSp z+_G)TOmhY!)$h(U*Jwl|$xL&nMpOl%|;qgjht<^w|JxN zbIrRf-W~RGbEQU9Mt7O7X+-VsE_1g+cxKZ!WuAFPqkL~ypsXbvANtQeDf7)<8Xfa) zZgjV~hS5Cqn1s%2-c{xm{!N7o%Wj);w>e*w+_nA*KQh9wp_h(Yj`qEFaOEju9CU$rvaDg=w;}JRJY%70Lk_1rTP56ji#M@Dsrxw#QMe5jnl|Kk$|iHS$|dBB%lQwp z#?3naea0 zUCSuG(?%xlT$x7!yZ+s(?GwOn>2bEl2^dN)26ly>0 z*|a!$PNUvHiPF1T@kR{Wn$}3>tszt)rVslxEman*CA2`?$7uIDLd$`^18>B$inn6e zFKKCVyGGALGEIv0iuV@t<}+F@J{y*qm?qa~bb44!%goV)XpF0C zZy`4@Z#nMVMl}Zd2l1AR^KBk$?3IT#YLdI9akd=s67e1s>xVz)%#jxuk>n4Jb7jQK zDqO={N0U6cMx!RV2~Aqd=*`T-^P_uf<;x6(5bkhjz8uI%-QO0-I~dW3usgIsmNPo- ze!1ltTN}AbqtT(TmUCx5^@o-fy!pw4s7K-J$LFR(=?(x=t9|9BiffxC?_yl z?s~5EGffKRLPjbF3*`r|k(T9R%kbC06I%#9C=LwY(WJZFx0Mi$Bz5gQWZvt9mb*?4 z-`}K%EMipYs*(LU&=f|yUBmMUE!L9Hwf+idz2@cSe*?6e(E`_hPzQU+;~F_f%y9IO zmo=(6f_SyIQG5$rO_-OZQG4bUYSfR>1dYZsTBuPuqcs{WWwcYHwTzBx^a`Vk8okdb zdb^7KD5DIGt}yDX(M==C&M_J_Wi(%-_Ka3(G=R}|jeLv_YjiK83mQGmDB=wj`)ea9 zU(z&sk9i$6I?22d8eL>wxklATQMi>FC5$3Fw`i2l=#WN5jLvH`Wt5TLLx$~8vER=; zI@q7eVFRP~jMSXAha9L8&HQ`F89PZ*)vO+}^IL?}v-uu!(%XcViw;Ag(tFBBEmSwX zm)vQgz3L3$;lfBsW>%I;Rhj zhb`1Qy;zEONM{9}On%;En5@NUxj0-fG<}%NVpJ(|awh^6YV=GFp$V2`DbPYjG}oV) zK3qP@NIm-;F1IqG9&cj(vW!~lM4917ken#B2Ly6Pp#%Nk*K7SGT8 z)%rwfX*_mfdbuprs0(=Ia-l|p!K;uvHJS`wg}kWIeDLm)8J{XGkAZiW9HY@D@aD-? z8of96U7+I{9RY8?toNA;_XBwILb%eY@|w@|~3CGwPonr1v8!;X?p z(vp+$pv=-}�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