YellowLab Posted March 20, 2009 Posted March 20, 2009 I had a need to add fractions and display them as strings and couldn't find a function to do that. So, I created one and thought I would share it.Input are two "strings" representing the fractions. Valid inputs are integers or strings in the following formats: "#", "#/#" or "# #/#"These formats were chosen because they work for my script.Output is either a two row array in the form of [Numerator, Denominator] or a string in the same format as the inputs.The function uses PSaltyDS's Factor function found here to reduce the fraction to its lowest denominator._ArraySearch and _ArrayDelete are also used from Array.au3 to assist in the reduced fraction calculation."Fractions.au3"CODE#include-once#include <Array.au3>;input $sFraction1 and $sFraction2 can either be a string "#", "#/#", "# #/#" or an Int;input $Result can either be "ARRAY" for the result to be returned as a two row array, [Numerator, Denominator];or anything else to return it as a string "#", "#/#", "# #/#";@error = 1, input incorrectly formated, extended=1->first, extended=2->second;@error = 2, multiple /'s, extended=1->first, extended=2->secondFunc _AddFractions($sFraction1, $sFraction2, $Result="STRING") Local $nNum1, $nDen1, $nNum2, $nDen2, $nNumSum, $nDenSum, $nIndex Local $arTemp, $arFNum, $arFDen ;Test for valid input (integers or strings) If NOT IsInt($sFraction1) Then If IsString($sFraction1) Then If NOT IsInt(Number($sFraction1)) Then If StringInStr($sFraction1,"/") = -1 Then SetError(1,1) Return -1 EndIf Else ;check for zero If $sFraction1<>"0" And Number($sFraction1)=0 Then SetError(1,1) Return -1 EndIf EndIf Else ;not a string, not an int SetError(1,1) Return -1 EndIf EndIf If Not IsInt($sFraction2) Then If IsString($sFraction2) Then If NOT IsInt(Number($sFraction2)) Then If StringInStr($sFraction2,"/") = -1 Then SetError(1,2) Return -1 EndIf Else ;check for zero If $sFraction2<>"0" And Number($sFraction2)=0 Then SetError(1,2) Return -1 EndIf EndIf Else ;not a string, not an int SetError(1,2) Return -1 EndIf EndIf If IsInt($sFraction1) Then $nNum1=$sFraction1 $nDen1=1 Else $arTemp=StringSplit($sFraction1,'/') If $arTemp[0] > 2 Then SetError(2,1) Return -1 ElseIf $arTemp[0]=1 Then $nNum1=$arTemp[1] $nDen1=1 Else $nNum1=$arTemp[1] $nDen1=$arTemp[2] $arTemp=StringSplit($nNum1," ") If $arTemp[0]=2 Then $nNum1=$arTemp[1]*$nDen1+$arTemp[2] EndIf EndIf EndIf If IsInt($sFraction2) Then $nNum2=$sFraction2 $nDen2=1 Else $arTemp=StringSplit($sFraction2,'/') If $arTemp[0] > 2 Then SetError(2,2) Return -1 ElseIf $arTemp[0]=1 Then $nNum2=$arTemp[1] $nDen2=1 Else $nNum2=$arTemp[1] $nDen2=$arTemp[2] $arTemp=StringSplit($nNum2," ") If $arTemp[0]=2 Then $nNum2=$arTemp[1]*$nDen2+$arTemp[2] EndIf EndIf EndIf $nNumSum=$nNum1*$nDen2+$nNum2*$nDen1 $nDenSum=$nDen1*$nDen2 ;reduce fraction $arFNum=_Factor($nNumSum) $arFDen=_Factor($nDenSum) If $arFNum[0] < $arFDen[0] Then For $i=2 To UBound($arFNum)-1 $nIndex=_ArraySearch($arFDen,$arFNum[$i]) If $nIndex>1 Then _ArrayDelete($arFDen,$nIndex) $nNumSum=$nNumSum/$arFNum[$i] $nDenSum=$nDenSum/$arFNum[$i] EndIf Next Else For $i=2 To Ubound($arFDen)-1 $nIndex=_ArraySearch($arFNum,$arFDen[$i]) If $nIndex>1 Then _ArrayDelete($arFNum,$nIndex) $nNumSum=$nNumSum/$arFDen[$i] $nDenSum=$nDenSum/$arFDen[$i] EndIf Next EndIf If $Result="ARRAY" Then Dim $arTemp[2]=[$nNumSum,$nDenSum] Return $arTemp Else Local $sTemp If Floor($nNumSum/$nDenSum)=0 Then $sTemp=$nNumSum&'/'&$nDenSum Else $sTemp=Floor($nNumSum/$nDenSum)&' '&Mod($nNumSum,$nDenSum)&'/'&$nDenSum EndIf Return $sTemp EndIf EndFunc ;==>_AddFractions;http://www.autoitscript.com/forum/index.php?showtopic=57541&st=15&p=455305&#entry455305; ----------------------------------------------------------------------; Function: _Factor(); Purpose: Return all factors of a signed integer; Syntax: _Factor($i); Where: $i = signed integer value; Returns: A 1D array with [0] = count of factors; On success: [1] = 1 or -1 depending on sign of input $i, [2] thru [n] = prime factors; On failure: [0] = 0 and sets @error; Notes: Based on the simplest "naive" prime number test, sped up some by inclusion; of a short prime number table.; The number 1 is not normally given as a prime factor, but is included so; the function can handle signed/unsigned numbers in a predictable manner.; Element [1] of the array will be 1 or -1 depending on the sign of the input.; Returns @error for non-integer or 0 inputs.; Author: PsaltyDS at www.autoitscript.com/forum; Version: 1.0.0 dated 28 December, 2007; ----------------------------------------------------------------------Func _Factor($i) Local $avRET[1] = [0] Local $iTest, $iWorking, $iOddNum ; Test for valid input If IsInt($i) = 0 Then SetError(1) Return $avRET ElseIf $i = 0 Then SetError(2) Return $avRET EndIf ; Check for negative number Local $avRET[1024] = [1, 1] If $i < 0 Then $avRET[1] = -1 $i = $i * - 1 EndIf $iWorking = $i ; Quick return for input = 1 If $iWorking = 1 Then ReDim $avRET[2] Return $avRET EndIf ; Initial table of primes Local $avPrimes[201] = [200, _ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, _ 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, _ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, _ 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, _ 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, _ 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, _ 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, _ 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, _ 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, _ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, _ 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, _ 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, _ 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, _ 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, _ 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, _ 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, _ 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, _ 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, _ 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, _ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223] ; Use static table of primes to start with For $p = 1 To $avPrimes[0] While 1 $iTest = Mod($iWorking, $avPrimes[$p]) If $iTest = 0 Then ; This is a factor $avRET[0] += 1 $avRET[$avRET[0]] = $avPrimes[$p] $iWorking = $iWorking / $avPrimes[$p] If $iWorking = 1 Then ; Last factor found ExitLoop 2 Else ; Try same prime again ContinueLoop EndIf Else ; This is not a factor, next prime ExitLoop EndIf WEnd Next ; Continue if remaining factors are larger than the static primes If $iWorking > $avPrimes[$avPrimes[0]] Then $iOddNum = $avPrimes[$avPrimes[0]] + 2 While 1 While 1 $iTest = Mod($iWorking, $iOddNum) If $iTest = 0 Then ; This is a factor $avRET[0] += 1 $avRET[$avRET[0]] = $iOddNum $iWorking = $iWorking / $iOddNum If $iWorking = 1 Then ; Last factor found ExitLoop 2 Else ; Try same odd number again ContinueLoop EndIf Else ; This not a factor, next odd number ExitLoop EndIf WEnd $iOddNum += 2 If $iOddNum > Int(Sqrt($i)) Then ; Remaining number is prime = last factor $avRET[0] += 1 $avRET[$avRET[0]] = $iWorking ExitLoop EndIf WEnd EndIf ; Resize the array and return ReDim $avRET[$avRET[0] + 1] Return $avRETEndFunc ;==>_Factortestfractions.au3CODE#include "Fractions.au3"$nTemp1=_AddFractions("1/16", "3/4")$nTemp2=_AddFractions("3 21/43","5 12/103")$nTemp3=_AddFractions(3,"2/14")$nTemp4=_AddFractions("3","7/59")$nTemp5=_AddFractions(4,5)$nTemp6=_AddFractions("9/16","9/16","ARRAY");to reduce a fraction, just add zero$nTemp7=_AddFractions("33/99",0)ConsoleWrite('1/16 + 3/4 = '&$nTemp1&@crlf& _ '3 21/43 + 5 12/103 = '&$nTemp2&@CRLF& _ '3 (int) + 2/14 = '&$nTemp3&@CRLF& _ '3 + 7/59 = '&$nTemp4&@CRLF& _ '4 (int) + 5 (int) = '&$nTemp5&@CRLF& _ 'Reduce 33/99 => '&$nTemp7&@CRLF)_ArrayDisplay($nTemp6)Bob You can't see a rainbow without first experiencing the rain.
gseller Posted March 20, 2009 Posted March 20, 2009 Cool, now it just needs a GUI to plug in variables and your good to go.. Nice start..
YellowLab Posted March 30, 2009 Author Posted March 30, 2009 (edited) Just a quick little update to handle float values. If one (or both) of the inputs are type Float, then the resultant sum will be returned as a type Float. "fractions.au3" CODE ;input $sFraction1 and $sFraction2 can either be a string "#", "#/#", "# #/#" or an Int or Float ;input $Result can either be "ARRAY" for the result to be returned as a two row array, [Numerator, Denominator] ;or anything else to return it as a string "#", "#/#", "# #/#" ;if either input is a Float then the sum of the two is returned as a Float ;@error = 1, input incorrectly formated, extended=1->first, extended=2->second ;@error = 2, multiple /'s, extended=1->first, extended=2->second Func _AddFractions($sFraction1, $sFraction2, $Result="STRING") Local $nNum1, $nDen1, $nNum2, $nDen2, $nNumSum, $nDenSum, $nIndex Local $arTemp, $arFNum, $arFDen Local $b1Float=False, $b2Float=False ;Test for valid input (integers or strings) If NOT IsInt($sFraction1) Then If IsString($sFraction1) Then If NOT IsInt(Number($sFraction1)) Then If StringInStr($sFraction1,"/") = -1 Then SetError(1,1) Return -1 EndIf Else ;check for zero If $sFraction1<>"0" And Number($sFraction1)=0 Then SetError(1,1) Return -1 EndIf EndIf Else If IsFloat($sFraction1) Then $b1Float=True Else ;not a string, not an int SetError(1,1) Return -1 EndIf EndIf EndIf If Not IsInt($sFraction2) Then If IsString($sFraction2) Then If NOT IsInt(Number($sFraction2)) Then If StringInStr($sFraction2,"/") = -1 Then SetError(1,2) Return -1 EndIf Else ;check for zero If $sFraction2<>"0" And Number($sFraction2)=0 Then SetError(1,2) Return -1 EndIf EndIf Else If IsFloat($sFraction2) Then $b2Float=True Else ;not a string, not an int SetError(1,2) Return -1 EndIf EndIf EndIf ;If $sFraction1 or $sFraction2 is a floating point return the floating point addendum If BitOr($b1Float,$b2Float) Then If IsFloat($sFraction1) AND IsFloat($sFraction2) Then Return $sFraction1+$sFraction2 If IsFloat($sFraction1) Then $arTemp=_StringToNumDen($sFraction2) Return $sFraction1+$arTemp[0]/$arTemp[1] Else $arTemp=_StringToNumDen($sFraction1) Return $sFraction2+$arTemp[0]/$arTemp[1] EndIf EndIf $arTemp=_StringToNumDen($sFraction1) If $arTemp=-1 Then SetError(2,1) Return -1 Else $nNum1=$arTemp[0] $nDen1=$arTemp[1] EndIf $arTemp=_StringToNumDen($sFraction2) If $arTemp=-1 Then SetError(2,2) Return -1 Else $nNum2=$arTemp[0] $nDen2=$arTemp[1] EndIf $nNumSum=$nNum1*$nDen2+$nNum2*$nDen1 $nDenSum=$nDen1*$nDen2 ;reduce fraction $arFNum=_Factor($nNumSum) $arFDen=_Factor($nDenSum) If $arFNum[0] < $arFDen[0] Then For $i=2 To UBound($arFNum)-1 $nIndex=_ArraySearch($arFDen,$arFNum[$i]) If $nIndex>1 Then _ArrayDelete($arFDen,$nIndex) $nNumSum=$nNumSum/$arFNum[$i] $nDenSum=$nDenSum/$arFNum[$i] EndIf Next Else For $i=2 To Ubound($arFDen)-1 $nIndex=_ArraySearch($arFNum,$arFDen[$i]) If $nIndex>1 Then _ArrayDelete($arFNum,$nIndex) $nNumSum=$nNumSum/$arFDen[$i] $nDenSum=$nDenSum/$arFDen[$i] EndIf Next EndIf If $Result="ARRAY" Then Dim $arTemp[2]=[$nNumSum,$nDenSum] Return $arTemp Else Local $sTemp If Floor($nNumSum/$nDenSum)=0 Then $sTemp=$nNumSum&'/'&$nDenSum Else If Mod($nNumSum,$nDenSum) = 0 Then $sTemp=Floor($nNumSum/$nDenSum) Else $sTemp=Floor($nNumSum/$nDenSum)&' '&Mod($nNumSum,$nDenSum)&'/'&$nDenSum EndIf EndIf Return $sTemp EndIf EndFunc ;==>_AddFractions ;converts String to 2 element array - 1st is numerator, 2nd is denominator, -1 is fail Func _StringToNumDen($sFraction1) Local $nDen1, $nNum1, $arTemp If IsInt($sFraction1) Then $nNum1=$sFraction1 $nDen1=1 Else $arTemp=StringSplit($sFraction1,'/') If $arTemp[0] > 2 Then Return -1 ElseIf $arTemp[0]=1 Then $nNum1=$arTemp[1] $nDen1=1 Else $nNum1=$arTemp[1] $nDen1=$arTemp[2] $arTemp=StringSplit($nNum1," ") If $arTemp[0]=2 Then $nNum1=$arTemp[1]*$nDen1+$arTemp[2] EndIf EndIf EndIf Dim $arTemp[2]=[$nNum1,$nDen1] Return $arTemp EndFunc ;==>_StringToNumDen ;http://www.autoitscript.com/forum/index.php?showtopic=57541&st=15&p=455305&#entry455305 ; ---------------------------------------------------------------------- ; Function: _Factor() ; Purpose: Return all factors of a signed integer ; Syntax: _Factor($i) ; Where: $i = signed integer value ; Returns: A 1D array with [0] = count of factors ; On success: [1] = 1 or -1 depending on sign of input $i, [2] thru [n] = prime factors ; On failure: [0] = 0 and sets @error ; Notes: Based on the simplest "naive" prime number test, sped up some by inclusion ; of a short prime number table. ; The number 1 is not normally given as a prime factor, but is included so ; the function can handle signed/unsigned numbers in a predictable manner. ; Element [1] of the array will be 1 or -1 depending on the sign of the input. ; Returns @error for non-integer or 0 inputs. ; Author: PsaltyDS at www.autoitscript.com/forum ; Version: 1.0.0 dated 28 December, 2007 ; ---------------------------------------------------------------------- Func _Factor($i) Local $avRET[1] = [0] Local $iTest, $iWorking, $iOddNum ; Test for valid input If IsInt($i) = 0 Then SetError(1) Return $avRET ElseIf $i = 0 Then SetError(2) Return $avRET EndIf ; Check for negative number Local $avRET[1024] = [1, 1] If $i < 0 Then $avRET[1] = -1 $i = $i * - 1 EndIf $iWorking = $i ; Quick return for input = 1 If $iWorking = 1 Then ReDim $avRET[2] Return $avRET EndIf ; Initial table of primes Local $avPrimes[201] = [200, _ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, _ 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, _ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, _ 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, _ 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, _ 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, _ 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, _ 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, _ 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, _ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, _ 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, _ 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, _ 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, _ 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, _ 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, _ 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, _ 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, _ 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, _ 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, _ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223] ; Use static table of primes to start with For $p = 1 To $avPrimes[0] While 1 $iTest = Mod($iWorking, $avPrimes[$p]) If $iTest = 0 Then ; This is a factor $avRET[0] += 1 $avRET[$avRET[0]] = $avPrimes[$p] $iWorking = $iWorking / $avPrimes[$p] If $iWorking = 1 Then ; Last factor found ExitLoop 2 Else ; Try same prime again ContinueLoop EndIf Else ; This is not a factor, next prime ExitLoop EndIf WEnd Next ; Continue if remaining factors are larger than the static primes If $iWorking > $avPrimes[$avPrimes[0]] Then $iOddNum = $avPrimes[$avPrimes[0]] + 2 While 1 While 1 $iTest = Mod($iWorking, $iOddNum) If $iTest = 0 Then ; This is a factor $avRET[0] += 1 $avRET[$avRET[0]] = $iOddNum $iWorking = $iWorking / $iOddNum If $iWorking = 1 Then ; Last factor found ExitLoop 2 Else ; Try same odd number again ContinueLoop EndIf Else ; This not a factor, next odd number ExitLoop EndIf WEnd $iOddNum += 2 If $iOddNum > Int(Sqrt($i)) Then ; Remaining number is prime = last factor $avRET[0] += 1 $avRET[$avRET[0]] = $iWorking ExitLoop EndIf WEnd EndIf ; Resize the array and return ReDim $avRET[$avRET[0] + 1] Return $avRET EndFunc ;==>_Factor "fractiontest.au3" CODE #include "Fractions.au3" $nTemp1=_AddFractions("1/16", "3/4") $nTemp2=_AddFractions("3 21/43",.25) $nTemp3=_AddFractions(3,"2/14") $nTemp4=_AddFractions("3","7/59") $nTemp5=_AddFractions(4,5) $nTemp6=_AddFractions("9/16","9/16","ARRAY") ;to reduce a fraction, just add zero $nTemp7=_AddFractions("33/99",0) ConsoleWrite('1/16 + 3/4 = '&$nTemp1&@crlf& _ '3 21/43 + 5 12/103 = '&$nTemp2&@CRLF& _ '3 (int) + 2/14 = '&$nTemp3&@CRLF& _ '3 + 7/59 = '&$nTemp4&@CRLF& _ '4 (int) + 5 (int) = '&$nTemp5&@CRLF& _ 'Reduce 33/99 => '&$nTemp7&@CRLF) _ArrayDisplay($nTemp6) Bob Edited March 30, 2009 by YellowLab You can't see a rainbow without first experiencing the rain.
3xM3NT4Lx4 Posted March 30, 2009 Posted March 30, 2009 Nice work. I was wondering if that same method could be used for a basic arithmetic script?
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now