Post Reply 
Repeating Decimals / Recurring Decimals
02-08-2018, 09:03 PM (This post was last modified: 02-08-2018 10:10 PM by StephenG1CMZ.)
Post: #10
RE: Repeating Decimals / Recurring Decimals
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.

Stephen Lewkowicz (G1CMZ)
https://my.numworks.com/python/steveg1cmz
Visit this user's website Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
RE: Repeating Decimals - StephenG1CMZ - 01-21-2018, 11:01 AM
RE: Repeating Decimals / Recurring Decimals - StephenG1CMZ - 02-08-2018 09:03 PM



User(s) browsing this thread: 6 Guest(s)