diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj
index 9f90dbd3951f05..047cbdcc98aea9 100644
--- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj
+++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj
@@ -363,6 +363,7 @@
+
diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/MD4.cs b/src/libraries/System.Net.Security/src/System/Net/Security/MD4.cs
new file mode 100644
index 00000000000000..bdf3547912c911
--- /dev/null
+++ b/src/libraries/System.Net.Security/src/System/Net/Security/MD4.cs
@@ -0,0 +1,256 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+//
+// MD4.cs - Message Digest 4 Abstract class
+//
+// Author:
+// Sebastien Pouliot (sebastien@xamarin.com)
+//
+// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System.Buffers.Binary;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Numerics;
+
+//
+// This class is a port of the Mono managed implementation of the MD4 algorithm
+// and required to support NTLM in Android only.
+// It's an implementation detail and is not intended to be a public API.
+// Assuming that NTLM would be System.Net.Security, it makes sense to put MD4 here as well.
+//
+namespace System.Net.Security
+{
+ internal sealed class MD4
+ {
+ private const int S11 = 3;
+ private const int S12 = 7;
+ private const int S13 = 11;
+ private const int S14 = 19;
+ private const int S21 = 3;
+ private const int S22 = 5;
+ private const int S23 = 9;
+ private const int S24 = 13;
+ private const int S31 = 3;
+ private const int S32 = 9;
+ private const int S33 = 11;
+ private const int S34 = 15;
+
+ internal static void HashData(ReadOnlySpan source, Span destination)
+ {
+ Debug.Assert(destination.Length == 128 >> 3);
+
+ Span buffer = stackalloc byte[64];
+ buffer.Clear();
+ // Initialize the context
+ Span state = stackalloc uint[4] { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 };
+ Span count = stackalloc uint[2] { 0, 0 };
+
+ HashCore(source, state, count, buffer);
+
+ // Save number of bits
+ Span bits = stackalloc byte[8];
+ Encode(bits, count);
+
+ // Pad out to 56 mod 64
+ uint index = ((count[0] >> 3) & 0x3f);
+ int padLen = (int)((index < 56) ? (56 - index) : (120 - index));
+ Span padding = stackalloc byte[padLen];
+ padding.Clear();
+ padding[0] = 0x80;
+ HashCore(padding, state, count, buffer);
+
+ // Append length (before padding)
+ HashCore(bits, state, count, buffer);
+
+ // Write state to destination
+ Encode(destination, state);
+ }
+
+ private static void HashCore(ReadOnlySpan input, Span state, Span count, Span buffer)
+ {
+ // Compute number of bytes mod 64
+ int index = (int)((count[0] >> 3) & 0x3F);
+ // Update number of bits
+ count[0] += (uint)(input.Length << 3);
+ if (count[0] < (input.Length << 3))
+ {
+ count[1]++;
+ }
+
+ count[1] += (uint)(input.Length >> 29);
+
+ int partLen = 64 - index;
+ int i = 0;
+ // Transform as many times as possible.
+ if (input.Length >= partLen)
+ {
+ if (index != 0)
+ {
+ input.Slice(0, partLen).CopyTo(buffer.Slice(index));
+ MD4Transform(state, buffer);
+ index = 0;
+ }
+ else
+ {
+ partLen = 0;
+ }
+
+ for (i = partLen; i + 63 < input.Length; i += 64)
+ {
+ MD4Transform(state, input.Slice(i));
+ }
+ }
+
+ // Buffer remaining input
+ input.Slice(i).CopyTo(buffer.Slice(index));
+ }
+
+ //--- private methods ---------------------------------------------------
+
+ // F, G and H are basic MD4 functions.
+ private static uint F(uint x, uint y, uint z)
+ {
+ return (uint)(((x) & (y)) | ((~x) & (z)));
+ }
+
+ private static uint G(uint x, uint y, uint z)
+ {
+ return (uint)(((x) & (y)) | ((x) & (z)) | ((y) & (z)));
+ }
+
+ private static uint H(uint x, uint y, uint z)
+ {
+ return (uint)((x) ^ (y) ^ (z));
+ }
+
+ // FF, GG and HH are transformations for rounds 1, 2 and 3.
+ // Rotation is separate from addition to prevent recomputation.
+ private static void FF(ref uint a, uint b, uint c, uint d, uint x, byte s)
+ {
+ a += F(b, c, d) + x;
+ a = BitOperations.RotateLeft(a, s);
+ }
+
+ private static void GG(ref uint a, uint b, uint c, uint d, uint x, byte s)
+ {
+ a += G(b, c, d) + x + 0x5a827999;
+ a = BitOperations.RotateLeft(a, s);
+ }
+
+ private static void HH(ref uint a, uint b, uint c, uint d, uint x, byte s)
+ {
+ a += H(b, c, d) + x + 0x6ed9eba1;
+ a = BitOperations.RotateLeft(a, s);
+ }
+
+ private static void Encode(Span output, Span input)
+ {
+ for (int i = 0, j = 0; j < output.Length; i++, j += 4)
+ {
+ BinaryPrimitives.WriteUInt32LittleEndian(output.Slice(j), input[i]);
+ }
+ }
+
+ private static void Decode(Span output, ReadOnlySpan input)
+ {
+ for (int i = 0, j = 0; i < output.Length; i++, j += 4)
+ {
+ output[i] = BinaryPrimitives.ReadUInt32LittleEndian(input.Slice(j));
+ }
+ }
+
+ private static void MD4Transform(Span state, ReadOnlySpan block)
+ {
+ uint a = state[0];
+ uint b = state[1];
+ uint c = state[2];
+ uint d = state[3];
+ Span x = stackalloc uint[16];
+
+ Decode(x, block);
+
+ // Round 1
+ FF(ref a, b, c, d, x[0], S11); // 1
+ FF(ref d, a, b, c, x[1], S12); // 2
+ FF(ref c, d, a, b, x[2], S13); // 3
+ FF(ref b, c, d, a, x[3], S14); // 4
+ FF(ref a, b, c, d, x[4], S11); // 5
+ FF(ref d, a, b, c, x[5], S12); // 6
+ FF(ref c, d, a, b, x[6], S13); // 7
+ FF(ref b, c, d, a, x[7], S14); // 8
+ FF(ref a, b, c, d, x[8], S11); // 9
+ FF(ref d, a, b, c, x[9], S12); // 10
+ FF(ref c, d, a, b, x[10], S13); // 11
+ FF(ref b, c, d, a, x[11], S14); // 12
+ FF(ref a, b, c, d, x[12], S11); // 13
+ FF(ref d, a, b, c, x[13], S12); // 14
+ FF(ref c, d, a, b, x[14], S13); // 15
+ FF(ref b, c, d, a, x[15], S14); // 16
+
+ // Round 2
+ GG(ref a, b, c, d, x[0], S21); // 17
+ GG(ref d, a, b, c, x[4], S22); // 18
+ GG(ref c, d, a, b, x[8], S23); // 19
+ GG(ref b, c, d, a, x[12], S24); // 20
+ GG(ref a, b, c, d, x[1], S21); // 21
+ GG(ref d, a, b, c, x[5], S22); // 22
+ GG(ref c, d, a, b, x[9], S23); // 23
+ GG(ref b, c, d, a, x[13], S24); // 24
+ GG(ref a, b, c, d, x[2], S21); // 25
+ GG(ref d, a, b, c, x[6], S22); // 26
+ GG(ref c, d, a, b, x[10], S23); // 27
+ GG(ref b, c, d, a, x[14], S24); // 28
+ GG(ref a, b, c, d, x[3], S21); // 29
+ GG(ref d, a, b, c, x[7], S22); // 30
+ GG(ref c, d, a, b, x[11], S23); // 31
+ GG(ref b, c, d, a, x[15], S24); // 32
+
+ HH(ref a, b, c, d, x[0], S31); // 33
+ HH(ref d, a, b, c, x[8], S32); // 34
+ HH(ref c, d, a, b, x[4], S33); // 35
+ HH(ref b, c, d, a, x[12], S34); // 36
+ HH(ref a, b, c, d, x[2], S31); // 37
+ HH(ref d, a, b, c, x[10], S32); // 38
+ HH(ref c, d, a, b, x[6], S33); // 39
+ HH(ref b, c, d, a, x[14], S34); // 40
+ HH(ref a, b, c, d, x[1], S31); // 41
+ HH(ref d, a, b, c, x[9], S32); // 42
+ HH(ref c, d, a, b, x[5], S33); // 43
+ HH(ref b, c, d, a, x[13], S34); // 44
+ HH(ref a, b, c, d, x[3], S31); // 45
+ HH(ref d, a, b, c, x[11], S32); // 46
+ HH(ref c, d, a, b, x[7], S33); // 47
+ HH(ref b, c, d, a, x[15], S34); // 48
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ }
+ }
+}
diff --git a/src/libraries/System.Net.Security/tests/UnitTests/MD4Tests.cs b/src/libraries/System.Net.Security/tests/UnitTests/MD4Tests.cs
new file mode 100644
index 00000000000000..a3667ebb896e54
--- /dev/null
+++ b/src/libraries/System.Net.Security/tests/UnitTests/MD4Tests.cs
@@ -0,0 +1,158 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO;
+using System.Net.Security;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Net.Security.Tests
+{
+ public class MD4Tests
+ {
+ // MD4("") = 31d6cfe0d16ae931b73c59d7e0c089c0
+ [Fact]
+ public void TryEncrypt_Empty()
+ {
+ ReadOnlySpan input = new byte[0];
+ ReadOnlySpan expected = new byte[] { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31, 0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 };
+ Verify(input, expected);
+ }
+
+ // // MD4("a") = bde52cb31de33e46245e05fbdbd6fb24
+ [Fact]
+ public void TryEncrypt_SingleLetter()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("a"));
+ ReadOnlySpan expected = new byte[] { 0xbd, 0xe5, 0x2c, 0xb3, 0x1d, 0xe3, 0x3e, 0x46, 0x24, 0x5e, 0x05, 0xfb, 0xdb, 0xd6, 0xfb, 0x24 };
+ Verify(input, expected);
+ }
+
+ // MD4("abc") = a448017aaf21d8525fc10ae87aa6729d
+ [Fact]
+ public void TryEncrypt_ThreeLetters()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("abc"));
+ ReadOnlySpan expected = new byte[] { 0xa4, 0x48, 0x01, 0x7a, 0xaf, 0x21, 0xd8, 0x52, 0x5f, 0xc1, 0x0a, 0xe8, 0x7a, 0xa6, 0x72, 0x9d };
+ Verify(input, expected);
+ }
+
+ // MD4("message digest") = d9130a8164549fe818874806e1c7014b
+ [Fact]
+ public void TryEncrypt_Phrase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("message digest"));
+ ReadOnlySpan expected = new byte[] { 0xd9, 0x13, 0x0a, 0x81, 0x64, 0x54, 0x9f, 0xe8, 0x18, 0x87, 0x48, 0x06, 0xe1, 0xc7, 0x01, 0x4b };
+ Verify(input, expected);
+ }
+
+ // MD4("abcdefghijklmnopqrstuvwxyz") = d79e1c308aa5bbcdeea8ed63df412da9
+ [Fact]
+ public void TryEncrypt_AlphabetInLowercase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("abcdefghijklmnopqrstuvwxyz"));
+ ReadOnlySpan expected = new byte[] { 0xd7, 0x9e, 0x1c, 0x30, 0x8a, 0xa5, 0xbb, 0xcd, 0xee, 0xa8, 0xed, 0x63, 0xdf, 0x41, 0x2d, 0xa9 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = 043f8582f241db351ce627e153e7f0e4
+ [Fact]
+ public void TryEncrypt_AlphabetInUpperLowerCasesAndNumbers()
+ {
+ ReadOnlySpan input = new ReadOnlySpan((Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")));
+ ReadOnlySpan expected = new byte[] { 0x04, 0x3f, 0x85, 0x82, 0xf2, 0x41, 0xdb, 0x35, 0x1c, 0xe6, 0x27, 0xe1, 0x53, 0xe7, 0xf0, 0xe4 };
+ Verify(input, expected);
+ }
+
+ // MD4("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = e33b4ddc9c38f2199c3e7b164fcc0536
+ [Fact]
+ public void TryEncrypt_RepeatedSequenceOfNumbers()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("12345678901234567890123456789012345678901234567890123456789012345678901234567890"));
+ ReadOnlySpan expected = new byte[] { 0xe3, 0x3b, 0x4d, 0xdc, 0x9c, 0x38, 0xf2, 0x19, 0x9c, 0x3e, 0x7b, 0x16, 0x4f, 0xcc, 0x05, 0x36 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012") = 14fdf2056bf88b3491c385d8ac4f48e6
+ // 55 bytes (padLen == 56 - 55 => 1)
+ [Fact]
+ public void TryEncrypt_55bytes_HitsEdgeCaseForPaddingLength()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012"));
+ ReadOnlySpan expected = new byte[] { 0x14, 0xfd, 0xf2, 0x05, 0x6b, 0xf8, 0x8b, 0x34, 0x91, 0xc3, 0x85, 0xd8, 0xac, 0x4f, 0x48, 0xe6 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123") = db837dbb6098a50a2d3974bc1cc76133
+ // 56 bytes (padLen == 120 - 56 => 64)
+ [Fact]
+ public void TryEncrypt_56bytes_HitsEdgeCaseForPaddingLength()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123"));
+ ReadOnlySpan expected = new byte[] { 0xdb, 0x83, 0x7d, 0xbb, 0x60, 0x98, 0xa5, 0x0a, 0x2d, 0x39, 0x74, 0xbc, 0x1c, 0xc7, 0x61, 0x33 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890") = ce64c40ecfbe896462f3c1a925884624
+ [Fact]
+ public void TryEncrypt_63bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890"));
+ ReadOnlySpan expected = new byte[] { 0xce, 0x64, 0xc4, 0x0e, 0xcf, 0xbe, 0x89, 0x64, 0x62, 0xf3, 0xc1, 0xa9, 0x25, 0x88, 0x46, 0x24 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901") = 4b0e77758d2ede1eb21d267d492ae70b
+ [Fact]
+ public void TryEncrypt_64bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901"));
+ ReadOnlySpan expected = new byte[] { 0x4b, 0x0e, 0x77, 0x75, 0x8d, 0x2e, 0xde, 0x1e, 0xb2, 0x1d, 0x26, 0x7d, 0x49, 0x2a, 0xe7, 0x0b };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789012") = 3b46ad159b3fd800d254e3c4cc71fe36
+ [Fact]
+ public void TryEncrypt_65bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789012"));
+ ReadOnlySpan expected = new byte[] { 0x3b, 0x46, 0xad, 0x15, 0x9b, 0x3f, 0xd8, 0x00, 0xd2, 0x54, 0xe3, 0xc4, 0xcc, 0x71, 0xfe, 0x36 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890") = a3e23048e4ade47a0f00fa8aed2a0248
+ [Fact]
+ public void TryEncrypt_127bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890"));
+ ReadOnlySpan expected = new byte[] { 0xa3, 0xe2, 0x30, 0x48, 0xe4, 0xad, 0xe4, 0x7a, 0x0f, 0x00, 0xfa, 0x8a, 0xed, 0x2a, 0x02, 0x48 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901") = de49da96c105be37b242f2bee86c4759
+ [Fact]
+ public void TryEncrypt_128bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901"));
+ ReadOnlySpan expected = new byte[] { 0xde, 0x49, 0xda, 0x96, 0xc1, 0x05, 0xbe, 0x37, 0xb2, 0x42, 0xf2, 0xbe, 0xe8, 0x6c, 0x47, 0x59 };
+ Verify(input, expected);
+ }
+
+ // MD4("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789012") = a6c10c320f8827d08248a2d8b124b040
+ [Fact]
+ public void TryEncrypt_129bytes_HitsEdgeCase()
+ {
+ ReadOnlySpan input = new ReadOnlySpan(Encoding.Default.GetBytes("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678901ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789012"));
+ ReadOnlySpan expected = new byte[] { 0xa6, 0xc1, 0x0c, 0x32, 0x0f, 0x88, 0x27, 0xd0, 0x82, 0x48, 0xa2, 0xd8, 0xb1, 0x24, 0xb0, 0x40 };
+ Verify(input, expected);
+ }
+
+ private void Verify(ReadOnlySpan input, ReadOnlySpan expected)
+ {
+ Span output = stackalloc byte[expected.Length];
+ MD4.HashData(input, output);
+ Assert.Equal(expected.ToArray(), output.ToArray());
+ }
+ }
+}
diff --git a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
index da4dde4e192b90..28b3f81b02b3d7 100644
--- a/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
+++ b/src/libraries/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
@@ -10,13 +10,17 @@
436
$(NoWarn);3021
- $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS
+ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-Android
annotations
true
+
+
+
+