''''''''''''''''''''''''''''''''
Private Function Insert_New_Column_To_DataTable(ByVal DataTable_name As String, ByVal num As Long, _
                    ByVal col_type As Variant, ByVal col_name As Variant) As Object
    '' データテーブルに列を追加
    '' DataTable_name: DT名
    '' numで指定した列番号のあとに新規列を追加
    '' col_type: 追加する列のデータ型
    '' col_name: 追加する列名
    '' col_typeとcol_nameはデータ数が同じである必要があります。
    
    ''''''''''''''''''''''''''''''''
    
    Dim pk As Long
    Dim New_dt() As Variant
    Dim ncol As Long
    Dim new_dic As Object
    Dim output As Object
    Dim dic As Object
    Dim n_check As Variant
    Dim ch As Variant
    Dim dt As Variant
    Dim i, j As Long
    
    If TypeName(col_type) = "String" And TypeName(col_name) = "String" Then
        col_type = Array(col_type)
        col_name = Array(col_name)
    End If
    
    If UBound(col_type) <> UBound(col_name) Then
        MsgBox "col_nameとcol_typeのデータ数が違います。" & vbCrLf & _
                "データ数を揃えてください。", vbInformation
        Exit Function
    End If
    
    ncol = UBound(col_type) + 1
    
    '' データを取得
    Set dic = Read_DataTable(DataTable_name)
    
    '' 列名の重複チェック
    n_check = join(dic.item("name"), vbTab)
    For Each ch In col_name
        If InStr(n_check, ch) > 0 Then
            Debug.Print "登録しようとしている列名に重複があります: " & ch
            Exit Function
        End If
    Next
    
    '' 配列化
    dt = Dictionary_to_Array(dic)
    
    
    '' プライマリーキー列
    pk = dic.item("primary_key")(1)
    If pk > num Then
        dic.item("primary_key")(1) = pk + UBound(col_type) + 1
    End If
    
    '' 列を追加
    ReDim New_dt(LBound(dt, 1) To UBound(dt, 1), LBound(dt, 2) To UBound(dt, 2) + UBound(col_type) + 1)
    
    For j = LBound(dt, 2) To UBound(dt, 2)
        '' 目的の列のあとに列を追加
        If j <= num Then
            For i = LBound(New_dt, 1) To UBound(New_dt, 1)
                New_dt(i, j) = dt(i, j)
            Next
        Else
            For i = LBound(New_dt, 1) To UBound(New_dt, 1)
                New_dt(i, j + ncol) = dt(i, j)
            Next
            
        End If
    Next
    
    '' 追加列情報を登録
    For j = LBound(col_type) To UBound(col_type)
        New_dt(LBound(New_dt, 1), num + 1 + j) = col_type(j)
        New_dt(LBound(New_dt, 1) + 1, num + 1 + j) = col_name(j)
    Next
    
    '' 配列からディクショナリーに変換
    Set new_dic = Array_to_Dictionary(New_dt, pk)
    
    '' データの上書き
    Set output = OverWrite_DataTable(dic, new_dic)
    
    Set Insert_New_Column_To_DataTable = output
    
    '' データの保存
    Call Write_DataTable(DataTable_name, output)
    
    
End Function
Private Function OverWrite_DataTable(ByVal dic As Object, ByVal new_dic As Object) As Object
    Dim nkey As Variant
    
    For Each nkey In new_dic
        If dic.exists(nkey) Then
            dic.item(nkey) = new_dic.item(nkey)
        End If
    Next
    
    Set OverWrite_DataTable = dic
    
End Function
Private Function Array_to_Dictionary(ByVal dt As Variant, ByVal num As Long) As Object
    '' num: プライマリーキー番号
    '' dt: データテーブル
    
    Dim dic As Object
    Dim ar() As Variant
    Dim i, j As Long
    Set dic = Set_Dictionary
    
    ReDim ar(LBound(dt, 2) To UBound(dt, 2))
    
    For i = LBound(dt, 1) To UBound(dt, 1)
        '' 登録データを1次元に変換
        For j = LBound(dt, 2) To UBound(dt, 2)
            ar(j) = dt(i, j)
        Next
        '' データを登録
        Select Case ar(0)
            Case "TYPE"
                dic.Add "type", ar
            Case "NAME"
                dic.Add "name", ar
            Case Else
                dic.Add CStr(ar(num)), ar
        End Select
        
        '' 格納したデータを消去
        For j = LBound(dt, 2) To UBound(dt, 2)
            ar(j) = ""
        Next
        
    Next
    
    Set Array_to_Dictionary = dic
    
End Function

'Private Function Update_DataTable(ByVal DataTable_name As String, _
'                                    ByVal add_data As Variant) As Object
'    '' 既存のデータテーブルに新たな行を追加
'    '' add_data: Dictionary型を内包する配列型(array)
'
'    Dim dic, New_dic As Object
'    Dim cname As Variant
'    Dim ncol As Long
'    Dim pk As Long
'    Dim ndim As Long
'    Dim nrow As Long
'    Dim dt, New_dt As Variant
'    Dim ctype As Variant
'    Dim dic_type As Object
'    Dim set_date As String
'    Dim i As Long
'    Dim key_name As Variant
'    Dim d As Variant
'    Dim ikey As Variant
'    Dim push As Variant
'    Dim dkey As Variant
'
'
'    '' 登録日作成
'    set_date = Format(Now(), "yyyy-mm-dd hh:MM:ss")
'    '' 一覧データ取得
'    Set dic = Read_DataTable(DataTable_name)
'    '' 列名取得
'    cname = dic.item("name")
'    '' データタイプ取得
'    ctype = dic.item("type")
'    Set dic_type = Set_Dictionary
'    For i = LBound(cname) To UBound(cname)
'        dic_type.Add cname(i), ctype(i)
'    Next
'
'    '' プライマリーキー列番号
'    pk = dic.item("primary_key")(1)
'
'    '' 列名をもとにDictionaryを作成
'    Set New_dic = Set_Dictionary
'    For Each key_name In cname
'        New_dic.Add key_name, ""
'    Next
'
'    ''データ型を変換
'    '' すでに変換されている場合は除外、2次元配列であれば変換
'    If Search_Dimentions(add_data) = 2 Then
'        add_data = Get_Table_To_Array(add_data)
'    End If
'
'    '' 新規データを登録
'    For Each d In add_data
'        ''データを修正
'        For Each ikey In d
'            If New_dic.exists(ikey) Then
'                New_dic.item(ikey) = Input_Format(d.item(ikey), dic_type.item(ikey))
'            End If
'        Next
'        New_dic.item("NAME") = set_date
'        '' データ行を追加, 既存の場合は上書き
'        push = New_dic.items
'        If dic.exists(push(pk)) Then
'            dic.item(push(pk)) = push
'        Else
'            dic.Add push(pk), push
'        End If
'
'        '' 既存のデータを一度消去
'        For Each dkey In New_dic
'            New_dic.item(dkey) = ""
'        Next
'        push = ""
'    Next
'
'    ''データを保存
'    ''Call Write_DataTable(DataTable_name, New_dic)
'    Set Update_DataTable = dic
'
'End Function
Private Function Update_DataTable(ByVal DataTable_name As String, _
                                    ByVal add_data As Variant) As Object
    '' 既存のデータテーブルに新たな行を追加
    '' add_data: Dictionary型を内包する配列型(array)
    
    Dim dic, new_dic As Object
    Dim cName As Variant
    Dim ncol As Long
    Dim pk As Long
    Dim ndim As Long
    Dim nrow As Long
    Dim dt, New_dt As Variant
    Dim ctype As Variant
    Dim dic_type As Object
    Dim set_date As String
    Dim i As Long
    Dim key_name As Variant
    Dim d As Variant
    Dim ikey As Variant
    Dim push As Variant
    Dim dkey As Variant
    Dim pkv As String
    Dim set_values As Variant
    
    '' 登録日作成
    set_date = Format(Now(), "yyyy-mm-dd hh:MM:ss")
    '' 一覧データ取得
    Set dic = Read_DataTable(DataTable_name)
    '' 列名取得
    cName = dic.item("name")
    '' データタイプ取得
    ctype = dic.item("type")
    Set dic_type = Set_Dictionary
    For i = LBound(cName) To UBound(cName)
        dic_type.Add cName(i), ctype(i)
    Next
    
    '' プライマリーキー列番号
    pk = dic.item("primary_key")(1)
    
    '' 列名をもとにDictionaryを作成
'    Set new_dic = Set_Dictionary
'    For Each key_name In cName
'        new_dic.Add key_name, ""
'    Next
    
    ''データ型を変換
    '' すでに変換されている場合は除外、2次元配列であれば変換
    If Search_Dimentions(add_data) = 2 Then
        add_data = Get_Table_To_Array(add_data)
    End If
    
    '' 新規データを登録
    For Each d In add_data
        ''キーを取得
        pkv = d.item(cName(pk))
        '' 既存のデータ検索
        If dic.exists(pkv) Then
            set_values = dic.item(pkv)
        
            '' 初期値として既存のデータを指定
            Set new_dic = Set_Dictionary
            i = LBound(set_values)
            For Each key_name In cName
                new_dic.Add key_name, set_values(i)
                i = i + 1
            Next
        Else
            '' 初期値として既存のデータを指定
            Set new_dic = Set_Dictionary
            For Each key_name In cName
                new_dic.Add key_name, ""
            Next
        End If
        
        ''データを修正
        For Each ikey In d
            
            If new_dic.exists(ikey) Then
                new_dic.item(ikey) = Input_Format(d.item(ikey), dic_type.item(ikey))
            End If

        Next
        new_dic.item("NAME") = set_date
        
        '' データ行を追加, 既存の場合は上書き
        push = new_dic.items
        If dic.exists(pkv) Then
            dic.item(pkv) = push
        Else
            dic.Add pkv, push
        End If
'
'        '' 既存のデータを一度消去
'        For Each dkey In new_dic
'            new_dic.item(dkey) = ""
'        Next
        push = ""
    Next
    
    ''データを保存
    ''Call Write_DataTable(DataTable_name, New_dic)
    Set Update_DataTable = dic
    
End Function
Private Function Dictionary_to_Array(ByVal dic As Object) As Variant
    '' Dictionary → 二次元配列に変換
    '' dicはDictionary型データを指定
    
    Dim data() As Variant
    Dim nrow, ncol As Long
    Dim key_names As Variant
    Dim i, j As Long
    Dim ld As Variant
    
    nrow = dic.Count - 1
    
'    If nrow < 2 Then
'        Debug.Print "データテーブルにデータが登録されていません。"
'        Exit Function
'    End If
    
    key_names = dic.keys
    
    ncol = UBound(dic.item(key_names(2)))
    
    ReDim data(1 To nrow, ncol)
    
    For i = 1 To nrow
    
        ld = dic.item(key_names(i))
        
        For j = 0 To ncol
            data(i, j) = ld(j)
        Next
        
        ld = ""
    Next
    
    
    Dictionary_to_Array = data


End Function
Private Function Read_DataTable(ByVal DataTable_name As String) As Object
    '' データテーブルを取り込み、Dictionary型で出力する関数
    '' 区切り文字はタブ、改行文字はVbCr
    
    Dim n As Integer
    Dim i As LongPtr
    Dim text As String
    Dim dic As Object
    Dim pk As Long
    Dim data As Variant
    
    
    Set dic = Set_Dictionary
    n = FreeFile
    
    Open DT_name(DataTable_name) For Input As #n
        i = 1
        Do While Not EOF(n)
            Line Input #n, text
            
            If text = "" Then GoTo Continue
            
            Select Case i
                Case 1
                    dic.Add "primary_key", Split(text, vbTab)
                    pk = CLng(dic.item("primary_key")(1))
                Case 2
                    dic.Add "type", Split(text, vbTab)
                Case 3
                    dic.Add "name", Split(text, vbTab)
                Case Else
                    data = Split(text, vbTab)
                    dic.Add data(pk), data
            End Select
            
Continue:
            i = i + 1
        Loop
    
    Close #n
    
    
    Set Read_DataTable = dic
    
End Function
Private Sub Add_Data_Table(ByVal name As String, ByVal KEY As LongPtr, _
                        ByVal col_type As Variant, ByVal col_name As Variant)
    '' データテーブルを作成
    '' name: DTの名称
    '' KEY: Primary Keyの列番号 1~
    '' col_type: 追加する列のデータ型
    '' col_name: 追加する列名
    '' col_typeとcol_nameはデータ数が同じである必要があります。
    
    Dim n As Integer
    Dim text As String
    Dim filename As String
    Dim msg As Variant
    
    If TypeName(col_type) = "String" And TypeName(col_name) = "String" Then
        col_type = Array(col_type)
        col_name = Array(col_name)
    End If
    
    If UBound(col_type) <> UBound(col_name) Then
        MsgBox "col_nameとcol_typeのデータ数が違います。" & vbCrLf & _
                "データ数を揃えてください。", vbInformation
        Exit Sub
    End If
    
    n = FreeFile
    text = "PRIMARY KEY: " & vbTab & KEY & vbCr & _
            "TYPE" & vbTab & join(col_type, vbTab) & vbCr & _
            "NAME" & vbTab & join(col_name, vbTab) & vbCr
    filename = DT_name(name)
    If dir(filename, vbDirectory) <> "" Then
        msg = MsgBox(name & ": このテーブルは既に作成されています。" & vbCrLf & _
                        "続行するとテーブルが削除されます。", vbYesNo)
        If msg = 7 Then Exit Sub
    End If
    Open filename For Output As #n
        Print #n, text
    Close #n
    
    Debug.Print "Create New Data Table: " & filename
    
End Sub
Public Sub WriteTableWithoutHeader(ByVal data_table_name As String, ByVal data_object As Variant)
    Call Write_DataTable(data_table_name, data_object)
    Call Make_Backup_File(data_table_name)
End Sub
Public Function ReadTableWithoutHeader(ByVal data_table_name As String, _
                                        Optional read_type As readtype = readtype.RowHead, _
                                        Optional separate_key As SepKey = SepKey.keyTab) As Object
    '' データテーブルを取り込み、Dictionary型で出力する関数
    '' 区切り文字はタブ、改行文字はVbCr
    
    Dim n As Integer
    Dim i As LongPtr
    Dim text As String
    Dim dic As Object
    Dim pk As Long
    Dim data As Variant
    
    
    Set dic = Set_Dictionary
    n = FreeFile
    If InStr(data_table_name, "\") = 0 Then data_table_name = DT_name(data_table_name)
        
    Open data_table_name For Input As #n
        i = 1
        Do While Not EOF(n)
            Line Input #n, text
            
            If text = "" Then GoTo Continue
            
            If separate_key = keyTab Then
                data = Split(text, vbTab)
            ElseIf separate_key = keyComma Then
                data = Split(text, ",")
            End If
            
            If read_type = readtype.RowHead Then
                dic.Add CStr(data(0)), data
            ElseIf read_type = readtype.Number Then
                dic.Add CStr(i), data
            End If
Continue:
            i = i + 1
        Loop
    
    Close #n
    
    
    Set ReadTableWithoutHeader = dic
    
End Function
Private Sub Write_DataTable(ByVal DataTable_name As String, ByVal Write_data As Variant)
    '' データテーブルを上書き保存
    '' DataTable_name: テーブル名
    '' Write_data: 上書きするDictionary型データ
    On Error GoTo Error
    
    Dim n As Integer
    Dim text As String
    Dim filename As String
    Dim dic As Object
    Dim cnt As Long
    Dim ikey As Variant
    
    cnt = 1
    
    Select Case VarType(Write_data)
        Case 9 ''Object
            Set dic = Set_Dictionary
            For Each ikey In Write_data
                dic.item(ikey) = join(Write_data.item(ikey), vbTab)
            Next
            text = join(dic.items, vbCr)
        
        Case 8 '' String
            text = Write_data
        
        Case 8192 ''Array
            
    End Select
    filename = DT_name(DataTable_name)

Write_Run:
    n = FreeFile
    Open filename For Output Lock Write As #n
        Print #n, text
    Close #n
    GoTo Continue
    
Error:
    ''openされていれば閉じる処理を追加
    If Err.Number > 0 Then Close #n
    
    If cnt > 5 Then
        MsgBox "他のユーザーがファイルを開いています。少し待ってから再開してください。" & vbCrLf & _
                "※ [OK]ボタンを押すと処理が再開します。", vbCritical
    Else
        Application.Wait Now() + TimeValue("00:00:01")
    End If
    cnt = cnt + 1
    GoTo Write_Run
    
Continue:
    Debug.Print "Save Data Table: " & filename
    
End Sub
Public Sub MakeDirectory(ByVal dir_name As String)
    Call Create_New_Folder(dir_name)
End Sub
Private Sub Create_New_Folder(ByVal fold_name As String)
    Dim fol_name As String
    Dim check_dir As String
    
    If fold_name = "" Then
        fol_name = working_dir
    Else
        fol_name = Join_Path(working_dir, fold_name)
    End If
    
    check_dir = dir(fol_name, vbDirectory)
    If check_dir = "" Then
        mkdir fol_name
        Debug.Print "Make directory: " & fol_name
    End If
    
End Sub
Private Sub Set_New_Data_Base()
    '' 新規でデータベースフォルダを作成
    
    Dim dir_name As Variant
    
'' サブディレクトリの作成 ==============================
    Call Create_New_Folder("")
    
'    For Each dir_name In Array("Backups", "Report", "Picture")
'        Call Create_New_Folder(dir_name)
'    Next
    
    Call Create_New_Folder("Backups")


End Sub
Private Function Search_Dimentions(ByVal dt As Variant) As Long
    On Error GoTo Continue
    Dim i As Integer
    Dim nmax, nmin As Variant
    Dim dims As String
    
    i = 1
    nmax = Empty
    
    Do
        nmax = UBound(dt, i)
        If IsEmpty(nmax) Then GoTo Continue
        i = i + 1
    Loop
    
Continue:
    
    Search_Dimentions = i - 1
    
End Function
Public Function Join_Path(ByVal path As String, ByVal name As String) As String
    Join_Path = path & "\" & name
End Function
Private Function DT_name(ByVal name As String)
    DT_name = Join_Path(working_dir, name & ".txt")
End Function
Public Function Set_Dictionary() As Object
    Dim dic As Object
    Set dic = CreateObject("Scripting.Dictionary")
    Set Set_Dictionary = dic
End Function
Private Function QuickSort(ByVal data As Variant, ByVal L As Long, _
                    ByVal U As Long, ByVal order As Integer) As Variant
    '' 逆順番に並び替え
    '' data: 1次元配列
    '' order: 0:昇順 1:降順
    
    Dim i As Long
    Dim j As Long
    Dim S As Variant
    Dim Tmp As Variant
    
Continue:

    S = data(Int((L + U) / 2))
    i = L
    j = U
    Do
        If order = 0 Then
            Do While data(i) < S
                i = i + 1
            Loop
            
            Do While data(j) > S
                j = j - 1
            Loop
        ElseIf order = 1 Then
            Do While data(i) > S
                i = i + 1
            Loop
            
            Do While data(j) < S
                j = j - 1
            Loop
        End If
        If i >= j Then Exit Do
        
        Tmp = data(i)
        data(i) = data(j)
        data(j) = Tmp
        i = i + 1
        j = j - 1
    Loop
    If L < i - 1 Then
        U = i - 1
        L = L
        GoTo Continue
    End If
    If U > j + 1 Then
        L = j + 1
        U = U
        GoTo Continue
    End If
      
    QuickSort = data
    
End Function
Property Get WorkDir() As String
    WorkDir = working_dir
End Property
Property Let WorkDir(ByVal working_directory As String)
    working_dir = working_directory & "\data"
End Property

'Private Sub Class_Initialize()
'    working_dir = "set-working-directory" & "\data"
'End Sub