Batch macro vs function call test

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
einstein1969
Expert
Posts: 763
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch macro vs function call test

#16 Post by einstein1969 » 19 Oct 2017 09:27

I have probed to tuning for best performance the version 4.

There are few things to do but I have tried and I have done the follow:

- The SET/A is faster if use costant then I have removed the variable @
- Remove unused variables, move at bottom of environment less used variables. N_FORMULA->_FORMULA Operator->_Operator
- Keep clean the environment set Oper2=
- When the dos create a new environment that is empty it recreate some variables. COMSPEC , PATHEXT, PROMPT
I have keep clean the environment.

With this, for high number of Quads, I have free about 15% of execution time.

time is in second
Image

the new version 4.1

Code: Select all

@if defined talk (echo on) else (echo off)
setlocal EnableDelayedExpansion
if "%1" equ "ArctanLoop" goto ArctanLoop

echo pi.bat  -  By Don Cross  -  http://cosinekitty.com

REM Empty the environment
for /F "delims=" %%a in ('where sort') do set "sort=%%a"
(
for /F "delims==" %%v in ('set') do set "%%v="
set "sort=%sort%"
)

REM Define the number of groups = 25 Quads = 100 digits
set /A A_NumGroups = 2

Rem SET/A is faster with constants
rem set /A @ = 10000

rem Remove unused variables, move at bottom of environment less used variables. N_FORMULA->_FORMULA Operator->_Operator
rem set /A A_NumQuads = A_NumGroups*25, A_MaxQuadIndex = A_NumQuads-1

call :CreateMacros
echo %time% - started

call :PiEngine 48 18 32 57 -20 239
call :PiEngine 16 5 -4 239
goto :EOF

:PiEngine
    REM call :SetToInteger Pi 0
    copy /Y Zero.txt Pi.txt > NUL
    set "_Formula="

    :PiTermLoop
        call :ArctanRecip PiTerm %2
        if %1 lss 0 (

            REM call :MultiplyByInteger PiTerm -%1
            set /A "#=-%1,'=0"
            (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "%M_MultiplyByInteger#%"
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

            REM call :Subtract Pi PiTerm
            set "'=0"
            < PiTerm.txt (
            for /F "tokens=1-26" %%A in (Pi.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Subtract%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del Pi.txt & ren Result.txt Pi.txt

            set "_Operator=-"

        ) else (

            REM call :MultiplyByInteger PiTerm %1
            set /A "#=%1,'=0"
            (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "%M_MultiplyByInteger#%"
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

            REM call :Add Pi PiTerm
            set "'=0"
            < PiTerm.txt (
            for /F "tokens=1-26" %%A in (Pi.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Add%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del Pi.txt & ren Result.txt Pi.txt

            set "_Operator=+"

        )

        Rem Keep clean the environment
        set Oper2=

        if not defined _Formula (
            set "_Formula=pi = %1*arctan(1/%2)"
        ) else (
            set "_Formula=%_Formula% %_Operator% %#%*arctan(1/%2)"
        )
        shift
        shift
    if "%1" neq "" goto PiTermLoop
    call :Print Pi
    echo %time% - finished %_Formula%
exit /B


:ArctanRecip   %1 = PiTerm *always*

    REM call :SetToInteger PiTerm 1
    copy /Y OneR.txt PiTerm.txt > NUL

    REM call :DivideByInteger PiTerm %2
    set /A "#=%2,'=0"
    (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
        set /A "%M_DivideRbyInteger#%"
        echo %%A%M_Result%
    )) > AR_Recip.txt

    REM call :CopyValue AR_Recip PiTerm
    "%sort%" AR_Recip.txt /O PiTerm.txt

    set /A _AR_Toggle = -1,  _AR_K = 3


    REM Re-execute the code starting at ArctanLoop label in a new cmd.exe context
    for %%a in ("%sort%") do "%%~DPacmd.exe" /D /C "%~F0" ArctanLoop %2
    exit /B

    :ArctanLoop

    (
    REM Remove the macros from the environment after they have been expanded
    for /F "delims==" %%v in ('set M_') do set "%%v="

    Rem Clean up the environment
    set "A_NumGroups="
    set COMSPEC=
    set PATHEXT=
    set PROMPT=
    set SORT=

    for /L %%? in ( ) do (

        REM call :DivideByInteger AR_Recip %2
        set /A "#=%2,'=0"
        (for /F "tokens=1-26" %%A in (AR_Recip.txt) do (
            set /A "%M_DivideRbyInteger#%"
            echo %%A%M_Result%
        )) > Result.txt
        REM call :DivideByInteger AR_Recip %2
        set "'=0"
        (for /F "tokens=1-26" %%A in (Result.txt) do (
            set /A "%M_DivideRbyInteger#%"
            echo %%A%M_Result%
        )) > AR_Recip.txt

        REM call :CopyValue AR_Term AR_Recip
        copy /Y AR_Recip.txt AR_Term.txt > NUL

        REM call :DivideByInteger AR_Term !AR_K!
        set /A "#=_AR_K,'=0, AR_Term=0"
        (for /F "tokens=1-26" %%A in (AR_Term.txt) do (
            set /A "%M_DivideRbyInteger#%"
            set /A "AR_Term+=%M_Sum%"
            echo %%A%M_Result%
        )) > Result.txt

        REM Exit from the infinite FOR /L loop (while loop)
        if !AR_Term! equ 0 exit

        "%sort%" Result.txt /O AR_Term.txt
        if !_AR_Toggle! lss 0 (

            REM call :Subtract PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Subtract%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

        ) else (

            REM call :Add PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Add%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt
        )

        rem Keep clean the environment
        set oper2=

        set /a _AR_K += 2, _AR_Toggle *= -1

    ))

exit /B


:Print
    echo/
    < NUL (
    set /P "=%1 = "
    set "line=0"
    for /F "tokens=1-26" %%A in ('"%sort%" /R %1.txt') do (
        set /A "line+=1,%M_PrintAdjust%"
        if !line! equ 1 (
            set /P "=%M_PrintFormat:!Z:~1!=!Z:~-1!.%"
        ) else if !line! lss %A_NumGroups% (
            set /P "=%M_PrintFormat%"
        ) else (
            set /P "=%M_PrintFormat:!C:~1!!B:~1!=%"
        )
    ))
    echo/
    echo/
exit /B


REM    $Log: pi.bat,v $
REM    Revision 1.2  2007/09/06 21:49:15  Don.Cross
REM    Added time stamps and display of formula.
REM
REM    Revision 1.1  2007/09/06 21:12:36  Don.Cross
REM    Batch file for calculating pi
REM


:CreateMacros

    set "n="
    for /L %%i in (2,1,25) do set "n=!n!0 "
    set /A "i=100+A_NumGroups"
    (
    echo !i:~1! %n%1
    for /L %%i in (2,1,%A_NumGroups%) do (
        set /A i-=1
        echo !i:~1! %n%0
    )
    ) > OneR.txt
    set /A "i=101"
    (for /L %%i in (1,1,%A_NumGroups%) do (
        echo !i:~1! %n%0
        set /A i+=1
    )) > Zero.txt

    set  "upCase=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
    set "lowCase=a b c d e f g h i j k l m n o p q r s t u v w x y z"

    REM One-char variables: $=Digit, '=Carry, @=10000, #=int number for multiply/divide

    set "M_MultiplyByInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_MultiplyByInteger#=!M_MultiplyByInteger#!,$='+%%%%i*#,%%i=$%%10000,'=$/10000"
    )
    set "M_MultiplyByInteger#=!M_MultiplyByInteger#:~1!"

    set "M_DivideRbyInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_DivideRbyInteger#=$='*10000+%%%%i,%%i=$/#,'=$%%#,!M_DivideRbyInteger#!"
    )
    set "M_DivideRbyInteger#=!M_DivideRbyInteger#:~0,-1!"

    set "M_Add="
    set "M_Sum="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Add=!M_Add!,$='+%%%%i+%%%%j,%%i=$%%10000,'=$/10000"
            set "lowCase2=%%k"
        )
        set "M_Sum=!M_Sum!+%%%%i"
    )
    set "M_Add=%M_Add:~1%"
    set "M_Sum=%M_Sum:~1%"

    set "M_Subtract="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Subtract=!M_Subtract!,$=%%%%i-%%%%j+','=$>>31,%%i=$-'*10000"
            set "lowCase2=%%k"
        )
    )
    set "M_Subtract=%M_Subtract:~1%"

    set "M_Result="
    for %%i in (%upCase:~2%) do (
        set "M_Result=!M_Result! ^!%%i^!"
    )

    set "M_PrintAdjust="
    set "M_PrintFormat="
    for %%i in (%upCase:~2%) do (
        set "M_PrintAdjust=!M_PrintAdjust!,%%i=10000+%%%%i"
        set "M_PrintFormat=^!%%i:~1^!!M_PrintFormat!"
    )
    set "M_PrintAdjust=%M_PrintAdjust:~1%"

    set "upCase="
    set "lowCase="

exit /B


Francesco
Last edited by einstein1969 on 21 Oct 2017 01:37, edited 1 time in total.

einstein1969
Expert
Posts: 763
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch macro vs function call test

#17 Post by einstein1969 » 21 Oct 2017 01:35

I thinks that next step is using multi-thread creating a pipeline...

But in the middle there is an optimization in the block of division:

Code: Select all

AR_Recip/N
AR_Recip/N        =>    AR_Recip/(N*N)

But this can have an overflow if N*N > 214747

For formulas :

Pi = 48*arctan(1/18) + 32*arctan(1/57) - 20*arctan(1/239)
and
Pi = 16*arctan(1/5) - 4*arctan(1/239)

I think there isn't problem of overflow. Right?

Francesco

einstein1969
Expert
Posts: 763
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: Batch macro vs function call test

#18 Post by einstein1969 » 21 Oct 2017 04:21

I have done the above optimizzation

The new gain is about 15% or more for big NumQuads. 8)

time is in seconds
Image

the new version include, if defined pidebug variable, print on title with current time and percent achieved.
It sync the timer at 00 second before start for easy time calculous.

the code of version 4.2

Code: Select all

@if defined talk (echo on) else (echo off)
setlocal EnableDelayedExpansion
if "%1" equ "ArctanLoop" goto ArctanLoop

echo pi.bat  -  By Don Cross  -  http://cosinekitty.com

REM Empty the environment
for /F "delims=" %%a in ('where sort') do set "sort=%%a"
(
  for /F "delims==" %%v in ('set') do set "%%v="
  set "sort=%sort%"
  set pidebug=%pidebug%
)

REM Define the number of groups = 25 Quads = 100 digits
set /A A_NumGroups = 2

call :CreateMacros

:loop
 If %Time:~6,2% neq 0 goto :loop

echo %time% - started

call :PiEngine 48 18 32 57 -20 239

rem call :PiEngine 16 5 -4 239

goto :EOF

:PiEngine
    if defined pidebug (
       set _CC=0
       >_cc.dat echo %_CC%
    )
    REM call :SetToInteger Pi 0
    copy /Y Zero.txt Pi.txt > NUL
    set "_Formula="

    :PiTermLoop
        call :ArctanRecip PiTerm %2
        if %1 lss 0 (

            REM call :MultiplyByInteger PiTerm -%1
            set /A "#=-%1,'=0"
            (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "%M_MultiplyByInteger#%"
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

            REM call :Subtract Pi PiTerm
            set "'=0"
            < PiTerm.txt (
            for /F "tokens=1-26" %%A in (Pi.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Subtract%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del Pi.txt & ren Result.txt Pi.txt

            set "_Operator=-"

        ) else (

            REM call :MultiplyByInteger PiTerm %1
            set /A "#=%1,'=0"
            (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "%M_MultiplyByInteger#%"
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

            REM call :Add Pi PiTerm
            set "'=0"
            < PiTerm.txt (
            for /F "tokens=1-26" %%A in (Pi.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Add%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del Pi.txt & ren Result.txt Pi.txt

            set "_Operator=+"

        )

        Rem Keep clean the environment
        set Oper2=

        if not defined _Formula (
            set "_Formula=pi = %1*arctan(1/%2)"
        ) else (
            set "_Formula=%_Formula% %_Operator% %#%*arctan(1/%2)"
        )
        shift
        shift
    if "%1" neq "" goto PiTermLoop
    call :Print Pi
    echo %time% - finished %_Formula%
exit /B


:ArctanRecip   %1 = PiTerm *always*

    REM call :SetToInteger PiTerm 1
    copy /Y OneR.txt PiTerm.txt > NUL

    REM call :DivideByInteger PiTerm %2
    set /A "#=%2,'=0"
    (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
        set /A "%M_DivideRbyInteger#%"
        echo %%A%M_Result%
    )) > AR_Recip.txt

    REM call :CopyValue AR_Recip PiTerm
    "%sort%" AR_Recip.txt /O PiTerm.txt

    set /A _AR_Toggle = -1,  _AR_K = 3


    REM Re-execute the code starting at ArctanLoop label in a new cmd.exe context
    for %%a in ("%sort%") do "%%~DPacmd.exe" /D /C "%~F0" ArctanLoop %2
    exit /B

    :ArctanLoop

    if defined pidebug set/P "_CC="<_cc.dat

    (
    REM Remove the macros from the environment after they have been expanded
    for /F "delims==" %%v in ('set M_') do set "%%v="

    Rem Clean up the environment
    set "A_NumGroups="
    set COMSPEC=
    set PATHEXT=
    set PROMPT=
    set SORT=

    for /L %%? in ( ) do (

        REM call :DivideByInteger AR_Recip %2
        set /A "#=%2*%2,'=0"
        (for /F "tokens=1-26" %%A in (AR_Recip.txt) do (
            set /A "%M_DivideRbyInteger#%"
            echo %%A%M_Result%
        )) > Result.txt
        Copy /Y Result.txt AR_Recip.txt > NUL

        REM call :CopyValue AR_Term AR_Recip
        copy /Y AR_Recip.txt AR_Term.txt > NUL

        REM call :DivideByInteger AR_Term !AR_K!
        set /A "#=_AR_K,'=0, AR_Term=0"
        (for /F "tokens=1-26" %%A in (AR_Term.txt) do (
            set /A "%M_DivideRbyInteger#%"
            set /A "AR_Term+=%M_Sum%"
            echo %%A%M_Result%
        )) > Result.txt

        if defined pidebug (
          set /a "_CC+=3, perc=_CC*100/(%A_NumGroups%*25*11)"
          title !perc!%% [!_CC!] [!time:~0,-3!]               
          rem if !perc! equ 10 >_cc.dat echo !_CC! & exit     
          set perc=
        )                                         

        REM Exit from the infinite FOR /L loop (while loop)
        if !AR_Term! equ 0 (
           if defined pidebug >_cc.dat echo !_CC!
           exit
        )

        "%sort%" Result.txt /O AR_Term.txt
        if !_AR_Toggle! lss 0 (

            REM call :Subtract PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Subtract%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt

        ) else (

            REM call :Add PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Add%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            del PiTerm.txt & ren Result.txt PiTerm.txt
        )

        rem Keep clean the environment
        set oper2=

        set /a _AR_K += 2, _AR_Toggle *= -1

    ))

exit /B


:Print
    echo/
    < NUL (
    set /P "=%1 = "
    set "line=0"
    for /F "tokens=1-26" %%A in ('"%sort%" /R %1.txt') do (
        set /A "line+=1,%M_PrintAdjust%"
        if !line! equ 1 (
            set /P "=%M_PrintFormat:!Z:~1!=!Z:~-1!.%"
        ) else if !line! lss %A_NumGroups% (
            set /P "=%M_PrintFormat%"
        ) else (
            set /P "=%M_PrintFormat:!C:~1!!B:~1!=%"
        )
    ))
    echo/
    echo/
exit /B


REM    $Log: pi.bat,v $
REM    Revision 1.2  2007/09/06 21:49:15  Don.Cross
REM    Added time stamps and display of formula.
REM
REM    Revision 1.1  2007/09/06 21:12:36  Don.Cross
REM    Batch file for calculating pi
REM


:CreateMacros

    set "n="
    for /L %%i in (2,1,25) do set "n=!n!0 "
    set /A "i=100+A_NumGroups"
    (
    echo !i:~1! %n%1
    for /L %%i in (2,1,%A_NumGroups%) do (
        set /A i-=1
        echo !i:~1! %n%0
    )
    ) > OneR.txt
    set /A "i=101"
    (for /L %%i in (1,1,%A_NumGroups%) do (
        echo !i:~1! %n%0
        set /A i+=1
    )) > Zero.txt

    set  "upCase=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
    set "lowCase=a b c d e f g h i j k l m n o p q r s t u v w x y z"

    REM One-char variables: $=Digit, '=Carry, @=10000, #=int number for multiply/divide

    set "M_MultiplyByInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_MultiplyByInteger#=!M_MultiplyByInteger#!,$='+%%%%i*#,%%i=$%%10000,'=$/10000"
    )
    set "M_MultiplyByInteger#=!M_MultiplyByInteger#:~1!"

    set "M_DivideRbyInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_DivideRbyInteger#=$='*10000+%%%%i,%%i=$/#,'=$%%#,!M_DivideRbyInteger#!"
    )
    set "M_DivideRbyInteger#=!M_DivideRbyInteger#:~0,-1!"

    set "M_Add="
    set "M_Sum="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Add=!M_Add!,$='+%%%%i+%%%%j,%%i=$%%10000,'=$/10000"
            set "lowCase2=%%k"
        )
        set "M_Sum=!M_Sum!+%%%%i"
    )
    set "M_Add=%M_Add:~1%"
    set "M_Sum=%M_Sum:~1%"

    set "M_Subtract="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Subtract=!M_Subtract!,$=%%%%i-%%%%j+','=$>>31,%%i=$-'*10000"
            set "lowCase2=%%k"
        )
    )
    set "M_Subtract=%M_Subtract:~1%"

    set "M_Result="
    for %%i in (%upCase:~2%) do (
        set "M_Result=!M_Result! ^!%%i^!"
    )

    set "M_PrintAdjust="
    set "M_PrintFormat="
    for %%i in (%upCase:~2%) do (
        set "M_PrintAdjust=!M_PrintAdjust!,%%i=10000+%%%%i"
        set "M_PrintFormat=^!%%i:~1^!!M_PrintFormat!"
    )
    set "M_PrintAdjust=%M_PrintAdjust:~1%"

    set "upCase="
    set "lowCase="

exit /B


EDIT:I have noted that there are some DELETE + RENAME that can be sobstitute by COPY /Y that is faster

Francesco

Aacini
Expert
Posts: 1615
Joined: 06 Dec 2011 22:15
Location: México City, México

Re: Batch macro vs function call test

#19 Post by Aacini » 22 Oct 2017 10:53

einstein1969 wrote:I have probed to tuning for best performance the version 4.

There are few things to do but I have tried and I have done the follow:

- The SET/A is faster if use costant then I have removed the variable @
- Remove unused variables, move at bottom of environment less used variables. N_FORMULA->_FORMULA Operator->_Operator
- Keep clean the environment set Oper2=
- When the dos create a new environment that is empty it recreate some variables. COMSPEC , PATHEXT, PROMPT
I have keep clean the environment.

With this, for high number of Quads, I have free about 15% of execution time.

Excellent! :)

einstein1969 wrote:I thinks that next step is using multi-thread creating a pipeline...

But in the middle there is an optimization in the block of division:

Code: Select all

AR_Recip/N
AR_Recip/N        =>    AR_Recip/(N*N)


But this can have an overflow if N*N > 214747

For formulas :

Pi = 48*arctan(1/18) + 32*arctan(1/57) - 20*arctan(1/239)
and
Pi = 16*arctan(1/5) - 4*arctan(1/239)

I think there isn't problem of overflow. Right?

Francesco

Correct. The maximum value for N*N is 0x7FFFFFFF / 9999 = 214769, so the maximum N in arctan(1/N) formula is 463.

einstein1969 wrote:I have done the above optimizzation

The new gain is about 15% or more for big NumQuads. 8)

Fantastic! :D 8)


I completed an additional modification to Version 4.2. When two operations are performed in sequence over the previous result, each operation is performed via its own FOR loop. However, the two (or more) operations can be done in the same loop if certain adjustments are made in the SET /A expression.

For example, below : PiTermLoop label there is an IF with a Multiply and Subtract operations in THEN part, and a Multiply and Add operations in ELSE part. In the new Version 4.3 these two operations have been combined in the same FOR loop, so the execution of two FOR commands/loops have been eliminated.

Also, in the :ArctanLoop there was two Divisions (executed in a single FOR loop in Version 4.2 using %2*%2 divisor), one Copy and one additional Division. These four operations are now executed in a single FOR loop in Version 4.3. I have no time to make extensive tests with this new version right now, but a preliminary test with low number of Quads show a time gain of about 7%, so it is likely that the time gain increase with high number of Quads...

This is the new Version 4.3:

Code: Select all

@if defined talk (echo on) else (echo off)
setlocal EnableDelayedExpansion
if "%1" equ "ArctanLoop" goto ArctanLoop

echo pi.bat  -  By Don Cross  -  http://cosinekitty.com

REM Empty the environment
for /F "delims=" %%a in ('where sort') do set "sort=%%a"
(
  for /F "delims==" %%v in ('set') do set "%%v="
  set "sort=%sort%"
  set pidebug=%pidebug%
)

REM Define the number of groups = 25 Quads = 100 digits
set /A A_NumGroups = 2

call :CreateMacros

echo Waiting for next minute . . .
:loop
 If %Time:~6,2% neq 0 goto :loop

echo %time% - started

call :PiEngine 48 18 32 57 -20 239
call :PiEngine 16 5 -4 239
del Zero.txt OneR.txt Result?.txt AR_Recip.txt AR_Term.txt PiTerm.txt Pi.txt
goto :EOF

:PiEngine
    if defined pidebug (
       set _CC=0
       >_cc.dat echo %_CC%
    )
    REM call :SetToInteger Pi 0
    copy /Y Zero.txt Pi.txt > NUL
    set "_Formula="

    :PiTermLoop
        call :ArctanRecip PiTerm %2
        if %1 lss 0 (

            REM call :MultiplyByInteger PiTerm -%1
            REM call :Subtract Pi PiTerm

            set /A "#=-%1, C1=0,C2=0"
            < Pi.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "'=C1,%M_MultiplyByInteger#%,C1='"
                echo %%A%M_Result%
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "'=C2,%M_SubtractPrevious%,C2='"
                )
                >&2 echo %%A%M_Result%
            )) > Result1.txt 2> Result2.txt
            (copy /Y Result1.txt PiTerm.txt & copy /Y Result2.txt Pi.txt) > NUL

            set "_Operator=-"

        ) else (

            REM call :MultiplyByInteger PiTerm %1
            REM call :Add Pi PiTerm

            set /A "#=%1, C1=0,C2=0"
            < Pi.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /A "'=C1,%M_MultiplyByInteger#%,C1='"
                echo %%A%M_Result%
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "'=C2,%M_AddPrevious%,C2='"
                )
                >&2 echo %%A%M_Result%
            )) > Result1.txt 2> Result2.txt
            (copy /Y Result1.txt PiTerm.txt & copy /Y Result2.txt Pi.txt) > NUL

            set "_Operator=+"

        )

        Rem Keep clean the environment
        set "oper2="

        if not defined _Formula (
            set "_Formula=pi = %1*arctan(1/%2)"
        ) else (
            set "_Formula=%_Formula% %_Operator% %#%*arctan(1/%2)"
        )
        shift
        shift
    if "%1" neq "" goto PiTermLoop
    call :Print Pi
    echo %time% - finished %_Formula%
exit /B


:ArctanRecip   %1 = PiTerm *always*

    REM call :SetToInteger PiTerm 1
    copy /Y OneR.txt PiTerm.txt > NUL

    REM call :DivideByInteger PiTerm %2
    set /A "#=%2,'=0"
    (for /F "tokens=1-26" %%A in (PiTerm.txt) do (
        set /A "%M_DivideRbyInteger#%"
        echo %%A%M_Result%
    )) > AR_Recip.txt

    REM call :CopyValue AR_Recip PiTerm
    "%sort%" AR_Recip.txt /O PiTerm.txt

    set /A _AR_Toggle = -1,  _AR_K = 3


    REM Re-execute the code starting at ArctanLoop label in a new cmd.exe context
    for %%a in ("%sort%") do "%%~DPacmd.exe" /D /C "%~F0" ArctanLoop %2
    exit /B

    :ArctanLoop

    if defined pidebug set/P "_CC="<_cc.dat

    (
    REM Remove the macros from the environment after they have been expanded
    for /F "delims==" %%v in ('set M_') do set "%%v="

    Rem Clean up the environment
    set "A_NumGroups="
    set "COMSPEC="
    set "PATHEXT="
    set "PROMPT="
    set "sort="

    for /L %%? in ( ) do (

        REM call :DivideByInteger AR_Recip %2
        REM call :DivideByInteger AR_Recip %2
        REM call :CopyValue AR_Term AR_Recip
        REM call :DivideByInteger AR_Term !AR_K!

        set /A "C1=0,C2=0, AR_Term=0"
        (for /F "tokens=1-26" %%A in (AR_Recip.txt) do (
            set /A "#=%2*%2,'=C1,%M_DivideRbyInteger#%,C1='"
            echo %%A%M_Result%
            set /A "#=_AR_K,'=C2,%M_DividePreviousByInteger#%,C2='"
            set /A "AR_Term+=%M_Sum%"
            >&2 echo %%A%M_Result%
        )) > Result.txt 2> AR_Term.txt

        if defined pidebug (
          set /a "_CC+=3, perc=_CC*100/(%A_NumGroups%*25*11)"
          title !perc!%% [!_CC!] [!time:~0,-3!]               
          rem if !perc! equ 10 >_cc.dat echo !_CC! & exit     
          set perc=
        )                                         

        REM Exit from the infinite FOR /L loop (while loop)
        if !AR_Term! equ 0 (
           if defined pidebug >_cc.dat echo !_CC!
           exit
        )

        copy /Y Result.txt AR_Recip.txt > NUL
        "%sort%" AR_Term.txt /O AR_Term.txt
        if !_AR_Toggle! lss 0 (

            REM call :Subtract PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Subtract%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            copy /Y Result.txt PiTerm.txt > NUL

        ) else (

            REM call :Add PiTerm AR_Term
            set "'=0"
            < AR_Term.txt (
            for /F "tokens=1-26" %%A in (PiTerm.txt) do (
                set /P "oper2="
                for /F "tokens=1-26" %%a in ("!oper2!") do (
                    set /A "%M_Add%"
                )
                echo %%A%M_Result%
            )) > Result.txt
            copy /Y Result.txt PiTerm.txt > NUL
        )

        rem Keep clean the environment
        set "oper2="

        set /a _AR_K += 2, _AR_Toggle *= -1

    ))

exit /B


:Print
    echo/
    < NUL (
    set /P "=%1 = "
    set "line=0"
    for /F "tokens=1-26" %%A in ('"%sort%" /R %1.txt') do (
        set /A "line+=1,%M_PrintAdjust%"
        if !line! equ 1 (
            set /P "=%M_PrintFormat:!Z:~1!=!Z:~-1!.%"
        ) else if !line! lss %A_NumGroups% (
            set /P "=%M_PrintFormat%"
        ) else (
            set /P "=%M_PrintFormat:!C:~1!!B:~1!=%"
        )
    ))
    echo/
    echo/
exit /B


REM    $Log: pi.bat,v $
REM    Revision 1.2  2007/09/06 21:49:15  Don.Cross
REM    Added time stamps and display of formula.
REM
REM    Revision 1.1  2007/09/06 21:12:36  Don.Cross
REM    Batch file for calculating pi
REM


:CreateMacros

    set "n="
    for /L %%i in (2,1,25) do set "n=!n!0 "
    set /A "i=100+A_NumGroups"
    (
    echo !i:~1! %n%1
    for /L %%i in (2,1,%A_NumGroups%) do (
        set /A i-=1
        echo !i:~1! %n%0
    )
    ) > OneR.txt
    set /A "i=101"
    (for /L %%i in (1,1,%A_NumGroups%) do (
        echo !i:~1! %n%0
        set /A i+=1
    )) > Zero.txt

    set  "upCase=A B C D E F G H I J K L M N O P Q R S T U V W X Y Z"
    set "lowCase=a b c d e f g h i j k l m n o p q r s t u v w x y z"

    REM One-char variables: $=Digit, '=Carry, #=int number for multiply/divide

    set "M_MultiplyByInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_MultiplyByInteger#=!M_MultiplyByInteger#!,$='+%%%%i*#,%%i=$%%10000,'=$/10000"
    )
    set "M_MultiplyByInteger#=!M_MultiplyByInteger#:~1!"

    set "M_DivideRbyInteger#="
    set "M_DividePreviousByInteger#="
    for %%i in (%upCase:~2%) do (
        set "M_DivideRbyInteger#=$='*10000+%%%%i,%%i=$/#,'=$%%#,!M_DivideRbyInteger#!"
        set "M_DividePreviousByInteger#=$='*10000+%%i,%%i=$/#,'=$%%#,!M_DividePreviousByInteger#!"
    )
    set "M_DivideRbyInteger#=!M_DivideRbyInteger#:~0,-1!"
    set "M_DividePreviousByInteger#=!M_DividePreviousByInteger#:~0,-1!"

    set "M_Add="
    set "M_Sum="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Add=!M_Add!,$='+%%%%i+%%%%j,%%i=$%%10000,'=$/10000"
            set "lowCase2=%%k"
        )
        set "M_Sum=!M_Sum!+%%%%i"
    )
    set "M_Add=%M_Add:~1%"
    set "M_Sum=%M_Sum:~1%"

    set "M_Subtract="
    set "lowCase2=%lowCase:~2%"
    for %%i in (%upCase:~2%) do (
        for /F "tokens=1*" %%j in ("!lowCase2!") do (
            set "M_Subtract=!M_Subtract!,$=%%%%i-%%%%j+','=$>>31,%%i=$-'*10000"
            set "lowCase2=%%k"
        )
    )
    set "M_Subtract=%M_Subtract:~1%"

    set "M_AddPrevious="
    set "M_SubtractPrevious="
    for %%i in (%lowCase:~2%) do (
        set "M_AddPrevious=!M_AddPrevious!,$='+%%%%i+%%i,%%i=$%%10000,'=$/10000"
        set "M_SubtractPrevious=!M_SubtractPrevious!,$=%%%%i-%%i+','=$>>31,%%i=$-'*10000"
    )
    set "M_AddPrevious=%M_AddPrevious:~1%"
    set "M_SubtractPrevious=%M_SubtractPrevious:~1%"

    set "M_Result="
    for %%i in (%upCase:~2%) do (
        set "M_Result=!M_Result! ^!%%i^!"
    )

    set "M_PrintAdjust="
    set "M_PrintFormat="
    for %%i in (%upCase:~2%) do (
        set "M_PrintAdjust=!M_PrintAdjust!,%%i=10000+%%%%i"
        set "M_PrintFormat=^!%%i:~1^!!M_PrintFormat!"
    )
    set "M_PrintAdjust=%M_PrintAdjust:~1%"

    set "upCase="
    set "lowCase="

exit /B

Antonio

Post Reply