digital root?

digital root?

mid-awe
Collaborator Collaborator
1,271 Views
18 Replies
Message 1 of 19

digital root?

mid-awe
Collaborator
Collaborator

Hi all,

I need help with a lisp to calculate digital roots.

 

It seems like a simple thing, but I am having dificulty with the recursive addition.

 

(defun c:droot (/ digits dr num lst)
  (setq num (getint))
  (setq digits (strlen (rtos num)))
  (while (>= digits 0)
    (setq *digi* (cons (substr (rtos num) digits 1) (list *digi*)))
    (setq digits (1- digits))
  )
  (setq dr (mapcar '+ (list *digi*)))
  (setq num (strcat "\n\t=>> " num " = " dr " digital root "))
)

Anyone interested in helping solve this?

 

Thank you in advance for any help and/or advice.

0 Likes
Accepted solutions (3)
1,272 Views
18 Replies
Replies (18)
Message 2 of 19

hmsilva
Mentor
Mentor
Accepted solution

Hi mid-awe,

 

using your code as 'base code', and slightly modified

 

(defun c:droot (/ drt num)

  (defun drt (int / lst n str)
    (setq n (strlen (setq str (itoa int))))
    (while (> n 0)
      (setq lst (cons (atoi (substr str n 1)) lst))
      (setq n (1- n))
    )
    (if (> (strlen (itoa (setq int (apply '+ lst)))) 1)
      (drt int)
      int
    )
  )

  (if (and (not (initget 4))
           (setq num (getint "\nEnter an integer to get it's digital root: "))
      )
    (princ (strcat "\n\t=>> " (itoa num) " = " (itoa (drt num)) " digital root "))
  )
  (princ)
)

 

Hope this helps,
Henrique

EESignature

Message 3 of 19

hmsilva
Mentor
Mentor

mid-awe,

another approach:

 

(defun c:droot (/ int int1 lst x)
  (cond ((and (not (initget 4))
              (setq int (getint "\nEnter an integer to get it's digital root: "))
         )
         (setq int1 int)
         (while (> (length (setq lst (vl-string->list (itoa int1)))) 1)
           (setq int1 (apply '+ (mapcar '(lambda (x) (atoi (chr x))) lst)))
         )
         (princ (strcat "\n\t=>> " (itoa int) " = " (itoa int1) " digital root "))
        )
  )
  (princ)
)

 

 

Hope this helps,
Henrique

EESignature

Message 4 of 19

dbroad
Mentor
Mentor
Accepted solution

A couple of other functional alternatives (assuming they are fed correctly positive integers).

 

(defun droot (num)
  (if (> num 9)
    (droot (apply '+ (mapcar (function(lambda(x) (atoi(chr x)))) (vl-string->list (itoa num)))))
    num
    ))

(defun droot2 (int1 / lst)
    (while (> (length (setq lst (vl-string->list (itoa int1)))) 1)
           (setq int1 (apply '+ (mapcar '(lambda (x) (atoi (chr x))) lst)))
         )
  )
Architect, Registered NC, VA, SC, & GA.
Message 5 of 19

mid-awe
Collaborator
Collaborator
Thank you very much. You all are awesome.
0 Likes
Message 6 of 19

mid-awe
Collaborator
Collaborator
I need to do some more digging. It should be possible make any number possitive.

Thanks again.
0 Likes
Message 7 of 19

hmsilva
Mentor
Mentor

@Anonymous wrote:

A couple of other functional alternatives (assuming they are fed correctly positive integers).

 

(defun droot (num)
  (if (> num 9)
    (droot (apply '+ (mapcar (function(lambda(x) (atoi(chr x)))) (vl-string->list (itoa num)))))
    num
    ))

Nicely done!

 

Henrique

EESignature

0 Likes
Message 8 of 19

dbroad
Mentor
Mentor

Thanks Henrique. 

 

@mid-awe  (abs num) is always positive.

Architect, Registered NC, VA, SC, & GA.
Message 9 of 19

Kent1Cooper
Consultant
Consultant
Accepted solution

@mid-awe wrote:

....

I need help with a lisp to calculate digital roots.

....


Because of the principle of "casting out nines," I believe this approach also gives correct results:

 

(defun C:DROOT (/ int rem9)
  (setq
    int (getint "\nInteger to find Digital Root of: ")
    rem9 (rem (abs int) 9) ; (abs) needed only if User might enter negative integer
  );setq
  (cond
    ((= int 0) 0)
    ((= rem9 0) 9)
    (rem9)
  ); cond
); defun

 

However, any routine that uses (getint) has a limitation to numbers between -32768 and 32767.  If you might need to find it for larger numbers, you can do it with a function with an argument, rather than using (getint) to ask for a number:

 

(defun DROOT (int / rem9)
  (setq rem9 (rem (abs int) 9))
  (cond
    ((= int 0) 0)
    ((= rem9 0) 9)
    (rem9)
  ); cond
); defun

Kent Cooper, AIA
Message 10 of 19

dbroad
Mentor
Mentor

Interesting Kent!  This should do the same thing.  I agree with the integer limitations.

 

(defun droot3 (num)
  (- num (* 9 (fix (/ (- num 1) 9.0)))))

or if you're sure that integers are input:

(defun droot4 (num)
  (- num (* 9 (/ (- num 1) 9))))
Architect, Registered NC, VA, SC, & GA.
Message 11 of 19

mid-awe
Collaborator
Collaborator
Wow, Kent thank you. The getint limitation did already appear to be a deal breaker. Your work around is great.

dbroad, your latest droot4 must be as tight as it can be. Well done thank you.
0 Likes
Message 12 of 19

hmsilva
Mentor
Mentor

@Kent1Cooper wrote:

(defun DROOT (int / rem9)
  (setq rem9 (rem (abs int) 9))
  (cond
    ((= int 0) 0)
    ((= rem9 0) 9)
    (rem9)
  ); cond
); defun


Well thought out!

 

Henrique

EESignature

0 Likes
Message 13 of 19

mid-awe
Collaborator
Collaborator

@dbroad FYI, your droot3 & droot4 both return only 0.0 when the integer is too big. Kent's version is still working with 12345678987654321.

0 Likes
Message 14 of 19

hmsilva
Mentor
Mentor

@mid-awe wrote:

@dbroad FYI, your droot3 & droot4 both return only 0.0 when the integer is too big. Kent's version is still working with 12345678987654321.


Hi mid-awe,

Kent's version is still working with 12345678987654321, but errors.

The digital root from 12345678987654321 is 9, not 8.0...

 

AutoLISP integers are numbers with values ranging from +2,147,483,647 to -2,147,483,648

 

There are several approaches to deal with this limitation (if you do a search you will find several), one possible method is using a string instead of an integer, of course you have to ensure that the supplied string, represents an integer...
To test if a string represents an integer, we can use something like this

 

(defun IsIntStr (str)
  (cond ((and (eq (type str) 'STR)
              (/= str "")
         )
         (vl-every (function (lambda (x) (vl-position x '(48 49 50 51 52 53 54 55 56 57)))) (vl-string->list str))
        )
  )
)

 

 

A few tests to demonstrate what I'm saying

_$ (defun droot_h (int_str / lst)
(while (> (length (setq lst (vl-string->list int_str))) 1)
(setq int_str (itoa (apply '+ (mapcar '(lambda (x) (atoi (chr x))) lst))))
)
)
DROOT_H
_$ (defun DROOT2 (int / rem9)
  (setq rem9 (rem (abs int) 9))
  (cond
    ((= int 0) 0)
    ((= rem9 0) 9)
    (rem9)
  ); cond
); defun
DROOT2
_$ (droot2 12345678987654321)
8.0
_$ (droot_h "12345678987654321")
"9"
_$ (droot2 8888888888888888888888888888888888888888)
2.0
_$ (droot_h "8888888888888888888888888888888888888888")
"5"
_$

 

Hope this helps,
Henrique

 

 

EESignature

Message 15 of 19

mid-awe
Collaborator
Collaborator
Thank you Henrique. That was very informative.

While test the various solutions, I failed to use the same number for all tests and so I did not even notice the error. Simply looking for a returned number other than 0.0 was a bad idea. I greatly appreciate you help.

Thanks again.
0 Likes
Message 16 of 19

hmsilva
Mentor
Mentor

You're welcome, mid-awe!

Henrique

EESignature

0 Likes
Message 17 of 19

Kent1Cooper
Consultant
Consultant

@hmsilva wrote:
Kent's version is still working with 12345678987654321, but errors.

....
To test if a string represents an integer, we can use something like this

 

(defun IsIntStr (str)
  (cond ((and (eq (type str) 'STR)
              (/= str "")
         )
         (vl-every (function (lambda (x) (vl-position x '(48 49 50 51 52 53 54 55 56 57)))) (vl-string->list str))
        )
  )
)

.... 


I should have realized that there's a limit to the size of numbers AutoLisp [or is it a limitation in AutoCAD ?] can deal with, not just in (getint) functions, though the overall limit is at least greater than (getint)'s limit.  Given that, if such large numbers are possible, the (rem) approach must give way to an adding-up approach.

 

A somewhat more concise way to configure the same test:

 

(defun IsStrInt (str)
  (and
    (= (type str) 'STR)
    (/= str "")
    (vl-every '(lambda (x) (< 47 x 58)) (vl-string->list str))
  )
)

 

EDIT:  There's another thread on the same topic, and it may be worth comparing approaches there to those add-up suggestions earlier on this one.

Kent Cooper, AIA
0 Likes
Message 18 of 19

hmsilva
Mentor
Mentor

@Kent1Cooper wrote:

...

I should have realized that there's a limit to the size of numbers AutoLisp [or is it a limitation in AutoCAD ?] can deal with, not just in (getint) functions, though the overall limit is at least greater than (getint)'s limit.  ...


 

integers are 32-bit signed numbers. and have limitation.

 

Henrique

EESignature

0 Likes
Message 19 of 19

hmsilva
Mentor
Mentor

@Kent1Cooper wrote:
EDIT:  There's another thread on the same topic, and it may be worth comparing approaches there to those add-up suggestions earlier on this one.

Sorry, but I could no longer edit my previous message...

 

Tim Willey's approach, it's similar to mine.

 

Henrique

EESignature

0 Likes