; _______________________________________________________________________________________
;|                                                                                       |
;| ..:: Fresh IDE ::..  template project.                                                |
;|_______________________________________________________________________________________|
;
;  Description: FreshLib portable console application.
;
;  Target OS: Any, supported by FreshLib
;
;  Dependencies: FreshLib
;
;  Notes:
;_________________________________________________________________________________________

include "%lib%/freshlib.inc"

LINUX_INTERPRETER equ './ld-musl-i386.so'

@BinaryType console, default

options.DebugMode = 0
options.AlignCode = 1
options.FastEnter = 1

include "%lib%/freshlib.asm"

uglobal
  cnt dd ?
  total dd ?
  hfile dd ?
endg


start:
        InitializeAll

        stdcall ReadDictionary8, txt "dd/Dataset.csv", 147

        stdcall FileWriteString, [STDOUT], txt "Dictionary (SetA) length: "
        stdcall NumToStr, ecx, ntsDec or ntsUnsigned
        push    eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall StrDel ; from the stack
        stdcall FileWriteString, [STDOUT], <txt 13, 10, 13, 10>

        stdcall FileOpen, "dd/t2.csv"
        mov     [hfile], eax

.loop_out:
        stdcall FileReadLine, [hfile]
        test    eax, eax
        jz      .end_of_file2

        mov     edi, eax

        stdcall GetTimestamp
        push    eax

        stdcall ComputeMinDist8, edx, ecx, edi
        mov     esi, eax

        stdcall GetTimestamp
        sub     eax, [esp]
        mov     [esp], eax

        stdcall NumToStr, [cnt], ntsDec or ntsUnsigned
        push    eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall StrDel ; from the stack

        inc     [cnt]

        stdcall FileWriteString, [STDOUT], txt ": Dist: "

        stdcall NumToStr, esi, ntsDec or ntsUnsigned
        push    eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall StrDel ; from the stack

        stdcall FileWriteString, [STDOUT], txt ", Time: "

        pop     eax
        add     [total], eax

        stdcall NumToStr, eax, ntsDec or ntsSigned
        push    eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall StrDel ; from the stack

        stdcall FileWriteString, [STDOUT], <txt " ms", 13, 10>

;        stdcall FileWriteString, [STDOUT], <txt " ms, Index: ">
;
;        stdcall NumToStr, ebx, ntsDec or ntsSigned
;        push    eax
;        stdcall FileWriteString, [STDOUT], eax
;        stdcall StrDel ; from the stack
;        stdcall FileWriteString, [STDOUT], <txt 13, 10>


        stdcall StrDel, edi
        jmp     .loop_out


.end_of_file2:
        stdcall FileClose, [hfile]

        stdcall FileWriteString, [STDOUT], <txt 13, 10, "Total time spend: ">
        stdcall NumToStr, [total], ntsDec or ntsSigned
        push    eax
        stdcall FileWriteString, [STDOUT], eax
        stdcall StrDel ; from the stack
        stdcall FileWriteString, [STDOUT], <txt " ms", 13, 10>

.finalize:
        FinalizeAll
        stdcall TerminateAll, 0






proc ReadDictionary8, .hFileName, .len
.line dd ?
.cnt  dd ?
.exit dd ?
.file dd ?
begin
        pushad

        xor     eax, eax
        mov     [.cnt], eax
        mov     [.line], eax

        inc     eax
        mov     [.exit], eax

        stdcall TextCreate, sizeof.TText
        mov     edx, eax

        stdcall FileOpen, [.hFileName]
        mov     [.file], eax

.loop_out:
        mov     eax, [.len]
        lea     eax, [8*eax + 32]
        stdcall TextSetGapSize, edx, eax

        mov     edi, edx
        add     edi, [edx+TText.GapBegin]
        mov     ebx, 8

.loop8:
        stdcall FileReadLine, [.file]
        mov     [.exit], eax
        test    eax, eax
        jz      @f

        inc     [.cnt]
        stdcall StrDel, [.line]
        mov     [.line], eax

@@:
        stdcall StrPtr, [.line]
        mov     esi, eax
        xor     ecx, ecx

.loop1:
        mov     al, [esi + ecx]
        mov     [edi + 8*ecx], al

        inc     ecx
        cmp     ecx, [.len]
        jne     .loop1

        inc     edi
        dec     ebx
        jnz     .loop8

; align
        lea     edi, [edi+8*ecx]
        add     edi, 7
        and     edi, $fffffff8
        sub     edi, edx
        mov     [edx+TText.GapBegin], edi

        cmp     [.exit], 0
        jne     .loop_out

        stdcall StrDel, [.line]
        stdcall FileClose, [.file]

        mov     ecx, [.cnt]

        mov     [esp+4*regECX], ecx
        mov     [esp+4*regEDX], edx
        popad
        return
endp





proc Levenshtein8, .pString8, .len8, .pString1, .len1, .Limit, .dummy
.buff0  rq 256
begin
        push      ebx ecx edx esi edi


; Scan the equal prefix.

        mov       edi, [.pString1]
        mov       esi, [.pString8]

        mov       ecx, [.len1]
        mov       edx, [.len8]

.loop_prefix:
        mov       al, [edi]
        mov       ah, al
        movd      mm1, eax

        movq      mm0, [esi]

        punpcklbw mm1, mm1              ; 00 00 00 00 al al al al
        punpcklbw mm1, mm1              ; al al al al al al al al

        pcmpeqb   mm1, mm0
        pmovmskb  eax, mm1
        inc       al
        jnz       .prefix_ok

        lea     edi, [edi+1]
        lea     esi, [esi+8]
        dec     ecx
        dec     edx
        jmp     .loop_prefix


.prefix_ok:

        mov       [.pString8], esi

;.scan_suffix:
;        dec     ecx
;        dec     edx
;
;        mov       al, [edi+ecx]
;        mov       ah, al
;        movd      mm1, eax
;
;        movq      mm0, [esi+8*edx]
;
;        punpcklbw mm1, mm1              ; 00 00 00 00 al al al al
;        punpcklbw mm1, mm1              ; al al al al al al al al
;
;        pcmpeqb   mm1, mm0
;        pmovmskb  eax, mm1
;        inc       al
;        jz        .scan_suffix
;
;        inc     ecx
;        inc     edx

        mov       [.len8], edx

; Check for length of 0
;        mov     eax, edx
;        cmp     ecx, 0
;        jle     .finish
;
;        mov     eax, ecx
;        cmp     edx, 0
;        jle     .finish

; Init the matrix column

        mov       eax, $01010101
        movd      mm0, eax
        punpcklbw mm0, mm0              ; 01 01 01 01 01 01 01 01
        movq      mm1, mm0

.init0:
        movq      [.buff0 + 8*edx], mm0
        paddb     mm0, mm1
        dec       edx
        jns       .init0

; Start of the distance computing

.loop1:
        mov       edx, [.len8]

        movq      mm3, [.buff0 + 8*edx]
        movq      mm2, mm3                        ; mm2 <- D[j-1, j-1]
        paddb     mm3, mm1
        movq      [.buff0 + 8*edx], mm3           ; D[i, 0] = D[i-1, 0] + 1

        mov       al, [edi]
        lea       edi, [edi+1]

        mov       ah, al
        movd      mm0, eax
        punpcklbw mm0, mm0              ; 00 00 00 00 al al al al
        punpcklbw mm0, mm0              ; al al al al al al al al       ; mm0 <- the current character from the pString1

        mov       esi, [.pString8]

.loop2:
        dec       edx

        movq      mm3, [esi]
        lea       esi, [esi+8]

        pcmpeqb   mm3, mm0               ; mm0 =  0 or -1
        paddb     mm2, mm3               ; mm2 = D[i-1, j-1]+1  - (0 | 1)


        movq      mm4, [.buff0 + 8*edx]        ; D[i-1, j] + 1
        pminub    mm2, [.buff0 + 8*edx + 8]    ; D[i, j-1] + 1
        pminub    mm2, mm4

        paddb     mm2, mm1                     ; inc(D[i,j])

        movq      [.buff0 + 8*edx], mm2        ;D[i, j] = min(D[i-1, j]+1, D[i, j-1]+1, D[i-1,j-1] + (0, 1))
        movq      mm2, mm4                     ; mm2 = D[i-1, j-1]+1

        jnz       .loop2                       ; dec EDX is the last opperation that affects the flags!!!

; check the current limit

        mov       eax, [.Limit]
        mov       edx, 7
        xor       ebx, ebx

.loop_min:
        mov       bl, byte [.buff0 + edx]
        sub       ebx, ecx
        jns       @f
        neg       ebx
@@:
        cmp       eax, ebx
        cmova     eax, ebx
        dec       edx
        jns       .loop_min

        cmp       eax, [.Limit]
        jae       .finish

        dec       ecx
        jnz       .loop1

.finish:
        pop       edi esi edx ecx ebx
        return
endp



proc ComputeMinDist8, .pDict, .DictSize, .hString
begin
        pushad

        stdcall StrPtr, [.hString]
        mov     edi, eax

        mov     edx, [.pDict]

        mov     esi, 64
        xor     ebx, ebx
        dec     ebx

        xor     ecx, ecx

.loop:
        stdcall Levenshtein8, edx, 147, edi, [edi+string.len], esi, 0
        cmp     esi, eax
        cmovg   esi, eax
        cmovg   ebx, ecx

        add     edx, 8*147 + 7 + 8
        and     edx, $fffffff8

        add     ecx, 8
        cmp     ecx, [.DictSize]
        jb      .loop

.finish:
        mov     [esp+4*regEAX], esi
        mov     [esp+4*regEBX], ebx
        popad
        return
endp
