text:
//******************************************************************************************************
//  UdpClient.cs - Gbtc
//
//  Copyright © 2012, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/MIT
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  07/06/2006 - Pinal C. Patel
//       Original version of source code generated.
//  09/06/2006 - J. Ritchie Carroll
//       Added bypass optimizations for high-speed socket access.
//  09/27/2007 - J. Ritchie Carroll
//       Added disconnect timeout overload.
//  09/29/2008 - J. Ritchie Carroll
//       Converted to C#.
//  07/08/2009 - J. Ritchie Carroll
//       Added WaitHandle return value from asynchronous connection.
//  07/09/2009 - Pinal C. Patel
//       Modified to attempt resuming reception on SocketException for non-Handshake enabled connection.
//  07/15/2009 - Pinal C. Patel
//       Modified Disconnect() to add error checking.
//  07/17/2009 - Pinal C. Patel
//       Added support to specify a specific interface address on a multiple interface machine.
//  07/20/2009 - Pinal C. Patel
//       Allowed for UDP endpoint to not be bound to a local interface by specifying -1 for port number.
//  09/14/2009 - Stephen C. Wills
//       Added new header and license agreement.
//  10/30/2009 - Pinal C. Patel
//       Added true multicast support by allowing for socket level subscription to a multicast group.
//  11/17/2009 - Pinal C. Patel
//       Fixed a issue in the creation of random server endpoint when server endpoint information is
//       omitted from the ConnectionString.
//  03/24/2010 - Pinal C. Patel
//       Updated the interpretation of server property in ConnectionString to correctly interpret
//       IPv6 IP addresses according to IETF - A Recommendation for IPv6 Address Text Representation.
//  11/29/2010 - Pinal C. Patel
//       Corrected the implementation of ConnectAsync() method.
//  02/13/2011 - Pinal C. Patel
//       Modified ConnectAsync() to handle loop-back address resolution failure on IPv6 enabled OSes.
//  07/23/2012 - Stephen C. Wills
//       Performed a full refactor to use the SocketAsyncEventArgs API calls.
//  10/31/2012 - Stephen C. Wills
//       Replaced single-threaded BlockingCollection pattern with asynchronous loop pattern.
//  12/13/2012 - Starlynn Danyelle Gilliam
//       Modified Header.
//  09/24/2015 - Allan V. Scheid
//       Fixed Mono socket error with System.Net.Sockets.Socket.IOControl method and SIO_UDP_CONNRESET
//       inside OpenPort().
//
//******************************************************************************************************

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using GSF.Configuration;
using GSF.IO;
using GSF.Threading;

namespace GSF.Communication
{
    ///
    /// Represents a UDP-based communication server.
    ///
    /// <remarks>
    /// <para>
    /// Use <see cref="UdpClient"/> when the primary purpose is to receive data.
    /// </para>
    /// <para>
    /// The <see cref="UdpClient.Client"/> socket can be bound to a specified interface on a machine with multiple interfaces by
    /// specifying the interface in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=8989; Interface=127.0.0.1")
    /// </para>
    /// <para>
    /// The <see cref="UdpClient.Client"/> socket can be used just for transmitting data without being bound to a local interface
    /// by specifying -1 for the port number in the <see cref="ClientBase.ConnectionString"/> (Example: "Server=localhost:8888; Port=-1")
    /// </para>
    /// </remarks>
    /// <example>
    /// This example shows how to use the <see cref="UdpClient"/> component:
    ///
    /// using System;
    /// using GSF;
    /// using GSF.Communication;
    /// using GSF.Security.Cryptography;
    /// using GSF.IO.Compression;
    ///
    /// class Program
    /// {
    ///     static UdpClient s_client;
    ///
    ///     static void Main(string[] args)
    ///     {
    ///         // Initialize the client.
    ///         s_client = new UdpClient("Server=localhost:8888; Port=8989");
    ///         s_client.Handshake = false;
    ///         s_client.ReceiveTimeout = -1;
    ///         s_client.Encryption = CipherStrength.None;
    ///         s_client.Compression = CompressionStrength.NoCompression;
    ///         s_client.SecureSession = false;
    ///         s_client.Initialize();
    ///         // Register event handlers.
    ///         s_client.ConnectionAttempt += s_client_ConnectionAttempt;
    ///         s_client.ConnectionEstablished += s_client_ConnectionEstablished;
    ///         s_client.ConnectionTerminated += s_client_ConnectionTerminated;
    ///         s_client.ReceiveDataComplete += s_client_ReceiveDataComplete;
    ///         // Connect the client.
    ///         s_client.Connect();
    ///
    ///         // Transmit user input to the server.
    ///         string input;
    ///         while (string.Compare(input = Console.ReadLine(), "Exit", true) != 0)
    ///         {
    ///             s_client.Send(input);
    ///         }
    ///
    ///         // Disconnect the client on shutdown.
    ///         s_client.Dispose();
    ///     }
    ///
    ///     static void s_client_ConnectionAttempt(object sender, EventArgs e)
    ///     {
    ///         Console.WriteLine("Client is connecting to server.");
    ///     }
    ///
    ///     static void s_client_ConnectionEstablished(object sender, EventArgs e)
    ///     {
    ///         Console.WriteLine("Client connected to server.");
    ///     }
    ///
    ///     static void s_client_ConnectionTerminated(object sender, EventArgs e)
    ///     {
    ///         Console.WriteLine("Client disconnected from server.");
    ///     }
    ///
    ///     static void s_client_ReceiveDataComplete(object sender, EventArgs<byte[], int> e)
    ///     {
    ///         Console.WriteLine(string.Format("Received data - {0}.", s_client.TextEncoding.GetString(e.Argument1, 0, e.Argument2)));
    ///     }
    /// }
    ///
    /// </example>
    // ReSharper disable UnusedVariable
    public class UdpClient : ClientBase
    {
        #region [ Members ]

        // Nested Types

        private class UdpClientPayload
        {
            public EndPoint Destination;
            public byte[] Data;
            public int Offset;
            public int Length;

            public ManualResetEventSlim WaitHandle;
        }

        // Constants

        ///
        /// Specifies the default value for the <see cref="ClientBase.ReceiveBufferSize"/> property.
        ///
        public new const int DefaultReceiveBufferSize = 65536;

        ///
        /// Specifies the default value for the <see cref="MaxPacketSize"/> property.
        ///
        public const int DefaultMaxPacketSize = 65536;

        ///
        /// Specifies the default value for the <see cref="AllowDualStackSocket"/> property.
        ///
        public const bool DefaultAllowDualStackSocket = true;

        ///
        /// Specifies the default value for the <see cref="MaxSendQueueSize"/> property.
        ///
        public const int DefaultMaxSendQueueSize = 500000;

        ///
        /// Specifies the default value for the <see cref="ClientBase.ConnectionString"/> property.
        ///
        public const string DefaultConnectionString = "Server=localhost:8888; Port=8989";

        ///
        /// Specifies the constant to be used for disabling <see cref="SocketError.ConnectionReset"/> when endpoint is not listening.
        ///
        private const int SIO_UDP_CONNRESET = -1744830452;

        // Events

        ///
        /// Occurs when unprocessed data has been received from the server.
        ///
        /// <remarks>
        /// <para>
        /// This event can be used to receive a notification that server data has arrived. The <see cref="Read"/> method can then be used
        /// to copy data to an existing buffer. In many cases it will be optimal to use an existing buffer instead of subscribing to the
        /// <see cref="ReceiveDataFromComplete"/> event.
        /// </para>
        /// <para>
        /// <see cref="EventArgs{T1,T2}.Argument1"/> is the end-point that data has been received from.

        /// <see cref="EventArgs{T1,T2}.Argument2"/> is the number of bytes received in the buffer from the server.
        /// </para>
        /// </remarks>
        [Category("Data"),
        Description("Occurs when unprocessed data has been received from the server.")]
        public event EventHandler<EventArgs<EndPoint, IPPacketInformation, int>> ReceiveDataFrom;

        ///
        /// Occurs when data received from the server has been processed and is ready for consumption.
        ///
        /// <remarks>
        /// <see cref="EventArgs{T1,T2,T3}.Argument1"/> is the end-point that data has been received from.

        /// <see cref="EventArgs{T1,T2,T3}.Argument2"/> is a new buffer containing post-processed data received from the server starting at index zero.

        /// <see cref="EventArgs{T1,T2,T3}.Argument3"/> is the number of post-processed bytes received in the buffer from the server.
        /// </remarks>
        [Category("Data"),
        Description("Occurs when data received from the server has been processed and is ready for consumption.")]
        public event EventHandler<EventArgs<EndPoint, IPPacketInformation, byte[], int>> ReceiveDataFromComplete;

        // Fields
        //private bool m_destinationReachableCheck;
        private IPEndPoint m_udpServer;
        private TransportProvider<Socket> m_udpClient;
        private IPStack m_ipStack;
        private bool m_allowDualStackSocket;
        private int m_maxPacketSize;
        private int m_maxSendQueueSize;
        private Dictionary<string, string> m_connectData;
        private ManualResetEvent m_connectionHandle;
#if ThreadTracking
        private ManagedThread m_connectionThread;
#else
        private Thread m_connectionThread;
#endif

        private int m_sending;
        private int m_receiving;
        private readonly object m_sendLock;
        private readonly ConcurrentQueue<UdpClientPayload> m_sendQueue;
        private readonly ShortSynchronizedOperation m_dumpPayloadsOperation;
        private SocketAsyncEventArgs m_sendArgs;
        private SocketAsyncEventArgs m_receiveArgs;
        private readonly EventHandler<SocketAsyncEventArgs> m_sendHandler;
        private readonly EventHandler<SocketAsyncEventArgs> m_receiveHandler;
        private bool m_receivePacketInfo;

        private bool m_disposed;

        #endregion

        #region [ Constructors ]

        ///
        /// Initializes a new instance of the <see cref="UdpClient"/> class.
        ///
        public UdpClient()
            : this(DefaultConnectionString)
        {
        }

        ///
        /// Initializes a new instance of the <see cref="UdpClient"/> class.
        ///
        /// <param name="connectString">Connect string of the <see cref="UdpClient"/>. See <see cref="DefaultConnectionString"/> for format.</param>
        public UdpClient(string connectString)
            : base(TransportProtocol.Udp, connectString)
        {
            base.ReceiveBufferSize = DefaultReceiveBufferSize;
            m_maxPacketSize = DefaultMaxPacketSize;
            m_allowDualStackSocket = DefaultAllowDualStackSocket;
            m_maxSendQueueSize = DefaultMaxSendQueueSize;

            m_sendLock = new object();
            m_sendQueue = new ConcurrentQueue<UdpClientPayload>();
            m_dumpPayloadsOperation = new ShortSynchronizedOperation(DumpPayloads, OnSendDataException);
            m_sendHandler += (sender, args) => ProcessSend();
            m_receiveHandler += (sender, args) => ProcessReceive();
        }

        ///
        /// Initializes a new instance of the <see cref="UdpClient"/> class.
        ///
        /// <param name="container"><see cref="IContainer"/> object that contains the <see cref="UdpClient"/>.</param>
        public UdpClient(IContainer container)
            : this()
        {
            if (container != null)
                container.Add(this);
        }

        #endregion

        #region [ Properties ]

        ///
        /// Gets or sets a boolean value that determines if dual-mode socket is allowed when endpoint address is IPv6.
        ///
        [Category("Settings"),
        DefaultValue(DefaultAllowDualStackSocket),
        Description("Determines if dual-mode socket is allowed when endpoint address is IPv6.")]
        public bool AllowDualStackSocket
        {
            get
            {
                return m_allowDualStackSocket;
            }
            set
            {
                m_allowDualStackSocket = value;
            }
        }

        ///
        /// Gets or sets the maximum size for the send queue before payloads are dumped from the queue.
        ///
        [Category("Settings"),
        DefaultValue(DefaultMaxSendQueueSize),
        Description("The maximum size for the send queue before payloads are dumped from the queue.")]
        public int MaxSendQueueSize
        {
            get
            {
                return m_maxSendQueueSize;
            }
            set
            {
                m_maxSendQueueSize = value;
            }
        }

        ///
        /// Gets the <see cref="Socket"/> object for the <see cref="UdpClient"/>.
        ///
        [Browsable(false)]
        public Socket Client
        {
            get
            {
                return m_udpClient.Provider;
            }
        }

        ///
        /// Gets the server URI of the <see cref="UdpClient"/>.
        ///
        [Browsable(false)]
        public override string ServerUri
        {
            get
            {
                return $"{TransportProtocol}://{m_connectData["server"]}".ToLower();
            }
        }

        ///
        /// Gets or sets the size of the buffer used by the client for receiving data from the server.
        ///
        /// <exception cref="ArgumentException">The value being assigned is either zero or negative.</exception>
        public override int ReceiveBufferSize
        {
            get
            {
                return base.ReceiveBufferSize;
            }
            set
            {
                base.ReceiveBufferSize = value;

                if ((object)m_udpClient != null && (object)m_udpClient.Provider != null)
                    m_udpClient.Provider.ReceiveBufferSize = value;
            }
        }

        ///
        /// Gets or sets the maximum expected size for packets being received by this <see cref="UdpClient"/>.
        ///
        public int MaxPacketSize
        {
            get
            {
                return m_maxPacketSize;
            }
            set
            {
                m_maxPacketSize = value;

                if ((object)m_udpClient != null)
                    m_udpClient.SetReceiveBuffer(value);
            }
        }

        ///
        /// Gets or sets the flag that determines whether the UDP client
        /// should attempt to receive packet info when receiving data
        /// from the socket.
        ///
        public bool ReceivePacketInfo
        {
            get
            {
                return m_receivePacketInfo;
            }
            set
            {
#if MONO
                if (value)
                    throw new NotImplementedException("Not supported under Mono.");

                m_receivePacketInfo = false;
#else
                m_receivePacketInfo = value;

                if ((object)m_udpClient != null && (object)m_udpClient.Provider != null)
                    m_udpClient.Provider.SetSocketOption(m_udpClient.Provider.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.PacketInformation, value);
#endif
            }
        }

        ///
        /// Determines whether the base class should track statistics.
        ///
        protected override bool TrackStatistics
        {
            get
            {
                return false;
            }
        }

        ///
        /// Gets the descriptive status of the client.
        ///
        public override string Status
        {
            get
            {
                StringBuilder statusBuilder = new StringBuilder(base.Status);

                if ((object)m_sendQueue != null)
                {
                    statusBuilder.AppendFormat("           Queued payloads: {0}", m_sendQueue.Count);
                    statusBuilder.AppendLine();
                }

                return statusBuilder.ToString();
            }
        }

        #endregion

        #region [ Methods ]

        ///
        /// Reads a number of bytes from the current received data buffer and writes those bytes into a byte array at the specified offset.
        ///
        /// <param name="buffer">Destination buffer used to hold copied bytes.</param>
        /// <param name="startIndex">0-based starting index into destination <paramref name="buffer"/> to begin writing data.</param>
        /// <param name="length">The number of bytes to read from current received data buffer and write into <paramref name="buffer"/>.</param>
        /// <returns>The number of bytes read.</returns>
        /// <remarks>
        /// This function should only be called from within the <see cref="ClientBase.ReceiveData"/> event handler. Calling this method outside
        /// this event will have unexpected results.
        /// </remarks>
        /// <exception cref="InvalidOperationException">No received data buffer has been defined to read.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> is null.</exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// <paramref name="startIndex"/> or <paramref name="length"/> is less than 0 -or-
        /// <paramref name="startIndex"/> and <paramref name="length"/> will exceed <paramref name="buffer"/> length.
        /// </exception>
        public override int Read(byte[] buffer, int startIndex, int length)
        {
            buffer.ValidateParameters(startIndex, length);

            if ((object)m_udpClient.ReceiveBuffer != null)
            {
                int sourceLength = m_udpClient.BytesReceived - ReadIndex;
                int readBytes = length > sourceLength ? sourceLength : length;
                Buffer.BlockCopy(m_udpClient.ReceiveBuffer, ReadIndex, buffer, startIndex, readBytes);

                // Update read index for next call
                ReadIndex += readBytes;

                if (ReadIndex >= m_udpClient.BytesReceived)
                    ReadIndex = 0;

                return readBytes;
            }

            throw new InvalidOperationException("No received data buffer has been defined to read.");
        }

        ///
        /// Saves <see cref="TcpServer"/> settings to the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true.
        ///
        public override void SaveSettings()
        {
            base.SaveSettings();
            if (PersistSettings)
            {
                // Save settings under the specified category.
                ConfigurationFile config = ConfigurationFile.Current;
                CategorizedSettingsElementCollection settings = config.Settings[SettingsCategory];
                settings["AllowDualStackSocket", true].Update(m_allowDualStackSocket);
                settings["MaxSendQueueSize", true].Update(m_maxSendQueueSize);
                config.Save();
            }
        }

        ///
        /// Loads saved <see cref="TcpServer"/> settings from the config file if the <see cref="ServerBase.PersistSettings"/> property is set to true.
        ///
        public override void LoadSettings()
        {
            int maxSendQueueSize;

            base.LoadSettings();
            if (PersistSettings)
            {
                // Load settings from the specified category.
                ConfigurationFile config = ConfigurationFile.Current;
                CategorizedSettingsElementCollection settings = config.Settings[SettingsCategory];
                settings.Add("AllowDualStackSocket", m_allowDualStackSocket, "True if dual-mode socket is allowed when IP address is IPv6, otherwise False.");
                settings.Add("MaxSendQueueSize", m_maxSendQueueSize, "The maximum size of the send queue before payloads are dumped from the queue.");
                AllowDualStackSocket = settings["AllowDualStackSocket"].ValueAs(m_allowDualStackSocket);
                MaxSendQueueSize = settings["MaxSendQueueSize"].ValueAs(m_maxSendQueueSize);

                // Overwrite config file if max send queue size exists in connection string.
                if (m_connectData.ContainsKey("maxSendQueueSize") && int.TryParse(m_connectData["maxSendQueueSize"], out maxSendQueueSize))
                    m_maxSendQueueSize = maxSendQueueSize;
            }
        }

        ///
        /// Disconnects the <see cref="UdpClient"/> from the connected server synchronously.
        ///
        public override void Disconnect()
        {
            if (CurrentState != ClientState.Disconnected)
            {
                try
                {
                    base.Disconnect();

                    if ((object)m_udpServer != null && (object)m_udpClient.Provider != null)
                    {
                        // If the IP specified for the server is a multicast IP, unsubscribe from the specified multicast group.
                        IPEndPoint serverEndpoint = (IPEndPoint)m_udpServer;

                        if (Transport.IsMulticastIP(serverEndpoint.Address))
                            DropMulticastMembership(serverEndpoint.Address, null, m_udpClient.MulticastMembershipAddresses);
                    }
                }
                catch (Exception ex)
                {
                    OnSendDataException(new InvalidOperationException($"Failed to drop multicast membership: {ex.Message}", ex));
                }

                m_udpClient.Reset();

                if ((object)m_connectionThread != null)
                    m_connectionThread.Abort();
            }
        }

        ///
        /// Connects the <see cref="UdpClient"/> to the server asynchronously.
        ///
        /// <exception cref="FormatException">Server property in <see cref="ClientBase.ConnectionString"/> is invalid.</exception>
        /// <exception cref="InvalidOperationException">Attempt is made to connect the <see cref="UdpClient"/> when it is not disconnected.</exception>
        /// <returns><see cref="WaitHandle"/> for the asynchronous operation.</returns>
        public override WaitHandle ConnectAsync()
        {
            // Client may still be attempting to receive data from a prior connection
            if (Interlocked.CompareExchange(ref m_receiving, 0, 0) != 0)
                throw new InvalidOperationException("Client is not yet fully disconnected");

            m_connectionHandle = (ManualResetEvent)base.ConnectAsync();

            m_udpClient = new TransportProvider<Socket>();
            m_udpClient.SetReceiveBuffer(m_maxPacketSize);

            // Create a server endpoint.
            if (m_connectData.ContainsKey("server"))
            {
                // Client has a server endpoint specified.
                Match endpoint = Regex.Match(m_connectData["server"], Transport.EndpointFormatRegex);

                if (endpoint != Match.Empty)
                    m_udpServer = Transport.CreateEndPoint(endpoint.Groups["host"].Value, int.Parse(endpoint.Groups["port"].Value), m_ipStack);
                else
                    throw new FormatException($"Server property in ConnectionString is invalid (Example: {DefaultConnectionString})");
            }
            else
            {
                // Create a random server endpoint since one is not specified.
                m_udpServer = Transport.CreateEndPoint(m_connectData["interface"], 0, m_ipStack);
            }

#if ThreadTracking
            m_connectionThread = new ManagedThread(OpenPort);
            m_connectionThread.Name = "GSF.Communication.UdpClient.OpenPort()";
#else
            m_connectionThread = new Thread(OpenPort);
#endif
            m_connectionThread.Start();

            return m_connectionHandle;
        }

        ///
        /// Adds a multicast membership to the UDP socket.
        ///
        /// <param name="serverAddress">Multicast address to join.</param>
        /// <param name="sourceAddress">Address which defines the source of the data or null if the membership is not source-specific.</param>
        public void AddMulticastMembership(IPAddress serverAddress, IPAddress sourceAddress)
        {
            byte[] multicastMembershipAddresses;
            AddMulticastMembership(serverAddress, sourceAddress, out multicastMembershipAddresses);
        }

        ///
        /// Drops a multicast membership from the UDP socket.
        ///
        /// <param name="serverAddress">Multicast address to drop.</param>
        /// <param name="sourceAddress">Address which defines the source of the data or null if the membership is not source-specific.</param>
        public void DropMulticastMembership(IPAddress serverAddress, IPAddress sourceAddress)
        {
            DropMulticastMembership(serverAddress, sourceAddress, null);
        }

        ///
        /// Releases the unmanaged resources used by the <see cref="UdpClient"/> and optionally releases the managed resources.
        ///
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            if (!m_disposed)
            {
                try
                {
                    // This will be done regardless of whether the object is finalized or disposed.
                    if (disposing)
                    {
                        // This will be done only when the object is disposed by calling Dispose().
                        if ((object)m_connectionHandle != null)
                        {
                            m_connectionHandle.Dispose();
                            m_connectionHandle = null;
                        }

                        if ((object)m_sendArgs != null)
                        {
                            m_sendArgs.Dispose();
                            m_sendArgs = null;
                        }

                        if ((object)m_receiveArgs != null)
                        {
                            m_receiveArgs.Dispose();
                            m_receiveArgs = null;
                        }
                    }
                }
                finally
                {
                    m_disposed = true;          // Prevent duplicate dispose.
                    base.Dispose(disposing);    // Call base class Dispose().
                }
            }
        }

        ///
        /// Validates the specified <paramref name="connectionString"/>.
        ///
        /// <param name="connectionString">Connection string to be validated.</param>
        /// <exception cref="ArgumentException">Port property is missing.</exception>
        /// <exception cref="ArgumentOutOfRangeException">Port property value is not between <see cref="Transport.PortRangeLow"/> and <see cref="Transport.PortRangeHigh"/>.</exception>
        protected override void ValidateConnectionString(string connectionString)
        {
            string setting;
            int value;

            m_connectData = connectionString.ParseKeyValuePairs();

            // Derive desired IP stack based on specified "interface" setting, adding setting if it's not defined
            m_ipStack = Transport.GetInterfaceIPStack(m_connectData);

            // Backwards compatibility adjustments.
            // New Format: Server=localhost:8888; Port=8989
            // Old Format: Server=localhost; RemotePort=8888; LocalPort=8888
            if (m_connectData.ContainsKey("localport") && !m_connectData.ContainsKey("port"))
                m_connectData.Add("port", m_connectData["localport"]);

            if (m_connectData.ContainsKey("server") && m_connectData.ContainsKey("remoteport"))
                m_connectData["server"] = $"{m_connectData["server"]}:{m_connectData["remoteport"]}";

            // Check if 'port' property is missing.
            if (!m_connectData.ContainsKey("port"))
                throw new ArgumentException($"Port property is missing (Example: {DefaultConnectionString})");

            // Check if 'port' property is valid.
            if (!Transport.IsPortNumberValid(m_connectData["port"]) && int.Parse(m_connectData["port"]) != -1)
                throw new ArgumentOutOfRangeException(nameof(connectionString), $"Port number must be {-1} or between {Transport.PortRangeLow} and {Transport.PortRangeHigh}");

            if (!m_connectData.ContainsKey("multicastTimeToLive"))
                m_connectData.Add("multicastTimeToLive", "10");

            // Make sure a valid multi-cast time-to-live value is defined in the connection string
            if (!(m_connectData.TryGetValue("multicastTimeToLive", out setting) && int.TryParse(setting, out value)))
                m_connectData["multicastTimeToLive"] = "10";
        }

        ///
        /// Connects to the <see cref="UdpClient"/>.
        ///
        private void OpenPort()
        {
            int connectionAttempts = 0;

            while (MaxConnectionAttempts == -1 || connectionAttempts < MaxConnectionAttempts)
            {
                try
                {
                    OnConnectionAttempt();

                    m_udpClient.Provider = Transport.CreateSocket(m_connectData["interface"], int.Parse(m_connectData["port"]), ProtocolType.Udp, m_ipStack, m_allowDualStackSocket);

                    // Disable SocketError.ConnectionReset exception from being thrown when the endpoint is not listening.
                    // Fix MONO bug with SIO_UDP_CONNRESET
                    try
                    {
                        m_udpClient.Provider.IOControl(SIO_UDP_CONNRESET, new[] { Convert.ToByte(false) }, null);
                    }
                    catch
                    {
                    }

                    m_udpClient.Provider.ReceiveBufferSize = ReceiveBufferSize;

                    // If the IP specified for the server is a multicast IP, subscribe to the specified multicast group.
                    IPEndPoint serverEndpoint = (IPEndPoint)m_udpServer;

                    if (Transport.IsMulticastIP(serverEndpoint.Address))
                    {
                        string multicastSource;
                        byte[] multicastMembershipAddresses;
                        IPAddress sourceAddress = null;

                        if (m_connectData.TryGetValue("multicastSource", out multicastSource))
                            sourceAddress = IPAddress.Parse(multicastSource);

                        AddMulticastMembership(serverEndpoint.Address, sourceAddress, out multicastMembershipAddresses);
                        m_udpClient.MulticastMembershipAddresses = multicastMembershipAddresses;
                    }

#if !MONO
                    // If the client requires packet info when
                    // receiving data, set the socket option now.
                    if (m_receivePacketInfo)
                    {
                        m_udpClient.Provider.SetSocketOption(serverEndpoint.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.PacketInformation, true);
                    }
#endif

                    // Listen for data to send.
                    using (SocketAsyncEventArgs sendArgs = m_sendArgs)
                    {
                        m_sendArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction();
                    }

                    m_udpClient.SetSendBuffer(SendBufferSize);
                    m_sendArgs.SetBuffer(m_udpClient.SendBuffer, 0, m_udpClient.SendBufferSize);
                    m_sendArgs.Completed += m_sendHandler;

                    m_connectionHandle.Set();
                    OnConnectionEstablished();

                    // Listen for incoming data only if endpoint is bound to a local interface.
                    if (m_udpClient.Provider.LocalEndPoint != null)
                    {
                        using (SocketAsyncEventArgs receiveArgs = m_receiveArgs)
                        {
                            m_receiveArgs = FastObjectFactory<SocketAsyncEventArgs>.CreateObjectFunction();
                        }

                        try
                        {
                            Interlocked.Exchange(ref m_receiving, 1);
                            m_receiveArgs.Completed += m_receiveHandler;
                            ReceivePayloadAsync();
                        }
                        catch
                        {
                            Interlocked.Exchange(ref m_receiving, 0);
                            throw;
                        }
                    }
                    break;
                }
                catch (ThreadAbortException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    connectionAttempts++;
                    OnConnectionException(ex);
                }
            }
        }

        ///
        /// Sends data to the server asynchronously.
        ///
        /// <param name="data">The buffer that contains the binary data to be sent.</param>
        /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>
        /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>
        /// <returns><see cref="WaitHandle"/> for the asynchronous operation.</returns>
        protected override WaitHandle SendDataAsync(byte[] data, int offset, int length)
        {
            return SendDataToAsync(data, offset, length, m_udpServer);
        }

        ///
        /// Sends data to the server asynchronously.
        ///
        /// <param name="data">The buffer that contains the binary data to be sent.</param>
        /// <param name="offset">The zero-based position in the <paramref name="data"/> at which to begin sending data.</param>
        /// <param name="length">The number of bytes to be sent from <paramref name="data"/> starting at the <paramref name="offset"/>.</param>
        /// <param name="destination">The end point which identifies the destination for the data.</param>
        /// <returns><see cref="WaitHandle"/> for the asynchronous operation.</returns>
        public WaitHandle SendDataToAsync(byte[] data, int offset, int length, EndPoint destination)
        {
            UdpClientPayload payload;
            UdpClientPayload dequeuedPayload;
            ManualResetEventSlim handle;

            if (CurrentState != ClientState.Connected)
                throw new InvalidOperationException("Client is not connected");

            // Execute operation to see if the client has reached the maximum send queue size.
            m_dumpPayloadsOperation.TryRun();

            // Create payload and wait handle.
            payload = FastObjectFactory<UdpClientPayload>.CreateObjectFunction();
            handle = FastObjectFactory<ManualResetEventSlim>.CreateObjectFunction();

            payload.Destination = destination;
            payload.Data = data;
            payload.Offset = offset;
            payload.Length = length;
            payload.WaitHandle = handle;
            handle.Reset();

            // Queue payload for sending.
            m_sendQueue.Enqueue(payload);

            lock (m_sendLock)
            {
                // Send the next queued payload.
                if (Interlocked.CompareExchange(ref m_sending, 1, 0) == 0)
                {
                    if (m_sendQueue.TryDequeue(out dequeuedPayload))
                        ThreadPool.QueueUserWorkItem(state => SendPayload((UdpClientPayload)state), dequeuedPayload);
                    else
                        Interlocked.Exchange(ref m_sending, 0);
                }
            }

            // Notify that the send operation has started.
            OnSendDataStart();

            // Return the async handle that can be used to wait for the async operation to complete.
            return handle.WaitHandle;
        }

        ///
        /// Raises the <see cref="ClientBase.ConnectionException"/> event.
        ///
        /// <param name="ex">Exception to send to <see cref="ClientBase.ConnectionException"/> event.</param>
        protected override void OnConnectionException(Exception ex)
        {
            base.Disconnect();
            base.OnConnectionException(ex);
        }

        ///
        /// Raises the <see cref="ClientBase.SendDataException"/> event.
        ///
        /// <param name="ex">Exception to send to <see cref="ClientBase.SendDataException"/> event.</param>
        protected override void OnSendDataException(Exception ex)
        {
            if (CurrentState != ClientState.Disconnected)
                base.OnSendDataException(ex);
        }

        ///
        /// Raises the <see cref="ClientBase.ReceiveDataException"/> event.
        ///
        /// <param name="ex">Exception to send to <see cref="ClientBase.ReceiveDataException"/> event.</param>
        protected override void OnReceiveDataException(Exception ex)
        {
            if (CurrentState != ClientState.Disconnected)
                base.OnReceiveDataException(ex);
        }

        ///
        /// Raises the <see cref="ClientBase.ConnectionTerminated"/> event.
        ///
        protected override void OnConnectionTerminated()
        {
            if (CurrentState != ClientState.Disconnected)
                base.OnConnectionTerminated();
        }

        ///
        /// Sends a payload on the socket.
        ///
        private void SendPayload(UdpClientPayload payload)
        {
            int copyLength;

            try
            {
                // Set the user token of the socket args.
                m_sendArgs.UserToken = payload;

                // Copy payload into send buffer.
                copyLength = Math.Min(payload.Length, m_udpClient.SendBufferSize);
                Buffer.BlockCopy(payload.Data, payload.Offset, m_udpClient.SendBuffer, 0, copyLength);

                // Set buffer and end point of send args.
                m_sendArgs.SetBuffer(0, copyLength);
                m_sendArgs.RemoteEndPoint = payload.Destination;

                // Update payload offset and length.
                payload.Offset += copyLength;
                payload.Length -= copyLength;

                // Send data over socket.
                if (!m_udpClient.Provider.SendToAsync(m_sendArgs))
                    ProcessSend();
            }
            catch (Exception ex)
            {
                OnSendDataException(ex);

                // Assume process send was not able
                // to continue the asynchronous loop.
                Interlocked.Exchange(ref m_sending, 0);
            }
        }

        ///
        /// Callback method for asynchronous send operation.
        ///
        private void ProcessSend()
        {
            UdpClientPayload payload = null;
            ManualResetEventSlim handle = null;

            try
            {
                // Get the payload and its wait handle.
                payload = (UdpClientPayload)m_sendArgs.UserToken;
                handle = payload.WaitHandle;

                // Determine whether we are finished with this
                // payload and, if so, set the wait handle.
                if (payload.Length <= 0)
                    handle.Set();

                // Check for errors during send operation.
                if (m_sendArgs.SocketError != SocketError.Success)
                    throw new SocketException((int)m_sendArgs.SocketError);

                // Update statistics.
                UpdateBytesSent(m_sendArgs.BytesTransferred);
                m_udpClient.Statistics.UpdateBytesSent(m_sendArgs.BytesTransferred);

                // Send operation is complete.
                if (payload.Length <= 0)
                    OnSendDataComplete();
            }
            catch (Exception ex)
            {
                // Send operation failed to complete.
                OnSendDataException(ex);
            }
            finally
            {
                if ((object)payload != null)
                {
                    try
                    {
                        if (payload.Length > 0)
                        {
                            // Still more to send for this payload.
                            ThreadPool.QueueUserWorkItem(state => SendPayload((UdpClientPayload)state), payload);
                        }
                        else
                        {
                            payload.WaitHandle = null;

                            // Begin sending next client payload.
                            if (m_sendQueue.TryDequeue(out payload))
                            {
                                ThreadPool.QueueUserWorkItem(state => SendPayload((UdpClientPayload)state), payload);
                            }
                            else
                            {
                                lock (m_sendLock)
                                {
                                    if (m_sendQueue.TryDequeue(out payload))
                                        ThreadPool.QueueUserWorkItem(state => SendPayload((UdpClientPayload)state), payload);
                                    else
                                        Interlocked.Exchange(ref m_sending, 0);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string errorMessage = $"Exception encountered while attempting to send next payload: {ex.Message}";
                        OnSendDataException(new Exception(errorMessage, ex));
                        Interlocked.Exchange(ref m_sending, 0);
                    }
                }
            }
        }

        ///
        /// Initiate method for asynchronous receive operation of payload data.
        ///
        private void ReceivePayloadAsync()
        {
            // Set up event args for receive operation.
            if (m_udpClient.ReceiveBufferSize != m_receiveArgs.Count)
                m_receiveArgs.SetBuffer(m_udpClient.ReceiveBuffer, 0, m_udpClient.ReceiveBufferSize);
            else
                m_receiveArgs.SetBuffer(0, m_udpClient.ReceiveBufferSize);

            m_receiveArgs.RemoteEndPoint = m_udpServer;

            if (!m_receivePacketInfo)
            {
                if (!m_udpClient.Provider.ReceiveFromAsync(m_receiveArgs))
                    ThreadPool.QueueUserWorkItem(state => ProcessReceive());
            }
#if !MONO
            else
            {
                if (!m_udpClient.Provider.ReceiveMessageFromAsync(m_receiveArgs))
                    ThreadPool.QueueUserWorkItem(state => ProcessReceive());
            }
#endif
        }

        ///
        /// Callback method for asynchronous receive operation of payload data.
        ///
        private void ProcessReceive()
        {
            try
            {
                if (CurrentState == ClientState.Disconnected)
                    throw new SocketException((int)SocketError.Disconnecting);

                if (m_receiveArgs.SocketError != SocketError.Success)
                    throw new SocketException((int)m_receiveArgs.SocketError);

                // Update statistics and pointers.
                UpdateBytesReceived(m_receiveArgs.BytesTransferred);
                m_udpClient.Statistics.UpdateBytesReceived(m_receiveArgs.BytesTransferred);
                m_udpClient.BytesReceived = m_udpClient.Statistics.LastBytesReceived;

                // Notify of received data and resume receive operation.
                OnReceive(m_receiveArgs.RemoteEndPoint, m_receiveArgs.ReceiveMessageFromPacketInfo, m_udpClient.ReceiveBuffer, m_udpClient.BytesReceived);
                ReceivePayloadAsync();
            }
            catch (ObjectDisposedException)
            {
                // Make sure connection is terminated when client is disposed.
                TerminateConnection(true);
            }
            catch (SocketException ex)
            {
                try
                {
                    // Notify and resume receive.
                    OnReceiveDataException(ex);
                    ReceivePayloadAsync();
                }
                catch
                {
                    // Terminate connection if resuming receiving fails.
                    TerminateConnection(true);
                }
            }
            catch (Exception ex)
            {
                try
                {
                    // For any other exception, notify and resume receive.
                    OnReceiveDataException(ex);
                    ReceivePayloadAsync();
                }
                catch
                {
                    // Terminate connection if resuming receiving fails.
                    TerminateConnection(true);
                }
            }
        }

        private void AddMulticastMembership(IPAddress serverAddress, IPAddress sourceAddress, out byte[] multicastMembershipAddresses)
        {
            multicastMembershipAddresses = null;

            try
            {
                if (!Transport.IsMulticastIP(serverAddress))
                    throw new InvalidOperationException("Cannot add multicast membership if server address is not a multicast IP.");

                SocketOptionLevel level = serverAddress.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP;

                if ((object)sourceAddress == null)
                {
                    // Execute multicast subscribe for any source
                    m_udpClient.Provider.SetSocketOption(level, SocketOptionName.AddMembership, new MulticastOption(serverAddress));
                    m_udpClient.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_connectData["multicastTimeToLive"]));
                }
                else
                {
                    IPAddress localAddress = ((IPEndPoint)m_udpClient.Provider.LocalEndPoint).Address;

                    if (sourceAddress.AddressFamily != serverAddress.AddressFamily)
                        throw new InvalidOperationException($"Source address \"{sourceAddress}\" is not in the same IP format as server address \"{serverAddress}\"");

                    if (localAddress.AddressFamily != serverAddress.AddressFamily)
                        throw new InvalidOperationException($"Local address \"{localAddress}\" is not in the same IP format as server address \"{serverAddress}\"");

                    using (BlockAllocatedMemoryStream membershipAddresses = new BlockAllocatedMemoryStream())
                    {
                        byte[] serverAddressBytes = serverAddress.GetAddressBytes();
                        byte[] sourceAddressBytes = sourceAddress.GetAddressBytes();
                        byte[] localAddressBytes = localAddress.GetAddressBytes();

                        membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length);
                        membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length);
                        membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length);

                        multicastMembershipAddresses = membershipAddresses.ToArray();
                    }

                    // Execute multicast subscribe for specific source
                    m_udpClient.Provider.SetSocketOption(level, SocketOptionName.AddSourceMembership, multicastMembershipAddresses);
                    m_udpClient.Provider.SetSocketOption(level, SocketOptionName.MulticastTimeToLive, int.Parse(m_connectData["multicastTimeToLive"]));
                }
            }
            catch (SocketException ex)
            {
                // TODO: Need to add comment as to why this is necessary...
                if (ex.SocketErrorCode != SocketError.InvalidArgument)
                    throw;
            }
        }

        private void DropMulticastMembership(IPAddress serverAddress, IPAddress sourceAddress, byte[] multicastMembershipAddresses)
        {
            try
            {
                if (!Transport.IsMulticastIP(serverAddress))
                    throw new InvalidOperationException("Cannot drop multicast membership if server address is not a multicast IP.");

                if ((object)sourceAddress == null && (object)multicastMembershipAddresses == null)
                {
                    // Execute multicast unsubscribe for any source
                    m_udpClient.Provider.SetSocketOption(serverAddress.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.DropMembership, new MulticastOption(serverAddress));
                }
                else
                {
                    if ((object)multicastMembershipAddresses == null)
                    {
                        IPAddress localAddress = ((IPEndPoint)m_udpClient.Provider.LocalEndPoint).Address;

                        if (sourceAddress.AddressFamily != serverAddress.AddressFamily)
                            throw new InvalidOperationException($"Source address \"{sourceAddress}\" is not in the same IP format as server address \"{serverAddress}\"");

                        if (localAddress.AddressFamily != serverAddress.AddressFamily)
                            throw new InvalidOperationException($"Local address \"{localAddress}\" is not in the same IP format as server address \"{serverAddress}\"");

                        using (BlockAllocatedMemoryStream membershipAddresses = new BlockAllocatedMemoryStream())
                        {
                            byte[] serverAddressBytes = serverAddress.GetAddressBytes();
                            byte[] sourceAddressBytes = sourceAddress.GetAddressBytes();
                            byte[] localAddressBytes = localAddress.GetAddressBytes();

                            membershipAddresses.Write(serverAddressBytes, 0, serverAddressBytes.Length);
                            membershipAddresses.Write(sourceAddressBytes, 0, sourceAddressBytes.Length);
                            membershipAddresses.Write(localAddressBytes, 0, localAddressBytes.Length);

                            multicastMembershipAddresses = membershipAddresses.ToArray();
                        }
                    }

                    // Execute multicast unsubscribe for specific source
                    m_udpClient.Provider.SetSocketOption(serverAddress.AddressFamily == AddressFamily.InterNetworkV6 ? SocketOptionLevel.IPv6 : SocketOptionLevel.IP, SocketOptionName.DropSourceMembership, multicastMembershipAddresses);
                }
            }
            catch (SocketException ex)
            {
                if (ex.SocketErrorCode != SocketError.InvalidArgument)
                    throw;
            }
        }

        ///
        /// Dumps payloads from the send queue when the send queue grows too large.
        ///
        private void DumpPayloads()
        {
            UdpClientPayload payload;

            // Check to see if the client has reached the maximum send queue size.
            if (m_maxSendQueueSize > 0 && m_sendQueue.Count >= m_maxSendQueueSize)
            {
                for (int i = 0; i < m_maxSendQueueSize; i++)
                {
                    if (m_sendQueue.TryDequeue(out payload))
                    {
                        payload.WaitHandle.Set();
                        payload.WaitHandle.Dispose();
                        payload.WaitHandle = null;
                    }
                }

                throw new InvalidOperationException($"UDP client reached maximum send queue size. {m_maxSendQueueSize} payloads dumped from the queue.");
            }
        }

        ///
        /// Processes the termination of client.
        ///
        private void TerminateConnection(bool raiseEvent)
        {
            if (CurrentState != ClientState.Disconnected)
                m_udpClient.Reset();

            Interlocked.Exchange(ref m_receiving, 0);

            if (raiseEvent)
                OnConnectionTerminated();
        }

        ///
        /// Calls all the receive handlers in sequence.
        ///
        private void OnReceive(EndPoint remoteEndPoint, IPPacketInformation packetInformation, byte[] data, int size)
        {
            OnReceiveDataFrom(remoteEndPoint, packetInformation, size);
            OnReceiveDataFromComplete(remoteEndPoint, packetInformation, data, size);
            OnReceiveDataComplete(data, size);
        }

        ///
        /// Raises the <see cref="ReceiveDataFrom"/> event.
        ///
        /// <param name="remoteEndPoint">End-point from which data has been received.</param>
        /// <param name="packetInformation">Packet information for received data, if available.</param>
        /// <param name="size">Number of bytes received from the client.</param>
        private void OnReceiveDataFrom(EndPoint remoteEndPoint, IPPacketInformation packetInformation, int size)
        {
            try
            {
                if ((object)ReceiveDataFrom != null)
                    ReceiveDataFrom(this, new EventArgs<EndPoint, IPPacketInformation, int>(remoteEndPoint, packetInformation, size));
            }
            catch (Exception userException)
            {
                OnUnhandledUserException(userException);
            }
        }

        ///
        /// Raises the <see cref="ReceiveDataFromComplete"/> event.
        ///
        /// <param name="remoteEndPoint">End-point from which data has been received.</param>
        /// <param name="packetInformation">Packet information for received data, if available.</param>
        /// <param name="data">Data received from the client.</param>
        /// <param name="size">Number of bytes received from the client.</param>
        private void OnReceiveDataFromComplete(EndPoint remoteEndPoint, IPPacketInformation packetInformation, byte[] data, int size)
        {
            try
            {
                if ((object)ReceiveDataFromComplete != null)
                    ReceiveDataFromComplete(this, new EventArgs<EndPoint, IPPacketInformation, byte[], int>(remoteEndPoint, packetInformation, data, size));
            }
            catch (Exception userException)
            {
                OnUnhandledUserException(userException);
            }
        }

        #endregion
    }
}


text:
void CUDA_interface (
  struct pb_TimerSet* timers,
  unsigned int n,       // Number of input elements
  parameters params,    // Parameter struct which defines output gridSize, cutoff distance, etc.
  ReconstructionSample* sample, // Array of input elements
  float* LUT,           // Precomputed LUT table of Kaiser-Bessel function.
                          // Used for computation on CPU instead of using the function every time
  int sizeLUT,          // Size of LUT
  cmplx* gridData,      // Array of output grid points. Each element has a real and imaginary component
  float* sampleDensity  // Array of same size as gridData couting the number of contributions
                          // to each grid point in the gridData array
);

redpajama_set_name: RedPajamaGithub


text:
"""Reusable math functions for scientists."""
import math
import numbers

def dilute_stock(cI, vI, **values):
    """Dilute a stock concentration.

    Args:
        cI ([type]): [description]
        vI ([type]): [description]

    Raises:
        UserWarning: [description]
        KeyError: [description]

    Returns:
        [type]: [description]
    """
    if not values:
        raise UserWarning('You did not enter any values.')
    for key, value in values.items():
        if key == 'vF' or 'vf':
            value = values['vF']
            final_concentration = (cI * vI) / value
            return final_concentration
        elif key == 'cF' or 'cf':
            value = values['cF']
            final_volume = (cI * vI) / value
            return final_volume
        else:
            raise KeyError('%s is not a valid key.' % key)

def mass_recorder(grams, item, sigfigs):
    """Record the mass of different items.

    Args:
        grams ([type]): [description]
        item ([type]): [description]
        sigfigs ([type]): [description]

    Raises:
        ValueError: [description]
    """
    item_dict = {}
    if isinstance(grams, float) and isinstance(item, str) and isinstance(sigfigs, int):
        if item in item_dict:
            formatter = "{0:." + str(Sigfigs) + "f}"
            grams = formatter.format(grams)
            item_Dict[item] = grams
        else:
            formatter = "{0:." + str(sigfigs) + "f}"
            grams = formatter.format(grams)
            item_dict[item] += grams
    else:
        raise ValueError('Please input grams as a float value, item as a string, and sigfigs as an integer value")

def transmittance_to_absorbance(transmittance):
    """Convert transmittance to absorbance.

    Args:
        transmittance ([type]): [description]

    Raises:
        ValueError: [description]

    Returns:
        [type]: [description]
    """
    if isinstance(transmittance, numbers.Number):
        t = transmittance / 100
        return math.log(t ** -1)
    else:
        raise ValueError("{} must be a number (percentage).")

def calculate_molarity(moles, volume, units):
    """Calculate the molarity of a solution given moles and liters or mL.

    Args:
        moles ([type]): [description]
        volume ([type]): [description]
        units ([type]): [description]

    Raises:
        ValueError: [description]

    Returns:
        [type]: [description]
    """
    if (units == 'ml' or units == 'mL'):
        volume = volume * 1000
    elif (units != 'l' and units != 'L'):
        raise ValueError('This unit of measurement is not supported.')
    return moles / volume

def refractive_index_prism(prism, deviation, angle_measurement):
    """Calculate the refractive index of prism.

    This function uses the angle of prism and minimum angle of deviation.

    Args:
        prism ([type]): [description]
        deviation ([type]): [description]
        angle_measurement ([type]): [description]

    Raises:
        ValueError: [description]

    Returns:
        [type]: [description]
    """
    if (angle_measurement == 'rad'):
        refractive_index = (math.sin((prism + deviation) / 2)) / math.sin(prism / 2)
        return refractive_index

    elif (angle_measurement == 'deg'):
        p = math.radians(prism)
        d = math.radians(deviation)
        refractive_index = (math.sin((p + d) / 2)) / math.sin(p / 2)
        return refractive_index

    else:
        raise ValueError('The angle measurement has to be in deg or rad format.')


text:
@interface PAMTrainingWithAutolayoutTests : SenTestCase

@end


text:
import json
import re

from itertools import groupby

with open('terraform.tfstate') as tfstate_file:
    tfstate = json.load(tfstate_file)

modules = tfstate['modules']
resources = sorted(sum(map(lambda m: list(m['resources'].iteritems()), modules), []), key=lambda (x, y): x)
groups = groupby(resources, lambda (k, v): re.search('\\.([\w-]+)', k, re.IGNORECASE).group(1))

print json.dumps(dict([ (k, map(lambda (x,y): y['primary']['attributes']['ipv4_address'], list(v))) for k,v in groups ]))


text:
'use strict';

var React = require('react');

module.exports = React.createClass({
  render: function () {
    var that = this;
    var wonDiv = YOU WON;
    var lostDiv = YOU LOST;
    var getDiv = function () {
      if (that.props.isWon) {
        return wonDiv;
      } else if (that.props.isLost) {
        return lostDiv;
      } else {
        return ;
      }
    };

    return (

            <input className="form-control"
                   value={this.props.exclamations}
                   readOnly />

            <input className="form-control" value={this.props.time} readOnly />

          {getDiv()}

    );
  }
});


text:
module ActiveSupport #:nodoc:
  module CoreExtensions #:nodoc:
    module Integer #:nodoc:
      # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
      #
      # These methods use Time#advance for precise date calculations when using from_now, ago, etc.
      # as well as adding or subtracting their results from a Time object. For example:
      #
      #   # equivalent to Time.now.advance(:months => 1)
      #   1.month.from_now
      #
      #   # equivalent to Time.now.advance(:years => 2)
      #   2.years.from_now
      #
      #   # equivalent to Time.now.advance(:months => 4, :years => 5)
      #   (4.months + 5.years).from_now
      #
      # While these methods provide precise calculation when used as in the examples above, care
      # should be taken to note that this is not true if the result of `months', `years', etc is
      # converted before use:
      #
      #   # equivalent to 30.days.to_i.from_now
      #   1.month.to_i.from_now
      #
      #   # equivalent to 365.25.days.to_f.from_now
      #   1.year.to_f.from_now
      #
      # In such cases, Ruby's core
      # Date[http://stdlib.rubyonrails.org/libdoc/date/rdoc/index.html] and
      # Time[http://stdlib.rubyonrails.org/libdoc/time/rdoc/index.html] should be used for precision
      # date and time arithmetic
      module Time
        def months
          ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
        end
        alias :month :months

        def years
          ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
        end
        alias :year :years
      end
    end
  end
end


text:
import jm.audio.Instrument;
import jm.audio.io.*;
import jm.audio.synth.*;
import jm.music.data.Note;
import jm.audio.AudioObject;

/**
 * A basic pulsewave waveform instrument implementation
 * which implements envelope , pan, and volume control
 * @author Andrew Brown and Andrew Sorensen
 */

public final class PulsewaveInst extends Instrument{
	//----------------------------------------------
	// Attributes
	//----------------------------------------------

	/** The points to use in the construction of Envelopes */
	private EnvPoint[] pointArray = new EnvPoint[10];
	private int sampleRate;
    private SampleOut sout;

	//----------------------------------------------
	// Constructor
	//----------------------------------------------
	/**
	 * Basic default constructor to set an initial
	 * sampling rate and buffersize in addition
	 * to the neccessary frequency relationships
	 * and volumes for each frequency to be added
	 * the instrument
	 * @param sampleRate
	 * @param buffersize
	 * @param frequencies the relative freqencies to use
	 * @param volumes the volumes to use for the frequencies
	 */
	public PulsewaveInst(int sampleRate){
		this.sampleRate = sampleRate;
		EnvPoint[] tempArray = {
			new EnvPoint((float)0.0, (float)0.0),
			new EnvPoint((float)0.02, (float)1.0),
			new EnvPoint((float)0.15, (float)0.6),
			new EnvPoint((float)0.9, (float)0.3),
			new EnvPoint((float)1.0, (float)0.0)
		};
		pointArray = tempArray;
	}

	//----------------------------------------------
	// Methods
	//----------------------------------------------

	/**
	 * Initialisation method used to build the objects that
	 * this instrument will use
	 */
	public void createChain(){
		Oscillator wt = new Oscillator(this, Oscillator.PULSE_WAVE,
                    this.sampleRate, 2);
                wt.setPulseWidth(0.15);
		Envelope env = new Envelope(wt, pointArray);
		Volume vol = new Volume(env);
		StereoPan span = new StereoPan(vol);
		if(output == RENDER) sout = new SampleOut(span);
	}
}


text:
package com.hearthsim.card.classic.minion.rare;

import com.hearthsim.card.Card;
import com.hearthsim.card.CardPlayBeginInterface;
import com.hearthsim.card.classic.minion.common.VioletApprentice;
import com.hearthsim.card.minion.Minion;
import com.hearthsim.card.spellcard.SpellCard;
import com.hearthsim.model.PlayerModel;
import com.hearthsim.model.PlayerSide;
import com.hearthsim.util.tree.HearthTreeNode;

public class VioletTeacher extends Minion implements CardPlayBeginInterface {

    public VioletTeacher() {
        super();
    }

    /**
     * Called whenever another card is used
     * <p>
     * When you cast a spell, summon a 1/1 Violet Apprentice
     *
     * @param thisCardPlayerSide The player index of the card receiving the event
     * @param cardUserPlayerSide
     * @param usedCard           The card that was used
     * @param boardState         The BoardState before this card has performed its action.  It will be manipulated and returned.
     * @return The boardState is manipulated and returned
     */
    @Override
    public HearthTreeNode onCardPlayBegin(
        PlayerSide thisCardPlayerSide,
        PlayerSide cardUserPlayerSide,
        Card usedCard,
        HearthTreeNode boardState) {
        HearthTreeNode toRet = boardState;
        if (thisCardPlayerSide != PlayerSide.CURRENT_PLAYER)
            return toRet;
        if (inHand)
            return toRet;
        PlayerModel currentPlayer = toRet.data_.modelForSide(PlayerSide.CURRENT_PLAYER);
        if (usedCard instanceof SpellCard && !currentPlayer.isBoardFull()) {
            Minion newMinion = new VioletApprentice();
            toRet = newMinion.summonMinion(thisCardPlayerSide, this, toRet, false);
        }
        return toRet;
    }
}


text:
<法規>
<法規性質>命令</法規性質>
<英文法規名稱>Organization Regulations for the Copyright Regulatory and Mediation Board of the Intellectual Property Office, Ministry of Economic Affairs</英文法規名稱>
<中文法規名稱>經濟部智慧財產局著作權審議及調解委員會組織規程</中文法規名稱>
<法規網址>https://law.moj.gov.tw/Eng/LawClass/LawAll.aspx?pcode=J0000053</法規網址>
<最新英文異動日期>20191127</最新英文異動日期>
<廢止註記>
</廢止註記>
<附件 />
<沿革內容><![CDATA[1.Promulgated by Ministry of Interior on August 28, 1992 per Letter No. Tai-(81)-Nai-Chu-Tze 8183016.
2.Promulgated by Ministry of Interior on November 27, 1996 per Letter No. Tai-(85)-Nai-Chu-Tze 8588560.
3.Amended and Promulgated by Ministry of Economic Affairs on July 28, 1999 per Letter No. Ging-(88)-Zhi-Tze 88461851.
4.Amended and Promulgated by Ministry of Economic Affairs on March 31, 2004 per Letter No. Ging-Zhi-Tze 09304602820.
5.Amended and Issued by Ministry of Economic Affairs on August 24, 2010 per Order No. Ging-Zhi-Tze 09904605270.
6.Amended and Promulgated by Ministry of Economic Affairs on November 27, 2019 per Letter No. Ging-Zhi-Tze 10804605270.]]></沿革內容>
<前言><![CDATA[]]></前言>
<法規內容>
  <條文>
    <條號>Article 1</條號>
    <條文內容><![CDATA[These Regulations are promulgated under the provisions of Article 83 of the Copyright Act.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 2</條號>
    <條文內容><![CDATA[A Copyright Review and Mediation Committee (hereinafter, "the Committee") shall be established by the Intellectual Property Office (hereinafter, "TIPO") of the Ministry of Economic Affairs to handle matters under Article 82 of the Copyright Act and under Article 25, Paragraph 4, Article 26, Paragraph 2, and Article 30, Paragraph 5 of the Copyright Collective Management Organization Act.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 3</條號>
    <條文內容><![CDATA[The Committee shall consist of a chair to be concurrently assumed by the Director General of TIPO, and 21 to 29 members to be appointed by the Director General of TIPO from among relevant government agency representatives, scholars, experts, rights holder representatives, exploiter representatives, and relevant TIPO personnel, to serve term of two years concurrently with their regular positions.Neither gender should occupy less than one third of the seats of the committee.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 4</條號>
    <條文內容><![CDATA[The Committee shall appoint one executive secretary, three secretaries, and two to six staff, all of whom shall be relevant TIPO personnel serving on assignment concurrently with their regular positions.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 5</條號>
    <條文內容><![CDATA[Meetings of the Committee shall be convened and presided over by the chair. In the event that the chair is unable to attend a meeting, the chair shall designate one member to chair the meeting.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 6</條號>
    <條文內容><![CDATA[A resolution on a matter under review by the Committee may be adopted only by the consent of a majority of the members in attendance at a meeting attended by one-half or more of all members.
A resolution on a matter under consultation by the Committee may be adopted only by the consent of a majority of the members in attendance at a meeting attended by one-third or more of all members. However, for consultation on matters set out in the Copyright Collective Management Organization Act, depending on the actual circumstances of a case, after giving consideration to the scope of the market for copyright exploitation and the affect on its licensing order, the Committee may designate three to five members to form a consultation subcommittee, which may adopt a resolution only by the consent of more than two-thirds of the subcommittee members in attendance at a subcommittee meeting attended by two-thirds or more of the subcommittee members.
Members shall attend Committee meetings in person. However, government agency representative who is unable to attend may appoint a representative to attend on their behalf. That representative shall be counted among the members in attendance and have a voice and a vote at the meeting.
When the Committee handles dispute resolution matters, it shall do so in accordance with the Regulations of Copyright Dispute Mediation.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 7</條號>
    <條文內容><![CDATA[The Committee may invite relevant persons to attend meetings and provide opinions.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 8</條號>
    <條文內容><![CDATA[All personnel serving in a concurrent capacity on the committee do so in a pro bono capacity.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 9</條號>
    <條文內容><![CDATA[The funds required by the Committee shall be allocated from TIPO's annual budget.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 10</條號>
    <條文內容><![CDATA[The Committee has no capacity to issue any official documents externally. All resolutions of the Committee, after being approved by the Director General of TIPO, shall be issued in the name of TIPO.]]></條文內容>
  </條文>
  <條文>
    <條號>Article 11</條號>
    <條文內容><![CDATA[These Regulations shall come into force from the date of promulgation.]]></條文內容>
  </條文>
</法規內容>
</法規>


text:
buildah bud -v "$(pwd)":/tmp/context:ro,Z -f Containerfile.api -t maw-api-test
buildah bud -v "$(pwd)":/tmp/context:ro,Z -f Containerfile.auth -t maw-auth-test
buildah bud -v "$(pwd)":/tmp/context:ro,Z -f Containerfile.www -t maw-www-test
buildah bud -v "$(pwd)":/tmp/context:ro,Z -f Containerfile.cache -t maw-cache-sync-test


text:
package net.ontrack.extension.jenkins;

import net.ontrack.core.model.Decoration;

public interface JenkinsDecorator {

    Decoration getJobDecoration(JenkinsJobState jenkinsJobState);

}


text:
package net.desgrange.pwad.functional.utils;

import net.desgrange.pwad.Main;
import net.desgrange.pwad.utils.UiTestCase;

import org.junit.After;
import org.junit.Before;
import org.uispec4j.UISpecAdapter;
import org.uispec4j.Window;
import org.uispec4j.interception.MainClassAdapter;

public abstract class PwadTestCase extends UiTestCase {
  private UISpecAdapter adapter;

  @Before
  public void ensureEnvironmentIsInitialized() throws Exception {
    FunctionalTestEnvironment.getInstance();
  }

  @After
  public void afterTest() {
    adapter = null;
  }

  public Window getMainWindow() {
    return getAdapter().getMainWindow();
  }

  private UISpecAdapter getAdapter() {
    if (adapter == null) {
      adapter = new MainClassAdapter(Main.class);
    }
    return adapter;
  }
}


text:
using System;
using System.Text;
using System.Security.Permissions;
using System.Runtime.Serialization;
using OpenADK.Library;
using OpenADK.Library.Global;
using OpenADK.Library.au.Common;

namespace OpenADK.Library.au.Student{

/// A StaffActivity
/// <remarks>
///
/// <para>Author: Generated by adkgen</para>
/// <para>Version: 2.6</para>
/// <para>Since: 2.4</para>
/// </remarks>
[Serializable]
public class StaffActivity : SifKeyedElement
{
	///
	/// Creates an instance of a StaffActivity
	///
	public StaffActivity() : base ( StudentDTD.STAFFACTIVITY ){}

	///
	/// Constructor that accepts values for all mandatory fields
	///
	///<param name="code">A Code</param>
	///
	public StaffActivity( AUCodeSetsStaffActivityType code ) : base( StudentDTD.STAFFACTIVITY )
	{
		this.SetCode( code );
	}

	///
	/// Constructor used by the .Net Serialization formatter
	///
	[SecurityPermission( SecurityAction.Demand, SerializationFormatter=true )]
	protected StaffActivity( SerializationInfo info, StreamingContext context ) : base( info, context ) {}
	///
	/// Gets the metadata fields that make up the key of this object
	///
	/// <value>
	/// an array of metadata fields that make up the object's key
	/// </value>
	public override IElementDef[] KeyFields {
		get { return new IElementDef[] { StudentDTD.STAFFACTIVITY_CODE }; }
	}

	///
	/// Gets or sets the value of the <c></c> element.
	///
	/// <value> The <c>Code</c> element of this object.</value>
	/// <remarks>
	/// <para>Version: 2.6</para>
	/// <para>Since: 2.4</para>
	/// </remarks>
	public string Code
	{
		get
		{
			return GetFieldValue( StudentDTD.STAFFACTIVITY_CODE );
		}
		set
		{
			SetField( StudentDTD.STAFFACTIVITY_CODE, value );
		}
	}

	///
	/// Sets the value of the <c></c> element.
	///
	/// <param name="val">A AUCodeSetsStaffActivityType object</param>
	/// <remarks>
	/// <para>Version: 2.6</para>
	/// <para>Since: 2.4</para>
	/// </remarks>
	public void SetCode( AUCodeSetsStaffActivityType val )
	{
		SetField( StudentDTD.STAFFACTIVITY_CODE, val );
	}

	///
	/// Gets or sets the value of the <c><OtherCodeList></c> element.
	///
	/// <value> An OtherCodeList </value>
	/// <remarks>
	/// <para>To remove the <c>OtherCodeList</c>, set <c>OtherCodeList</c> to <c>null</c></para>
	/// <para>Version: 2.6</para>
	/// <para>Since: 2.4</para>
	/// </remarks>
	public OtherCodeList OtherCodeList
	{
		get
		{
			return (OtherCodeList)GetChild( StudentDTD.STAFFACTIVITY_OTHERCODELIST);
		}
		set
		{
			RemoveChild( StudentDTD.STAFFACTIVITY_OTHERCODELIST);
			if( value != null)
			{
				AddChild( StudentDTD.STAFFACTIVITY_OTHERCODELIST, value );
			}
		}
	}

}}


text:
![action](./jfrogcli.png)

## Details

| Item              | Description                                                                  |
|-------------------|------------------------------------------------------------------------------|
| Purpose           | A wrapper around the [JFrog CLI](https://jfrog.com/getcli/)                  |
| Usage             | Executes commands against [JFrog Artifactory](https://jfrog.com/artifactory) |
| Limitations       | Only supports JFrog Artifactory, not any of the other JFrog products         |
| Base container    | [golang:alpine](https://hub.docker.com/_/golang?tab=description)             |
| Language runtimes | [Go 1.12.6](https://golang.org/doc/go1.12)                                   |
| Additional tools  | git, curl, wget                                                              |

## Usage

The action can be used in a workflow to execute actions against JFrog Artifactory. The actions must be supplied as the `args` parameter and will be prefixed with `jfrog rt` by the [script](./entrypoint.sh). This action supports three mechanisms to authenticate to Artifactory and based on which type you choose the variable `CRED` (which determines the credential type) has to be set as well.

| Authentication type | Variables to set  | Set `CRED` to |
|---------------------|-------------------|---------------|
| Login Credentials   | USER and PASSWORD | username      |
| API Key             | APIKEY            | apikey        |
| Access Token        | ACCESSTOKEN       | accesstoken   |

As you're setting these, _please make sure you set them as "secrets"_

There are two other environment variables that are required

| Variable | Description                                                                      |
|----------|----------------------------------------------------------------------------------|
| `URL`    | The Artifactory URL                                                              |
| `args`   | The command to execute, but without `jfrog rt` (like `build-publish my-build 1`) |

```hcl
action "JFrog CLI for GitHub Actions" {
  uses = "retgits/actions/jfrog-cli-go@master"
  secrets = ["USER", "PASSWORD", "URL"]
  args = ["cmd"]
  env = {
    CRED = "username"
  }
}
```


text:
/**
 * Select2 <Language> translation.
 *
 * Author: Your Name <your@email>
 */
(function ($) {
    "use strict";

    $.extend($.fn.select2.defaults, {
        formatNoMatches: function () { return "Δεν βρέθηκαν αποτελέσματα"; },
        formatInputTooShort: function (input, min) { var n = min - input.length; return "Παρακαλούμε εισάγετε " + n + " περισσότερους χαρακτήρες" + (n == 1 ? "" : "s"); },
        formatInputTooLong: function (input, max) { var n = input.length - max; return "Παρακαλούμε διαγράψτε " + n + " χαρακτήρες" + (n == 1 ? "" : "s"); },
        formatSelectionTooBig: function (limit) { return "Μπορείτε να επιλέξετε μόνο " + limit + " αντικείμενο" + (limit == 1 ? "" : "s"); },
        formatLoadMore: function (pageNumber) { return "Φόρτωση περισσότερων..."; },
        formatSearching: function () { return "Αναζήτηση..."; }
    });
})(jQuery);


text:
/**
 * Created by ihor on 8/8/16.
 */
angular.module('myApp.user.list')
    .controller('user.list.ctrl', ['userList', 'user.service', '$state', function(userList, userService, $state) {

        this.edit = function(id) {
            $state.go('user.edit', {userId:id});
        };

        this.delete = function (id) {
            userService.deleteUserById(id);
            $state.reload();
        };

        this.users = userList;
    }]);


text:
module Rugged
  class Walker
    include Enumerable
  end
end


text:
<?php
namespace RatePAY;

use RatePAY\Service\ModelMapper;
use RatePAY\Exception\ModelException;

class ModelBuilder
{

    /**
     * Current model object
     *
     * @var mixed
     */
    private $model;

    /**
     * ModelBuilder constructor. Loads requested model.
     *
     * @param null|string $modelName
     */
    public function __construct($modelName = null)
    {
        if (!is_null($modelName)) {
            $modelWithPath = ModelMapper::getFullPathRequestSubModel(ucfirst($modelName));

            if (class_exists($modelWithPath)) {
                $this->setModel($modelWithPath);
            } else {
                throw new ModelException("Model '" . $modelName . "' invalid");
            }
        } else { // If no model is set initialize with Head model
            $this->setModel(ModelMapper::getFullPathRequestSubModel("Head"));
        }
    }

    /**
     * Catches all calls.
     * If called as setter, call magic setter of current model object
     * If called as model name, start and return new ModelBuilder instance with requested model
     *
     * @param $name
     * @param $arguments
     * @return mixed|ModelBuilder
     */
    public function __call($name, $arguments)
    {
        $fieldToSet = false;
        $requestedModel = false;

        if (substr($name, 0, 3) == 'set') {
            $fieldToSet = substr($name, 3);
        } else {
            $requestedModel = $name;
        }

        if ($fieldToSet) {
            if (!key_exists($fieldToSet, $this->model->admittedFields)) {
                throw new ModelException("Field '" . $fieldToSet . "' invalid");
            }

            $this->model->commonSetter($fieldToSet, $arguments);

            return $this->model;
        } else {
            return new ModelBuilder($requestedModel);
        }
    }

    /**
     * Returns the current model object
     *
     * @return mixed
     */
    public function getModel()
    {
        return $this->model;
    }

    /**
     * Sets model as current model (expects full namespaced model path)
     *
     * @param $model
     */
    private function setModel($model)
    {
        $this->model = new $model;
    }

    /**
     * Sets and fills model instance by array
     *
     * @param $array
     */
    public function setArray($array)
    {
        foreach ($array as $field => $value) {
            if (!is_array($value)) {
                $this->model->commonSetter($field, [0 => $value]);
            } else {
                if (is_numeric($field)) {
                    $field = key($value);
                    $value = current($value);
                }
                $mb = new ModelBuilder($field);
                $mb->setArray($value);
                $this->model->commonSetter($field, [0 => $mb->getModel()]);
            }
        }
    }

    /**
     * Converts JSON input to array and uses setArray method
     *
     * @param $json
     */
    public function setJson($json) {
        $this->setArray(json_decode($json, true));
    }

}


text:
const Component = require('../Component');
const template = require('../../../templates/components/extended/Board.hbs');

const Card = require('./Card');

class Board extends Component {

	static events = {
		JOIN: 'join'
	};

	_cards = [];

	/**
	 * @param {Object} data - component's data
	 */
	constructor(data) {
		super({name: 'board', template, data});
	}

	//override!!!
	update(data) {
		this.resetInnerContainer();

		data.cards.forEach((cardData) => {
			let card = new Card(cardData);
			this.innerContainer.appendChild(card.container);

			card.on(Card.events.JOIN, (data) => {
				this.emit(Board.events.JOIN, data);
			});

			this._cards.push(card);
		});

	}

	join(data) {
		let card = this._cards.find((item) => {
			return item.data.id = data.id;
		});

		card && card.join();
	}

}

module.exports = Board;


text:
import HttpAgent from './HttpAgent';
import GenericError from '../error/GenericError';

/**
 * Implementation of the {@codelink HttpAgent} interface with internal caching
 * of completed and ongoing HTTP requests and cookie storage.
 */
export default class HttpAgentImpl extends HttpAgent {
  /**
   * Initializes the HTTP handler.
   *
   * @param {HttpProxy} proxy The low-level HTTP proxy for sending the HTTP
   *        requests.
   * @param {Cache} cache Cache to use for caching ongoing and completed
   *        requests.
   * @param {CookieStorage} cookie The cookie storage to use internally.
   * @param {Object<string, *>} config Configuration of the HTTP handler for
   *        the current application environment, specifying the various
   *        default request option values and cache option values.
   * @example
   *      http
   *          .get('url', { data: data }, {
   *              ttl: 2000,
   *              repeatRequest: 1,
   *              withCredentials: true,
   *              timeout: 2000,
   *              accept: 'application/json',
   *              language: 'en',
   *              listeners: { 'progress': callbackFunction }
   *          })
   *          .then((response) => {
   *              //resolve
   *          }
   *          .catch((error) => {
   *             //catch
   *          });
   * @example
   *      http
   *          .setDefaultHeader('Accept-Language', 'en')
   *          .clearDefaultHeaders();
   */
  constructor(proxy, cache, cookie, config) {
    super();

    /**
     * HTTP proxy, used to execute the HTTP requests.
     *
     * @type {HttpProxy}
     */
    this._proxy = proxy;

    /**
     * Internal request cache, used to cache completed request results.
     *
     * @type {Cache}
     */
    this._cache = cache;

    /**
     * Cookie storage, used to keep track of cookies received from the
     * server and send them with the subsequent requests to the server.
     *
     * @type {CookieStorage}
     */
    this._cookie = cookie;

    /**
     * Cache options.
     *
     * @type {Object<string, string>}
     */
    this._cacheOptions = config.cacheOptions;

    /**
     * Default request options.
     *
     * @type {{
     *         timeout: number,
     *         ttl: number,
     *         repeatRequest: number,
     *         headers: Object<string, string>,
     *         cache: boolean,
     *         withCredentials: boolean,
     *         fetchOptions: Object<string, *>,
     *         postProcessor: function(Object<string, *>)
     *       }}
     */
    this._defaultRequestOptions = config.defaultRequestOptions;

    /**
     * Internal request cache, used to cache ongoing requests.
     *
     * @type {Map<string, Promise<{
     *         status: number,
     *         body: *,
     *         params: {
     *           method: string,
     *           url: string,
     *           transformedUrl: string,
     *           data: Object<string, (boolean|number|string)>
     *         },
     *         headers: Object<string, string>,
     *         cached: boolean
     *       }>>}
     */
    this._internalCacheOfPromises = new Map();
  }

  /**
   * @inheritdoc
   */
  get(url, data, options = {}) {
    return this._requestWithCheckCache('get', url, data, options);
  }

  /**
   * @inheritdoc
   */
  post(url, data, options = {}) {
    return this._requestWithCheckCache(
      'post',
      url,
      data,
      Object.assign({ cache: false }, options)
    );
  }

  /**
   * @inheritdoc
   */
  put(url, data, options = {}) {
    return this._requestWithCheckCache(
      'put',
      url,
      data,
      Object.assign({ cache: false }, options)
    );
  }

  /**
   * @inheritdoc
   */
  patch(url, data, options = {}) {
    return this._requestWithCheckCache(
      'patch',
      url,
      data,
      Object.assign({ cache: false }, options)
    );
  }

  /**
   * @inheritdoc
   */
  delete(url, data, options = {}) {
    return this._requestWithCheckCache(
      'delete',
      url,
      data,
      Object.assign({ cache: false }, options)
    );
  }

  /**
   * @inheritdoc
   */
  getCacheKey(method, url, data) {
    return (
      this._cacheOptions.prefix + this._getCacheKeySuffix(method, url, data)
    );
  }

  /**
   * @inheritdoc
   */
  setDefaultHeader(header, value) {
    this._proxy.setDefaultHeader(header, value);

    return this;
  }

  /**
   * @inheritdoc
   */
  clearDefaultHeaders() {
    this._proxy.clearDefaultHeaders();

    return this;
  }

  /**
   * Check cache and if data isnt available then make real request.
   *
   * @param {string} method The HTTP method to use.
   * @param {string} url The URL to which the request should be sent.
   * @param {Object<string, (boolean|number|string|Date)>} data The data to
   *        send with the request.
   * @param {HttpAgent~RequestOptions=} options Optional request options.
   * @return {Promise<HttpAgent~Response>} A promise that resolves to the response
   *         with body parsed as JSON.
   */
  _requestWithCheckCache(method, url, data, options) {
    options = this._prepareOptions(options);

    if (options.cache) {
      let cachedData = this._getCachedData(method, url, data);

      if (cachedData) {
        return cachedData;
      }
    }

    return this._request(method, url, data, options);
  }

  /**
   * Tests whether an ongoing or completed HTTP request for the specified URL
   * and data is present in the internal cache and, if it is, the method
   * returns a promise that resolves to the response body parsed as JSON.
   *
   * The method returns {@code null} if no such request is present in the
   * cache.
   *
   * @param {string} method The HTTP method used by the request.
   * @param {string} url The URL to which the request was made.
   * @param {Object<string, (boolean|number|string|Date)>} data The data sent
   *        to the server with the request.
   * @return {?Promise<HttpAgent~Response>} A promise that will resolve to the
   *         server response with the body parsed as JSON, or {@code null} if
   *         no such request is present in the cache.
   */
  _getCachedData(method, url, data) {
    let cacheKey = this.getCacheKey(method, url, data);

    if (this._internalCacheOfPromises.has(cacheKey)) {
      return this._internalCacheOfPromises.get(cacheKey);
    }

    if (this._cache.has(cacheKey)) {
      let cacheData = this._cache.get(cacheKey);

      return Promise.resolve(cacheData);
    }

    return null;
  }

  /**
   * Sends a new HTTP request using the specified method to the specified
   * url. The request will carry the provided data as query parameters if the
   * HTTP method is GET, but the data will be sent as request body for any
   * other request method.
   *
   * @param {string} method HTTP method to use.
   * @param {string} url The URL to which the request is sent.
   * @param {Object<string, (boolean|number|string|Date)>} data The data sent
   *        with the request.
   * @param {HttpAgent~RequestOptions=} options Optional request options.
   * @return {Promise<HttpAgent~Response>} A promise that resolves to the response
   *         with the body parsed as JSON.
   */
  _request(method, url, data, options) {
    let cacheKey = this.getCacheKey(method, url, data);

    let cachePromise = this._proxy
      .request(method, url, data, options)
      .then(
        response => this._proxyResolved(response),
        error => this._proxyRejected(error)
      );

    this._internalCacheOfPromises.set(cacheKey, cachePromise);

    return cachePromise;
  }

  /**
   * Handles successful completion of an HTTP request by the HTTP proxy.
   *
   * The method also updates the internal cookie storage with the cookies
   * received from the server.
   *
   * @param {HttpAgent~Response} response Server response.
   * @return {HttpAgent~Response} The post-processed server response.
   */
  _proxyResolved(response) {
    let agentResponse = {
      status: response.status,
      body: response.body,
      params: response.params,
      headers: response.headers,
      headersRaw: response.headersRaw,
      cached: false
    };
    let cacheKey = this.getCacheKey(
      agentResponse.params.method,
      agentResponse.params.url,
      agentResponse.params.data
    );

    this._internalCacheOfPromises.delete(cacheKey);

    if (this._proxy.haveToSetCookiesManually()) {
      this._setCookiesFromResponse(agentResponse);
    }

    let { postProcessor, cache } = agentResponse.params.options;
    if (typeof postProcessor === 'function') {
      agentResponse = postProcessor(agentResponse);
    }

    if (cache) {
      this._saveAgentResponseToCache(agentResponse);
    }

    return agentResponse;
  }

  /**
   * Handles rejection of the HTTP request by the HTTP proxy. The method
   * tests whether there are any remaining tries for the request, and if
   * there are any, it attempts re-send the request.
   *
   * The method rejects the internal request promise if there are no tries
   * left.
   *
   * @param {GenericError} error The error provided by the HttpProxy,
   *        carrying the error parameters, such as the request url, data,
   *        method, options and other useful data.
   * @return {Promise<HttpAgent~Response>} A promise that will either resolve to a
   *         server's response (with the body parsed as JSON) if there are
   *         any tries left and the re-tried request succeeds, or rejects
   *         with an error containing details of the cause of the request's
   *         failure.
   */
  _proxyRejected(error) {
    let errorParams = error.getParams();
    let method = errorParams.method;
    let url = errorParams.url;
    let data = errorParams.data;

    if (errorParams.options.repeatRequest > 0) {
      errorParams.options.repeatRequest--;

      return this._request(method, url, data, errorParams.options);
    } else {
      let cacheKey = this.getCacheKey(method, url, data);
      this._internalCacheOfPromises.delete(cacheKey);

      let errorName = errorParams.errorName;
      let errorMessage = `${errorName}: ima.http.Agent:_proxyRejected: ${error.message}`;
      let agentError = new GenericError(errorMessage, errorParams);
      return Promise.reject(agentError);
    }
  }

  /**
   * Prepares the provided request options object by filling in missing
   * options with default values and addding extra options used internally.
   *
   * @param {HttpAgent~RequestOptions} options Optional request options.
   * @return {HttpAgent~RequestOptions} Request options with set filled-in
   *         default values for missing fields, and extra options used
   *         internally.
   */
  _prepareOptions(options) {
    let composedOptions = Object.assign(
      {},
      this._defaultRequestOptions,
      options
    );
    let cookieHeader = {};

    if (composedOptions.withCredentials) {
      cookieHeader = { Cookie: this._cookie.getCookiesStringForCookieHeader() };
    }

    composedOptions.headers = Object.assign(
      cookieHeader,
      this._defaultRequestOptions.headers,
      options.headers || {}
    );

    return composedOptions;
  }

  /**
   * Generates cache key suffix for an HTTP request to the specified URL with
   * the specified data.
   *
   * @param {string} method The HTTP method used by the request.
   * @param {string} url The URL to which the request is sent.
   * @param {Object<string, (boolean|number|string|Date)>} data The data sent
   *        with the request.
   * @return {string} The suffix of a cache key to use for a request to the
   *         specified URL, carrying the specified data.
   */
  _getCacheKeySuffix(method, url, data) {
    let dataQuery = '';
    if (data) {
      try {
        dataQuery = JSON.stringify(data).replace(/<\/script/gi, '<\\/script');
      } catch (error) {
        console.warn('The provided data does not have valid JSON format', data);
      }
    }
    return `${method}:${url}?${dataQuery}`;
  }

  /**
   * Sets all cookies from the {@code Set-Cookie} response header to the
   * cookie storage.
   *
   * @param {HttpAgent~Response} agentResponse The response of the server.
   */
  _setCookiesFromResponse(agentResponse) {
    if (agentResponse.headersRaw) {
      let receivedCookies = agentResponse.headersRaw.raw()['set-cookie'];

      if (receivedCookies) {
        if (!Array.isArray(receivedCookies)) {
          receivedCookies = [receivedCookies];
        }
        receivedCookies.forEach(cookieHeader => {
          this._cookie.parseFromSetCookieHeader(cookieHeader);
        });
      }
    }
  }

  /**
   * Saves the server response to the cache to be used as the result of the
   * next request of the same properties.
   *
   * @param {HttpAgent~Response} agentResponse The response of the server.
   */
  _saveAgentResponseToCache(agentResponse) {
    let cacheKey = this.getCacheKey(
      agentResponse.params.method,
      agentResponse.params.url,
      agentResponse.params.data
    );

    agentResponse.cached = true;
    let ttl = agentResponse.params.options.ttl;
    this._cache.set(cacheKey, agentResponse, ttl);
    agentResponse.cached = false;
  }
}


text:
import './setup';
import {ExpressionObserver} from '../src/expression-observer';
import {AccessScope} from '../src/ast';
import {createOverrideContext} from '../src/scope';
import {
  createObserverLocator,
	executeSharedPropertyObserverTests
} from './shared';

describe('SetterObserver', () => {
  var obj, observer;

  beforeAll(() => {
    obj = { foo: 'bar' };
    let scope = { bindingContext: obj, overrideContext: createOverrideContext(obj) };
    observer = new ExpressionObserver(scope, new AccessScope('foo', 0), createObserverLocator());
  });

  it('implements the property observer api', done => {
    executeSharedPropertyObserverTests(obj, observer, done);
  });
});