YellowLab Posted March 20, 2009 Share 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. Link to comment Share on other sites More sharing options...
gseller Posted March 20, 2009 Share Posted March 20, 2009 Cool, now it just needs a GUI to plug in variables and your good to go.. Nice start.. Link to comment Share on other sites More sharing options...
YellowLab Posted March 30, 2009 Author Share 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. Link to comment Share on other sites More sharing options...
3xM3NT4Lx4 Posted March 30, 2009 Share Posted March 30, 2009 Nice work. I was wondering if that same method could be used for a basic arithmetic script? Link to comment Share on other sites More sharing options...
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