TheSwamp

Code Red => AutoLISP (Vanilla / Visual) => Topic started by: Hangman on October 06, 2006, 09:15:53 PM

Title: Determining Even or Odd
Post by: Hangman on October 06, 2006, 09:15:53 PM
Hey guys, I'm in a bit of a pickle.  I'm about to get swamped (no pun intended   :?  ) with a "need it yesterday" type of project.  This thing will be an ongoing project for the next 6 months.  It's a nightmare, long story.  So I'm trying to put together a couple routines that will help me for the onslaught come Monday morning.

Is there a way to determine a real number be either Even or Odd without having to type in every single number from 1 to 50 ??

Code: [Select]
...
  (setq pt-dist_12 (distance pt1 pt2))
  (setq plate-ctr (/ pt-dist_12 3))
  (setq plate-ctr (fix plate-ctr))
  (progn
    (cond  ( (  ... ;;If plate-ctr is Odd then do something ...
              ( (  ... ;;If plate-ctr is Even then do something else ...
  ))))

Is there a possibility of that or do I need to do something like this ...

Code: [Select]
...
  (setq pt-dist_12 (distance pt1 pt2))
  (setq plate-ctr (/ pt-dist_12 3))
  (setq plate-ctr (fix plate-ctr))
  (progn
    (cond  ( (or (= plate-ctr 1) (= plate-ctr 3) (= plate-ctr 5) (= plate-ctr 7) (= plate-ctr 9)) ;; all the way to 49
                ( ...  do something here ... ))
              ( (or (= plate-ctr 2) (= plate-ctr 4) (= plate-ctr 6) (= plate-ctr 8)  (= plate-ctr 10)) ;; all the way to 50
                ( ...  do something here ... ))
  ))))

Thank you for your thoughts & help.
Title: Re: Determining Even or Odd
Post by: MickD on October 06, 2006, 09:22:59 PM
is there a modulo operator in lisp??

if num%2 > 0 answer is odd. (where '%' = modulo operator)
Title: Re: Determining Even or Odd
Post by: MickD on October 06, 2006, 09:33:24 PM
lisp arithmetic function 'rem' looks like it should do the trick ;)
Title: Re: Determining Even or Odd
Post by: JohnK on October 06, 2006, 09:59:37 PM
Another spin on the topic:
If the last number is a one, its odd. If its not, then its even.

1: 00000001
2: 00000010
3: 00000011
4: 00000100
...
Title: Re: Determining Even or Odd
Post by: JohnK on October 06, 2006, 10:05:55 PM
(defun odd? (i)
  ;; odd?
  ;; Returns 0 if even and 1 if odd
   (logand i 1))

Title: Re: Determining Even or Odd
Post by: MP on October 06, 2006, 10:19:43 PM
Code: [Select]
(defun IsEven ( x )
    (zerop (rem x 2))
)


Code: [Select]
...
(setq
    pt-dist_12 (distance pt1 pt2)
    plate-ctr  (fix (/ pt-dist_12 3))
)

(if (IsEven plate-ctr)
    (DoStuffForEvenCondition)
    (DoStuffForOddCondition)
)
...
Title: Re: Determining Even or Odd
Post by: MickD on October 06, 2006, 10:42:13 PM
logand would be by far the quickest given the fact you don't need the remainder and division is a slow(er) operation for a cpu, another practical use for 0's and 1's :)
Title: Re: Determining Even or Odd
Post by: Hangman on October 06, 2006, 11:15:42 PM
Hey guys, thanks for the quick responses.  Looking over these, I'm think'n MickD is correct, I'd prefer to use the logand function.  I like the post Se7en posted,
Quote
(defun odd? (i)
  ;; odd?
  ;; Returns 0 if even and 1 if odd
   (logand i 1))
But I'm not understanding exactly how it's to work.  I've tried it with several different numbers, but it keeps coming back as Odd.  I don't think I'm working it properly and the examples in the help files don't give me much.  Could you guys post a couple examples for me. ??

Nice post MP, I like the way you did that.  Thanks for the input & help.
Title: Re: Determining Even or Odd
Post by: MP on October 06, 2006, 11:16:05 PM
logand would be by far the quickest given the fact you don't need the remainder and division is a slow(er) operation for a cpu, another practical use for 0's and 1's :)

... but no practical speed difference is realized in lisp --

Elapsed milliseconds / relative speed for 32768 iteration(s):

    (zerop (rem 1 2)) ............... 1438 / 1.01 <fastest>
    (zerop (logand 1 1)) ............ 1453 / 1.00 <slowest>

    (zerop (rem 255 2)) ............. 1437 / 1.01 <fastest>
    (zerop (logand 255 1)) .......... 1453 / 1.00 <slowest>

    (zerop (rem 65535 2)) ........... 1469 / 1.01 <fastest>
    (zerop (logand 65535 1)) ........ 1485 / 1.00 <slowest>

    (zerop (rem 1073741824 2)) ...... 1468 / 1.02 <fastest>
    (zerop (logand 1073741824 1)) ... 1500 / 1.00 <slowest>


:)
Title: Re: Determining Even or Odd
Post by: MickD on October 06, 2006, 11:33:14 PM
layer upon layer upon layer.....

oh well, it was food for thought though. :)

in technical terms, rem/modulo returns the remainder from a division eg 12/10 = 2 and 12/2 = 0.
Title: Re: Determining Even or Odd
Post by: Hangman on October 06, 2006, 11:45:06 PM
Yeah, well, my head feels like it's going to burst into flames.

K, so, 'rem' might be faster.  I don't think a couple milliseconds is going to make too much of a difference here.  But I'm learn'n someth'n new.

I just found the Logand info  (couldn't find it online, had to revert to an old book written for release 12).
So, the code Se7en posted:
Code: [Select]
(defun odd? (i)
  ;; odd?
  ;; Returns 0 if even and 1 if odd
   (logand i 1))
The 'i', to my understanding, is the integer to be tested ?.
So, one would setq i to an integer, and the 'logand' would test i for an odd bitcode ?.

So I could set my code something like:
Code: [Select]
...
(setq
    pt-dist_12 (distance pt1 pt2)
    plate-ctr  (fix (/ pt-dist_12 3))
)
(setq Odd-Even (logand plate-ctr 1))
(progn
(if (= Odd-Even 0)
    (DoStuffForEvenCondition)
    (DoStuffForOddCondition)
))
...

Hey look, it worked !!!  Too cool.  Thanks guys.
Title: Re: Determining Even or Odd
Post by: CAB on October 06, 2006, 11:53:19 PM
Code: [Select]
;; returns t of num is odd
(defun odd? (num)
   (= (logand num 1) 1)
)


Code: [Select]
(print (odd? 1))
(print (odd? 2))
(print (odd? 3))
(print (odd? 4))


Code: [Select]
;; returns t of num is even
(defun even? (num)
   (zerop (logand num 1))
)


Code: [Select]
(print (even? 1))
(print (even? 2))
(print (even? 3))
(print (even? 4))
Title: Re: Determining Even or Odd
Post by: JohnK on October 07, 2006, 02:07:01 AM
<snip>
    (zerop (rem 65535 2)) ........... 1469 / 1.01 <fastest>
    (zerop (logand 65535 1)) ........ 1485 / 1.00 <slowest>

    (zerop (rem 1073741824 2)) ...... 1468 / 1.02 <fastest>
</snip>
:)

Hey, wait a min. Well that scenario has got to be slower. Your pinning one boolean test up against two (Native mathematics is the fastest hands down so that can be negated from the test.).

If we pin my one Boole up against your math&Boole i bet i could do better then that.
Title: Re: Determining Even or Odd
Post by: JohnK on October 07, 2006, 02:09:02 AM
<snip>
Code: [Select]
;; returns t of num is even
(defun even? (num)
   (zerop (logand num 1))
)
</snip>

One question: Why have a test for both odd and even? ``If its not one...''
Title: Re: Determining Even or Odd
Post by: JohnK on October 07, 2006, 02:13:26 AM
<snip>
The 'i', to my understanding, is the integer to be tested ?.
So, one would setq i to an integer, and the 'logand' would test i for an odd bitcode ?.
</snip>

And returns true if and oly if both tests are true. In the case of `logand ... 1' we are testing to see if the first bit is a one in an int.  (Does that make sence?)

1 = 00000001
2 = 00000010

So

(logand 2 1) returns what?
Title: Re: Determining Even or Odd
Post by: CAB on October 07, 2006, 08:13:43 AM
One question: Why have a test for both odd and even? ``If its not one...''

Readability.

Code: [Select]
(If (even? num)
Code: [Select]
(If (not (odd? num))
I don't see it as costing you anything, especially if it pleases you when you read it. :-)
Title: Re: Determining Even or Odd
Post by: MP on October 07, 2006, 09:03:08 AM
Readability ... I don't see it as costing you anything, especially if it pleases you when you read it.

True, but ... in the bigger sense it just makes a library bigger than it need be, and while the code in this example is trivial it does mean there is more code to maintain. I would surmise that's why you don't see the compliment to predicate functions in most languages (e.g. exception: vbnet's is/isnot operators), for example in lisp there are no NotBoundP, NotNumberP, NotZeroP ... functions. :)

If we pin my one Boole up against your math&Boole i bet i could do better then that.

Go for it but be aware any performance gains are not realized because of the additional comparison testing one has to do in-situ, so then it comes down to practicality; ease of use. :)

/imo
Title: Re: Determining Even or Odd
Post by: MP on October 07, 2006, 10:32:08 AM
Another thing to consider --

Code: [Select]
(   (lambda ( / IsEvenRem IsEvenLogand TestFoo )

        (defun IsEvenRem (x) (zerop (rem x 2)))

        (defun IsEvenLogand (x) (zerop (logand 1 x)))
       
        (defun TestFoo ( foo x )       
            (princ
                (strcat
                    "Testing:" (vl-prin1-to-string foo) "\n\n"
                )   
            )
            (repeat 10
                (princ
                    (strcat     
                        (rtos (setq x (+ x 0.5)) 2 1)
                        " is "
                        (if (foo x) "even\n" "not even\n")
                    )
                )
            )
            (princ "\n")
            (princ)
        )
       
        (TestFoo IsEvenRem 0)
       
        (TestFoo IsEvenLogand 0)
       
    )
)
   

Testing:#<SUBR @10327c94 ISEVENREM>

0.5 is not even
1.0 is not even
1.5 is not even
2.0 is even
2.5 is not even
3.0 is not even
3.5 is not even
4.0 is even
4.5 is not even
5.0 is not even

Testing:#<SUBR @10327ca8 ISEVENLOGAND>

Backtrace:
[0.64] (VL-BT)
[1.60] (*ERROR* "bad argument type: fixnump: 0.5")
[2.55] (_call-err-hook #<SUBR ... *ERROR*> "bad argument type: fixnump: 0.5")
[3.49] (sys-error "bad argument type: fixnump: 0.5")
:ERROR-BREAK.44 nil
[4.41] (logand 1 0.5)
[5.35] (LOGAND 1 0.5)
[6.29] (FOO 0.5)
[7.24] (TESTFOO #<SUBR @103a4f8c ISEVENLOGAND> 0)
[8.18] (#<SUBR @103f91e0 -lambda->)
[9.15] (#<SUBR @103f9258 -rts_top->)
[10.12] (#<SUBR @0de62334 veval-str-body> "((lambda ..." T #<FILE internal>)
:CALLBACK-ENTRY.6 (:CALLBACK-ENTRY)
:ARQ-SUBR-CALLBACK.3 (nil 0)


Should reals (or integers larger than 2147483647, less than -2147483647) be excluded from an even test? I don't know, what's your call?

:)
Title: Re: Determining Even or Odd
Post by: CAB on October 07, 2006, 11:03:39 AM
Readability ... I don't see it as costing you anything, especially if it pleases you when you read it.

True, but ... in the bigger sense it just makes a library bigger than it need be, and while the code in this example is trivial it does mean there is more code to maintain. I would surmise that's why you don't see the compliment to predicate functions in most languages (e.g. exception: vbnet's is/isnot operators), for example in lisp there are no NotBoundP, NotNumberP, NotZeroP ... functions. :)

I don't practice that but for the sake of discussion, you could implement it this way & not worry about the maintenance issue.

Code: [Select]
(defun NotBoundP (arg)
  (not (BoundP arg))
)

(defun NotNumberP (arg)
  (not (NumberP arg))
)

(defun NotZeroP (arg)
  (not (ZeroP arg))
)

Title: Re: Determining Even or Odd
Post by: JohnK on October 07, 2006, 11:16:59 AM
*zing* ... *lmao* (I didn't see that coming)

My call: no. Not in this case. I say that because of the orig. scope of the request. The author is asking for a test to test numbers from 1-50. HOWEVER! i know that's the ``easy'' route here. -i.e. i know most people are most likely not going to ensure only int's get tested if they just toss this into their library and use it later but he does use the `fix' procedure in this case so...but, again that's kind of a cop out. Maybe i should just note that in my header that if you plan to use this or if there is a chance that there will be other types of numbers besides int's tested please seek other methods.

It all comes down to personal preference. When creating applications i prefer the proced's to be as ``little'' as they can (And by that i mean to not add too much more as far as scope goes) to save on readability and time. Its not that I would much rather come back with a diff revision later, but i like to keep my code as lean as possible.

I guess what it comes down to is: `Fun' is fun, but `production' is diff. ...I love creating procedures that take into account alot of diff situations for fun, but in production I create what needes to be created.

That's why I love using block structure in my code; I can come back and totaly change the application by adding a few lines here and a few there.

For example.

Orig: (defun odd? (i) (logand i 1) )
New:
Code: [Select]
(defun odd? ( i )
  ;; determine if a number is odd?
  ;; scope: numbers 1 -- 50 only
  (if (and (>= i 0) (<= i 50))
     (logand i 1)
     '*Error* ) )

I dont know, its a wash I suppose.

CAB, It creates maintence issues with the code. Its a bit redundant. HOWEVER! if  you are in need of a way to ``test for evens'' in a diff proced, then i can see using it. (Im saying try to name your procedures in the spirit of your application. -e.g. if you spend 100 lines `talking about evens' and then all of a sudden you `test for odd' becuse you were to lazy to alter a library function then...)