Min and max values of an array

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Min and max values of an array

Post by wilbert »

Probably not very useful for most people.
It's just because I like to use asm now and then :wink:
It's a module to quickly determine the min and max values of a large array.
Unfortunately quad is not very fast but the other types should be okay.
If the CPU doesn't support SSE2, it uses PB code instead of asm so in that case it will be slower.

Float_x2 and Word_x2 are added because they are used quite often for 2 channel interleaved audio samples.
Ascii_x4 because it can be used for 32 bit RGBA data to find the min and max values for each color component.

Code: Select all

; MinMax module by Wilbert

; A module to get the minimum and maximum values of an array

; Last code update : June 10, 2018

DeclareModule MinMax
  
  Structure Float_x2 : f.f[2] : EndStructure
  Structure Word_x2  : w.w[2] : EndStructure
  Structure Ascii_x4 : a.a[4] : EndStructure
  
  Declare MinMaxD(*Array.Double, Count, *Min.Double, *Max.Double)
  Declare MinMaxF(*Array.Float, Count, *Min.Float, *Max.Float)
  Declare MinMaxQ(*Array.Quad, Count, *Min.Quad, *Max.Quad)
  Declare MinMaxI(*Array.Integer, Count, *Min.Integer, *Max.Integer)
  Declare MinMaxL(*Array.Long, Count, *Min.Long, *Max.Long)
  Declare MinMaxW(*Array.Word, Count, *Min.Word, *Max.Word)
  Declare MinMaxU(*Array.Unicode, Count, *Min.Unicode, *Max.Unicode)
  Declare MinMaxB(*Array.Byte, Count, *Min.Byte, *Max.Byte)
  Declare MinMaxA(*Array.Ascii, Count, *Min.Ascii, *Max.Ascii)
  
  Declare MinMaxF2(*Array.Float_x2, Count, *Min.Float_x2, *Max.Float_x2)
  Declare MinMaxW2(*Array.Word_x2, Count, *Min.Word_x2, *Max.Word_x2)
  Declare MinMaxA4(*Array.Ascii_x4, Count, *Min.Ascii_x4, *Max.Ascii_x4)
  
EndDeclareModule

Module MinMax
  
  EnableExplicit
  DisableDebugger
  EnableASM
  
  ; *** SSE2 check for 32 bit mode ***
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    Global.l _NoSSE2_
    Procedure SSE2_Check()
      Protected.l NoSSE2
      !mov eax, 1
      !push ebx
      !cpuid
      !pop ebx
      !test edx, 0x04000000
      !setz [p.v_NoSSE2]
      _NoSSE2_ = NoSSE2
    EndProcedure
    SSE2_Check()
  CompilerEndIf    
        
  Macro M_SSE2_Check()
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
      Protected.l NoSSE2 = _NoSSE2_
      !test dword [p.v_NoSSE2], 1
      !jnz .pb  
    CompilerEndIf
  EndMacro
    
  ; *** General PB Fallback code ***
  
  Macro M_MinMaxPB(type)
    If Count : Count - 1
      *Min\type = *Array\type: *Max\type = *Array\type
      While Count : Count - 1 : *Array + SizeOf(*Array\type)
        If *Array\type < *Min\type : *Min\type = *Array\type
          ElseIf *Array\type > *Max\type : *Max\type = *Array\type : EndIf
      Wend
    EndIf  
  EndMacro
  
  ; *** Some additional macros ***
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    Macro rcx : ecx : EndMacro
    Macro rdx : edx : EndMacro
  CompilerEndIf
  
  Macro M_(opcode, arg1, arg2)
    !opcode arg1, arg2
  EndMacro
  
  ; *** Implementation of declared procedures ***
  
  Procedure MinMaxD(*Array.Double, Count, *Min.Double, *Max.Double)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 2
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movupd, xmm0, [rdx + rcx*8 - 16])
    !movapd xmm1, xmm0
    !and ecx, -2
    !jz .l1
    ; main loop
    !.l0:
    M_(movupd, xmm2, [rdx])
    add rdx, 16
    !minpd xmm0, xmm2
    !maxpd xmm1, xmm2
    !sub ecx, 2
    !jnz .l0
    !.l1:
    ; combine 2 into 1
    !movhlps xmm2, xmm0
    !movhlps xmm3, xmm1
    !minsd xmm0, xmm2
    !maxsd xmm1, xmm3
    mov rdx, [p.p_Min]
    M_(movsd, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movsd, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    If Count
      *Min\d = *Array\d : *Max\d = *Array\d
    EndIf    
  EndProcedure
  
  Procedure MinMaxF(*Array.Float, Count, *Min.Float, *Max.Float)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 4
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movups, xmm0, [rdx + rcx*4 - 16])
    !movaps xmm1, xmm0
    !and ecx, -4
    !jz .l1
    ; main loop
    !.l0:
    M_(movups, xmm2, [rdx])
    add rdx, 16
    !minps xmm0, xmm2
    !maxps xmm1, xmm2
    !sub ecx, 4
    !jnz .l0
    !.l1:
    ; combine 4 into 2
    !movhlps xmm2, xmm0
    !movhlps xmm3, xmm1
    !minps xmm0, xmm2
    !maxps xmm1, xmm3
    ; combine 2 into 1
    !movaps xmm2, xmm0
    !movaps xmm3, xmm1
    !shufps xmm2, xmm2, 1
    !shufps xmm3, xmm3, 1
    !minss xmm0, xmm2
    !maxss xmm1, xmm3
    mov rdx, [p.p_Min]
    M_(movss, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movss, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    M_MinMaxPB(f)
  EndProcedure
  
  Procedure MinMaxQ(*Array.Quad, Count, *Min.Quad, *Max.Quad)
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      !mov ecx, [p.v_Count]
      !cmp ecx, 0
      !je .l4    
      !mov rdx, [p.p_Array]
      !mov r8, [rdx]
      !add rdx, 8
      !mov r9, r8
      !sub ecx, 1
      !jz .l3
      !.l0:
      !mov rax, [rdx]
      !add rdx, 8
      !cmp rax, r8
      !jge .l1
      !mov r8, rax
      !jmp .l2
      !.l1:
      !cmp rax, r9
      !jle .l2
      !mov r9, rax
      !.l2:
      !sub ecx, 1
      !jnz .l0
      !.l3:
      !mov rdx, [p.p_Min]
      !mov [rdx], r8
      !mov rdx, [p.p_Max]
      !mov [rdx], r9
      !.l4:
    CompilerElse
      M_MinMaxPB(q)
    CompilerEndIf
  EndProcedure    
  
  Procedure MinMaxI(*Array.Integer, Count, *Min.Integer, *Max.Integer)
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      MinMaxQ(*Array, Count, *Min, *Max)
    CompilerElse
      MinMaxL(*Array, Count, *Min, *Max)
    CompilerEndIf
  EndProcedure    
  
  Procedure MinMaxL(*Array.Long, Count, *Min.Long, *Max.Long)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 4
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx*4 - 16])
    !movdqa xmm1, xmm0
    !and ecx, -4
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !movdqa xmm4, xmm2
    !movdqa xmm5, xmm1
    !pcmpgtd xmm4, xmm0
    !pcmpgtd xmm5, xmm2
    !pand xmm0, xmm4
    !pand xmm1, xmm5
    !pandn xmm4, xmm2
    !pandn xmm5, xmm2
    !por xmm0, xmm4
    !por xmm1, xmm5
    !sub ecx, 4
    !jnz .l0
    !.l1:
    ; combine 4 into 2
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !movdqa xmm4, xmm2
    !movdqa xmm5, xmm1
    !pcmpgtd xmm4, xmm0
    !pcmpgtd xmm5, xmm3
    !pand xmm0, xmm4
    !pand xmm1, xmm5
    !pandn xmm4, xmm2
    !pandn xmm5, xmm3
    !por xmm0, xmm4
    !por xmm1, xmm5
    ; combine 2 into 1
    !pshufd xmm2, xmm0, 1
    !pshufd xmm3, xmm1, 1
    !movdqa xmm4, xmm2
    !movdqa xmm5, xmm1
    !pcmpgtd xmm4, xmm0
    !pcmpgtd xmm5, xmm3
    !pand xmm0, xmm4
    !pand xmm1, xmm5
    !pandn xmm4, xmm2
    !pandn xmm5, xmm3
    !por xmm0, xmm4
    !por xmm1, xmm5
    mov rdx, [p.p_Min]
    M_(movd, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movd, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    M_MinMaxPB(l)
  EndProcedure
  
  Procedure MinMaxW(*Array.Word, Count, *Min.Word, *Max.Word)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 8
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx*2 - 16])
    !movdqa xmm1, xmm0
    !and ecx, -8
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm2
    !sub ecx, 8
    !jnz .l0
    !.l1:
    ; combine 8 into 4
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    ; combine 4 into 2
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    ; combine 2 into 1
    !pshuflw xmm2, xmm0, 1
    !pshuflw xmm3, xmm1, 1
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    !movd eax, xmm0
    !movd ecx, xmm1
    mov rdx, [p.p_Min]
    mov [rdx], ax
    mov rdx, [p.p_Max]
    mov [rdx], cx
    ProcedureReturn
    !.pb:
    M_MinMaxPB(w)
  EndProcedure
  
  Procedure MinMaxU(*Array.Unicode, Count, *Min.Unicode, *Max.Unicode)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 8
    !jb .pb
    !pcmpeqw xmm4, xmm4
    !psllw xmm4, 15
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx*2 - 16])
    !pxor xmm0, xmm4
    !movdqa xmm1, xmm0
    !and ecx, -8
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pxor xmm2, xmm4
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm2
    !sub ecx, 8
    !jnz .l0
    !.l1:
    ; combine 8 into 4
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    ; combine 4 into 2
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    ; combine 2 into 1
    !pshuflw xmm2, xmm0, 1
    !pshuflw xmm3, xmm1, 1
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    !movd eax, xmm0
    !movd ecx, xmm1
    !xor eax, 0x8000
    !xor ecx, 0x8000
    mov rdx, [p.p_Min]
    mov [rdx], ax
    mov rdx, [p.p_Max]
    mov [rdx], cx
    ProcedureReturn
    !.pb:
    M_MinMaxPB(u)    
  EndProcedure
  
  Procedure MinMaxB(*Array.Byte, Count, *Min.Byte, *Max.Byte)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 16
    !jb .pb
    !pcmpeqw xmm4, xmm4
    !psllw xmm4, 15
    !packsswb xmm4, xmm4
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx - 16])
    !pxor xmm0, xmm4  
    !movdqa xmm1, xmm0
    !and ecx, -16
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pxor xmm2, xmm4
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm2
    !sub ecx, 16
    !jnz .l0
    !.l1:
    ; combine 16 into 8
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 8 into 4
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 4 into 2
    !pshuflw xmm2, xmm0, 1
    !pshuflw xmm3, xmm1, 1
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 2 into 1
    !movdqa xmm2, xmm0
    !movdqa xmm3, xmm1
    !psrlw xmm2, 8
    !psrlw xmm3, 8
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    !movd eax, xmm0
    !movd ecx, xmm1
    !xor eax, 0x80
    !xor ecx, 0x80
    mov rdx, [p.p_Min]
    mov [rdx], al
    mov rdx, [p.p_Max]
    mov [rdx], cl
    ProcedureReturn
    !.pb:
    M_MinMaxPB(b)    
  EndProcedure
  
  Procedure MinMaxA(*Array.Ascii, Count, *Min.Ascii, *Max.Ascii)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 16
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx - 16])
    !movdqa xmm1, xmm0
    !and ecx, -16
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm2
    !sub ecx, 16
    !jnz .l0
    !.l1:
    ; combine 16 into 8
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 8 into 4
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 4 into 2
    !pshuflw xmm2, xmm0, 1
    !pshuflw xmm3, xmm1, 1
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 2 into 1
    !movdqa xmm2, xmm0
    !movdqa xmm3, xmm1
    !psrlw xmm2, 8
    !psrlw xmm3, 8
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    !movd eax, xmm0
    !movd ecx, xmm1
    mov rdx, [p.p_Min]
    mov [rdx], al
    mov rdx, [p.p_Max]
    mov [rdx], cl
    ProcedureReturn
    !.pb:
    M_MinMaxPB(a)
  EndProcedure
  
  Procedure MinMaxF2(*Array.Float_x2, Count, *Min.Float_x2, *Max.Float_x2)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 2
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movups, xmm0, [rdx + rcx*8 - 16])
    !movaps xmm1, xmm0
    !and ecx, -2
    !jz .l1
    ; main loop
    !.l0:
    M_(movups, xmm2, [rdx])
    add rdx, 16
    !minps xmm0, xmm2
    !maxps xmm1, xmm2
    !sub ecx, 2
    !jnz .l0
    !.l1:
    ; combine 2 into 1
    !movhlps xmm2, xmm0
    !movhlps xmm3, xmm1
    !minps xmm0, xmm2
    !maxps xmm1, xmm3
    mov rdx, [p.p_Min]
    M_(movlps, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movlps, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    If Count
      *Min\f[0] = *Array\f[0] : *Max\f[0] = *Array\f[0]
      *Min\f[1] = *Array\f[1] : *Max\f[1] = *Array\f[1]
    EndIf
  EndProcedure
  
  Procedure MinMaxW2(*Array.Word_x2, Count, *Min.Word_x2, *Max.Word_x2)
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 4
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx*4 - 16])
    !movdqa xmm1, xmm0
    !and ecx, -4
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm2
    !sub ecx, 4
    !jnz .l0
    !.l1:
    ; combine 4 into 2
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    ; combine 2 into 1
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminsw xmm0, xmm2
    !pmaxsw xmm1, xmm3
    mov rdx, [p.p_Min]
    M_(movd, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movd, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    If Count : Count - 1
      *Min\w[0] = *Array\w[0] : *Max\w[0] = *Array\w[0]
      *Min\w[1] = *Array\w[1] : *Max\w[1] = *Array\w[1]
      While Count : Count - 1 : *Array + 4
        If *Array\w[0] < *Min\w[0] : *Min\w[0] = *Array\w[0]
          ElseIf *Array\w[0] > *Max\w[0] : *Max\w[0] = *Array\w[0] : EndIf
        If *Array\w[1] < *Min\w[1] : *Min\w[1] = *Array\w[1]
          ElseIf *Array\w[1] > *Max\w[1] : *Max\w[1] = *Array\w[1] : EndIf
      Wend
    EndIf  
  EndProcedure
  
  Procedure MinMaxA4(*Array.Ascii_x4, Count, *Min.Ascii_x4, *Max.Ascii_x4)  
    M_SSE2_Check()
    !mov ecx, [p.v_Count]
    !cmp ecx, 4
    !jb .pb
    mov rdx, [p.p_Array]
    M_(movdqu, xmm0, [rdx + rcx*4 - 16])
    !movdqa xmm1, xmm0
    !and ecx, -4
    !jz .l1
    ; main loop
    !.l0:
    M_(movdqu, xmm2, [rdx])
    add rdx, 16
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm2
    !sub ecx, 4
    !jnz .l0
    !.l1:
    ; combine 4 into 2
    !pshufd xmm2, xmm0, 1110b
    !pshufd xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    ; combine 2 into 1
    !pshuflw xmm2, xmm0, 1110b
    !pshuflw xmm3, xmm1, 1110b
    !pminub xmm0, xmm2
    !pmaxub xmm1, xmm3
    mov rdx, [p.p_Min]
    M_(movd, [rdx], xmm0)
    mov rdx, [p.p_Max]
    M_(movd, [rdx], xmm1)
    ProcedureReturn
    !.pb:
    If Count : Count - 1
      *Min\a[0] = *Array\a[0] : *Max\a[0] = *Array\a[0]
      *Min\a[1] = *Array\a[1] : *Max\a[1] = *Array\a[1]
      *Min\a[2] = *Array\a[2] : *Max\a[2] = *Array\a[2]
      *Min\a[3] = *Array\a[3] : *Max\a[3] = *Array\a[3]
      While Count : Count - 1 : *Array + 4
        If *Array\a[0] < *Min\a[0] : *Min\a[0] = *Array\a[0]
          ElseIf *Array\a[0] > *Max\a[0] : *Max\a[0] = *Array\a[0] : EndIf
        If *Array\a[1] < *Min\a[1] : *Min\a[1] = *Array\a[1]
          ElseIf *Array\a[1] > *Max\a[1] : *Max\a[1] = *Array\a[1] : EndIf
        If *Array\a[2] < *Min\a[2] : *Min\a[2] = *Array\a[2]
          ElseIf *Array\a[2] > *Max\a[2] : *Max\a[2] = *Array\a[2] : EndIf
        If *Array\a[3] < *Min\a[3] : *Min\a[3] = *Array\a[3]
          ElseIf *Array\a[3] > *Max\a[3] : *Max\a[3] = *Array\a[3] : EndIf
      Wend
    EndIf      
  EndProcedure
  
EndModule
Last edited by wilbert on Sun Jun 10, 2018 9:08 am, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Min and max values of an array

Post by wilbert »

Example

Code: Select all

UseModule MinMax

Dim L.l(9999999)
RandomSeed(0)
RandomData(@L(), 10000000*SizeOf(Long))

t1 = ElapsedMilliseconds()
MinMaxL(@L(), 10000000, @min.l, @max.l)
t2 = ElapsedMilliseconds()
MessageRequester("Result", "min:"+Str(min)+" max:"+Str(max)+" ["+Str(t2-t1)+"msec]")
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Min and max values of an array

Post by Kwai chang caine »

It's always also nice :shock:
Works perfectly on W10 X64 v5.61 x86
Thanks for this code 8)
ImageThe happiness is a road...
Not a destination
User avatar
Mijikai
Addict
Addict
Posts: 1360
Joined: Sun Sep 11, 2016 2:17 pm

Re: Min and max values of an array

Post by Mijikai »

Thanks :)
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Min and max values of an array

Post by davido »

@wilbert,

Excellent!
Thank you for sharing :D
DE AA EB
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Min and max values of an array

Post by Dude »

wilbert wrote:CPU with SSE2 support required
Is this supported by most CPUs these days? How can I be sure your code will run on any given PC?
User avatar
Demivec
Addict
Addict
Posts: 4086
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Min and max values of an array

Post by Demivec »

Dude wrote:
wilbert wrote:CPU with SSE2 support required
Is this supported by most CPUs these days? How can I be sure your code will run on any given PC?
By way of matter of fact, SSE2 support is required for Windows 8.1 and I would presume, its sequels.
Last edited by Demivec on Sun Jun 10, 2018 1:47 am, edited 1 time in total.
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Min and max values of an array

Post by Dude »

So not for Vista and 7?
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Min and max values of an array

Post by StarBootics »

Hello everyone,

A code that should run on any computer it's a linear search not as optimize as Wibert version :

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : ArrayMinMax - Module
; File Name : ArrayMinMax - Module.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : 16-09-2015
; Last Update : 09-06-2018
; PureBasic code : V5.70 beta 1
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule ArrayMinMax
  
  Declare Byte(Array Values.b(1), *Min, *Max)
  Declare AsciiCharacter(Array Values.a(1), *Min, *Max)
  Declare Character(Array Values.c(1), *Min, *Max)
  Declare UnicodeCharacter(Array Values.u(1), *Min, *Max)
  Declare Word(Array Values.w(1), *Min, *Max)
  Declare Long(Array Values.l(1), *Min, *Max)
  Declare Integer(Array Values.i(1), *Min, *Max)
  Declare Quad(Array Values.q(1), *Min, *Max)
  Declare Float(Array Values.f(1), *Min, *Max)
  Declare Double(Array Values.d(1), *Min, *Max)
  
EndDeclareModule

Module ArrayMinMax
  
  Procedure Byte(Array Values.b(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.b = Values(0)
    Protected MaxValue.b = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeB(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeB(*Max, MaxValue)
    EndIf
 
  EndProcedure
  
  Procedure AsciiCharacter(Array Values.a(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.a = Values(0)
    Protected MaxValue.a = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeA(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeA(*Max, MaxValue)
    EndIf

  EndProcedure
  
  Procedure Character(Array Values.c(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.c = Values(0)
    Protected MaxValue.c = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeC(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeC(*Max, MaxValue)
    EndIf
  
  EndProcedure
  
  Procedure UnicodeCharacter(Array Values.u(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.u = Values(0)
    Protected MaxValue.u = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeU(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeU(*Max, MaxValue)
    EndIf

  EndProcedure
  
  Procedure Word(Array Values.w(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.w = Values(0)
    Protected MaxValue.w = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeW(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeW(*Max, MaxValue)
    EndIf

  EndProcedure
  
  Procedure Long(Array Values.l(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.l = Values(0)
    Protected MaxValue.l = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeL(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeL(*Max, MaxValue)
    EndIf

  EndProcedure
  
  Procedure Integer(Array Values.i(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.i = Values(0)
    Protected MaxValue.i = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeI(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeI(*Max, MaxValue)
    EndIf

  EndProcedure
  
  Procedure Quad(Array Values.q(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.q = Values(0)
    Protected MaxValue.q = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeQ(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeQ(*Max, MaxValue)
    EndIf
      
  EndProcedure
  
  Procedure Float(Array Values.f(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.f = Values(0)
    Protected MaxValue.f = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeF(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeF(*Max, MaxValue)
    EndIf
     
  EndProcedure
  
  Procedure Double(Array Values.d(1), *Min, *Max)
    
    Protected ArraySizeMax.l = ArraySize(Values(), 1)
    Protected Index.l
    
    Protected MinValue.d = Values(0)
    Protected MaxValue.d = Values(0)
    
    For Index = 1 To ArraySizeMax
      
      If Values(Index) < MinValue
        MinValue = Values(Index)
      EndIf
      
      If Values(Index) > MaxValue
        MaxValue = Values(Index)
      EndIf
      
    Next
    
    If *Min <> #Null
      PokeD(*Min, MinValue)
    EndIf
    
    If *Max <> #Null
      PokeD(*Max, MaxValue)
    EndIf
    
  EndProcedure

EndModule

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
And the same thing for linked list :

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project name : ListMinMax - Module
; File Name : ListMinMax - Module.pb
; File version: 1.0.0
; Programming : OK
; Programmed by : StarBootics
; Date : 16-09-2015
; Last Update : 09-06-2018
; PureBasic code : V5.70 beta 1
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule ListMinMax
  
  Declare Byte(List Values.b(), *Min, *Max)
  Declare AsciiCharacter(List Values.a(), *Min, *Max)
  Declare Character(List Values.c(), *Min, *Max)
  Declare UnicodeCharacter(List Values.u(), *Min, *Max)
  Declare Word(List Values.w(), *Min, *Max)
  Declare Long(List Values.l(), *Min, *Max)
  Declare Integer(List Values.i(), *Min, *Max)
  Declare Quad(List Values.q(), *Min, *Max)
  Declare Float(List Values.f(), *Min, *Max)
  Declare Double(List Values.d(), *Min, *Max)
  
EndDeclareModule

Module ListMinMax
  
  Procedure Byte(List Values.b(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.b = Values()
      Protected MaxValue.b = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeB(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeB(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure AsciiCharacter(List Values.a(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.a = Values()
      Protected MaxValue.a = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeA(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeA(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Character(List Values.c(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.c = Values()
      Protected MaxValue.c = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeC(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeC(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure UnicodeCharacter(List Values.u(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.u = Values()
      Protected MaxValue.u = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeU(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeU(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Word(List Values.w(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.w = Values()
      Protected MaxValue.w = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeW(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeW(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Long(List Values.l(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.l = Values()
      Protected MaxValue.l = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeL(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeL(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Integer(List Values.i(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.i = Values()
      Protected MaxValue.i = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeI(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeI(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Quad(List Values.q(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.q = Values()
      Protected MaxValue.q = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeQ(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeQ(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Float(List Values.f(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.f = Values()
      Protected MaxValue.f = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeF(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeF(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
  Procedure Double(List Values.d(), *Min, *Max)
    
    If FirstElement(Values())
      
      Protected MinValue.d = Values()
      Protected MaxValue.d = Values()
      
      ForEach Values()
        
        If Values() < MinValue
          MinValue = Values()
        EndIf
        
        If Values() > MaxValue
          MaxValue = Values()
        EndIf
        
      Next
      
      If *Min <> #Null
        PokeD(*Min, MinValue)
      EndIf
      
      If *Max <> #Null
        PokeD(*Max, MaxValue)
      EndIf
      
    EndIf
    
  EndProcedure
  
EndModule

CompilerIf #PB_Compiler_IsMainFile
  
  NewList Values.f()
 
  For Index = 0 To 29
    AddElement(Values())
    Values() = Sin(Index) * Random(25) * 3 * #PI
    Debug Values()
  Next

  Debug ""
  
  ListMinMax::Float(Values(), @Min.f, @Max.f)
  
  Debug StrF(Min, 6)
  Debug StrF(Max, 6)
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Min and max values of an array

Post by wilbert »

Dude wrote:
wilbert wrote:CPU with SSE2 support required
Is this supported by most CPUs these days? How can I be sure your code will run on any given PC?
SSE2 was introduced over 15 years ago.
It's also part of the x64 specification so any Intel/Amd cpu capable of 64 bit has SSE2 support.
It is possible to create a small routine yo check if a cpu has SSE2 support but there's also a lot of current software and Operating Systems that simply require it to work.

Code: Select all

Procedure Has_SSE2()
  !mov eax, 1
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    !push ebx
    !cpuid
    !pop ebx
    !xor eax, eax
    !test edx, 0x04000000
    !setnz al
  CompilerEndIf
  ProcedureReturn  
EndProcedure

Debug Has_SSE2()
Last edited by wilbert on Sun Jun 10, 2018 9:10 am, edited 3 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Demivec
Addict
Addict
Posts: 4086
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Min and max values of an array

Post by Demivec »

Dude wrote:So not for Vista and 7?
Vista was released in 2006 and 7 was released in 2009. They did not require SSE2 support so it is not guaranteed on which CPUs they are running. As wilbert said, SSE2 was introduced 15+ years ago and they were released after that time and you can check the cpu to see if it is SSE2 capable.


Also, all CPUs running MacOS X are also SSE2 capable.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Min and max values of an array

Post by wilbert »

Just to be sure I updated the code with a check for SSE2 support when the code is compiled for 32 bit.
If SSE2 is not present, it uses slower PB code instead.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: Min and max values of an array

Post by RSBasic »

Thank you for your code.
Image
Image
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: Min and max values of an array

Post by Dude »

Yes, thanks Wilbert! :)
tester
User
User
Posts: 30
Joined: Sun Dec 28, 2014 1:12 pm

Re: Min and max values of an array

Post by tester »

Not working with C-backend PB601 - Error: 'EndModule' is missing.
Post Reply