diff --git a/FSharpPlus.sln b/FSharpPlus.sln index e0a51372d..e1b8c9ae2 100644 --- a/FSharpPlus.sln +++ b/FSharpPlus.sln @@ -131,6 +131,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEM .github\ISSUE_TEMPLATE\03_general_feature.yml = .github\ISSUE_TEMPLATE\03_general_feature.yml EndProjectSection EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharpPlus.Core", "src\FSharpPlus.Core\FSharpPlus.Core.fsproj", "{B6170B72-3F6E-485C-8B65-1320DDCBB3F2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -229,6 +231,16 @@ Global {ACBBD11E-0746-4B9D-9CED-A90FE5824CE2}.Release|Any CPU.Build.0 = Release|Any CPU {ACBBD11E-0746-4B9D-9CED-A90FE5824CE2}.Test|Any CPU.ActiveCfg = Release|Any CPU {ACBBD11E-0746-4B9D-9CED-A90FE5824CE2}.Test|Any CPU.Build.0 = Release|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Fable|Any CPU.ActiveCfg = Fable|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Fable|Any CPU.Build.0 = Fable|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Fable3|Any CPU.ActiveCfg = Debug|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Fable3|Any CPU.Build.0 = Debug|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Release|Any CPU.Build.0 = Release|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Test|Any CPU.ActiveCfg = Test|Any CPU + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2}.Test|Any CPU.Build.0 = Test|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -246,6 +258,7 @@ Global {F4D5D32F-D47A-4727-8965-80A4BB7A29DD} = {A6A6AF7D-D6E3-442D-9B1E-58CC91879BE1} {877F7C02-2F44-4314-AA00-3F5C4FFEC187} = {153F4C68-EF70-4CF2-AA64-73482955999F} {B9238B1E-C83C-4196-8C86-DA3E2CCDA309} = {153F4C68-EF70-4CF2-AA64-73482955999F} + {B6170B72-3F6E-485C-8B65-1320DDCBB3F2} = {81F5F559-FD23-4E90-9EE6-3E2A2C1A7F96} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {789B5FFA-7891-4F60-831E-42C3C5ED2C51} diff --git a/src/FSharpPlus/Extensions/Array.fs b/src/FSharpPlus.Core/Extensions/Array.fs similarity index 100% rename from src/FSharpPlus/Extensions/Array.fs rename to src/FSharpPlus.Core/Extensions/Array.fs diff --git a/src/FSharpPlus/Extensions/Async.fs b/src/FSharpPlus.Core/Extensions/Async.fs similarity index 100% rename from src/FSharpPlus/Extensions/Async.fs rename to src/FSharpPlus.Core/Extensions/Async.fs diff --git a/src/FSharpPlus/Extensions/Choice.fs b/src/FSharpPlus.Core/Extensions/Choice.fs similarity index 100% rename from src/FSharpPlus/Extensions/Choice.fs rename to src/FSharpPlus.Core/Extensions/Choice.fs diff --git a/src/FSharpPlus/Extensions/Dict.fs b/src/FSharpPlus.Core/Extensions/Dict.fs similarity index 100% rename from src/FSharpPlus/Extensions/Dict.fs rename to src/FSharpPlus.Core/Extensions/Dict.fs diff --git a/src/FSharpPlus/Extensions/Dictionary.fs b/src/FSharpPlus.Core/Extensions/Dictionary.fs similarity index 100% rename from src/FSharpPlus/Extensions/Dictionary.fs rename to src/FSharpPlus.Core/Extensions/Dictionary.fs diff --git a/src/FSharpPlus/Extensions/Enumerator.fs b/src/FSharpPlus.Core/Extensions/Enumerator.fs similarity index 100% rename from src/FSharpPlus/Extensions/Enumerator.fs rename to src/FSharpPlus.Core/Extensions/Enumerator.fs diff --git a/src/FSharpPlus/Extensions/Exception.fs b/src/FSharpPlus.Core/Extensions/Exception.fs similarity index 100% rename from src/FSharpPlus/Extensions/Exception.fs rename to src/FSharpPlus.Core/Extensions/Exception.fs diff --git a/src/FSharpPlus/Extensions/Extensions.fs b/src/FSharpPlus.Core/Extensions/Extensions.fs similarity index 100% rename from src/FSharpPlus/Extensions/Extensions.fs rename to src/FSharpPlus.Core/Extensions/Extensions.fs diff --git a/src/FSharpPlus/Extensions/HashSet.fs b/src/FSharpPlus.Core/Extensions/HashSet.fs similarity index 100% rename from src/FSharpPlus/Extensions/HashSet.fs rename to src/FSharpPlus.Core/Extensions/HashSet.fs diff --git a/src/FSharpPlus/Extensions/IList.fs b/src/FSharpPlus.Core/Extensions/IList.fs similarity index 100% rename from src/FSharpPlus/Extensions/IList.fs rename to src/FSharpPlus.Core/Extensions/IList.fs diff --git a/src/FSharpPlus/Extensions/IReadOnlyCollection.fs b/src/FSharpPlus.Core/Extensions/IReadOnlyCollection.fs similarity index 100% rename from src/FSharpPlus/Extensions/IReadOnlyCollection.fs rename to src/FSharpPlus.Core/Extensions/IReadOnlyCollection.fs diff --git a/src/FSharpPlus/Extensions/IReadOnlyDictionary.fs b/src/FSharpPlus.Core/Extensions/IReadOnlyDictionary.fs similarity index 100% rename from src/FSharpPlus/Extensions/IReadOnlyDictionary.fs rename to src/FSharpPlus.Core/Extensions/IReadOnlyDictionary.fs diff --git a/src/FSharpPlus/Extensions/IReadOnlyList.fs b/src/FSharpPlus.Core/Extensions/IReadOnlyList.fs similarity index 100% rename from src/FSharpPlus/Extensions/IReadOnlyList.fs rename to src/FSharpPlus.Core/Extensions/IReadOnlyList.fs diff --git a/src/FSharpPlus/Extensions/Lazy.fs b/src/FSharpPlus.Core/Extensions/Lazy.fs similarity index 100% rename from src/FSharpPlus/Extensions/Lazy.fs rename to src/FSharpPlus.Core/Extensions/Lazy.fs diff --git a/src/FSharpPlus/Extensions/List.fs b/src/FSharpPlus.Core/Extensions/List.fs similarity index 100% rename from src/FSharpPlus/Extensions/List.fs rename to src/FSharpPlus.Core/Extensions/List.fs diff --git a/src/FSharpPlus/Extensions/Map.fs b/src/FSharpPlus.Core/Extensions/Map.fs similarity index 100% rename from src/FSharpPlus/Extensions/Map.fs rename to src/FSharpPlus.Core/Extensions/Map.fs diff --git a/src/FSharpPlus/Extensions/Nullable.fs b/src/FSharpPlus.Core/Extensions/Nullable.fs similarity index 100% rename from src/FSharpPlus/Extensions/Nullable.fs rename to src/FSharpPlus.Core/Extensions/Nullable.fs diff --git a/src/FSharpPlus/Extensions/Option.fs b/src/FSharpPlus.Core/Extensions/Option.fs similarity index 100% rename from src/FSharpPlus/Extensions/Option.fs rename to src/FSharpPlus.Core/Extensions/Option.fs diff --git a/src/FSharpPlus/Extensions/ResizeArray.fs b/src/FSharpPlus.Core/Extensions/ResizeArray.fs similarity index 100% rename from src/FSharpPlus/Extensions/ResizeArray.fs rename to src/FSharpPlus.Core/Extensions/ResizeArray.fs diff --git a/src/FSharpPlus/Extensions/Result.fs b/src/FSharpPlus.Core/Extensions/Result.fs similarity index 100% rename from src/FSharpPlus/Extensions/Result.fs rename to src/FSharpPlus.Core/Extensions/Result.fs diff --git a/src/FSharpPlus/Extensions/Seq.fs b/src/FSharpPlus.Core/Extensions/Seq.fs similarity index 100% rename from src/FSharpPlus/Extensions/Seq.fs rename to src/FSharpPlus.Core/Extensions/Seq.fs diff --git a/src/FSharpPlus/Extensions/String.fs b/src/FSharpPlus.Core/Extensions/String.fs similarity index 100% rename from src/FSharpPlus/Extensions/String.fs rename to src/FSharpPlus.Core/Extensions/String.fs diff --git a/src/FSharpPlus/Extensions/Task.fs b/src/FSharpPlus.Core/Extensions/Task.fs similarity index 100% rename from src/FSharpPlus/Extensions/Task.fs rename to src/FSharpPlus.Core/Extensions/Task.fs diff --git a/src/FSharpPlus/Extensions/Tuple.fs b/src/FSharpPlus.Core/Extensions/Tuple.fs similarity index 100% rename from src/FSharpPlus/Extensions/Tuple.fs rename to src/FSharpPlus.Core/Extensions/Tuple.fs diff --git a/src/FSharpPlus/Extensions/ValueOption.fs b/src/FSharpPlus.Core/Extensions/ValueOption.fs similarity index 100% rename from src/FSharpPlus/Extensions/ValueOption.fs rename to src/FSharpPlus.Core/Extensions/ValueOption.fs diff --git a/src/FSharpPlus/Extensions/ValueTask.fs b/src/FSharpPlus.Core/Extensions/ValueTask.fs similarity index 97% rename from src/FSharpPlus/Extensions/ValueTask.fs rename to src/FSharpPlus.Core/Extensions/ValueTask.fs index cc0152070..5f90ae227 100644 --- a/src/FSharpPlus/Extensions/ValueTask.fs +++ b/src/FSharpPlus.Core/Extensions/ValueTask.fs @@ -10,12 +10,12 @@ module ValueTask = open System.Threading open System.Threading.Tasks - let inline internal (|Succeeded|Canceled|Faulted|) (t: ValueTask<'T>) = + let inline (|Succeeded|Canceled|Faulted|) (t: ValueTask<'T>) = if t.IsCompletedSuccessfully then Succeeded t.Result elif t.IsCanceled then Canceled - else Faulted (t.AsTask().Exception) + else Faulted (t.AsTask().Exception) - let inline internal continueTask (tcs: TaskCompletionSource<'Result>) (x: ValueTask<'t>) (k: 't -> unit) = + let inline continueTask (tcs: TaskCompletionSource<'Result>) (x: ValueTask<'t>) (k: 't -> unit) = let f = function | Succeeded r -> k r | Canceled -> tcs.SetCanceled () @@ -23,7 +23,7 @@ module ValueTask = if x.IsCompleted then f x else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x) - let inline internal continueWith (x: ValueTask<'t>) f = + let inline continueWith (x: ValueTask<'t>) f = if x.IsCompleted then f x else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x) diff --git a/src/FSharpPlus/Extensions/ValueTuple.fs b/src/FSharpPlus.Core/Extensions/ValueTuple.fs similarity index 100% rename from src/FSharpPlus/Extensions/ValueTuple.fs rename to src/FSharpPlus.Core/Extensions/ValueTuple.fs diff --git a/src/FSharpPlus.Core/FSharpPlus.Core.fsproj b/src/FSharpPlus.Core/FSharpPlus.Core.fsproj new file mode 100644 index 000000000..f9ffb3fca --- /dev/null +++ b/src/FSharpPlus.Core/FSharpPlus.Core.fsproj @@ -0,0 +1,74 @@ + + + + $(FSC_ToolPathCompilerBuild) + $(FSC_ExePathCompilerBuild) + + + FSharpPlus.Core + FSharpPlus.Core + $(VersionPrefix).0 + $(VersionPrefix).0 + b6170b72-3f6e-485c-8b65-1320ddcbb3f2 + true + --warnon:1182 $(OtherFlags) + false + false + false + false + false + false + true + Debug;Release;Fable;Test + AnyCPU + 8.0 + 6.0 + + $(DefineConstants);TEST_TRACE + $(DefineConstants);FABLE_COMPILER + $(DefineConstants);FABLE_COMPILER;FABLE_COMPILER_4 + net8.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/FSharpPlus.Core/Internals.fs b/src/FSharpPlus.Core/Internals.fs new file mode 100644 index 000000000..05c715d0f --- /dev/null +++ b/src/FSharpPlus.Core/Internals.fs @@ -0,0 +1,472 @@ +namespace FSharpPlus.Internals + +#if TEST_TRACE +module Traces = + let private effects = ResizeArray [] + let reset () = effects.Clear () + let add x = effects.Add (x) + let get () = effects |> Seq.toList +#endif + +/// +/// +/// Internal to the library - please ignore +/// +/// + +#nowarn "0042" // retype + +module internal Prelude = + open System + + let inline flip f x y = f y x + let inline const' k _ = k + let inline tupleToOption x = match x with true, value -> Some value | _ -> None + let inline opaqueId x = Unchecked.defaultof<_>; x + + let inline retype (x: 'T) : 'U = + #if !FABLE_COMPILER + (# "" x: 'U #) + #else + unbox<'U> x + #endif + +module Errors = + let exnDivByZero = new System.DivideByZeroException () :> exn + let exnNoDivision = new System.Exception "These numbers are not divisible in this domain." + let exnSqrtOfNegative = new System.Exception "Cannot calculate square root of a negative number" + let exnNoSqrt = new System.Exception "No square root defined for this value in this domain." + let exnNoSubtraction = new System.Exception "No subtraction defined for these values in this domain." + let exnUnreachable = new System.InvalidOperationException "This execution path is unreachable." + + let inline raiseIfNull paramName paramValue = + if isNull paramValue then + nullArg paramName + +module Decimal = + let inline trySqrt x = + match sign x with + | -1 -> Error Errors.exnSqrtOfNegative + | 0 -> Ok 0.M + | _ -> + let rec loop previous = + let current = (previous + x / previous) / 2.0M + if previous - current = 0.0M then current else loop current + x |> float |> sqrt |> decimal |> loop |> Ok + +module Rational = + let inline numerator x = (^F : (member Numerator : 'R) x) + let inline denominator x = (^F : (member Denominator : 'R) x) + +module BigInteger = + open System.Numerics + let trySqrtRem x = + if sign x = -1 then Error Errors.exnSqrtOfNegative + else + let rec loop previous = + let current = (previous + x / previous) >>> 1 + if abs (previous - current) < 2I then current else loop current + #if !FABLE_COMPILER + let guess = 10I ** (((int (BigInteger.Log10 (x + 1I))) + 1) >>> 1) + #else + let guess = 10I ** (((int ((x + 1I))) + 1) >>> 1) + #endif + let r = loop guess + let r2 = r * r + match compare r2 x with + | 0 -> Ok (r, 0I) + | 1 -> let root = r - 1I in Ok (root, x - root * root) + | _ -> Ok (r, x - r2) + +// BitConverter + +#nowarn "9" +#nowarn "51" +open System +open Microsoft.FSharp.NativeInterop + +type BitConverter = + /// Converts a byte into an array of bytes with length one. + static member GetBytes (value: bool) = Array.singleton (if value then 1uy else 0uy) + + #if !FABLE_COMPILER + /// Converts a char into an array of bytes with length two. + static member GetBytes (value: char, isLittleEndian: bool) = BitConverter.GetBytes (int16 value, isLittleEndian) + + /// Converts a short into an array of bytes with length + /// two. + static member GetBytes (value: int16, isLittleEndian: bool) = + if not isLittleEndian then [|byte (value >>> 8); byte value|] + else + let bytes : byte [] = Array.zeroCreate 2 + use x = fixed bytes + x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value + bytes + + /// Converts an int into an array of bytes with length + /// four. + static member GetBytes (value: int, isLittleEndian) = + if not isLittleEndian then [|byte (value >>> 24); byte (value >>> 16); byte (value >>> 8); byte value|] + else + let bytes : byte [] = Array.zeroCreate 4 + use x = fixed bytes + x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value + bytes + + /// Converts a long into an array of bytes with length + /// eight. + static member GetBytes (value: int64, isLittleEndian) = + if not isLittleEndian then [|byte (value >>> 56); byte (value >>> 48); byte (value >>> 40); byte (value >>> 32); byte (value >>> 24); byte (value >>> 16); byte (value >>> 8); byte value|] + else + let bytes : byte [] = Array.zeroCreate 8 + use x = fixed bytes + x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value + bytes + + + /// Converts an ushort into an array of bytes with + /// length two. + static member GetBytes (value: uint16, isLittleEndian) = BitConverter.GetBytes (int16 value, isLittleEndian) + + /// Converts an uint into an array of bytes with + /// length four. + static member GetBytes (value: uint32, isLittleEndian) = BitConverter.GetBytes (int value, isLittleEndian) + + /// Converts an unsigned long into an array of bytes with + /// length eight. + static member GetBytes (value: uint64, isLittleEndian) = BitConverter.GetBytes (int64 value, isLittleEndian) + + /// Converts a float into an array of bytes with length + /// four. + static member GetBytes (value: float32, isLittleEndian) = + let mutable value = value + BitConverter.GetBytes ((&&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read : int), isLittleEndian) + + /// Converts a double into an array of bytes with length + /// eight. + static member GetBytes (value: float, isLittleEndian) = + let mutable value = value + BitConverter.GetBytes ((&&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read : int64), isLittleEndian) + + /// Converts a Guid into an array of bytes with length + /// eight. + static member GetBytes (value: Guid, isLittleEndian) = + let bytes = value.ToByteArray () + if isLittleEndian then bytes + else + let p1 = Array.rev bytes.[0 .. 3] + let p2 = Array.rev bytes.[4 .. 5] + let p3 = Array.rev bytes.[6 .. 7] + let p4 = bytes.[8 .. 15] + Array.concat [p1; p2; p3; p4] + + /// Converts an array of bytes into a char. + static member ToChar (value: byte [], startIndex: int, isLittleEndian: bool) = + char <| BitConverter.ToInt16 (value, startIndex, isLittleEndian) + + /// Converts an array of bytes into a short. + static member ToInt16 (value: byte[], startIndex: int, isLittleEndian: bool) = + if isNull value then nullArg "value" + if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") + if startIndex > value.Length - 2 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" + use pbyte = fixed &value.[startIndex] + if isLittleEndian then + if startIndex % 2 = 0 then // data is aligned + pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read + else (int16 (NativePtr.get pbyte 0)) ||| (int16 (NativePtr.get pbyte 1) <<< 8) + else (int16 (NativePtr.get pbyte 0) <<< 8) ||| (int16 (NativePtr.get pbyte 1)) + + /// Converts an array of bytes into an int. + static member ToInt32 (value: byte[], startIndex: int, isLittleEndian: bool) : int = + if isNull value then nullArg "value" + if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") + if startIndex > value.Length - 4 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" + use pbyte = fixed &value.[startIndex] + if isLittleEndian then + if startIndex % 4 = 0 then // data is aligned + pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read + else (int (NativePtr.get pbyte 0)) ||| (int (NativePtr.get pbyte 1) <<< 8) ||| (int (NativePtr.get pbyte 2) <<< 16) ||| (int (NativePtr.get pbyte 3) <<< 24) + else (int (NativePtr.get pbyte 0) <<< 24) ||| (int (NativePtr.get pbyte 1) <<< 16) ||| (int (NativePtr.get pbyte 2) <<< 8) ||| (int (NativePtr.get pbyte 3)) + + /// Converts an array of bytes into a long. + static member ToInt64 (value: byte[], startIndex: int, isLittleEndian: bool) = + if isNull value then nullArg "value" + if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") + if startIndex > value.Length - 8 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" + use pbyte = fixed &value.[startIndex] + if isLittleEndian then + if startIndex % 8 = 0 then // data is aligned + pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read + else + let i1 = (int64 (NativePtr.get pbyte 0)) ||| (int64 (NativePtr.get pbyte 1) <<< 8) ||| (int64 (NativePtr.get pbyte 2) <<< 16) ||| (int64 (NativePtr.get pbyte 3) <<< 24) + let i2 = (int64 (NativePtr.get pbyte 4)) ||| (int64 (NativePtr.get pbyte 5) <<< 8) ||| (int64 (NativePtr.get pbyte 6) <<< 16) ||| (int64 (NativePtr.get pbyte 7) <<< 24) + int64 i1 ||| ((int64 i2) <<< 32) + else + let i1 = (int64 (NativePtr.get pbyte 0) <<< 24) ||| (int64 (NativePtr.get pbyte 1) <<< 16) ||| (int64 (NativePtr.get pbyte 2) <<< 8) ||| (int64 (NativePtr.get pbyte 3)) + let i2 = (int64 (NativePtr.get pbyte 4) <<< 24) ||| (int64 (NativePtr.get pbyte 5) <<< 16) ||| (int64 (NativePtr.get pbyte 6) <<< 8) ||| (int64 (NativePtr.get pbyte 7)) + i2 ||| (i1 <<< 32) + + static member ToGuid (value: byte[], startIndex: int, isLittleEndian: bool) = + if isNull value then nullArg "value" + if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") + if startIndex > value.Length - 16 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" + if isLittleEndian then + if startIndex = 0 then Guid value + else Guid value.[startIndex .. startIndex + 15] + else + let p1 = Array.rev value.[startIndex + 0 .. startIndex + 3] + let p2 = Array.rev value.[startIndex + 4 .. startIndex + 5] + let p3 = Array.rev value.[startIndex + 6 .. startIndex + 7] + let p4 = value.[startIndex + 8 .. startIndex + 15] + Guid (Array.concat [p1; p2; p3; p4]) + + /// Converts an array of bytes into an ushort. + /// + static member ToUInt16 (value: byte [], startIndex, isLittleEndian) = uint16 <| BitConverter.ToInt16 (value, startIndex, isLittleEndian) + + /// Converts an array of bytes into an uint. + /// + static member ToUInt32 (value: byte [], startIndex, isLittleEndian) = uint32 <| BitConverter.ToInt32 (value, startIndex, isLittleEndian) + + /// Converts an array of bytes into an unsigned long. + /// + static member ToUInt64 (value: byte [], startIndex, isLittleEndian) = uint64 <| BitConverter.ToInt64 (value, startIndex, isLittleEndian) + + /// Converts an array of bytes into a float. + static member ToSingle (value: byte [], startIndex, isLittleEndian) : float32 = + let mutable value = BitConverter.ToInt32 (value, startIndex, isLittleEndian) + &&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read + + /// Converts an array of bytes into a double. + static member ToDouble (value: byte [], startIndex, isLittleEndian) : float = + let mutable value = BitConverter.ToInt64 (value, startIndex, isLittleEndian) + &&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read + #endif + + static member private GetHexValue (i: int) = + Diagnostics.Debug.Assert (i >= 0 && i < 16, "i is out of range.") + if i < 10 then char i + '0' + else char (i - 10) + 'A' + + /// Converts an array of bytes into a String. + static member ToString (value: byte [], startIndex, length) = + if isNull value then nullArg "value" + let arrayLen = value.Length + if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_StartIndex") + let realLength = length + if realLength < 0 then raise <| new ArgumentOutOfRangeException ("length", "ArgumentOutOfRange_GenericPositive") + if startIndex > arrayLen - realLength then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" + if realLength = 0 then String.Empty + else + let chArray = Array.zeroCreate (realLength * 3) + let mutable index = startIndex + for i in 0 .. 3 .. (3 * realLength) - 1 do + let b = int value.[index] + index <- index + 1 + chArray.[i] <- BitConverter.GetHexValue (b / 16) + chArray.[i + 1] <- BitConverter.GetHexValue (b % 16) + chArray.[i + 2] <- '-' + + // We don't need the last '-' character + String (chArray, 0, chArray.Length - 1) + + /// Converts an array of bytes into a String. + static member ToString (value: byte []) = + if isNull value then nullArg "value" + BitConverter.ToString (value, 0, value.Length) + + /// Converts an array of bytes into a String. + static member ToString (value: byte [], startIndex) = + if isNull value then nullArg "value" + BitConverter.ToString (value, startIndex, value.Length - startIndex) + +#if !FABLE_COMPILER +// findSliceIndex +module FindSliceIndex = + open System.Collections.Generic + #if !FABLE_COMPILER + open System.Linq + let seqImpl (slice: seq<_>) (source: seq<_>) = + let cache = Queue<_>() + // we assume the slice is finite (otherwise it cannot be searched) + let slice = slice |> Seq.toArray + use sourceEnumerator = source.GetEnumerator() + // we also assume either the source is finite or it actually contains the slice. + let rec go index = + if sourceEnumerator.MoveNext() then + cache.Enqueue sourceEnumerator.Current + if cache.Count = slice.Length then + if cache.SequenceEqual slice then index - slice.Length + 1 + else + cache.Dequeue() |> ignore + go (index + 1) + else go (index + 1) + else -1 + go 0 + let sequenceEqual (a: _ seq) (b: _ seq) = a.SequenceEqual b + #else + let internal sequenceEqual (a: _ seq) (b: _ seq) :bool = Seq.compareWith Operators.compare a b = 0 + module internal Q= + type queue<'a> = | Queue of 'a list * 'a list + + let empty = Queue([], []) + + let enqueue q e = match q with | Queue(fs, bs) -> Queue(e :: fs, bs) + + let dequeue = + function + | Queue([], []) as q -> None, q + | Queue(fs, b :: bs) -> Some b, Queue(fs, bs) + | Queue(fs, []) -> + let bs = List.rev fs + Some bs.Head, Queue([], bs.Tail) + let toSeq = + function + | Queue([], []) -> Seq.empty + | Queue(fs, bs) -> bs @ List.rev fs |> List.toSeq + let length = + function + | Queue([], []) -> 0 + | Queue(fs, bs) -> List.length bs + List.length fs + open System.Collections + type Queue<'T> () = + let mutable q : Q.queue<'T> = Q.empty + interface IEnumerable<'T> with + member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() + interface IEnumerable with + member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() :> IEnumerator + member _.Enqueue (v) = q <- Q.enqueue q v + member _.Dequeue () = + let (dequeued, next) = Q.dequeue q in q <- next + match dequeued with | Some v -> v | None -> failwith "Empty queue!" + member _.Count = Q.length q + #endif + + let listImpl (slice: _ list) (source: _ list) = + let cache = Queue<_>() + // List.length is O(n) + let sliceLength = slice.Length + let rec go index source = + match source with + | h :: t -> + cache.Enqueue h + if cache.Count = sliceLength then + if sequenceEqual cache slice then index - sliceLength + 1 + else + cache.Dequeue() |> ignore + go (index + 1) t + else go (index + 1) t + | [] -> -1 + go 0 source + + let arrayImpl (slice: _ []) (source: _ []) = + let cache = Queue<_>() + let rec go index = + if index < source.Length then + let h = source.[index] + cache.Enqueue h + if cache.Count = slice.Length then + if sequenceEqual cache slice then index - slice.Length + 1 + else + cache.Dequeue() |> ignore + go (index + 1) + else go (index + 1) + else -1 + go 0 + +module FindLastSliceIndex = + open System.Collections.Generic + #if !FABLE_COMPILER + open System.Linq + let seqImpl (slice: seq<_>) (source: seq<_>) = + let cache = Queue<_>() + // we assume the slice is finite (otherwise it cannot be searched) + let slice = slice |> Seq.toArray + use sourceEnumerator = source.GetEnumerator() + // we also assume the source is finite + let rec go last index = + if sourceEnumerator.MoveNext() then + cache.Enqueue sourceEnumerator.Current + if cache.Count = slice.Length then + let last = if cache.SequenceEqual slice then index - slice.Length + 1 else last + cache.Dequeue() |> ignore + go last (index + 1) + else go last (index + 1) + else last + go -1 0 + let sequenceEqual (a: _ seq) (b: _ seq) = a.SequenceEqual b + #else + let internal sequenceEqual (a: _ seq) (b: _ seq) :bool = Seq.compareWith Operators.compare a b = 0 + module internal Q = + type queue<'a> = | Queue of 'a list * 'a list + + let empty = Queue([], []) + + let enqueue q e = match q with | Queue(fs, bs) -> Queue(e :: fs, bs) + + let dequeue = + function + | Queue([], []) as q -> None, q + | Queue(fs, b :: bs) -> Some b, Queue(fs, bs) + | Queue(fs, []) -> + let bs = List.rev fs + Some bs.Head, Queue([], bs.Tail) + let toSeq = + function + | Queue([], []) -> Seq.empty + | Queue(fs, bs) -> bs @ List.rev fs |> List.toSeq + let length = + function + | Queue([], []) -> 0 + | Queue(fs, bs) -> List.length bs + List.length fs + open System.Collections + type Queue<'T> () = + let mutable q : Q.queue<'T> = Q.empty + interface IEnumerable<'T> with + member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() + interface IEnumerable with + member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() :> IEnumerator + member _.Enqueue (v) = q <- Q.enqueue q v + member _.Dequeue () = + let (dequeued, next) = Q.dequeue q in q <- next + match dequeued with | Some v -> v | None -> invalidOp "Empty queue!" + member _.Count = Q.length q + #endif + + let listImpl (slice: _ list) (source: _ list) = + let cache = Queue<_>() + // List.length is O(n) + let sliceLength = slice.Length + let rec go last index source = + match source with + | h :: t -> + cache.Enqueue h + if cache.Count = sliceLength then + let last = if sequenceEqual cache slice then index - sliceLength + 1 else last + cache.Dequeue() |> ignore + go last (index + 1) t + else go last (index + 1) t + | [] -> last + go -1 0 source + + let arrayImpl (slice: _ []) (source: _ []) = + let revSlice = slice |> Array.rev + let cache = Queue<_>() + let rec go index = + if index >= 0 then + let h = source.[index] + cache.Enqueue h + if cache.Count = slice.Length then + if sequenceEqual cache revSlice then index + else + cache.Dequeue() |> ignore + go (index - 1) + else go (index - 1) + else -1 + go (source.Length - 1) +#endif + +#if FABLE_COMPILER +exception AggregateException of Exception seq + +#endif diff --git a/src/FSharpPlus/FSharpPlus.fsproj b/src/FSharpPlus/FSharpPlus.fsproj index 5ef7715ed..385164f4b 100644 --- a/src/FSharpPlus/FSharpPlus.fsproj +++ b/src/FSharpPlus/FSharpPlus.fsproj @@ -32,33 +32,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -130,4 +103,8 @@ + + + + diff --git a/src/FSharpPlus/Internals.fs b/src/FSharpPlus/Internals.fs index d3bf01922..11cc73ae1 100644 --- a/src/FSharpPlus/Internals.fs +++ b/src/FSharpPlus/Internals.fs @@ -1,13 +1,5 @@ namespace FSharpPlus.Internals -#if TEST_TRACE -module Traces = - let private effects = ResizeArray [] - let reset () = effects.Clear () - let add x = effects.Add (x) - let get () = effects |> Seq.toList -#endif - /// /// /// Internal to the library - please ignore @@ -41,53 +33,6 @@ module internal Prelude = [] module internal Implicit = let inline Invoke (x: ^t) = ((^R or ^t) : (static member op_Implicit : ^t -> ^R) x) : ^R -module Errors = - let exnDivByZero = new System.DivideByZeroException () :> exn - let exnNoDivision = new System.Exception "These numbers are not divisible in this domain." - let exnSqrtOfNegative = new System.Exception "Cannot calculate square root of a negative number" - let exnNoSqrt = new System.Exception "No square root defined for this value in this domain." - let exnNoSubtraction = new System.Exception "No subtraction defined for these values in this domain." - let exnUnreachable = new System.InvalidOperationException "This execution path is unreachable." - - let inline raiseIfNull paramName paramValue = - if isNull paramValue then - nullArg paramName - -module Decimal = - let inline trySqrt x = - match sign x with - | -1 -> Error Errors.exnSqrtOfNegative - | 0 -> Ok 0.M - | _ -> - let rec loop previous = - let current = (previous + x / previous) / 2.0M - if previous - current = 0.0M then current else loop current - x |> float |> sqrt |> decimal |> loop |> Ok - -module Rational = - let inline numerator x = (^F : (member Numerator : 'R) x) - let inline denominator x = (^F : (member Denominator : 'R) x) - -module BigInteger = - open System.Numerics - let trySqrtRem x = - if sign x = -1 then Error Errors.exnSqrtOfNegative - else - let rec loop previous = - let current = (previous + x / previous) >>> 1 - if abs (previous - current) < 2I then current else loop current - #if !FABLE_COMPILER - let guess = 10I ** (((int (BigInteger.Log10 (x + 1I))) + 1) >>> 1) - #else - let guess = 10I ** (((int ((x + 1I))) + 1) >>> 1) - #endif - let r = loop guess - let r2 = r * r - match compare r2 x with - | 0 -> Ok (r, 0I) - | 1 -> let root = r - 1I in Ok (root, x - root * root) - | _ -> Ok (r, x - r2) - module Constraints = /// Constrain 't to be a nested tuple of <'t1,'t2,'t3,'t4,'t5,'t6,'t7,'tr> let inline whenNestedTuple (t: 't) = @@ -138,396 +83,3 @@ type Set2<'T when 'T: comparison >() = class end type NonEmptySeq2<'T> = inherit System.Collections.Generic.IEnumerable<'T> abstract member First: 'T - -// BitConverter - -#nowarn "9" -#nowarn "51" -open System -open Microsoft.FSharp.NativeInterop - -type BitConverter = - /// Converts a byte into an array of bytes with length one. - static member GetBytes (value: bool) = Array.singleton (if value then 1uy else 0uy) - - #if !FABLE_COMPILER - /// Converts a char into an array of bytes with length two. - static member GetBytes (value: char, isLittleEndian: bool) = BitConverter.GetBytes (int16 value, isLittleEndian) - - /// Converts a short into an array of bytes with length - /// two. - static member GetBytes (value: int16, isLittleEndian: bool) = - if not isLittleEndian then [|byte (value >>> 8); byte value|] - else - let bytes : byte [] = Array.zeroCreate 2 - use x = fixed bytes - x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value - bytes - - /// Converts an int into an array of bytes with length - /// four. - static member GetBytes (value: int, isLittleEndian) = - if not isLittleEndian then [|byte (value >>> 24); byte (value >>> 16); byte (value >>> 8); byte value|] - else - let bytes : byte [] = Array.zeroCreate 4 - use x = fixed bytes - x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value - bytes - - /// Converts a long into an array of bytes with length - /// eight. - static member GetBytes (value: int64, isLittleEndian) = - if not isLittleEndian then [|byte (value >>> 56); byte (value >>> 48); byte (value >>> 40); byte (value >>> 32); byte (value >>> 24); byte (value >>> 16); byte (value >>> 8); byte value|] - else - let bytes : byte [] = Array.zeroCreate 8 - use x = fixed bytes - x |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.write <| value - bytes - - - /// Converts an ushort into an array of bytes with - /// length two. - static member GetBytes (value: uint16, isLittleEndian) = BitConverter.GetBytes (int16 value, isLittleEndian) - - /// Converts an uint into an array of bytes with - /// length four. - static member GetBytes (value: uint32, isLittleEndian) = BitConverter.GetBytes (int value, isLittleEndian) - - /// Converts an unsigned long into an array of bytes with - /// length eight. - static member GetBytes (value: uint64, isLittleEndian) = BitConverter.GetBytes (int64 value, isLittleEndian) - - /// Converts a float into an array of bytes with length - /// four. - static member GetBytes (value: float32, isLittleEndian) = - let mutable value = value - BitConverter.GetBytes ((&&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read : int), isLittleEndian) - - /// Converts a double into an array of bytes with length - /// eight. - static member GetBytes (value: float, isLittleEndian) = - let mutable value = value - BitConverter.GetBytes ((&&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read : int64), isLittleEndian) - - /// Converts a Guid into an array of bytes with length - /// eight. - static member GetBytes (value: Guid, isLittleEndian) = - let bytes = value.ToByteArray () - if isLittleEndian then bytes - else - let p1 = Array.rev bytes.[0 .. 3] - let p2 = Array.rev bytes.[4 .. 5] - let p3 = Array.rev bytes.[6 .. 7] - let p4 = bytes.[8 .. 15] - Array.concat [p1; p2; p3; p4] - - /// Converts an array of bytes into a char. - static member ToChar (value: byte [], startIndex: int, isLittleEndian: bool) = - char <| BitConverter.ToInt16 (value, startIndex, isLittleEndian) - - /// Converts an array of bytes into a short. - static member ToInt16 (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" - if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") - if startIndex > value.Length - 2 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" - use pbyte = fixed &value.[startIndex] - if isLittleEndian then - if startIndex % 2 = 0 then // data is aligned - pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read - else (int16 (NativePtr.get pbyte 0)) ||| (int16 (NativePtr.get pbyte 1) <<< 8) - else (int16 (NativePtr.get pbyte 0) <<< 8) ||| (int16 (NativePtr.get pbyte 1)) - - /// Converts an array of bytes into an int. - static member ToInt32 (value: byte[], startIndex: int, isLittleEndian: bool) : int = - if isNull value then nullArg "value" - if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") - if startIndex > value.Length - 4 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" - use pbyte = fixed &value.[startIndex] - if isLittleEndian then - if startIndex % 4 = 0 then // data is aligned - pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read - else (int (NativePtr.get pbyte 0)) ||| (int (NativePtr.get pbyte 1) <<< 8) ||| (int (NativePtr.get pbyte 2) <<< 16) ||| (int (NativePtr.get pbyte 3) <<< 24) - else (int (NativePtr.get pbyte 0) <<< 24) ||| (int (NativePtr.get pbyte 1) <<< 16) ||| (int (NativePtr.get pbyte 2) <<< 8) ||| (int (NativePtr.get pbyte 3)) - - /// Converts an array of bytes into a long. - static member ToInt64 (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" - if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") - if startIndex > value.Length - 8 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" - use pbyte = fixed &value.[startIndex] - if isLittleEndian then - if startIndex % 8 = 0 then // data is aligned - pbyte |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read - else - let i1 = (int64 (NativePtr.get pbyte 0)) ||| (int64 (NativePtr.get pbyte 1) <<< 8) ||| (int64 (NativePtr.get pbyte 2) <<< 16) ||| (int64 (NativePtr.get pbyte 3) <<< 24) - let i2 = (int64 (NativePtr.get pbyte 4)) ||| (int64 (NativePtr.get pbyte 5) <<< 8) ||| (int64 (NativePtr.get pbyte 6) <<< 16) ||| (int64 (NativePtr.get pbyte 7) <<< 24) - int64 i1 ||| ((int64 i2) <<< 32) - else - let i1 = (int64 (NativePtr.get pbyte 0) <<< 24) ||| (int64 (NativePtr.get pbyte 1) <<< 16) ||| (int64 (NativePtr.get pbyte 2) <<< 8) ||| (int64 (NativePtr.get pbyte 3)) - let i2 = (int64 (NativePtr.get pbyte 4) <<< 24) ||| (int64 (NativePtr.get pbyte 5) <<< 16) ||| (int64 (NativePtr.get pbyte 6) <<< 8) ||| (int64 (NativePtr.get pbyte 7)) - i2 ||| (i1 <<< 32) - - static member ToGuid (value: byte[], startIndex: int, isLittleEndian: bool) = - if isNull value then nullArg "value" - if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_Index") - if startIndex > value.Length - 16 then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" - if isLittleEndian then - if startIndex = 0 then Guid value - else Guid value.[startIndex .. startIndex + 15] - else - let p1 = Array.rev value.[startIndex + 0 .. startIndex + 3] - let p2 = Array.rev value.[startIndex + 4 .. startIndex + 5] - let p3 = Array.rev value.[startIndex + 6 .. startIndex + 7] - let p4 = value.[startIndex + 8 .. startIndex + 15] - Guid (Array.concat [p1; p2; p3; p4]) - - /// Converts an array of bytes into an ushort. - /// - static member ToUInt16 (value: byte [], startIndex, isLittleEndian) = uint16 <| BitConverter.ToInt16 (value, startIndex, isLittleEndian) - - /// Converts an array of bytes into an uint. - /// - static member ToUInt32 (value: byte [], startIndex, isLittleEndian) = uint32 <| BitConverter.ToInt32 (value, startIndex, isLittleEndian) - - /// Converts an array of bytes into an unsigned long. - /// - static member ToUInt64 (value: byte [], startIndex, isLittleEndian) = uint64 <| BitConverter.ToInt64 (value, startIndex, isLittleEndian) - - /// Converts an array of bytes into a float. - static member ToSingle (value: byte [], startIndex, isLittleEndian) : float32 = - let mutable value = BitConverter.ToInt32 (value, startIndex, isLittleEndian) - &&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read - - /// Converts an array of bytes into a double. - static member ToDouble (value: byte [], startIndex, isLittleEndian) : float = - let mutable value = BitConverter.ToInt64 (value, startIndex, isLittleEndian) - &&value |> NativePtr.toNativeInt |> NativePtr.ofNativeInt |> NativePtr.read - #endif - - static member private GetHexValue (i: int) = - Diagnostics.Debug.Assert (i >= 0 && i < 16, "i is out of range.") - if i < 10 then char i + '0' - else char (i - 10) + 'A' - - /// Converts an array of bytes into a String. - static member ToString (value: byte [], startIndex, length) = - if isNull value then nullArg "value" - let arrayLen = value.Length - if startIndex >= value.Length then raise <| new ArgumentOutOfRangeException ("startIndex", "ArgumentOutOfRange_StartIndex") - let realLength = length - if realLength < 0 then raise <| new ArgumentOutOfRangeException ("length", "ArgumentOutOfRange_GenericPositive") - if startIndex > arrayLen - realLength then raise <| new ArgumentException "Arg_ArrayPlusOffTooSmall" - if realLength = 0 then String.Empty - else - let chArray = Array.zeroCreate (realLength * 3) - let mutable index = startIndex - for i in 0 .. 3 .. (3 * realLength) - 1 do - let b = int value.[index] - index <- index + 1 - chArray.[i] <- BitConverter.GetHexValue (b / 16) - chArray.[i + 1] <- BitConverter.GetHexValue (b % 16) - chArray.[i + 2] <- '-' - - // We don't need the last '-' character - String (chArray, 0, chArray.Length - 1) - - /// Converts an array of bytes into a String. - static member ToString (value: byte []) = - if isNull value then nullArg "value" - BitConverter.ToString (value, 0, value.Length) - - /// Converts an array of bytes into a String. - static member ToString (value: byte [], startIndex) = - if isNull value then nullArg "value" - BitConverter.ToString (value, startIndex, value.Length - startIndex) - -#if !FABLE_COMPILER -// findSliceIndex -module FindSliceIndex = - open System.Collections.Generic - #if !FABLE_COMPILER - open System.Linq - let seqImpl (slice: seq<_>) (source: seq<_>) = - let cache = Queue<_>() - // we assume the slice is finite (otherwise it cannot be searched) - let slice = slice |> Seq.toArray - use sourceEnumerator = source.GetEnumerator() - // we also assume either the source is finite or it actually contains the slice. - let rec go index = - if sourceEnumerator.MoveNext() then - cache.Enqueue sourceEnumerator.Current - if cache.Count = slice.Length then - if cache.SequenceEqual slice then index - slice.Length + 1 - else - cache.Dequeue() |> ignore - go (index + 1) - else go (index + 1) - else -1 - go 0 - let sequenceEqual (a: _ seq) (b: _ seq) = a.SequenceEqual b - #else - let internal sequenceEqual (a: _ seq) (b: _ seq) :bool = Seq.compareWith Operators.compare a b = 0 - module internal Q= - type queue<'a> = | Queue of 'a list * 'a list - - let empty = Queue([], []) - - let enqueue q e = match q with | Queue(fs, bs) -> Queue(e :: fs, bs) - - let dequeue = - function - | Queue([], []) as q -> None, q - | Queue(fs, b :: bs) -> Some b, Queue(fs, bs) - | Queue(fs, []) -> - let bs = List.rev fs - Some bs.Head, Queue([], bs.Tail) - let toSeq = - function - | Queue([], []) -> Seq.empty - | Queue(fs, bs) -> bs @ List.rev fs |> List.toSeq - let length = - function - | Queue([], []) -> 0 - | Queue(fs, bs) -> List.length bs + List.length fs - open System.Collections - type Queue<'T> () = - let mutable q : Q.queue<'T> = Q.empty - interface IEnumerable<'T> with - member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() - interface IEnumerable with - member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() :> IEnumerator - member _.Enqueue (v) = q <- Q.enqueue q v - member _.Dequeue () = - let (dequeued, next) = Q.dequeue q in q <- next - match dequeued with | Some v -> v | None -> failwith "Empty queue!" - member _.Count = Q.length q - #endif - - let listImpl (slice: _ list) (source: _ list) = - let cache = Queue<_>() - // List.length is O(n) - let sliceLength = slice.Length - let rec go index source = - match source with - | h :: t -> - cache.Enqueue h - if cache.Count = sliceLength then - if sequenceEqual cache slice then index - sliceLength + 1 - else - cache.Dequeue() |> ignore - go (index + 1) t - else go (index + 1) t - | [] -> -1 - go 0 source - - let arrayImpl (slice: _ []) (source: _ []) = - let cache = Queue<_>() - let rec go index = - if index < source.Length then - let h = source.[index] - cache.Enqueue h - if cache.Count = slice.Length then - if sequenceEqual cache slice then index - slice.Length + 1 - else - cache.Dequeue() |> ignore - go (index + 1) - else go (index + 1) - else -1 - go 0 - -module FindLastSliceIndex = - open System.Collections.Generic - #if !FABLE_COMPILER - open System.Linq - let seqImpl (slice: seq<_>) (source: seq<_>) = - let cache = Queue<_>() - // we assume the slice is finite (otherwise it cannot be searched) - let slice = slice |> Seq.toArray - use sourceEnumerator = source.GetEnumerator() - // we also assume the source is finite - let rec go last index = - if sourceEnumerator.MoveNext() then - cache.Enqueue sourceEnumerator.Current - if cache.Count = slice.Length then - let last = if cache.SequenceEqual slice then index - slice.Length + 1 else last - cache.Dequeue() |> ignore - go last (index + 1) - else go last (index + 1) - else last - go -1 0 - let sequenceEqual (a: _ seq) (b: _ seq) = a.SequenceEqual b - #else - let internal sequenceEqual (a: _ seq) (b: _ seq) :bool = Seq.compareWith Operators.compare a b = 0 - module internal Q = - type queue<'a> = | Queue of 'a list * 'a list - - let empty = Queue([], []) - - let enqueue q e = match q with | Queue(fs, bs) -> Queue(e :: fs, bs) - - let dequeue = - function - | Queue([], []) as q -> None, q - | Queue(fs, b :: bs) -> Some b, Queue(fs, bs) - | Queue(fs, []) -> - let bs = List.rev fs - Some bs.Head, Queue([], bs.Tail) - let toSeq = - function - | Queue([], []) -> Seq.empty - | Queue(fs, bs) -> bs @ List.rev fs |> List.toSeq - let length = - function - | Queue([], []) -> 0 - | Queue(fs, bs) -> List.length bs + List.length fs - open System.Collections - type Queue<'T> () = - let mutable q : Q.queue<'T> = Q.empty - interface IEnumerable<'T> with - member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() - interface IEnumerable with - member _.GetEnumerator () = let s = Q.toSeq q in s.GetEnumerator() :> IEnumerator - member _.Enqueue (v) = q <- Q.enqueue q v - member _.Dequeue () = - let (dequeued, next) = Q.dequeue q in q <- next - match dequeued with | Some v -> v | None -> invalidOp "Empty queue!" - member _.Count = Q.length q - #endif - - let listImpl (slice: _ list) (source: _ list) = - let cache = Queue<_>() - // List.length is O(n) - let sliceLength = slice.Length - let rec go last index source = - match source with - | h :: t -> - cache.Enqueue h - if cache.Count = sliceLength then - let last = if sequenceEqual cache slice then index - sliceLength + 1 else last - cache.Dequeue() |> ignore - go last (index + 1) t - else go last (index + 1) t - | [] -> last - go -1 0 source - - let arrayImpl (slice: _ []) (source: _ []) = - let revSlice = slice |> Array.rev - let cache = Queue<_>() - let rec go index = - if index >= 0 then - let h = source.[index] - cache.Enqueue h - if cache.Count = slice.Length then - if sequenceEqual cache revSlice then index - else - cache.Dequeue() |> ignore - go (index - 1) - else go (index - 1) - else -1 - go (source.Length - 1) -#endif - -#if FABLE_COMPILER -exception AggregateException of Exception seq - -#endif