Jump to content

_MathCheckDiv


JohnOne
 Share

Recommended Posts

I don't understand this function _MathCheckDiv. Why not use Mod($n1, $n2) = 0?

Func Divisible(Const $n1, Const $n2)
  Return Mod($n1, $n2) = 0
EndFunc
Edited by jaberwacky
Link to comment
Share on other sites

I've been thinking this function should remain in Math.au3 for at least two reasons: Firstly because it's easier for people who are less mathematically inclined, and secondly because it's likely already being used in many scripts; although personally, I've only seen it used in a few. Following jaberwacky's suggestion to use Mod(), here's a new version. I think the return values are strange, but maybe that's just me.

;

Func _MathCheckDiv_2($iNum1, $iNum2 = 2)
    If Not (IsInt($iNum1) And IsInt($iNum2)) Then Return SetError(1, 0, -1)
    Return (Mod($iNum1, $iNum2) = 0) ? 2 : 1
EndFunc   ;==>_MathCheckDiv_2

;

 Mod() still needs testing for limits - if that's feasible.


Preliminary tests suggest Mod may work up to 9223372036854775806, but proving this by rigorous testing is not really feasible.

;

Local $iMax = 9223372036854775806
For $i = $iMax to $iMax - 100 Step - 1
    ConsoleWrite((_MathCheckDiv_2($i) = 2) & @LF)
Next

Func _MathCheckDiv_2($iNum1, $iNum2 = 2)
    If Not (IsInt($iNum1) And IsInt($iNum2)) Then Return SetError(1, 0, -1)
    Return (Mod($iNum1, $iNum2) = 0) ? 2 : 1
EndFunc   ;==>_MathCheckDiv_2

;


I found the above value through trial and error. If I add 1 to that value it becomes 0x7FFFFFFFFFFFFFFF. So I can tell you categorically that Mod() does not work beyond Int-64. After one or two more tests, it appears Mod() has worked for all Int-64 values I've tried. It's still not safe to assume it always will work on the grounds of so few tests, but the results are promising. I'll throw a few large primes in and see what happens.

Edited by czardas
Link to comment
Share on other sites

  • 2 weeks later...

I said I would throw some primes at this and I'm happy with the results.

;#include <Array.au3>

; Testing Int64 compatible version of _MathCheckDiv()

Func _MathCheckDiv($iNum1, $iNum2 = 2)
    If Not (IsInt($iNum1) And IsInt($iNum2)) Then Return SetError(1, 0, -1)
    Return (Mod($iNum1, $iNum2) = 0) ? 2 : 1
EndFunc   ;==>_MathCheckDiv

Local $iIterations = 25
Local $aPrime[$iIterations][2], $aProduct[$iIterations], $i0, $i1

; Generate random primes for testing.
For $i = 0 To $iIterations -1
    $i0 = Random(7, 12, 1) ; number of digits in the first prime
    $i1 = 18 - $i0 ; number of digits in the second prime
    $aPrime[$i][0] = _RandomPrime($i0)
    $aPrime[$i][1] = _RandomPrime($i1)
    $aProduct[$i] = $aPrime[$i][0] * $aPrime[$i][1] ; Int64
    Sleep(50) ; We are generating large primes
Next
_ClearDivisors()

;_ArrayDisplay($aPrime)

; Basically this checks that Mod() works on the generated Int64 numbers.
For $i = 0 To $iIterations -1
    ; The following commented out line tests for non-divisible values.
    ; $aPrime[$i][0] += Random(1, 2, 1)
    ConsoleWrite( "_MathCheckDiv(" & $aProduct[$i] & ", " & $aPrime[$i][0] & ") ;==> " & _MathCheckDiv($aProduct[$i], $aPrime[$i][0]))
    ConsoleWrite("    " & $aProduct[$i] & " / " & $aPrime[$i][0] & " = " & $aProduct[$i] / $aPrime[$i][0]   & @LF )
Next

#Region - UDF

; #FUNCTION# ====================================================================================================================
; Name...........: _ClearDivisors
; Description ...: Used to clear memory after calling _IsPrime() or _RandomPrime()
; Syntax.........: _ClearDivisors()
; Author ........: czardas
; Comments ......; Call this function if you no longer need to use _IsPrime() or _RandomPrime() and want to clear memory.
; ===============================================================================================================================

Func _ClearDivisors()
    _IsPrime("", True)
EndFunc ;==> _ClearDivisors


; #FUNCTION# ====================================================================================================================
; Name...........: _IsPrime
; Description ...: Tests a number for primality.
; Syntax.........: _IsPrime($iInt [, _ClearDivisors = False])
; Parameters ....: $iInt           - The number to test
;                : $bClearDivisors - DO NOT USE THIS PARAMETER
; Return values .: Success   - Returns True, or False if not prime.
;                  Failure   - Returns False and sets @error to 1 if $iInt is not an integer or is out of range:
; Author ........: czardas
; Comments ......; This function is designed to be called multiple times but uses lots of memory.
;                ; It is advisable to use Sleep() after any batch processes to allow the CPU to cool off a little.
;                ; After using this function you should free memory by calling the function _ClearDivisors()
; ===============================================================================================================================

Func _IsPrime($iInt, $bClearDivisors = False)

    Static $aDivisors = __GetDivisors()

    If $bClearDivisors Then
        $aDivisors = 0
        Return
    ElseIf Not IsArray($aDivisors) Then
        $aDivisors = __GetDivisors()
    EndIf

    If Not IsInt($iInt) Then Return SetError(1, 0, False) ; Only integers are allowed
    $iInt = Abs($iInt)

    If $iInt = 2 Or $iInt = 3 Or $iInt = 5 Or $iInt = 7 Or $iInt = 11 Or $iInt = 13 Then Return True
    If $iInt < 2 Or Not (Mod($iInt, 2) And Mod($iInt, 3) And Mod($iInt, 5) And Mod($iInt, 7) And Mod($iInt, 11) And Mod($iInt, 13)) Then Return False

    Local $bPrime = True, $iRoot = Sqrt($iInt)
    If IsInt($iRoot) Then Return False

    $iRoot = Floor($iRoot)
    For $i = 0 To 5759
        For $j = $aDivisors[$i] To $iRoot Step 30030 ; 2 *3 *5 *7 *11 *13
            If Mod($iInt, $j) Then ContinueLoop ; $j is not a factor
            $bPrime = False
            ExitLoop 2
        Next
    Next

    Return $bPrime
EndFunc ;==> _IsPrime


; #FUNCTION# ====================================================================================================================
; Name...........: _RandomPrime
; Description ...: Returns a random prime number of between 1 and 15 digits in length (length must be specified).
; Syntax.........: _RandomPrime($iDigits)
; Parameters ....: $iDigits - The number of digits in the random prime number
; Return values .: Success   - Returns a random prime number with the required number of digits.
;                  Failure   - Sets @error to 1 if $iDigits is not an integer or is out of range:
; Author ........: czardas
; Comments ......; This function is designed to be called multiple times but uses lots of memory.
;                ; It is advisable to use Sleep() after any batch processes to allow the CPU to cool off a little.
;                ; After using this function you should free memory by calling the function _ClearDivisors()
; ===============================================================================================================================

Func _RandomPrime($iDigits)
    If Not IsInt($iDigits) Or $iDigits < 1 Or $iDigits > 15 Then Return SetError(1)
    $iDigits -= 1

    Local $iNumber = Number(Random(1, 9, 1) & StringMid(Random(), 3, $iDigits))
    Local $iStep = 1 -2*Random(0, 1, 1)

    Local $aRange[15][2] = [[2,7],[11,97],[101,997],[1009,9973],[10007,99991],[100003,999983], _
    [1000003,9999991],[10000019,99999989],[100000007,999999937],[1000000007,9999999967], _
    [10000000019,99999999977],[100000000003,999999999989],[1000000000039,9999999999971], _
    [10000000000037,99999999999973],[100000000000031,999999999999989]]

    If $iNumber > $aRange[$iDigits][1] And $iStep = 1 Then
        $iNumber = $aRange[$iDigits][0]
    ElseIf $iNumber < $aRange[$iDigits][0] And $iStep = -1 Then
        $iNumber = $aRange[$iDigits][1]
    EndIf

    While 1 ; Do not increase the size of the next loop.
        For $iNumber = $iNumber To $iNumber + 10*$iStep Step $iStep
            If _IsPrime($iNumber) Then ExitLoop 2
        Next
        Sleep($iDigits + 16) ; CPU needs to cool down.
    WEnd
    Return $iNumber
EndFunc ;==> _RandomPrime


; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name...........: __GetDivisors
; Description ...: Sieve to remove prime multiples from the number range 17 to 30045.
; Syntax.........: __GetDivisors()
; Return values .: Success   - Returns an array of the remaining numbers within the range.
; Author ........: czardas
; Comments ......; This function is called internally by _IsPrime
; ===============================================================================================================================

Func __GetDivisors()
    Local $aPrimes[5] = [3,5,7,11,13], $aNumbers[15015], $iTemp
    For $i = 0 To 15014
        $iTemp = $i*2 +17 ; Produces odd numbers from 17 to 30045
        For $j = 0 To 4
            If Not Mod($iTemp, $aPrimes[$j]) Then ContinueLoop 2 ; Skip multiples of 3, 5, 7, 11 and 13
        Next
        $aNumbers[$i] = $iTemp
    Next
    $iTemp = 0

    Local $aDivisors[5760] ; Will only contain the factors that made it through the sieve.
    For $i = 0 To 15014
        If $aNumbers[$i] Then
            $aDivisors[$iTemp] = $aNumbers[$i]
            $iTemp += 1
        EndIf
    Next
    Return $aDivisors
EndFunc ;==> __GetDivisors

#EndRegion
Edited by czardas
Link to comment
Share on other sites

  • 3 months later...

Why all those concern about Mod()?

Anyway and beyond the fact that this function is a complete idiocy (including the dividend default and the returned values), input parameters can also be allowed to be integers in string form, thus the tests for IsInt are a waste. Using Int() on inputs would make more sense. And making the return value a boolean would have been wise (Jeez).

Anyway, the following code works as specified and expected in the release (even stringed integers), but the beta fails when inputs are stringed ints or when divisor = 0

#include <Math.au3>

ConsoleWrite(_MathCheckDiv(123456, 3) & @LF)
ConsoleWrite((Mod(123456, 3) ? 1 : 2) & @LF)
ConsoleWrite((Mod(Int("123456"), Int("3")) ? 1 : 2) & @LF)
Local $res = _MathCheckDiv("123456", "3")
ConsoleWrite("Error = " & @error & ", returns " & $res & @LF & @LF)

ConsoleWrite(_MathCheckDiv(123457, 3) & @LF)
ConsoleWrite((Mod(123457, 3) ? 1 : 2) & @LF)
ConsoleWrite((Mod(Int("123457"), Int("3")) ? 1 : 2) & @LF)
$res = _MathCheckDiv("123457", "3")
ConsoleWrite("Error = " & @error & ", returns " & $res & @LF & @LF)

$res = _MathCheckDiv(1, 0)
ConsoleWrite("Error = " & @error & ", returns " & $res & @LF)

 

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Hmm, my (script breaking) mistake: I didn't realise that strings were originally allowed in this function.

I agree with you on the return values being silly. I also mentioned this when I submitted the changes, but nobody agreed (nor did they disagree). What exactly would you have happen when the divisor = 0? Surely an error is the most sensible return value here, or would you rather the function return True because all numbers are divisible by zero an infinite number of times? - Confusing! Oops!

Edited by czardas
Link to comment
Share on other sites

Let's see: ConsoleWrite(VarGetType(1/0) & " : " & 1/0 & @LF) gives Double : 1.#INF

No, no integer is divisible by zero (division by zero is not allowed in the integers) so of course an error should result. You probably meant that zero is divisible by any non-zero integer, that is 0/x = 0 is true for any x <> 0

The (minor) issue with the (mad) return value is that making it a boolean would break a few (silly) scripts, but I wouldn't cry for long should that occur.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

I thought that result 1.#INF meant infinity. Division by zero was a success: in that it gave an appropriate answer. I would rather an undefined boolean return, but that doesn't exist in AutoIt. Perhaps it is simply a meaningless result.

Func _MathCheckDiv($iNum1, $iNum2)
    $iNum1 = Int($iNum1)
    $iNum2 = Int($iNum2)
    If $iNum2 = 0 Then Return SetError(1, 0, False) ; This return value worries me.
    Return Mod($iNum1, $iNum2) = 0 ; True or False
EndFunc   ;==>_MathCheckDiv

 

Edited by czardas
Link to comment
Share on other sites

Infinity is not an integer nor a real value and it represents a failure here. Null can (should probably) be that value but it isn't used at all in std functions return values.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

In my view Null is the best return value in case of error, meaning "can't deliver a meaningful result" or "no data in this result", quite similarly to what Null means in SQL (yes, again, I know, I know!). Others may object since Null is relatively recent and has been little used openly AFAIK. Its advantage is that it isn't itself any computable type (string, int, double, boolean, ...) even if it can be converted into one.

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Null is the best return value in case of error

At least when no more sensible result form is available, like in our case. Now for instance when a function is supposed to return an array but, while no actual error occured, there is nothing to populate the returned array then I'd like to get an empty array instead of an error. From this point of view, there is a lot of discrepancies in the conventions used along the various functions and UDFs. But it's way too late for v3, must await v4 for that...

This wonderful site allows debugging and testing regular expressions (many flavors available). An absolute must have in your bookmarks.
Another excellent RegExp tutorial. Don't forget downloading your copy of up-to-date pcretest.exe and pcregrep.exe here
RegExp tutorial: enough to get started
PCRE v8.33 regexp documentation latest available release and currently implemented in AutoIt beta.

SQLitespeed is another feature-rich premier SQLite manager (includes import/export). Well worth a try.
SQLite Expert (freeware Personal Edition or payware Pro version) is a very useful SQLite database manager.
An excellent eBook covering almost every aspect of SQLite3: a must-read for anyone doing serious work.
SQL tutorial (covers "generic" SQL, but most of it applies to SQLite as well)
A work-in-progress SQLite3 tutorial. Don't miss other LxyzTHW pages!
SQLite official website with full documentation (may be newer than the SQLite library that comes standard with AutoIt)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...