HP Forums
Repeating Decimals / Recurring Decimals - Printable Version

+- HP Forums (https://www.hpmuseum.org/forum)
+-- Forum: HP Software Libraries (/forum-10.html)
+--- Forum: HP Prime Software Library (/forum-15.html)
+--- Thread: Repeating Decimals / Recurring Decimals (/thread-9986.html)



Repeating Decimals / Recurring Decimals - StephenG1CMZ - 01-21-2018 11:00 AM

Given an integer reciprocal, analyzes the decimal representation to identify the transient and repeating/recurring parts of the decimal.

Eg
1/3 has no transient and 3 repeats.
1/4 has transient 0.25 and no repetition.
1/7 has no transient and 0.142857 recurring.
1/14 has transient 0 and 6 recurring digits.

See http://www.hpmuseum.org/forum/thread-9919.html
For a description of the algorithms and more examples.


RE: Repeating Decimals - StephenG1CMZ - 01-21-2018 11:01 AM

(PPL) Version 0.2 follows the sequence of steps in Joe Horn's algorithm using MultiplicateOrder, which can be found here: http://www.hpmuseum.org/forum/thread-3212.html

Output is currently a list:
1: {Length of transient, transient}
2: {Length of recurring part, both parts but omitting the 0. Or 1.}
3: Just as a check, the real value.
The formatting could be improved.

Note that if the length of the recurring part exceeds 12 the recurring part returned is unreliable and should probably be replaced by NaN (the indicated length is OK, but not the digits...and sometimes they are not digits).

Code:

 LOCAL CRID:="REPEATING DECIMAL (PPL) V 0.2";

 //Written 2018 StephenG1CMZ
 //Following Joe Horn`s algorithm using Multiplicative Order

 //This implement yields some useful results but
 //Large Repeating Lengths (>12repeatingdigits) yield weird output eg NN=301

 //The output format could be improved.

 LOCAL ImpRELEN:=12;//EMPIRICAL

 LOCAL FR2,FR5;
 EXPORT TESTED:={};

 ZEROS(NN)
 BEGIN
  IF NN>0 THEN
   RETURN "0"+ZEROS(NN-1);
  END;
  RETURN ""; 
 END;

 TRUNCRR(NN,DIGITS)
 //TRUNCATE A REAL RECIPROCAL
 //NN:DENOMINATOR
 //DIGITS:NUMBER OF PLACES TO KEEP
 BEGIN
  LOCAL TP;
  TP:=(1/NN)*10^DIGITS;
  TP:=IP(TP)/(10^DIGITS);
  RETURN TP;
 END;
  
 EXPORT 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 GETTRANSIENT(NN)
 BEGIN
  LOCAL TRANSIENTLEN,TRANSIENTPART;

  TRANSIENTLEN:=MAXFACTOR25(NN);
  TRANSIENTPART:=TRUNCRR(NN,TRANSIENTLEN);
 
  RETURN {TRANSIENTLEN,TRANSIENTPART};
 END;

 EXPORT ReDIGITS (NN )
 //NN INTEGER 
 BEGIN
  LOCAL TRANSIENT; //TRANSIENTLEN,TRANSIENTPART;
  LOCAL WHATSLEFT,MO;
  LOCAL RP,NDIGITS;
  LOCAL ST;//STRINGVERSION
  LOCAL RESULTS:={};
  LOCAL RL;
  IF NN THEN
   RL:=1/NN;//DIAGNOSTIC ONLY:USUAL REAL
  ELSE
   RETURN {"DIVBY0"};
  END;
  TRANSIENT:=GETTRANSIENT(NN); 
  WHATSLEFT:=exact(NN/(2^FR2*5^FR5));

  IF WHATSLEFT==1 THEN
   //AVOID STALL
   RETURN {TRANSIENT,{0,0},RL};//AVOID STALL
  END;
  //PRINT("BEFORE");
  //See http://www.hpmuseum.org/forum/thread-3212.html
  MO:=MultiplicativeOrder(WHATSLEFT,10);
  //PRINT("AFTER");

  NDIGITS:=TRANSIENT(1)+MO;//TRANSIENTLEN
  IF MO>ImpRELEN THEN
   RP:="(NaN)";
  END;//FOR NOW SHOW THE WEIRD OUTPUT
  RP:=iquo(1*10^NDIGITS,NN);
  //The string version asks for leading zeros I hoped
  //but no...pad it manually 
  //ST:=format(RP,"s"+NDIGITS);
   
  ST:=ZEROS(NDIGITS-DIM(STRING(RP)))+RP;

  RESULTS:={TRANSIENT,{MO,ST},RL};//
 
  IF 0 THEN //DEBUG INFO
   PRINT(); 
   PRINT("Input Fraction: 1/"+NN+" = "+RL);
   PRINT("Whats Left: "+WHATSLEFT);
   PRINT("Multiplicative Order: "+MO);
   //PRINT("Length: "+{TRANSIENTLEN,MO});
   IF MO>12 THEN //EXPECT REPEATING PART TO GO WEIRD
    PRINT("MO>12: CAUTION");
   END;
   PRINT("TRANSIENT PART: "+IFTE(TRANSIENT(1),TRANSIENT(2),TRANSIENT(2)+" (none)"));
   PRINT(ST);
   PRINT(RESULTS);
  END;
  
  //RETURN RESULTS
  //TRANSIENTLEN=0 =NO TRANSIENT
  //MO=0 =NO REPEATING
  //MO>Imp: IGNORE REPEATING DIGITS
  //RL:REAL JUST FOR COMPARISON
  RETURN RESULTS;
 END;

 EXPORT TESTS()
 BEGIN
  LOCAL III,RR;
  FOR III FROM 1 TO 1000  DO
   //RECT_P();
   //TEXTOUT_P(III,0,120);
    //WAIT(2);
   TESTED(0):=III;
   RR:=ReDIGITS(III);  
  END;
 END;

 EXPORT REDECIMALS()
 BEGIN
  PRINT(CRID);  
  //RECT_P();
  PRINT(TEVAL(TESTS));
 END;

Note: A comparable CAS implementation could improve ranges handled.

Update: 99989 crashes the Android emulator.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 01-24-2018 07:21 PM

Version 0.3 includes an optimised MultiplicativeOrder (base 10) and outputs a string with the recurring part annotated with an underscore (selectable).

Code:


 LOCAL CRID:="REPEATING DECIMAL (PPL) V 0.3 \nCode: 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 implement yields useful results but
 //Large Repeating Lengths (>12repeatingdigits) yield weird output eg NN=301

 //NB Sometimes the leading 0. is included in the output
 //but sometimes it is not...EG NN=14 Why?

 LOCAL ImpRELEN:=12;//(EMPIRICAL 12-15) 16 IS WRONG

 LOCAL FR2,FR5;//GLOBAL RETURN
 EXPORT TRANSIENTLEN;//Length of Transient
 EXPORT RELEN;//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:={};

 EXPORT ABOUT()
 BEGIN
  PRINT();
  PRINT(CRID);
  PRINT(ALGM);
  
 END;

 ZEROS(NN)
 BEGIN
  IF NN>0 THEN
   RETURN "0"+ZEROS(NN-1);
  END;
  RETURN ""; 
 END;
 
 EXPORT 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 MultiplicativeOrder10(WL)
 //WL: WHATSLEFT
 BEGIN
  LOCAL AP:=0;
  LOCAL NOTFND:=1;
  LOCAL LST:={};//POSSIBLES TO CHECK

  IF WL≠1 THEN
   //Get possibles
   LST:=mat2list(idivis(euler(WL)));
   //Search possiblesfor a match
   WHILE AP<SIZE(LST) AND NOTFND DO
    AP:=AP+1;
    IF powmod(10,LST(AP),WL)==1 THEN
     NOTFND:=0;//FOUND
    END;//IF
   END;//LOOP
  END;//IF
  //Return match
  //Return 0 if WL=1 or no match
  RETURN IFTE(NOTFND,0,LST(AP));//TBC
 END;

 EXPORT GetTransient(NN)
 BEGIN
  LOCAL TRANSIENTPART;

  TRANSIENTLEN:=MaxFactor25(NN);
  IF TRANSIENTLEN THEN
   TRANSIENTPART:=TRUNCATE((1/NN),TRANSIENTLEN);
  ELSE
   //DIVBY0 TBD
   TRANSIENTPART:=IP(1/NN)+DECSEP;
  END;
  RETURN TRANSIENTPART;
 END;
 
 EXPORT ReDIGITS (NN )
 //NN INTEGER 
 BEGIN
  LOCAL TRANSIENTPART;
  LOCAL WHATSLEFT;
  LOCAL RP,NDIGITS;
  LOCAL ST:="";//STRINGVERSION
  LOCAL RESULTS:={};
  LOCAL RL;
  IF NN THEN
   RL:=1/NN;//DIAGNOSTIC ONLY:USUAL REAL
  ELSE
   RETURN {"DIVBY0"};
  END;
  TRANSIENTPART:=GetTransient(NN); 
  WHATSLEFT:=exact(NN/(2^FR2*5^FR5));

  IF WHATSLEFT==1 THEN
   RETURN TRANSIENTPART;//AVOID STALL
  END;
  //See http://www.hpmuseum.org/forum/thread-3212.html
  //RELEN:=MultiplicativeOrder(WHATSLEFT,10);
  RELEN:=MultiplicativeOrder10(WHATSLEFT);

  NDIGITS:=TRANSIENTLEN+RELEN;
  IF RELEN>ImpRELEN THEN
   //TBD: A better test might be to search for ᴇ in the string
   ST:="(NaN)";//Implementation limit:BAD IQUO?
  END;//FOR NOW SHOW THE WEIRD OUTPUT
 
  RP:=iquo((1*10^NDIGITS),NN);
  //The string version asks for leading zeros I hoped
  //but no...pad it manually 
  //ST:=format(RP,"s"+NDIGITS); 
  ST:=ST+MID(ZEROS(NDIGITS-DIM(STRING(RP)))+RP,TRANSIENTLEN+1);
  RESULTS:=TRANSIENTPART+USCORE+ST;
  //RESULTS:={{TRANSIENTLEN,TRANSIENTPART},{RELEN,ST},RL};//
 
  IF 0 THEN //DEBUG INFO
   PRINT(); 
   PRINT("Input Fraction: 1/"+NN+" = "+RL);
   PRINT("Whats Left: "+WHATSLEFT);
   PRINT("Multiplicative Order: "+RELEN);
   //PRINT("Length: "+{TRANSIENTLEN,RELEN});
   IF RELEN>12 THEN //EXPECT REPEATING PART TO GO WEIRD
    PRINT("RELEN>12: CAUTION");
   END;
   PRINT("TRANSIENT PART: "+IFTE(TRANSIENTLEN,TRANSIENTPART,TRANSIENTPART+" (none)"));
   PRINT(ST);
   PRINT(RESULTS);
  END;
  //RETURN RESULTS
  //Return a formatted string
  //(Relevant Lengths are returned globally) 
  RETURN RESULTS;
 END;
 
 EXPORT Recurring(NN)
 BEGIN
  RETURN ReDIGITS(NN); 
 END;
 //Some call them Recurring
 //Some call them Repeating
 EXPORT Repeating(NN)
 BEGIN
  RETURN ReDIGITS(NN);
 END;



 EXPORT TESTS()
 BEGIN
  LOCAL III,RR;
  FOR III FROM 1 TO 1000  DO
   //RECT_P();
   //TEXTOUT_P(III,0,120);
    //WAIT(2);
   TESTED(0):=III;
   RR:=ReDIGITS(III);  
  END;
 END;

 EXPORT SHOWME()
 BEGIN
  LOCAL II;
  FOR II FROM 1 TO 1000 DO
    RECT_P();
    TEXTOUT_P(II,0,20);
    TEXTOUT_P(ReDIGITS(II),0,40);
    WAIT;
  END;

 END;

 EXPORT REDECIMALS()
 BEGIN
  ABOUT;
  //PRINT((ReDIGITS(301)));
  //WAIT;
  //RECT_P();
  PRINT(TEVAL(TESTS));
 END;

Note there is a bug with the string formatting...Sometimes it includes the integer part, sometimes not.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 01-26-2018 11:02 PM

Version 0.4 has cosmetic improvements and help. It adds a CAS variable which show more digits, but I have yet to find out how use that in PPL - Whenever I try to turn it into a string, it turns into a real instead.
(Update: I have found the required syntax to do that now, so expect another release soon.)

Code:


 LOCAL CRID:="REPEATING DECIMAL V 0.4 \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 implement yields useful results but
 //Large Repeating Lengths (>12repeatingdigits) yield weird output eg NN=301

 //NB Sometimes the leading 0. is included in the output
 //but sometimes it is not...EG NN=14 Why?

 LOCAL ImpRELEN:=12;//(EMPIRICAL)
 LOCAL ImpTransientLEN:=12;//(EMPIRICAL)
 //Should NDIGITS>12 fail?: not tested.

 LOCAL FR2,FR5;//GLOBAL RETURN
 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;
 LOCAL STNAN:="(NaN)";

 MultiplicativeOrder10();//FORWARD

 EXPORT ABOUT()
 BEGIN
  PRINT();
  PRINT(CRID);
  PRINT(ALGM);
 END;

 EXPORT HELP()
 BEGIN
  PRINT();
  PRINT("Recurring/Repeating: Given integer NN, return a string of its real reciprocal with _ marking any recurring digits.");
  PRINT("ShowMe: Shows a range of values.");
  PRINT(" ");
  PRINT("Variables In: Strings DECSEP,USCORE ");
  PRINT("Variables Out: RepetendLEN, TransientLEN");
  PRINT("CAS: temporary variable shows more digits: Can this be used from PPL?");
  PRINT("{Repetend,Transient}>12 digits: NaN implemented (PPL Limit).");
 END;

 ZEROS(NN)
 BEGIN
  IF NN>0 THEN
   RETURN "0"+ZEROS(NN-1);
  END;
  RETURN ""; 
 END;
 
 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 GetTransient(NN)
 //NOTE: THIS IS CALLED BY Recurring/Repeating.
 BEGIN
  LOCAL TRANSIENTPART;

  TransientLEN:=MaxFactor25(NN);
  IF TransientLEN THEN
   IF TransientLEN>12 THEN
    //EG NN=8192
    //MSGBOX("Caution:TransientLEN= "+TransientLEN);//FIX TBC
    TRANSIENTPART:="(NaN)";//IP(1/NN);//AVOID TRUNCATE
   ELSE
    TRANSIENTPART:=TRUNCATE((1/NN),TransientLEN);//TRUNCATE BREAKS AT 13
   END;
  ELSE
   IF NN==0 THEN
    RETURN STNAN+"DIVBY0";
   END;
   TRANSIENTPART:=IP(1/NN)+DECSEP;
  END;
  RETURN TRANSIENTPART;
 END;
 
 ReDIGITS (NN )
 //NN INTEGER 
 BEGIN
  LOCAL STR:="";
  LOCAL TRANSIENTPART;
  LOCAL WHATSLEFT;
  LOCAL RP,NDIGITS;
  LOCAL ST:="";//STRINGVERSION
  LOCAL RESULTS:={};
  LOCAL RL;
  IF NN THEN
   RL:=1/NN;//DIAGNOSTIC ONLY:USUAL REAL
  ELSE
   RETURN STNAN+"DIVBY0";
  END;
  TRANSIENTPART:=GetTransient(NN); 
  WHATSLEFT:=exact(NN/(2^FR2*5^FR5));

  IF WHATSLEFT==1 THEN
   //RETURN TRANSIENTPART;//AVOID STALL
  END;
  //RepetendLEN:=MultiplicativeOrder(WHATSLEFT,10);
  RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);

  NDIGITS:=TransientLEN+RepetendLEN;
  IF RepetendLEN THEN
   IF RepetendLEN>ImpRELEN THEN
    //TBD: A better test might be to search for ᴇ in the string
    ST:=STNAN;//Implementation limit:BAD IQUO?
   END;//FOR NOW SHOW THE WEIRD OUTPUT
   //MSGBOX({NDIGITS,NN});
   RP:=iquo((1*10^NDIGITS),NN);
  //MSGBOX(RP);
   CAS("temporary:=iquo((1*10^NDIGITS),NN)");
   //CAS("PRINT(temporary)");
   //PRINT(STR);
   //The string version asks for leading zeros I hoped
   //but no...pad it manually 
   //ST:=format(RP,"s"+NDIGITS); 
   ST:=ST+MID(ZEROS(NDIGITS-DIM(STRING(temporary)))+temporary,TransientLEN+1);
  END;
  RESULTS:=TRANSIENTPART+USCORE+ST;
 
  IF 0 THEN //DEBUG INFO
   PRINT(); 
   PRINT("Input Fraction: 1/"+NN+" = "+RL);
   PRINT("Whats Left: "+WHATSLEFT);
   PRINT("Multiplicative Order: "+RepetendLEN);
   //PRINT("Length: "+{TransientLEN,RepetendLEN});
   IF RepetendLEN>12 THEN //EXPECT REPEATING PART TO GO WEIRD
    PRINT("RepetendLEN>12: CAUTION");
   END;
   PRINT("TRANSIENT PART: "+IFTE(TRANSIENTLEN,TRANSIENTPART,TRANSIENTPART+" (none)"));
   PRINT(ST);
   PRINT(RESULTS);
  END;
  //RETURN RESULTS
  //Return a formatted string
  //(Relevant Lengths are returned globally) 
  RETURN RESULTS;
 END;
 
 EXPORT Recurring(NN)
 BEGIN
  RETURN ReDIGITS(NN); 
 END;
 //Some call them Recurring
 //Some call them Repeating
 EXPORT Repeating(NN)
 BEGIN
  RETURN ReDIGITS(NN);
 END;

 EXPORT Show1(NN)
 BEGIN
    LOCAL TRANSIENTPART;
    RECT_P();
    TEXTOUT_P(CRID,0,0,3);
    //TEXTOUT_P(ALGM,0,15,1);
    TEXTOUT_P("Fraction,Reduced Fraction,Real,Decimal:",0,40);
    TEXTOUT_P("1/"+NN,0,60);
    TEXTOUT_P(CAS(1/NN)+" Reduced Fraction",0,80);
    TEXTOUT_P(IFTE(NN,(1/NN)+" Real",(STNAN+"DIVBY0")),0,100);
    //DEBUG;
    TRANSIENTPART:=GetTransient(NN);
    TEXTOUT_P(TRANSIENTPART+" Transient Part",0,140);
    TEXTOUT_P(Recurring(NN)+" Decimal",0,120);
    TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
    TEXTOUT_P("Variable CAS.temporary may show more",0,200);
    //TEXTOUT_P(CAS("temporary"),0,180);//doesnt work:PPL
    TEXTOUT_P("Esc to continue",0,220);
    IF RepetendLEN>ImpRELEN OR TransientLEN>ImpTransientLEN THEN
     //Highlight Implement NaN
     TEXTOUT_P("Imp"+STNAN,320/2,220,3,RGB(255,0,0));
    END;
    WAIT; 
 END;


 EXPORT ShowMe(FIRST,LAST)
 BEGIN
  LOCAL NN;
  FOR NN FROM FIRST TO LAST DO
    Show1(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)));
   //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
  //Return 0 if WL=1 or no match
  RETURN IFTE(NOTFND,0,LST(AP));
 END;

 TESTS()
 BEGIN
  LOCAL III,RR;
  FOR III FROM 1 TO 1000  DO
   //RECT_P();
   //TEXTOUT_P(III,0,120);
    //WAIT(2);
   TESTED:=III;
   RR:=ReDIGITS(III);  
  END;
 END;

 EXPORT REDECIMALS()
 BEGIN
  ABOUT(); 
  PRINT(TEVAL(TESTS));
 END;
The implementation returns NaN if either the Transient part or Repetend exceed 12 digits.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 01-31-2018 09:10 AM

Version 0.5 is a much improved PPL program for showing transient parts and repetends of reciprocals. It has been rewritten to use CAS, enabling Transients and Repetends >12 digits to be handled, and is faster.

Code:

 
 LOCAL CRID:="REPEATING DECIMAL V 0.5 \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
 //8192 FIX TBD
 LOCAL FR2,FR5;//GLOBAL RETURN
 LOCAL WHATSLEFT,NDIGITS;//GLOBAL RETURN

 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;
 LOCAL DIVBY0:="(NaN)(DIVBY0)";

 MultiplicativeOrder10();//FORWARD

 EXPORT ABOUT()
 BEGIN
  PRINT();
  PRINT(CRID);
  PRINT(ALGM);
 END;

 EXPORT HELP()
 BEGIN
  PRINT();
  PRINT("GetLEN: Sets LENgth variables");
  PRINT("Recurring/Repeating: Given integer NN, return a string of its real reciprocal with _ marking any recurring digits.");
  PRINT("ShowReciprocal: Show Reciprocal");
  PRINT("ShowReciprocals: Shows a range of reciprocal values.");
  PRINT("Variables In: Strings DECSEP,USCORE ");
  PRINT("Variables Out: RepetendLEN, TransientLEN");
  PRINT("CAS: temporary variable");
 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;
 
 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(NN)
 BEGIN 
  TransientLEN:=MaxFactor25(NN);
  WHATSLEFT:=exact(NN/(2^FR2*5^FR5));
  RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);
  NDIGITS:=TransientLEN+RepetendLEN; 
  //PRINT({TransientLEN,RepetendLEN});
  //DO NOT INCLUDE IP LENGTH
  RETURN {TransientLEN,RepetendLEN,NDIGITS};
 END;

 ReDIGITS (NN)
 //NN INTEGER 
 BEGIN
  LOCAL LST;//UNUSED
  LOCAL ST:="";//STRINGVERSION
  LOCAL STR:="";
  LOCAL TRANSIENTPART,REPETEND,INTPART;
  LOCAL RESULTS;
  LOCAL DP;//DECIMAL PLACES

  LST:=GetLEN(NN);
  
  //GetDecimalPlaces
  //RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS  
  CAS("temporary:=iquo((1*10^NDIGITS),NN)");
  ST:=CAS("string(temporary)");
  STR:=ZEROS(NDIGITS-DIM(ST));
  DP:=STR+ST;

  //GetRepetend
  REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
  //GetTransientDecimalPlaces
  TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
  //Get IntegerPart
  INTPART:=IFTE(NN,(IP(1/NN)+DECSEP),DIVBY0);

  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(NN)
 BEGIN
  RETURN ReDIGITS(NN); 
 END;
 //Some call them Recurring
 //Some call them Repeating
 EXPORT Repeating(NN)
 BEGIN
  RETURN ReDIGITS(NN);
 END;

 GetTransient(NN)
 //NB Only when USCORE NOT EMPTY
 BEGIN
  LOCAL ST:=ReDIGITS(NN);
  RETURN LEFT(ST,INSTRING(ST,USCORE));
 END;

 EXPORT ShowReciprocal(NN)
 BEGIN
    LOCAL PXP:={0};
    LOCAL TRANSIENTPART;
    RECT_P();
    TEXTOUT_P(CRID,0,0,3);
    //TEXTOUT_P(ALGM,0,15,1);
    TEXTOUT_P("Fraction,Reduced Fraction,Real,Decimal:",0,40);
    TEXTOUT_P("1/"+NN,0,60);
    TEXTOUT_P(CAS(1/NN)+" Reduced Fraction",0,80);
    TEXTOUT_P(IFTE(NN,(1/NN),(DIVBY0))+" Real",0,100);
 
    TRANSIENTPART:=GetTransient(NN);
   
    PXP(0):=TEXTOUT_P(TRANSIENTPART+" Transient Part",0,140);
    PXP(0):=TEXTOUT_P(Recurring(NN)+" Decimal",0,120);
    
    TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
   
    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 ShowReciprocals(FIRST,LAST)
 BEGIN
  LOCAL NN;
  FOR NN FROM FIRST TO LAST DO
    ShowReciprocal(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)));
   //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
  //Return 0 if WL=1 or no match
  RETURN IFTE(NOTFND,0,LST(AP));
 END;

 TESTS(TESTN)
 //Perform some tests
 BEGIN
  LOCAL III,RR;
  FOR III FROM 1 TO TESTN DO
   //RECT_P();
   //TEXTOUT_P(III,0,120);
   DRAWMENU(III);
    //WAIT(2);
   TESTED:=III;
   RR:=ReDIGITS(III);  
  END;
 END;

 EXPORT REDECIMALS()
 BEGIN
  LOCAL TESTN:=1000;
  ABOUT(); 
  PRINT(TESTN+" reciprocals in "+TEVAL(TESTS(TESTN)));
  //HELP();
  //PRINT(Recurring(8912));
 END;
There are no specific limits other than unchecked list size/CAS integer limits.
In Show, a warning is given if screen size is exceeded, but such values can still be printed.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-01-2018 05:53 PM

Version 0.6
Show optimised. Note that the transient display assumes USCORE (underscore) is not empty.
Implementation Limits added (Some large values eg NN=1E14 exceed the limits of euler).

Code:


 LOCAL CRID:="REPEATING DECIMAL V 0.6 \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("GetLEN: Sets LENgth variables");
  PRINT("Recurring/Repeating: Given integer NN, return a string of its real reciprocal with _ marking any recurring digits.");
  PRINT("ShowReciprocal: Show Reciprocal");
  PRINT("ShowReciprocals: Shows a range of reciprocal values.");
  PRINT("Variables In: Strings DECSEP,USCORE ");
  PRINT("Variables Out: RepetendLEN, TransientLEN");
  PRINT("Hint:To PRINT long strings,");
  PRINT("PRINT(a few bytes at a time)");
 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;
 
 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(NN)
 BEGIN 
  TransientLEN:=MaxFactor25(NN);
  WHATSLEFT:=exact(NN/(2^FR2*5^FR5));
  RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);
  NDIGITS:=TransientLEN+RepetendLEN; 
  //PRINT({TransientLEN,RepetendLEN});
  //DO NOT INCLUDE IP LENGTH
  RETURN {TransientLEN,RepetendLEN,NDIGITS};
 END;

 ReDIGITS (NN)
 //NN INTEGER 
 BEGIN
  LOCAL LST;//UNUSED
  LOCAL ST:="";//STRINGVERSION
  LOCAL STR:="";
  LOCAL TRANSIENTPART,REPETEND,INTPART;
  LOCAL RESULTS;
  LOCAL DP;//DECIMAL PLACES
  
  //Get Length
  //A possible optimisation:
  //Avoid recalc 
  LST:=GetLEN(NN);
    //TBD:IF EXCESSIVE LENGTH RETURN NAN

  //GetDecimalPlaces
  //RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS  
  CAS("temporary:=iquo((1*10^NDIGITS),NN)");
  ST:=CAS("string(temporary)");
  STR:=ZEROS(NDIGITS-DIM(ST));
  DP:=STR+ST;

  //GetRepetend
  REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
  //GetTransientDecimalPlaces
  TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
  //Get IntegerPart
  INTPART:=IFTE(NN,(IP(1/NN)+DECSEP),DIVBY0);

  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(NN)
 BEGIN
  RETURN ReDIGITS(NN); 
 END;
 //Some call them Recurring
 //Some call them Repeating
 EXPORT Repeating(NN)
 BEGIN
  RETURN ReDIGITS(NN);
 END;

 GetTransient(NN)
 //NB Only when USCORE NOT EMPTY
 //NB RECALCULATES
 BEGIN
  LOCAL ST:=ReDIGITS(NN);
  RETURN LEFT(ST,INSTRING(ST,USCORE));
 END;

 GetTransientToo(NN,ST)
 //Requires ST to supply digits an4 USCORE
 BEGIN
  RETURN LEFT(ST,INSTRING(ST,USCORE)); 
 END;

 EXPORT ShowReciprocal(NN)
 BEGIN
    LOCAL PXP:={0};
    LOCAL TRANSIENTPART;
    LOCAL DST;
    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("1/"+NN,0,60);
    TEXTOUT_P(CAS(1/NN)+" Reduced Fraction",0,80);
    TEXTOUT_P(IFTE(NN,(1/NN),(DIVBY0))+" Real",0,100);

    //EARLY LENGTH
    //Note:This is recalculated in ReDIGITS
    GetLEN(NN);
    TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
 
    //Main Display
    DST:=Recurring(NN);
    //TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);//IF NOT EARLY
  
    PXP(0):=TEXTOUT_P(DST+" Decimal",0,120);
    TRANSIENTPART:=GetTransientToo(NN,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 ShowReciprocals(FIRST,LAST)
 BEGIN
  LOCAL NN;
  FOR NN FROM FIRST TO LAST DO
    ShowReciprocal(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(III);
   //Notethe 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+" reciprocals in "+TEVAL(TESTS(TESTN)));
  //PRINT(DIM(Recurring(29989)));
  //HELP();
  //PRINT(Recurring(8912));
 END;

Hope this is useful, although it is only implemented for reciprocals.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-04-2018 06:46 PM

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.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-06-2018 11:24 PM

I have noticed an issue with the step 0 fraction reduction as I have implemented it in V0.7.

In V0.6, 1/1E14 would be processed by the algorithm (no reduction being implemented).
An error message would issue from Multiplicateorder10 as implementation limits are exceeded.

In V0.7, 1/1E14 is reduced to 1E-14/1 and non-integer numerator is processed, resulting in a NaN with no error message from MultiplicativeOrder10 seen.

Potentially, there might be other values which MultiplicateOrder10 might have handled, but which are not being processed because the reduction step is introducing unexpected non-integer values.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-07-2018 08:02 PM

Note that some long repetends incorrectly return all zeros instead of the correct digits (the reported length is correct). If you see a value with just zeros assume it is incorrect.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-08-2018 09:03 PM

Version 0.8

Improved handling of implementation limitations.
Improved user interface shows more digits on-screen and allows long strings not to be generated.
Code:

  
 LOCAL CRID:="REPEATING DECIMAL V 0.8 \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

 
 LOCAL FR2,FR5;//GLOBAL RETURN
 LOCAL WHATSLEFT,NDIGITS;//GLOBAL RETURN
 LOCAL ImpPRINT:=2000;//PRINT LIMIT <2K
 LOCAL ImpSTRING:=64000;//LIMIT VALUE TBC
 
 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 WAITS:=0;
 EXPORT ImpRELEN:=ImpSTRING;//MAX LEN TO GENERATE
 EXPORT TESTED:=0;
 EXPORT BigT:=0;
 EXPORT BigR:=0;
LOCAL STNAN:="(NaN)";
 LOCAL DIVBY0:=STNAN+"{0}";//DIVBY0
 LOCAL NOTINT:=STNAN+"(NotInteger)";
 LOCAL STNANImp:=STNAN+"(Imp)";//Implemenation Limit
 LOCAL ImpLimST:="MultiplicativeOrder10: Implementation Limit!\n";
 
 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("[Esc] to continue to next page");
  PRINT("[View] more digits");
  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: (Selectable by user @Home)");
  PRINT("ImpRELEN: Recurring LEN limit");//Limit to manageable length
  PRINT("String USCORE: Underscore ");
  PRINT("WAITS: Show WAIT time (0=Esc)");
  PRINT(" ");
  PRINT("Variables Out:");
  PRINT("RepetendLEN, TransientLEN");
  PRINT(" ");
  PRINT("Known Limitations:");
  PRINT("Numbers beyond about 1/12109:");
  PRINT("Repetend digits incorrectly show just 0-s (LEN OK)");
  PRINT(" ");
  PRINT("Hint: To PRINT long strings,");
  PRINT("PRINT(a few bytes at a time)");
  PRINT(" ");
  PRINT("Interesting Test Values:");
  PRINT("{1/: 0,1,2,3,4,7,14,208,301,8192}");
  PRINT("{1/: 29989}//WRONG}");
  PRINT("{1/: 3^15}//CRASH AVOIDED");
  PRINT("{2/: 16384}")

 END;

 //STANDARD ROUTINES

 DecSepNow()
 //RETURN current system decimal separator
 //From the forum
 BEGIN
  RETURN IFTE((HSeparator MOD 9)<4,".",",");
 END;

 //QUERY:WOULD TAKING LOGS OF NUMERATOR AND DENOMINATOR AND SUBTRACTING
 //IMPROVE RANGE OR PRECISION TBD
 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 GD; 
  LOCAL RedNUMER,RedDENOM;
  IF DenomN THEN
   //Step 0: Reduce Fraction
   GD:=gcd(NumerN,DenomN);
   RedNUMER:=NumerN/GD;
   RedDENOM:=DenomN/GD;
  
   //Continue
   TransientLEN:=MaxFactor25(RedDENOM);
   WHATSLEFT:=exact(RedDENOM/(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
 //NEGATIVES OK
 //DENOMINATOR 0: RETURN NAN
 //NONINTEGER: UNCHECKED
 BEGIN
  LOCAL GD;
  LOCAL LST;//UNUSED
  LOCAL ST:="";//STRINGVERSION
  LOCAL STR:="";
  LOCAL TRANSIENTPART,REPETEND,INTPART;
  LOCAL RESULTS;
  LOCAL DP;//DECIMAL PLACES
  LOCAL IPLEN;
  LOCAL RedNUMER,RedDENOM;

  //MSGBOX({NUMER,NN});

  //GUARDS
  IF NN==0 THEN
   RETURN DIVBY0;
  END;
  IF FP(NUMER) OR FP(NN) THEN
   RETURN NOTINT+STRING({NUMER,NN});//PARAMETERS NOT INTEGER
  END;
  IF (NUMER/NN)<0 THEN
   RETURN "−"+ReDIGITS(ABS(NUMER),ABS(NN));
  END;
  //OK
  //Step 0: reduce fraction
  GD:=gcd(NUMER,NN);
  RedNUMER:=NUMER/GD;
  RedDENOM:=NN/GD;
 
  //Continue...

  //Get Length
  //A possible optimisation:
  //Avoid recalc 
  LST:=GetLEN(RedNUMER,RedDENOM);
  //IF EXCESSIVE LENGTH RETURN NAN
  IF NDIGITS>MIN(ImpRELEN,ImpSTRING-9) THEN //−9-ALLOW FOR DECSEP ETC
   //STRING:TOO LONG TO GENERATE
   //RELEN:USER LIMIT(TOO LONG/SLOW FOR USER)
   RETURN STNANImp; 
  END;
  //GetDecimalPlaces
  //RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS  
  CAS("temporary:=iquo(((RedNUMER*10^NDIGITS),RedDENOM)");
  ST:=CAS("string(temporary)");
  //MSGBOX(ST);
  STR:=ZEROS(NDIGITS-DIM(ST));
  DP:=STR+ST; 
  //GetINTEGERPART
  INTPART:=IFTE(RedDENOM,(IP(RedNUMER/RedDENOM)+DecSepNow()),DIVBY0);//THIS DIVBY IS GUARDED
  //IF NUMER==1 THEN
   //RECIPROCAL:iquo returns decimal places only
  IF RedNUMER≠1 THEN //NN
   //FRACTION: iquo returns IP and decimals with no point
   IPLEN:=DIGITSNEEDED(exact(RedNUMER/RedDENOM));//LEN OF IP
   DP:=MID(DP,IPLEN+1); //DP
  END;
  //GetRepetend
  REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
  //GetTransientDecimalPlaces
  TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
  
  RESULTS:=INTPART+TRANSIENTPART+USCORE+REPETEND;
  //PRINT("RESULTS: "+RESULTS);
  //Imp:Sometimes the string contains source
  IF INSTRING(RESULTS,"M") THEN
   RESULTS:=STNANImp+RESULTS;//MARK BAD RESULT 
  END; 
  //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;
    LOCAL KK,LST;

    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);//NEEDS FIXING
    TEXTOUT_P(IFTE(DenomN,(NumerN/DenomN),(DIVBY0))+" Real",0,100);

    //EARLY LENGTH
    //Note:This is recalculated in ReDIGITS
    LST:=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 INSTRING(DST,"00000000000000") THEN
     //Loss Of significance suspected
     TEXTOUT_P(" ! Check Possible Loss Of Significance !",0,200,3,RGB(255,0,0));
    END;
    IF MAX(PXP)≥320 THEN //OFFSCREEN
     TEXTOUT_P("[View]",320*(4/6),220,3,RGB(255,0,0));
    END;
 
    TEXTOUT_P(IFTE(WAITS>0,"Wait...","[Esc]"),320*(5/6),220,3);  
    KK:=WAIT(WAITS); 
    IF KK==9 THEN //VIEW KEY
     PRINT();
     PRINT(NumerN+"/"+DenomN);
     PRINT(LST);
     PRINT(MID(DST,1,ImpPRINT));
     IF DIM(DST)>ImpPRINT THEN
      PRINT("PRINT Unable");//Or:Loop
     END;

     //WAIT(WAITS);
    END;
    RETURN DST;
 END;

 EXPORT ShowRange(Numer1,Numer2,Denom1,Denom2)
 //Show range one by one
 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;
Note that some long repetends incorrectly show all zeros. These will be highlighted in Show but do not raise a NaN.


RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-10-2018 11:15 PM

Version 0.9 fixes a bug caused by "exact", introduced in V0.8. Thanks for spotting that.
Error MSGBOX is improved (using an AFile to save/restore the screen).

Code:

  
 LOCAL CRID:="REPEATING DECIMAL V 0.9 \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

 
 LOCAL FR2,FR5;//GLOBAL RETURN
 LOCAL WHATSLEFT,NDIGITS;//GLOBAL RETURN
 LOCAL ImpPRINT:=2000;//PRINT LIMIT <2K
 LOCAL ImpSTRING:=64000;//LIMIT VALUE TBC

 EXPORT RedNUMER,RedDENOM;
 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 WAITS:=0;
 EXPORT ImpRELEN:=ImpSTRING;//MAX LEN TO GENERATE
 EXPORT TESTED:=0;
 EXPORT BigT:=0;
 EXPORT BigR:=0;
 LOCAL STNAN:="(NaN)";
 LOCAL DIVBY0:=STNAN+"{0}";//DIVBY0
 LOCAL NOTINT:=STNAN+"(NotInteger)";
 LOCAL STNANImp:=STNAN+"(Imp)";//Implemenation Limit 
 LOCAL FL:="ZTmpMESSAGEBOX";

 LOCAL ImpLimST:="MultiplicativeOrder10: Implementation Limit!\n";
 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("Key [Esc] to continue to next page");
  PRINT("Key [View] for more digits");
  PRINT("(Do not tap)");
  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: (Selectable by user @Home)");
  PRINT("ImpRELEN: Recurring LEN limit");//Limit to manageable length
  PRINT("String USCORE: Underscore ");
  PRINT("WAITS: Show WAIT time (0=Esc)");
  PRINT(" ");
  PRINT("Variables Out:");
  PRINT("RepetendLEN, TransientLEN//Lengths");
  PRINT("RedNUMER, RedDENOM//Reduced");
  PRINT("(set by GetLEN,Recurring,Repeating)");
  PRINT(" ");
  PRINT("Known Limitations:");
  PRINT("Numbers beyond about 1/12109:");
  PRINT("Repetend digits incorrectly show just 0-s (LEN OK)");
  PRINT("Negative inputs may error.");
  PRINT(" ");
  PRINT("Hint: To PRINT long strings,");
  PRINT("PRINT(a few bytes at a time)");
  PRINT(" ");
  PRINT("Interesting Test Values:");
  PRINT("{1/: 0,1,2,3,4,7,14,208,301,8192}");
  PRINT("{1/: 29989}//WRONG}");
  PRINT("{1/: 1ᴇ14}//Imp");
  PRINT("{1/: 3^15}//ImpCRASH AVOIDED");
  PRINT("{2/: 3}");
  PRINT("{2/: 16384}");
  PRINT("{123/208}");

 END;

 //STANDARD ROUTINES

 DecSepNow()
 //RETURN current system decimal separator
 //From the forum
 BEGIN
  RETURN IFTE((HSeparator MOD 9)<4,".",",");
 END;

 DIGITSNEEDED(NN)
 //DIGITS NEEDED FOR IP
 //EXCLUDES SIGN POINT SEPARATOR
 BEGIN
  //RETURN IP(LOG(MAX(1,ABS(NN))))+1;//PORTABLE
  RETURN MAX(0,(XPON(NN)))+1;//PRIME XPON=BASE 10
 END;

 SCR_GET()
 BEGIN
  G0:=AFiles(FL)
 END;

 SCR_PUT()
 BEGIN
  AFiles(FL):=G0;
 END;

 ZMSGBOX(ST,OKC)
 BEGIN
 
  SCR_PUT();
  OKC:=MSGBOX(ST,OKC);
  SCR_GET();
  RETURN OKC;
 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}
 //NEGATIVES:UNCHECKED
 BEGIN
  LOCAL GD; 
  IF DenomN THEN
   //Step 0: Reduce Fraction
   GD:=gcd(NumerN,DenomN);
   RedNUMER:=NumerN/GD;
   RedDENOM:=DenomN/GD;
  
   //Continue
   TransientLEN:=MaxFactor25(RedDENOM);
   WHATSLEFT:=exact(RedDENOM/(2^FR2*5^FR5));
   RepetendLEN:=MultiplicativeOrder10(WHATSLEFT);
   NDIGITS:=TransientLEN+RepetendLEN; 
   //PRINT({TransientLEN,RepetendLEN});
  ELSE //DIVBY0
   TransientLEN:=0;
   RepetendLEN:=0;
   NDIGITS:=0;
  END;
  //NOTE: 0,0 IS OFTEN SUGGESTIVE OF AN ERROR
  //BUT IS ALSO RETURNED BY 1/1
  //SO MAYBE USE AN ERRORFLAG?
  RETURN {TransientLEN,RepetendLEN,NDIGITS};
 END;

 ReDIGITS (NUMER,NN)
 //Determine Digits of the fraction (NUMER/NN)
 //NUMERATOR INTEGER 
 //DENOMINATOR INTEGER
 //NEGATIVES:ATTEMPTED BUT RAISE ERROR
 //DENOMINATOR 0: RETURN NAN
 //NONINTEGER: RETURN NAN
 BEGIN
  LOCAL GD;
  LOCAL LST;//UNUSED
  LOCAL ST:="";//STRINGVERSION
  LOCAL STR:="";
  LOCAL TRANSIENTPART,REPETEND,INTPART;
  LOCAL RESULTS;
  LOCAL DP;//DECIMAL PLACES
  LOCAL IPLEN;

  //MSGBOX({NUMER,NN});

  //GUARDS
  IF NN==0 THEN
   RETURN DIVBY0;
  END;
  IF FP(NUMER) OR FP(NN) THEN
   RETURN NOTINT+STRING({NUMER,NN});//PARAMETERS NOT INTEGER
  END;
  IF NUMER<0 OR NN<0 THEN 
   RESULTS:=ReDIGITS(ABS(NUMER),ABS(NN));
   RETURN IFTE((NUMER/NN)<0,"−"+RESULTS,RESULTS);
  END;
  //OK
  //Step 0: reduce fraction
  GD:=gcd(NUMER,NN);
  RedNUMER:=NUMER/GD;
  RedDENOM:=NN/GD;
 
  //Continue...

  //Get Length
  //A possible optimisation:
  //Avoid recalc 
  LST:=GetLEN(RedNUMER,RedDENOM);
  //IF EXCESSIVE LENGTH RETURN NAN
  IF NDIGITS>MIN(ImpRELEN,ImpSTRING-9) THEN //−9-ALLOW FOR DECSEP ETC
   //STRING:TOO LONG TO GENERATE
   //RELEN:USER LIMIT(TOO LONG/SLOW FOR USER)
   RETURN STNANImp; 
  END;
  //GetDecimalPlaces
  
  //RP:=iquo((1*10^NDIGITS),NN);//works for repetends≤12digits:so use CAS  
  CAS("temporary:=iquo(((RedNUMER*10^NDIGITS),RedDENOM)");
  ST:=CAS("string(temporary)");
  //MSGBOX(ST);
  STR:=ZEROS(NDIGITS-DIM(ST));
  DP:=STR+ST; 
  //GetINTEGERPART
  INTPART:=IFTE(RedDENOM,(IP(RedNUMER/RedDENOM)+DecSepNow()),DIVBY0);//THIS DIVBY IS GUARDED
  //IF NUMER==1 THEN
   //RECIPROCAL:iquo returns decimal places only
  IF RedNUMER≠1 THEN //NN
  //DEBUG;
   //FRACTION: iquo returns IP and decimals with no point
   IPLEN:=DIGITSNEEDED((IP(RedNUMER/RedDENOM)));//LEN OF IP
   DP:=MID(DP,IPLEN+1); //DP
  END;
  //GetRepetend
  REPETEND:=IFTE(RepetendLEN,MID(DP,TransientLEN+1),"");
  //GetTransientDecimalPlaces
  TRANSIENTPART:=IFTE(TransientLEN,LEFT(DP,TransientLEN),"");
  
  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 KK,LST;

    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(IFTE(DenomN,(NumerN/DenomN),(DIVBY0))+" Real",0,100);

    //EARLY LENGTH
    //Note:This is recalculated in ReDIGITS
    LST:=GetLEN(NumerN,DenomN);
    TEXTOUT_P("Has "+TransientLEN+" Transient digits & "+RepetendLEN+" Repetend digits",0,180);
    TEXTOUT_P(RedNUMER+"/"+RedDENOM+" Reduced Fraction",0,80);
    
    //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 INSTRING(DST,"00000000000000") THEN
     //Loss Of significance suspected
     TEXTOUT_P(" ! Check Possible Loss Of Significance !",0,200,3,RGB(255,0,0));
    END;
    IF MAX(PXP)≥320 THEN //OFFSCREEN
     TEXTOUT_P("[View]",320*(4/6),220,3,RGB(255,0,0));
    END;
 
    TEXTOUT_P(IFTE(WAITS>0,"Wait...","[Esc]"),320*(5/6),220,3);  
    KK:=WAIT(WAITS); 
    IF KK==9 THEN //VIEW KEY
     PRINT();
     PRINT(NumerN+"/"+DenomN);
     PRINT(LST);
     PRINT(MID(DST,1,ImpPRINT));
     IF DIM(DST)>ImpPRINT THEN
      PRINT("PRINT Unable");//Or:Loop
     END;

     //WAIT(WAITS);
    END;
    RETURN DST;
 END;

 EXPORT ShowRange(Numer1,Numer2,Denom1,Denom2)
 //Show range one by one
 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 OKC:=0;
  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
    OKC:=ZMSGBOX((ImpLimST+LST),OKC);
    //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;

 TESTD()
 BEGIN
  LOCAL II;
  FOR II FROM 1 TO 1000 DO
   DIGITSNEEDED(123.4);
  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;

Some long repetends continue to incorrectly show all zeros.
Negative fractions may now cause a MultiplicativeOrder10 error message
Update: Some values are incorrect...