' *
' * The project site is at: http://sourceforge.jp/projects/fishbornas/
' *
' * First author tiritomato 2012.
' *
' * Distributed under the FishbornArchiveShelf License (See
' *  file "Licenses/License.txt" contained in a project, or the following link.
' *  http://sourceforge.jp/projects/fishbornas/scm/svn/blobs/head/trunk/Licenses/License.txt)
' *
' * 2012.06.07 Initial Revision (tiritomato)
' *

Partial Public Class Logic
    Partial Public Class Math
        Partial Public Class Statistics

            Public Shared Function GetDawnSampling(Of TValue)(ByVal SrcArray() As TValue, ByVal DawnSampleCount As Integer, Optional ByVal IsEdgeSampling As Boolean = False) As TValue()
                If SrcArray Is Nothing Or DawnSampleCount <= 0 Then Return Nothing
                If SrcArray.Length <= DawnSampleCount + IIf(IsEdgeSampling, 0, 2) Then Return SrcArray
                Dim ret(DawnSampleCount - 1) As TValue
                If IsEdgeSampling Then
                    Dim param As Double = CDbl(SrcArray.Length - 1) / CDbl(ret.Length - 1)
                    For idx As Integer = 0 To ret.Length - 1
                        ret(idx) = SrcArray(Fix(param * CDbl(idx)))
                    Next
                Else
                    Dim param As Double = CDbl(SrcArray.Length - 1) / CDbl(ret.Length + 1)
                    For idx As Integer = 0 To ret.Length - 1
                        ret(idx) = SrcArray(Fix(param * CDbl(idx + 1)))
                    Next
                End If
                Return ret
            End Function

            Public Class AutoHistogram(Of TValue As {IComparable, IConvertible})

                Public Delegate Function GetBinCountDelegate(ByVal SampleCount As Integer, ByVal MaxValue As TValue, ByVal MinValue As TValue) As Integer

                Public Class Bin
                    Inherits System.Collections.ObjectModel.ReadOnlyCollection(Of TValue)

                    Public Sub New(ByVal src As System.Collections.Generic.List(Of TValue))
                        MyBase.New(src)
                    End Sub
                    Public Function Average() As Double
                        Return Logic.Math.Average(Me)
                    End Function
                    Public Function Max() As TValue
                        Return Logic.Math.Max(Me)
                    End Function
                    Public Function Min() As TValue
                        Return Logic.Math.Min(Me)
                    End Function

                    Public Class ReadOnlyCollection
                        Inherits System.Collections.ObjectModel.ReadOnlyCollection(Of Bin)

                        Public Sub New(ByVal src As System.Collections.Generic.List(Of Bin))
                            MyBase.New(src)
                        End Sub

                        Public Function Average() As Double
                            Dim ct As Integer = 0, SumValue As Double = 0
                            For Each b As Bin In Me
                                If Not b Is Nothing AndAlso 0 < b.Count Then
                                    SumValue += b.Average()
                                    ct += 1
                                End If
                            Next
                            If 0 < ct Then SumValue /= CDbl(ct)
                            Return SumValue
                        End Function

                        Public Function Max() As TValue
                            For idx As Integer = Count - 1 To 0 Step -1
                                If Not Me(idx) Is Nothing AndAlso 0 < Me(idx).Count Then Return Me(idx).Max
                            Next
                        End Function

                        Public Function Min() As TValue
                            For idx As Integer = 0 To Count - 1
                                If Not Me(idx) Is Nothing AndAlso 0 < Me(idx).Count Then Return Me(idx).Min
                            Next
                        End Function

                    End Class ' Bin.ReadOnlyCollection

                End Class ' Bin

                Public Sub Clear()
                    ary = Nothing
                    aryModeBins = Nothing
                End Sub

                Public Sub Initialize(ByVal collections As System.Collections.Generic.IEnumerable(Of TValue), Optional ByVal collections_count As Integer = 0, Optional ByVal Bins As Integer = 0, Optional ByVal d As GetBinCountDelegate = Nothing)

                    Clear()
                    If collections Is Nothing Then Return

                    If collections_count < 1 Then
                        collections_count = 0
                        For Each v As TValue In collections
                            collections_count += 1
                        Next
                    End If
                    If collections_count = 0 Then Return

                    Dim MaxValue As TValue = Logic.Math.Max(collections)
                    Dim MinValue As TValue = Logic.Math.Min(collections)

                    Dim gram() As System.Collections.Generic.List(Of TValue)
                    Dim BinCount As Integer
                    Dim MaxBinLength As Integer = 0

                    If 0 < Bins Then
                        BinCount = Bins
                    Else
                        BinCount = GetBinCount(collections_count, MaxValue, MinValue, d)
                    End If

                    If 1 < BinCount Then

                        Dim MaxDouble As Double = MaxValue.ToDouble(Globalization.CultureInfo.CurrentCulture)
                        Dim MinDouble As Double = MinValue.ToDouble(Globalization.CultureInfo.CurrentCulture)
                        Dim SclDouble As Double = CDbl(BinCount) / (MaxDouble - MinDouble)
                        ReDim gram(BinCount - 1)
                        For Each _v As TValue In collections
                            Dim v As Double = _v.ToDouble(Globalization.CultureInfo.CurrentCulture)
                            Dim idx As Integer = Fix((v - MinDouble) * SclDouble)
                            If idx = BinCount Then
                                idx = BinCount - 1
                            End If
                            If gram(idx) Is Nothing Then gram(idx) = New System.Collections.Generic.List(Of TValue)
                            gram(idx).Add(_v)
                            MaxBinLength = System.Math.Max(gram(idx).Count, MaxBinLength)
                        Next

                    Else

                        ReDim gram(0)
                        gram(0) = New System.Collections.Generic.List(Of TValue)(collections_count)
                        For Each v As TValue In collections
                            gram(0).Add(v)
                        Next
                        MaxBinLength = gram(0).Count

                    End If

                    Dim ref_gram As New System.Collections.Generic.List(Of Bin)(gram.Length)
                    Dim ref_mode_bins As New System.Collections.Generic.List(Of Bin)
                    For Each b As System.Collections.Generic.List(Of TValue) In gram
                        Dim new_item As Bin = Nothing
                        If Not b Is Nothing Then
                            new_item = New Bin(b)
                            If MaxBinLength <= b.Count Then ref_mode_bins.Add(new_item)
                        End If
                        ref_gram.Add(new_item)
                    Next
                    ary = New Bin.ReadOnlyCollection(ref_gram)
                    aryModeBins = New Bin.ReadOnlyCollection(ref_mode_bins)

                End Sub

                Public Sub New()
                    Clear()
                End Sub

                Public Sub New(ByVal collections As System.Collections.Generic.IEnumerable(Of TValue), ByVal collections_count As Integer, ByVal d As GetBinCountDelegate)
                    Initialize(collections, collections_count, 0, d)
                End Sub

                Public Sub New(ByVal collections As System.Collections.Generic.IEnumerable(Of TValue), Optional ByVal collections_count As Integer = 0, Optional ByVal BinCount As Integer = 0)
                    Initialize(collections, collections_count, BinCount, Nothing)
                End Sub

                Public Sub New(ByVal param() As TValue, ByVal d As GetBinCountDelegate)
                    If param Is Nothing Then
                        Clear()
                    Else
                        Initialize(param, param.Length, 0, d)
                    End If
                End Sub

                Public Sub New(ByVal param() As TValue, Optional ByVal BinCount As Integer = 0)
                    If param Is Nothing Then
                        Clear()
                    Else
                        Initialize(param, param.Length, BinCount, Nothing)
                    End If
                End Sub

                Public ReadOnly Property Bins() As Bin.ReadOnlyCollection
                    Get
                        Return ary
                    End Get
                End Property

                Public ReadOnly Property ModeBins() As Bin.ReadOnlyCollection
                    Get
                        Return aryModeBins
                    End Get
                End Property

                Private ary As Bin.ReadOnlyCollection
                Private aryModeBins As Bin.ReadOnlyCollection

                Private Shared Function GetBinCount(ByVal SampleCount As Integer, ByVal MaxValue As TValue, ByVal MinValue As TValue, Optional ByVal d As GetBinCountDelegate = Nothing) As Integer
                    If SampleCount <= 0 Then Return 0
                    If MaxValue.Equals(MinValue) OrElse MaxValue.CompareTo(MinValue) <= 0 Then Return 0
                    If d Is Nothing Then
                        Dim Range As Double = MaxValue.ToDouble(Globalization.CultureInfo.CurrentCulture) - MinValue.ToDouble(Globalization.CultureInfo.CurrentCulture)
                        Return System.Math.Max(CInt(Logic.Math.RoundUneven(System.Math.Sqrt(System.Math.Min(System.Math.Abs(Range), CDbl(SampleCount))))), 3)
                    Else
                        Return d(SampleCount, MaxValue, MinValue)
                    End If
                End Function

            End Class ' AutoHistogram

        End Class ' Statistics
    End Class ' Math
End Class ' CustomLogic