Version 0.7 implements handling of fractions as well as reciprocals.
Code:
LOCAL CRID:="REPEATING DECIMAL V 0.7 \n©2018 StephenG1CMZ";
LOCAL ALGM:="Following Joe Horn's algorithm: http://www.hpmuseum.org/forum/thread-9919.html";
//Written 2018 StephenG1CMZ
//Following Joe Horn`s algorithm using Multiplicative Order
//This ppl implement yields useful results using CAS
//to provide Transients and Repetends>12 digits
//Interesting Test Values:
//0,1,2,3,4,7,14,208,301,8192,8912
LOCAL FR2,FR5;//GLOBAL RETURN
LOCAL WHATSLEFT,NDIGITS;//GLOBAL RETURN
LOCAL ImpLimST:="MultiplicativeOrder10: Implementation Limit!\n";
EXPORT TransientLEN;//Length of Transient
EXPORT RepetendLEN;//Length of Recurring Digits/Repeating Digits
//^Len == Decimal Digits Only
EXPORT USCORE:="_";//CHANGE TO EMPTY STRING IF YOU WISH TO EVAL THE STRING
EXPORT DECSEP:=".";//DECIMAL SEPARATOR (USED WHEN TRANSIENTLEN=0,IDEALLY GET FROM SYSTEM)
EXPORT TESTED:=0;
EXPORT BigT:=0;
EXPORT BigR:=0;
LOCAL DIVBY0:="(NaN)(DIVBY0)";
MultiplicativeOrder10();//FORWARD
EXPORT ABOUT()
BEGIN
PRINT();
PRINT(CRID);
PRINT(ALGM);
END;
EXPORT HELP()
BEGIN
PRINT();
PRINT(CRID);
PRINT(" Discovers characteristics of rational fractions that can be specified by an integer numerator and denominator.");
PRINT(" ");
PRINT("NB Fractions are input as 2 integers.\nDo not enter as Prime fractions.");
PRINT(" ");
PRINT("Scroll or Esc...");
PRINT(" ");
PRINT("API functions:");
PRINT(" ");
PRINT("GetLEN(fraction):");
PRINT("Returns lengths and Sets LENgth variables.\nLengths exclude the IP and non-digits.");
PRINT("GetLEN(1,3)={0,1}");
PRINT(" ");
PRINT("Recurring(fraction):");
PRINT("Repeating(fraction):");
PRINT("Returns a string of the real value with _ marking any recurring digits.");
PRINT("Recurring(1,3)=0._3");
PRINT(" ");
PRINT("MultiplicativeOrder10(WhatsLeft):");
PRINT("Implemented for base 10.");
PRINT(" ");
PRINT("Procedures:");
PRINT(" ");
PRINT("Show(fraction):");
PRINT("Show Representations on_screen");
PRINT("Show(1,3)");
PRINT(" ");
PRINT("ShowRange(Numer1,Numer2,Denom1,Denom2):");
PRINT("Show Range of fractions one by one\nShowRange(1,0,3,0) = Show(1,3)");
PRINT("ShowRange(1,2,3,4)=Show(1,3),Show(1,4),Show(2,3),Show(2,4)");
PRINT(" ");
PRINT("ShowReciprocals(Denom1,Denom2):");
PRINT("Shows a range of reciprocal values one by one.\nShowReciprocals(3,0)=Show(1,3)");
PRINT("ShowReciprocals(1,3)=Show(1,1),Show(1,2),Show(1,3)");
PRINT("");
PRINT("Variables In: Strings DECSEP,USCORE ");
PRINT("Variables Out: RepetendLEN, TransientLEN");
PRINT(" ");
PRINT("Hint: To PRINT long strings,");
PRINT("PRINT(a few bytes at a time)");
END;
//STANDARD ROUTINES
DIGITSNEEDED(NN)
//DIGITS NEEDED FOR IP
//EXCLUDES SIGN POINT SEPARATOR
BEGIN
IP(LOG(MAX(1,ABS(NN))))+1;
END;
ZEROS(NN)
//Recursive is faster than using CAS
//and can output more zeros than PPL
BEGIN
//MSGBOX(NN);
IF NN>0 THEN
RETURN "0"+ZEROS(NN-1);
END;
RETURN "";
END;
//END STANDARD
MaxFactor25(NN)
BEGIN
LOCAL MAXFACTR:=0;
LOCAL LST,LSTB,LP,II;
LST:=mat2list(ifactors(NN)); //BASES AND COUNTSETC
//EXTRACT BASES (2,5) ETC
LSTB:=IFTE(SIZE(LST),MAKELIST(LST(2*II-1),II,1,(SIZE(LST)/2)),{});//{} HANDLES NN=1
FR2:=0; FR5:=0;
LP:=POS(LSTB,2);
IF LP THEN
FR2:=LST(2*LP);//EXPONENT
END;
LP:=POS(LSTB,5);
IF LP THEN
FR5:=LST(2*LP);//EXPONENT
END;
MAXFACTR:=MAX(FR2,FR5);
RETURN MAXFACTR;//0=NONE
END;
EXPORT GetLEN(NumerN,DenomN)
//Get TransientLEN,RepetendLEN,NDIGITS
//These lengths are decimal places and exclude IPLEN
//0 DENOMINATOR: RETURN {0,0,0}
BEGIN
LOCAL RedNUMER,RedDENOM;
IF DenomN THEN
//Step 0: Reduce Fraction
RedNUMER:=CAS("numer(NumerN/DenomN)");
RedDENOM:=CAS("denom(NumerN/DenomN)");
IF RedNUMER≠NumerN OR RedDENOM≠DenomN THEN
RETURN GetLEN(RedNUMER,RedDENOM);
END;
//Continue
TransientLEN:=MaxFactor25(DenomN);
WHATSLEFT:=exact(DenomN/(2^FR2*5^FR5));
RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);
NDIGITS:=TransientLEN+RepetendLEN;
//PRINT({TransientLEN,RepetendLEN});
ELSE //DIVBY0
TransientLEN:=0;
RepetendLEN:=0;
NDIGITS:=0;
END;
RETURN {TransientLEN,RepetendLEN,NDIGITS};
END;
ReDIGITS (NUMER,NN)
//Determine Digits of the fraction (NUMER/NN)
//NUMERATOR INTEGER
//DENOMINATOR INTEGER
//DENOMINATOR 0 RETURNS NAN
BEGIN
LOCAL LST;//UNUSED
LOCAL ST:="";//STRINGVERSION
LOCAL STR:="";
LOCAL TRANSIENTPART,REPETEND,INTPART;
LOCAL RESULTS;
LOCAL DP;//DECIMAL PLACES
LOCAL IPLEN;
LOCAL RedNUMER,RedDENOM;
//Step 0: reduce fraction
RedNUMER:=CAS("numer(NUMER/NN)");
RedDENOM:=CAS("denom(NUMER/NN)");
IF RedNUMER≠NUMER OR RedDENOM≠NN THEN
RETURN ReDIGITS(RedNUMER,RedDENOM);
END;
//Continue...
//Get Length
//A possible optimisation:
//Avoid recalc
LST:=GetLEN(NUMER,NN);
//TBD:IF EXCESSIVE LENGTH RETURN NAN
//GetDecimalPlaces
//RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS
CAS("temporary:=iquo(((NUMER*10^NDIGITS),NN)");
ST:=CAS("string(temporary)");
//MSGBOX(ST);
STR:=ZEROS(NDIGITS-DIM(ST));
DP:=STR+ST;
INTPART:=IFTE(NN,(IP(NUMER/NN)+DECSEP),DIVBY0);
//IF NUMER==1 THEN
//RECIPROCAL:iquo returns decimal places only
IF NN AND NUMER≠1 THEN
//FRACTION: iquo returns IP and decimals with no point
IPLEN:=DIGITSNEEDED(IP(NUMER/NN));//LEN OF IP
//INTPART:=LEFT(DP,IPLEN)+DECSEP; //IP
DP:=MID(DP,IPLEN+1); //DP
END;
//GetRepetend
REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
//GetTransientDecimalPlaces
TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
//Get IntegerPart
RESULTS:=INTPART+TRANSIENTPART+USCORE+REPETEND;
//PRINT("RESULTS: "+RESULTS);
//Return a formatted string
//(Relevant Lengths are returned globally in ...LEN variables)
RETURN RESULTS;
END;
EXPORT Recurring(NumerN,DenomN)
BEGIN
RETURN ReDIGITS(NumerN,DenomN);
END;
//Some call them Recurring
//Some call them Repeating
EXPORT Repeating(NumerN,DenomN)
BEGIN
RETURN ReDIGITS(NumerN,DenomN);
END;
GetTransient(NUMER,NN)
//NB Only when USCORE NOT EMPTY
//NB RECALCULATES
BEGIN
LOCAL ST:=ReDIGITS(NUMER,NN);
RETURN LEFT(ST,INSTRING(ST,USCORE));
END;
GetTransientToo(NUMER,NN,ST)
//Requires ST to supply digits an4 USCORE
BEGIN
RETURN LEFT(ST,INSTRING(ST,USCORE));
END;
EXPORT Show(NumerN,DenomN)
BEGIN
LOCAL PXP:={0};
LOCAL TRANSIENTPART;
LOCAL DST;
LOCAL RedDENOM;
RECT_P();
TEXTOUT_P(CRID,0,0,3);
//TEXTOUT_P(ALGM,0,15,1);
TEXTOUT_P("Fraction,Reduced Fraction,Real,Decimal:",0,40);
// COMMON REPRESENTATIONS
TEXTOUT_P(NumerN+"/"+DenomN,0,60);
TEXTOUT_P(CAS(NumerN/DenomN)+" Reduced Fraction",0,80);
TEXTOUT_P(IFTE(DenomN,(NumerN/DenomN),(DIVBY0))+" Real",0,100);
//EARLY LENGTH
//Note:This is recalculated in ReDIGITS
GetLEN(NumerN,DenomN);
TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
//Main Display
DST:=Recurring(NumerN,DenomN);
//TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);//IF NOT EARLY
PXP(0):=TEXTOUT_P(DST+" Decimal",0,120);
TRANSIENTPART:=GetTransientToo(NumerN,DenomN,DST);
PXP(0):=TEXTOUT_P(TRANSIENTPART+" Transient Part",0,140);
IF MAX(PXP)≥320 THEN //OFFSCREEN
TEXTOUT_P("PRINT(Recurring(NN)) may show more digits",0,200,3,RGB(255,0,0));
END;
TEXTOUT_P("Esc to continue",0,220);
WAIT;
END;
EXPORT ShowRange(Numer1,Numer2,Denom1,Denom2)
BEGIN
LOCAL II,JJ;
FOR II FROM Numer1 TO MAX(Numer2,Numer1) DO
FOR JJ FROM Denom1 TO MAX(Denom2,Denom1) DO
Show(II,JJ);
END;
END;
END;
EXPORT ShowReciprocals(FIRST,LAST)
BEGIN
LOCAL NN;
FOR NN FROM FIRST TO MAX(LAST,FIRST) DO
Show(1,NN);
END;
END;
EXPORT MultiplicativeOrder10(WhatsLeft)
//See http://www.hpmuseum.org/forum/thread-3212.html
// WHATSLEFT
BEGIN
LOCAL AP:=0;
LOCAL NOTFND:=1;
LOCAL LST:={};//POSSIBLES TO CHECK
IF WhatsLeft≠1 THEN
//Get possibles
LST:=mat2list(idivis(euler(WhatsLeft)));
IF TYPE(LST)≠TYPE({}) THEN //EMPIRICAL TEST
//EG NN=1ᴇ14
MSGBOX(ImpLimST+LST);
RECT_P();//SAVE/RESTORE IDEALLY
//PERHAPS RETURN AND HANDLE NAN INSTEAD
//IF NOT CAUGHT powmod WILL FAIL
END;
IF TYPE(LST)==TYPE({}) THEN
//Search possiblesfor a match
WHILE AP<SIZE(LST) AND NOTFND DO
AP:=AP+1;
IF powmod(10,LST(AP),WhatsLeft)==1 THEN
NOTFND:=0;//FOUND
END; //IF
END;//LOOP
END;//IF
//Return match
END;//VALID LST
//Return 0 if WL=1 or no match
RETURN IFTE(NOTFND,0,LST(AP));
END;
EXPORT TESTS(TESTN)
//Perform some tests
BEGIN
LOCAL III,RR;
FOR III FROM 1 TO TESTN DO
//RECT_P();
//TEXTOUT_P(III,0,120);
TESTED:=III;
RR:=ReDIGITS(2,III);
//Note the biggest
IF TransientLEN>BigT THEN
BigT:=TransientLEN;
END;
IF RepetendLEN>BigR THEN
BigR:=RepetendLEN;
END;
DRAWMENU(III,BigT,BigR);//MAX
//DRAWMENU(III,TransientLEN,RepetendLEN);//CURRENT
END;
END;
EXPORT REDECIMALS()
BEGIN
LOCAL TESTN:=1000;
ABOUT();
//HEADLINE RATE: LARGER WILL BE SLOWER
PRINT(TESTN+" fractions in "+TEVAL(TESTS(TESTN)));
//PRINT(DIM(Recurring(1,29989)));
//HELP();
END;
Update: use with care.
On the Android, this implementation can be problematic with larger values such as 2/16384.
Sometimes it seems to work and can return useful results, but after a Bad Argument error is reported, problems persist in the running of other programs until the calculator (not the mobile) is switched off.