Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #58912 > unrolled thread

Rosetta: Caesar cipher

Started by"Robert L." <No_spamming@noWhere_7073.org>
First post2017-05-09 20:47 +0000
Last post2017-05-12 13:41 -0400
Articles 12 — 8 participants

Back to article view | Back to comp.lang.forth


Contents

  Rosetta: Caesar cipher "Robert L." <No_spamming@noWhere_7073.org> - 2017-05-09 20:47 +0000
    Re: Rosetta: Caesar cipher Joel Rees <joel.rees@gmail.com> - 2017-05-09 20:20 -0700
      Re: Rosetta: Caesar cipher hughaguilar96@gmail.com - 2017-05-12 13:52 -0700
    Re: Rosetta: Caesar cipher rickman <gnuarm@gmail.com> - 2017-05-10 20:07 -0400
      Re: Rosetta: Caesar cipher Julian Fondren <julian.fondren@gmail.com> - 2017-05-10 21:12 -0700
        Re: Rosetta: Caesar cipher rickman <gnuarm@gmail.com> - 2017-05-11 01:50 -0400
          Re: Rosetta: Caesar cipher Julian Fondren <julian.fondren@gmail.com> - 2017-05-11 05:34 -0700
            Re: Rosetta: Caesar cipher Alex <alex@rivadpm.com> - 2017-05-11 14:39 +0100
              Re: Rosetta: Caesar cipher anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2017-05-11 16:04 +0000
                Re: Rosetta: Caesar cipher Alex <alex@rivadpm.com> - 2017-05-11 21:35 +0100
              Re: Rosetta: Caesar cipher mhx@iae.nl - 2017-05-11 10:21 -0700
            Re: Rosetta: Caesar cipher rickman <gnuarm@gmail.com> - 2017-05-12 13:41 -0400

#58912 — Rosetta: Caesar cipher

From"Robert L." <No_spamming@noWhere_7073.org>
Date2017-05-09 20:47 +0000
SubjectRosetta: Caesar cipher
Message-ID<oet9tb$166r$1@gioia.aioe.org>
> Implement a Caesar cipher, both encoding and decoding. 
> The key is an integer from 1 to 25. 
> 
> This cipher rotates (either towards left or right) the letters
> of the alphabet (A to Z).
> 
> The encoding replaces each letter with the 1st to 25th next
> letter in the alphabet (wrapping Z to A).
> 
> So key 2 encrypts "HI" to "JK", but key 20 encrypts "HI" to "BC".


(use srfi-13)

(define lower-alphabet
  (string-tabulate
    (lambda(i) (integer->char (+ (char->integer #\a) i))) 26))

(define (shifted-alphabet n)
  ;; Rotate the lowercase letters.
  (define s (xsubstring lower-alphabet n))
  (string-append s (string-upcase s)))

(define (encipher n str)
  (string-translate str
    (shifted-alphabet 0)
    (shifted-alphabet n)))
 
(define (decipher n str) (encipher (- n) str))


(encipher 3 "However, the man wasn't there.[9]")
 ===>
"Krzhyhu, wkh pdq zdvq'w wkhuh.[9]"

(decipher 3 (encipher 3 "However, the man wasn't there.[9]"))
 ===>
"However, the man wasn't there.[9]"


In Forth?


-- 
[W]e had enough employees who made more than 85 to fill all the openings.  The
highest score that any of the blacks scored on the test was 11.  The lowest
score that any black made on the test was 4.  All four of those blacks went
into skilled-trades training.
https://archive.org/download/TheOldmanArchives/oldman29-081003.mp3

[toc] | [next] | [standalone]


#58917

FromJoel Rees <joel.rees@gmail.com>
Date2017-05-09 20:20 -0700
Message-ID<b6021280-68a3-4efe-8205-462ee60fa6ec@googlegroups.com>
In reply to#58912
On Wednesday, May 10, 2017 at 5:47:43 AM UTC+9, Robert L. wrote:
> > Implement a Caesar cipher, both encoding and decoding. 
> > The key is an integer from 1 to 25. 
> > 
> > This cipher rotates (either towards left or right) the letters
> > of the alphabet (A to Z).
> > 
> > The encoding replaces each letter with the 1st to 25th next
> > letter in the alphabet (wrapping Z to A).
> > 
> > So key 2 encrypts "HI" to "JK", but key 20 encrypts "HI" to "BC".
> 
> 
> (use srfi-13)
> 
> (define lower-alphabet
>   (string-tabulate
>     (lambda(i) (integer->char (+ (char->integer #\a) i))) 26))
> 
> (define (shifted-alphabet n)
>   ;; Rotate the lowercase letters.
>   (define s (xsubstring lower-alphabet n))
>   (string-append s (string-upcase s)))
> 
> (define (encipher n str)
>   (string-translate str
>     (shifted-alphabet 0)
>     (shifted-alphabet n)))
>  
> (define (decipher n str) (encipher (- n) str))
> 
> 
> (encipher 3 "However, the man wasn't there.[9]")
>  ===>
> "Krzhyhu, wkh pdq zdvq'w wkhuh.[9]"
> 
> (decipher 3 (encipher 3 "However, the man wasn't there.[9]"))
>  ===>
> "However, the man wasn't there.[9]"
> 
> 
> In Forth?

https://www.rosettacode.org/wiki/Caesar_cipher#Forth

It would seem to me you could have at least asked about Vigenère.
> 
> -- 
> [W]e had enough employees who made more than 85 to fill all the openings.  The
> highest score that any of the blacks scored on the test was 11.  The lowest
> score that any black made on the test was 4.  All four of those blacks went
> into skilled-trades training.
> https://archive.org/download/TheOldmanArchives/oldman29-081003.mp3

[toc] | [prev] | [next] | [standalone]


#59133

Fromhughaguilar96@gmail.com
Date2017-05-12 13:52 -0700
Message-ID<09c72d31-5a89-4d03-966f-7e0682f8ffce@googlegroups.com>
In reply to#58917
On Tuesday, May 9, 2017 at 8:20:51 PM UTC-7, Joel Rees wrote:
> On Wednesday, May 10, 2017 at 5:47:43 AM UTC+9, Robert L. wrote:
> > > Implement a Caesar cipher, both encoding and decoding. 
> > In Forth?
> 
> https://www.rosettacode.org/wiki/Caesar_cipher#Forth
> 
> It would seem to me you could have at least asked about Vigenère.

I wrote a Forth program to crack the Vigenère cipher --- it was published in "Forth Dimensions" magazine.

[toc] | [prev] | [next] | [standalone]


#59019

Fromrickman <gnuarm@gmail.com>
Date2017-05-10 20:07 -0400
Message-ID<of09pp$k46$1@dont-email.me>
In reply to#58912
On 5/9/2017 4:47 PM, Robert L. wrote:
>> Implement a Caesar cipher, both encoding and decoding.
>> The key is an integer from 1 to 25.
>>
>> This cipher rotates (either towards left or right) the letters
>> of the alphabet (A to Z).
>>
>> The encoding replaces each letter with the 1st to 25th next
>> letter in the alphabet (wrapping Z to A).
>>
>> So key 2 encrypts "HI" to "JK", but key 20 encrypts "HI" to "BC".
>
>
> (use srfi-13)
>
> (define lower-alphabet
>   (string-tabulate
>     (lambda(i) (integer->char (+ (char->integer #\a) i))) 26))
>
> (define (shifted-alphabet n)
>   ;; Rotate the lowercase letters.
>   (define s (xsubstring lower-alphabet n))
>   (string-append s (string-upcase s)))
>
> (define (encipher n str)
>   (string-translate str
>     (shifted-alphabet 0)
>     (shifted-alphabet n)))
>
> (define (decipher n str) (encipher (- n) str))
>
>
> (encipher 3 "However, the man wasn't there.[9]")
>  ===>
> "Krzhyhu, wkh pdq zdvq'w wkhuh.[9]"
>
> (decipher 3 (encipher 3 "However, the man wasn't there.[9]"))
>  ===>
> "However, the man wasn't there.[9]"
>
>
> In Forth?

Here is my attempt.   I had to work on this a bit as my string knowledge 
is a bit rusty... no, positively seized up!  I couldn't remember how to 
create strings in the various modes.  I'm not sure the test code will 
work universally.  It runs under Win32Forth.

marker rotc

: ScreenChar ( char -- char flag )  \ Check for alpha char
     dup [char] A dup 26 + within if [char] A else
         dup [char] a dup 26 + within if [char] a else
         FALSE then then ;

: RotChar ( char1 flag key -- char2 )  \ Rotate alpha char
     -rot tuck - rot + 26 mod + ;

0 value rotkey
variable tmpstr 30 chars allot

\ En/Decrypt the Caesar cipher with key
: RotCypher ( caddr u key -- ) \ Output to tmpstr
     to rotkey
     0 do                                ( caddr -- )
         dup I + c@ ScreenChar dup if    ( caddr char flag -- )
             rotkey RotChar else
             drop then                   ( caddr char -- )
         tmpstr I + c!                   ( caddr -- )
         loop
     drop ;

: "type" ( caddr u -- ) cr [char] " emit type [char] " emit .s ;

cr cr .( Start testing) .s
cr S" ABC mno 1, 2, 3" .s
2dup "type"
tuck 1 RotCypher tmpstr swap 2dup "type"
2dup 25 RotCypher "type"


-- 

Rick C

[toc] | [prev] | [next] | [standalone]


#59028

FromJulian Fondren <julian.fondren@gmail.com>
Date2017-05-10 21:12 -0700
Message-ID<27244bc8-3cf8-445d-803e-bef86c1e6122@googlegroups.com>
In reply to#59019
On Wednesday, May 10, 2017 at 7:07:29 PM UTC-5, rickman wrote:
> Here is my attempt.   I had to work on this a bit as my string knowledge 
> is a bit rusty... no, positively seized up!  I couldn't remember how to 
> create strings in the various modes.

What do you mean by 'modes'?

> I'm not sure the test code will 
> work universally.  It runs under Win32Forth.

It runs the same on many systems. Only VARIABLE ... ALLOT strikes me
as code to worry about. You should use CREATE if you want the ALLOT'd
dictionary space to be contiguous with the address the word returns.

> marker rotc
> 
> : ScreenChar ( char -- char flag )  \ Check for alpha char
>      dup [char] A dup 26 + within if [char] A else
>          dup [char] a dup 26 + within if [char] a else
>          FALSE then then ;
> 
> : RotChar ( char1 flag key -- char2 )  \ Rotate alpha char
>      -rot tuck - rot + 26 mod + ;

The 'flag' has a more interesting type than the stack comment
suggests. This is a good example of 'passing flags around' even so:
you test the char in ScreenChar and then, instead of rotating it at
once, you produce a value that's then tested again in RotCypher. Do
you anticipate any other use for ScreenChar's output than later
rotation?

That ScreenChar leaves 'char' on the stack looks like an optimization
or rather an unwillingness to drop something you know you'll need
right after anyway. I'd take that as an indication that some other
factorization should be sought. But anyway, why worry? DROP <CONSTANT>
is easy fodder for an optimizer:

  : x 1 ; : y drop 1 ;  ok
  see x
  80839BF   4 # EBP SUB                   83ED04
  80839C2   EBX 0 [EBP] MOV               895D00
  80839C5   1 # EBX MOV                   BB01000000
  80839CA   RET                           C3 ok
  see y 
  80839DF   1 # EBX MOV                   BB01000000
  80839E4   RET                           C3 ok

> 
> 0 value rotkey

A VALUE could make sense. You could have a GUI element control the
'encryption' by changing that. You could have words like REVERSIBLE
(rot13) or NAIVE (rot1) or SILLY (rot26) that set the value that a
later RotCypher would use... but then RotCypher sets rotkey itself, so
in the end this is just a local variable.

So why not use a local variable?

> variable tmpstr 30 chars allot
> 
> \ En/Decrypt the Caesar cipher with key
> : RotCypher ( caddr u key -- ) \ Output to tmpstr
>      to rotkey
>      0 do                                ( caddr -- )
>          dup I + c@ ScreenChar dup if    ( caddr char flag -- )
>              rotkey RotChar else
>              drop then                   ( caddr char -- )
>          tmpstr I + c!                   ( caddr -- )
>          loop
>      drop ;

So you loop over the indexes of the string provided and use that to
index into the input and output strings, both. If you'd used VALUEs
for those two addresses, this same code could support in-place
rotation (at a cost of unnecessary writes) but could also produce the
rotated string in its ultimate destination, a prepared buffer in a
data structure or whatever, without the caller having to move the
string there as an additional step. This would also free your code
from having to care about how long the string might be.

Not to propose those VALUEs , really, but just to say: *if* you're
going to use a VALUE like this, there seems to be more use for source
address in a VALUE than for the rotation 'key'.

Anyway, there's this alternative: loop over the addresses of the
source string's characters. This means you have to manage the
destination address differently. (And it's perfect for in-place
mutation.) It also gets source string off the data stack, which may
make the data stack easier to think about even if you end up
maintaining the same depths through the loop.

  : RotCypher ( c-addr u key -- )
    tmpstr 2swap bounds ?do ( key tmpstr )
      i c@ ScreenChar dup if fourth RotChar else drop then
      over c! 1+
    loop 2drop ;

Or with this rotchar:

  : rotchar ( key c -- c' )
    dup [char] A dup 26 + within if [char] A - + 26 mod [char] A + else
    dup [char] a dup 26 + within if [char] a - + 26 mod [char] a + else
    nip then then ;

  : RotCypher ( c-addr u key -- )
    tmpstr 2swap bounds ?do ( key tmpstr )
      over i c@ rotchar
      over c! 1+
    loop 2drop ;

This rotchar stands out as code that can be improved, but the rest is
fine. In your code RotChar looked pretty optimal on its own but
everything else looked more dubious. This rotchar, if improved, might
actually substantially improve the program. Improvements to your
ScreenChar or RotCypher would do less. I prefer this arrangement but
don't know if there's some design principle that it exhibits.

> : "type" ( caddr u -- ) cr [char] " emit type [char] " emit .s ;
> 
> cr cr .( Start testing) .s
> cr S" ABC mno 1, 2, 3" .s
> 2dup "type"
> tuck 1 RotCypher tmpstr swap 2dup "type"
> 2dup 25 RotCypher "type"
> 
> 
> -- 
> 
> Rick C

  : rot13: ( -- )
    source bounds ?do
      13 i c@ rotchar i c!
    loop ; immediate

  rot13: : "glcr" ( pnqqe h -- ) pe [pune] " rzvg glcr [pune] " rzvg .f ;
  rot13:
  rot13: pe pe .( Fgneg grfgvat) .f
  rot13: pe F" NOP zab 1, 2, 3" .f
  rot13: 2qhc "glcr"
  rot13: ghpx 1 EbgPlcure gzcfge fjnc 2qhc "glcr"
  rot13: 2qhc 25 EbgPlcure "glcr"

  : secret ( -- c-addr u )
    rot13: f" 12345" ;

somehow I can still see my password in that last example...

[toc] | [prev] | [next] | [standalone]


#59033

Fromrickman <gnuarm@gmail.com>
Date2017-05-11 01:50 -0400
Message-ID<of0tsi$6u0$1@dont-email.me>
In reply to#59028
On 5/11/2017 12:12 AM, Julian Fondren wrote:
> On Wednesday, May 10, 2017 at 7:07:29 PM UTC-5, rickman wrote:
>> Here is my attempt.   I had to work on this a bit as my string knowledge
>> is a bit rusty... no, positively seized up!  I couldn't remember how to
>> create strings in the various modes.
>
> What do you mean by 'modes'?

Compile, interpret.  Seems interpretation is a tough mode to create 
strings in unless I am totally misunderstanding what I've read in the 
ANS standard.


>> I'm not sure the test code will
>> work universally.  It runs under Win32Forth.
>
> It runs the same on many systems. Only VARIABLE ... ALLOT strikes me
> as code to worry about. You should use CREATE if you want the ALLOT'd
> dictionary space to be contiguous with the address the word returns.

Ok.  Thanks.

>> marker rotc
>>
>> : ScreenChar ( char -- char flag )  \ Check for alpha char
>>      dup [char] A dup 26 + within if [char] A else
>>          dup [char] a dup 26 + within if [char] a else
>>          FALSE then then ;
>>
>> : RotChar ( char1 flag key -- char2 )  \ Rotate alpha char
>>      -rot tuck - rot + 26 mod + ;
>
> The 'flag' has a more interesting type than the stack comment
> suggests. This is a good example of 'passing flags around' even so:
> you test the char in ScreenChar and then, instead of rotating it at
> once, you produce a value that's then tested again in RotCypher. Do
> you anticipate any other use for ScreenChar's output than later
> rotation?

Look at the problem and figure out why I did it that way.  There is a 
specific reason which isn't all that hard to see.  Maybe I missed 
something that would make it simpler.


> That ScreenChar leaves 'char' on the stack looks like an optimization
> or rather an unwillingness to drop something you know you'll need
> right after anyway. I'd take that as an indication that some other
> factorization should be sought. But anyway, why worry? DROP <CONSTANT>
> is easy fodder for an optimizer:
>
>   : x 1 ; : y drop 1 ;  ok
>   see x
>   80839BF   4 # EBP SUB                   83ED04
>   80839C2   EBX 0 [EBP] MOV               895D00
>   80839C5   1 # EBX MOV                   BB01000000
>   80839CA   RET                           C3 ok
>   see y
>   80839DF   1 # EBX MOV                   BB01000000
>   80839E4   RET                           C3 ok

You've lost me.  I don't write code thinking about optimizers or even 
optimization in general.  I try to write code to be easy to understand 
and use.  I can't remember the last time I wrote code that needed any 
sort of optimization.


>> 0 value rotkey
>
> A VALUE could make sense. You could have a GUI element control the
> 'encryption' by changing that. You could have words like REVERSIBLE
> (rot13) or NAIVE (rot1) or SILLY (rot26) that set the value that a
> later RotCypher would use... but then RotCypher sets rotkey itself, so
> in the end this is just a local variable.
>
> So why not use a local variable?

I have never gotten in a habit of locals.  Why is that a significant 
thing?  I only did this when I totally forgot my forth and tried to 
store it on the return stack inside the loop, lol, so I fixed it with a 
value.


>> variable tmpstr 30 chars allot
>>
>> \ En/Decrypt the Caesar cipher with key
>> : RotCypher ( caddr u key -- ) \ Output to tmpstr
>>      to rotkey
>>      0 do                                ( caddr -- )
>>          dup I + c@ ScreenChar dup if    ( caddr char flag -- )
>>              rotkey RotChar else
>>              drop then                   ( caddr char -- )
>>          tmpstr I + c!                   ( caddr -- )
>>          loop
>>      drop ;
>
> So you loop over the indexes of the string provided and use that to
> index into the input and output strings, both. If you'd used VALUEs
> for those two addresses, this same code could support in-place
> rotation (at a cost of unnecessary writes) but could also produce the
> rotated string in its ultimate destination, a prepared buffer in a
> data structure or whatever, without the caller having to move the
> string there as an additional step. This would also free your code
> from having to care about how long the string might be.

I started out writing this for modifications in place but decided to 
change it.  Instead of adding the index, I incremented the address.  But 
it was messy working with two addresses on the stack.  Also, like I 
said, figuring out how to feed it a string where the data could be 
altered was not so easy.  I understand why Hugh makes a big deal of the 
lack of array handling in Forth and has a library.  I also have an array 
library but didn't want to clutter this solution with all that. 
Obviously I'm not so familiar with Forth that this comes easy to me.  :( 
  It is always so long between my using Forth that I forget much of what 
I had learned the last time.


> Not to propose those VALUEs , really, but just to say: *if* you're
> going to use a VALUE like this, there seems to be more use for source
> address in a VALUE than for the rotation 'key'.

I'm not following this at all.


> Anyway, there's this alternative: loop over the addresses of the
> source string's characters. This means you have to manage the
> destination address differently. (And it's perfect for in-place
> mutation.) It also gets source string off the data stack, which may
> make the data stack easier to think about even if you end up
> maintaining the same depths through the loop.
>
>   : RotCypher ( c-addr u key -- )
>     tmpstr 2swap bounds ?do ( key tmpstr )
>       i c@ ScreenChar dup if fourth RotChar else drop then
>       over c! 1+
>     loop 2drop ;

Bounds?


> Or with this rotchar:
>
>   : rotchar ( key c -- c' )
>     dup [char] A dup 26 + within if [char] A - + 26 mod [char] A + else
>     dup [char] a dup 26 + within if [char] a - + 26 mod [char] a + else
>     nip then then ;
>
>   : RotCypher ( c-addr u key -- )
>     tmpstr 2swap bounds ?do ( key tmpstr )
>       over i c@ rotchar
>       over c! 1+
>     loop 2drop ;
>
> This rotchar stands out as code that can be improved, but the rest is
> fine. In your code RotChar looked pretty optimal on its own but
> everything else looked more dubious. This rotchar, if improved, might
> actually substantially improve the program. Improvements to your
> ScreenChar or RotCypher would do less. I prefer this arrangement but
> don't know if there's some design principle that it exhibits.
>
>> : "type" ( caddr u -- ) cr [char] " emit type [char] " emit .s ;
>>
>> cr cr .( Start testing) .s
>> cr S" ABC mno 1, 2, 3" .s
>> 2dup "type"
>> tuck 1 RotCypher tmpstr swap 2dup "type"
>> 2dup 25 RotCypher "type"
>>
>>
>> --
>>
>> Rick C
>
>   : rot13: ( -- )
>     source bounds ?do
>       13 i c@ rotchar i c!
>     loop ; immediate
>
>   rot13: : "glcr" ( pnqqe h -- ) pe [pune] " rzvg glcr [pune] " rzvg .f ;
>   rot13:
>   rot13: pe pe .( Fgneg grfgvat) .f
>   rot13: pe F" NOP zab 1, 2, 3" .f
>   rot13: 2qhc "glcr"
>   rot13: ghpx 1 EbgPlcure gzcfge fjnc 2qhc "glcr"
>   rot13: 2qhc 25 EbgPlcure "glcr"
>
>   : secret ( -- c-addr u )
>     rot13: f" 12345" ;
>
> somehow I can still see my password in that last example...

It's late and I still need to pay my bills.  I'll look at this again 
tomorrow to see if I can figure out source and bounds.

-- 

Rick C

[toc] | [prev] | [next] | [standalone]


#59081

FromJulian Fondren <julian.fondren@gmail.com>
Date2017-05-11 05:34 -0700
Message-ID<f1076906-0347-405f-ba3d-dadf1e9a065e@googlegroups.com>
In reply to#59033
On Thursday, May 11, 2017 at 12:50:19 AM UTC-5, rickman wrote:
> On 5/11/2017 12:12 AM, Julian Fondren wrote:
> > On Wednesday, May 10, 2017 at 7:07:29 PM UTC-5, rickman wrote:
> >> Here is my attempt.   I had to work on this a bit as my string knowledge
> >> is a bit rusty... no, positively seized up!  I couldn't remember how to
> >> create strings in the various modes.
> >
> > What do you mean by 'modes'?
> 
> Compile, interpret.  Seems interpretation is a tough mode to create 
> strings in unless I am totally misunderstanding what I've read in the 
> ANS standard.
> 

Oh. It's not that bad: S" and C" come with permanent storage when
compiled. When interpreted S" only comes with temporary storage, only
two buffers guaranteed in the presence of the file wordset.

So you create strings the same, you just have to think about where
you're putting them, since it's no longer assumed that you want to
hang onto them forever.

Forths often make it a little bit more convenient than that. Sometimes
they make it so convenient that you want for a way to only have
temporary strings again.

> >> marker rotc
> >>
> >> : ScreenChar ( char -- char flag )  \ Check for alpha char
> >>      dup [char] A dup 26 + within if [char] A else
> >>          dup [char] a dup 26 + within if [char] a else
> >>          FALSE then then ;
> >>
> >> : RotChar ( char1 flag key -- char2 )  \ Rotate alpha char
> >>      -rot tuck - rot + 26 mod + ;
> >
> > The 'flag' has a more interesting type than the stack comment
> > suggests. This is a good example of 'passing flags around' even so:
> > you test the char in ScreenChar and then, instead of rotating it at
> > once, you produce a value that's then tested again in RotCypher. Do
> > you anticipate any other use for ScreenChar's output than later
> > rotation?
> 
> Look at the problem and figure out why I did it that way.  There is a 
> specific reason which isn't all that hard to see.  Maybe I missed 
> something that would make it simpler.

I don't see it. If you think I didn't notice that the 'flag' is A|a|0
and that you use that, yes I noticed that -- that doesn't explain why
you don't immediately pass the 'flag' to RotChar in the A|a cases
instead of testing it again.

> You've lost me.  I don't write code thinking about optimizers or even 
> optimization in general.  I try to write code to be easy to understand 
> and use.  I can't remember the last time I wrote code that needed any 
> sort of optimization.

  : x [char] a [char] z 1+ within ;
  : y [char] a [ char z 1+ ] literal within ;

The first is easier to follow than the second and it also has the
overhead of an unnecessary 1+ every time it's run if you don't at
least have a peephole optimizer. The second is not so hard to read
that you'd worry about avoiding it though if you lacked an optimizer.
Even in scripting languages people try to keep in mind the costs of
different code and they tend to write code that's influenced by those
expectations. It's not 'microoptimization' if you write the code the
optimal way the first time you write it, right?

If you say you're above that -- then let's move on.

> I have never gotten in a habit of locals.  Why is that a significant 
> thing?  I only did this when I totally forgot my forth and tried to 
> store it on the return stack inside the loop, lol, so I fixed it with a 
> value.

Your code has the feature that someone can check and see what the
last-used rotation key was. I was aware of that while reading your
code and this awareness only distracted from the code because your
actual intent was to use a local.

> I started out writing this for modifications in place but decided to 
> change it.  Instead of adding the index, I incremented the address.  But 
> it was messy working with two addresses on the stack.

OK. In general if you're having trouble with the stack you can back
away from the immediate task and consider what factors would make life
easier.

  : third ( a b c -- a b c a ) 2 pick ;
  : under+ ( a b c -- a+c b ) rot + swap ;

  ( destA srcA ) count third c! 1 under+
  \ copies a char from src to dest and increments both addresses
  \ you could use 2DUP 1 MOVE but you probably want to modify the char

  ( srcA destA ) over c@ over c! 1+ 1 under+
  \ same

Two addresses isn't that bad. With the rotation key it gets more
annoying, but you're already using a local for that.

  ( key srcA destA ) third third c@ rotate over c! 1+ 1 under+

With ROTATE ( key c -- c' )

> I understand why Hugh makes a big deal of the 
> lack of array handling in Forth and has a library.

He makes too big of a deal about it. You can generally improve his
code just by removing the crutch he's leaned it on.

> > Not to propose those VALUEs , really, but just to say: *if* you're
> > going to use a VALUE like this, there seems to be more use for source
> > address in a VALUE than for the rotation 'key'.
> 
> I'm not following this at all.

  : RotCypher ( caddr u key -- ) \ Output to tmpstr
       locals| rotkey |
       0 do                                ( caddr -- )
           dup I + c@ ScreenChar dup if    ( caddr char flag -- )
               rotkey RotChar else
               drop then                   ( caddr char -- )
           tmpstr I + c!                   ( caddr -- )
           loop
       drop ;

vs.

  : RotChar ( key char1 flag -- char2 )  \ Rotate alpha char
       tuck - rot + 26 mod + ;
  
  : RotCypher ( caddr u key -- ) \ Output to tmpstr
       rot tmpstr locals| dest src |
       swap 0 do                           ( key -- )
           dup src I + c@ ScreenChar dup if
               RotChar else
               drop nip then
           dest I + c!
           loop
       drop ;

It's just as easy, but if you use VALUEs instead of local variables
then RotCypher's stack effect can become ( key u -- ) and you can set
up the VALUEs before calling it.

> 
> > Anyway, there's this alternative: loop over the addresses of the
> > source string's characters. This means you have to manage the
> > destination address differently. (And it's perfect for in-place
> > mutation.) It also gets source string off the data stack, which may
> > make the data stack easier to think about even if you end up
> > maintaining the same depths through the loop.
> >
> >   : RotCypher ( c-addr u key -- )
> >     tmpstr 2swap bounds ?do ( key tmpstr )
> >       i c@ ScreenChar dup if fourth RotChar else drop then
> >       over c! 1+
> >     loop 2drop ;
> 
> Bounds?

it's just OVER + SWAP ( c-addr1 u -- c-addr1+u c-addr )

BOUNDS DO ( I is the address of each character of the string ) LOOP

> It's late and I still need to pay my bills.  I'll look at this again 
> tomorrow to see if I can figure out source and bounds.

http://forth-standard.org/standard/core/SOURCE

Modifying it is a hack of course.

[toc] | [prev] | [next] | [standalone]


#59082

FromAlex <alex@rivadpm.com>
Date2017-05-11 14:39 +0100
Message-ID<of1pc1$ags$1@dont-email.me>
In reply to#59081
On 5/11/2017 13:34, Julian Fondren wrote:
>> Bounds?
> it's just OVER + SWAP ( c-addr1 u -- c-addr1+u c-addr )
> 
> BOUNDS DO ( I is the address of each character of the string ) LOOP
> 

I never understood what the Intel x86 XADD opcode was for until I came 
across BOUNDS. It enables OVER + SWAP in a single operation.

-- 
Alex

[toc] | [prev] | [next] | [standalone]


#59086

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2017-05-11 16:04 +0000
Message-ID<2017May11.180411@mips.complang.tuwien.ac.at>
In reply to#59082
Alex <alex@rivadpm.com> writes:
>I never understood what the Intel x86 XADD opcode

It's used with a lock prefix in concurrent programming
<https://en.wikipedia.org/wiki/Fetch-and-add>.

- anton
-- 
M. Anton Ertl  http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
     New standard: http://www.forth200x.org/forth200x.html
   EuroForth 2017: http://www.euroforth.org/ef17/

[toc] | [prev] | [next] | [standalone]


#59094

FromAlex <alex@rivadpm.com>
Date2017-05-11 21:35 +0100
Message-ID<of2hp1$3uk$1@dont-email.me>
In reply to#59086
On 5/11/2017 17:04, Anton Ertl wrote:
> Alex <alex@rivadpm.com> writes:
>> I never understood what the Intel x86 XADD opcode
> 
> It's used with a lock prefix in concurrent programming
> <https://en.wikipedia.org/wiki/Fetch-and-add>.
> 
> - anton
> 

I'd have used CAS for that (LOCK CMPXCHG etc). Trust Intel to have seven 
ways to skin a cat.

-- 
Alex

[toc] | [prev] | [next] | [standalone]


#59090

Frommhx@iae.nl
Date2017-05-11 10:21 -0700
Message-ID<cc16b15a-ce07-44ae-8cc0-312d6d4c6f87@googlegroups.com>
In reply to#59082
On Thursday, May 11, 2017 at 3:39:21 PM UTC+2, Alex wrote:
> On 5/11/2017 13:34, Julian Fondren wrote:
> >> Bounds?
> > it's just OVER + SWAP ( c-addr1 u -- c-addr1+u c-addr )
> > 
> > BOUNDS DO ( I is the address of each character of the string ) LOOP
> > 
> 
> I never understood what the Intel x86 XADD opcode was for until I came 
> across BOUNDS. It enables OVER + SWAP in a single operation.

iForth uses it for multi-threaded code.

[UNDEFINED] LOCKED+!
[IF]	: LOCKED+!IL ( delta addr -- [addr] )
		2 1 IN/OUT                       \ _bx is address, _cx is val
		$F0 C, 64b? $0F C, $C1 C, $0B C, \ lock, rcx -> [rbx] xadd,
		64b? $8B C, $D9 C,               \ rcx -> rbx mov,
		ADJUST-STACK ; IMMEDIATE PRIVATE
	\ Add n to the cell content at addr and leave the old value found there.
	\ A locked instruction so it can be used in multithreaded environments
	: LOCKED+! ( delta addr -- [addr] ) STATE @ IF POSTPONE LOCKED+!IL ELSE LOCKED+!IL ENDIF ; IMMEDIATE
[THEN]

-marcel

[toc] | [prev] | [next] | [standalone]


#59132

Fromrickman <gnuarm@gmail.com>
Date2017-05-12 13:41 -0400
Message-ID<of4rtu$2ii$1@dont-email.me>
In reply to#59081
On 5/11/2017 8:34 AM, Julian Fondren wrote:
> On Thursday, May 11, 2017 at 12:50:19 AM UTC-5, rickman wrote:
>> On 5/11/2017 12:12 AM, Julian Fondren wrote:
>>> On Wednesday, May 10, 2017 at 7:07:29 PM UTC-5, rickman wrote:
>>>> Here is my attempt.   I had to work on this a bit as my string knowledge
>>>> is a bit rusty... no, positively seized up!  I couldn't remember how to
>>>> create strings in the various modes.
>>>
>>> What do you mean by 'modes'?
>>
>> Compile, interpret.  Seems interpretation is a tough mode to create
>> strings in unless I am totally misunderstanding what I've read in the
>> ANS standard.
>>
>
> Oh. It's not that bad: S" and C" come with permanent storage when
> compiled. When interpreted S" only comes with temporary storage, only
> two buffers guaranteed in the presence of the file wordset.

Ah, found it.  In the core wordset S" is not defined for interpretation. 
  See?  That's what I mean about it being complicated.


> So you create strings the same, you just have to think about where
> you're putting them, since it's no longer assumed that you want to
> hang onto them forever.

The details of the behavior wasn't my problem.  My problem was figuring 
out which of the many string words to use (some are only for printing, 
some only for compiling...)  I don't use Forth all the time and so 
forget lots of the many details that are required.


> Forths often make it a little bit more convenient than that. Sometimes
> they make it so convenient that you want for a way to only have
> temporary strings again.
>
>>>> marker rotc
>>>>
>>>> : ScreenChar ( char -- char flag )  \ Check for alpha char
>>>>      dup [char] A dup 26 + within if [char] A else
>>>>          dup [char] a dup 26 + within if [char] a else
>>>>          FALSE then then ;
>>>>
>>>> : RotChar ( char1 flag key -- char2 )  \ Rotate alpha char
>>>>      -rot tuck - rot + 26 mod + ;
>>>
>>> The 'flag' has a more interesting type than the stack comment
>>> suggests. This is a good example of 'passing flags around' even so:
>>> you test the char in ScreenChar and then, instead of rotating it at
>>> once, you produce a value that's then tested again in RotCypher. Do
>>> you anticipate any other use for ScreenChar's output than later
>>> rotation?
>>
>> Look at the problem and figure out why I did it that way.  There is a
>> specific reason which isn't all that hard to see.  Maybe I missed
>> something that would make it simpler.
>
> I don't see it. If you think I didn't notice that the 'flag' is A|a|0
> and that you use that, yes I noticed that -- that doesn't explain why
> you don't immediately pass the 'flag' to RotChar in the A|a cases
> instead of testing it again.

I guess it seemed clearer to me originally that the third case was very 
different.  In the original code it did nothing since the encryption was 
done in place.  I was thinking the ScreenChar flow was three paths and 
the following code was two paths, RotChar or not.  The original code 
used the KEY on the return stack which was not accessible inside 
ScreenChar.  Now that the Key is in a variable it's not a big deal to 
call RotChar inside of ScreenChar.  In fact I refactored the code and it 
is a lot simpler with less on the stack.  But it calls AlphaTest twice, 
once for A-Z and again for a-z even if it was found in A-Z.


>> You've lost me.  I don't write code thinking about optimizers or even
>> optimization in general.  I try to write code to be easy to understand
>> and use.  I can't remember the last time I wrote code that needed any
>> sort of optimization.
>
>   : x [char] a [char] z 1+ within ;
>   : y [char] a [ char z 1+ ] literal within ;
>
> The first is easier to follow than the second and it also has the
> overhead of an unnecessary 1+ every time it's run if you don't at
> least have a peephole optimizer. The second is not so hard to read
> that you'd worry about avoiding it though if you lacked an optimizer.
> Even in scripting languages people try to keep in mind the costs of
> different code and they tend to write code that's influenced by those
> expectations. It's not 'microoptimization' if you write the code the
> optimal way the first time you write it, right?

Uh, if it walks like a duck...  If I am spending time at all thinking of 
things like the above, they are optimizations.  I'll let someone else 
make the value judgements of "micro" or not.  I'm more concerned with 
*when* they are done.  If they are done before I've found a need for 
them they are 'pointless-optimizations', no?


> If you say you're above that -- then let's move on.
>
>> I have never gotten in a habit of locals.  Why is that a significant
>> thing?  I only did this when I totally forgot my forth and tried to
>> store it on the return stack inside the loop, lol, so I fixed it with a
>> value.
>
> Your code has the feature that someone can check and see what the
> last-used rotation key was. I was aware of that while reading your
> code and this awareness only distracted from the code because your
> actual intent was to use a local.

Ok, I understand.  How about the new code.  The key is stored at the 
highest level and used at the lowest level of this code.  Same argument 
apply?  I'd love to pass it into each of the words, but it is just in 
the way as the stack is used resulting in much juggling if the key is 
passed.


>> I started out writing this for modifications in place but decided to
>> change it.  Instead of adding the index, I incremented the address.  But
>> it was messy working with two addresses on the stack.
>
> OK. In general if you're having trouble with the stack you can back
> away from the immediate task and consider what factors would make life
> easier.
>
>   : third ( a b c -- a b c a ) 2 pick ;
>   : under+ ( a b c -- a+c b ) rot + swap ;
>
>   ( destA srcA ) count third c! 1 under+
>   \ copies a char from src to dest and increments both addresses
>   \ you could use 2DUP 1 MOVE but you probably want to modify the char
>
>   ( srcA destA ) over c@ over c! 1+ 1 under+
>   \ same
>
> Two addresses isn't that bad. With the rotation key it gets more
> annoying, but you're already using a local for that.
>
>   ( key srcA destA ) third third c@ rotate over c! 1+ 1 under+
>
> With ROTATE ( key c -- c' )
>
>> I understand why Hugh makes a big deal of the
>> lack of array handling in Forth and has a library.
>
> He makes too big of a deal about it. You can generally improve his
> code just by removing the crutch he's leaned it on.
>
>>> Not to propose those VALUEs , really, but just to say: *if* you're
>>> going to use a VALUE like this, there seems to be more use for source
>>> address in a VALUE than for the rotation 'key'.
>>
>> I'm not following this at all.
>
>   : RotCypher ( caddr u key -- ) \ Output to tmpstr
>        locals| rotkey |
>        0 do                                ( caddr -- )
>            dup I + c@ ScreenChar dup if    ( caddr char flag -- )
>                rotkey RotChar else
>                drop then                   ( caddr char -- )
>            tmpstr I + c!                   ( caddr -- )
>            loop
>        drop ;
>
> vs.
>
>   : RotChar ( key char1 flag -- char2 )  \ Rotate alpha char
>        tuck - rot + 26 mod + ;
>
>   : RotCypher ( caddr u key -- ) \ Output to tmpstr
>        rot tmpstr locals| dest src |
>        swap 0 do                           ( key -- )
>            dup src I + c@ ScreenChar dup if
>                RotChar else
>                drop nip then
>            dest I + c!
>            loop
>        drop ;
>
> It's just as easy, but if you use VALUEs instead of local variables
> then RotCypher's stack effect can become ( key u -- ) and you can set
> up the VALUEs before calling it.

Ok.


>>> Anyway, there's this alternative: loop over the addresses of the
>>> source string's characters. This means you have to manage the
>>> destination address differently. (And it's perfect for in-place
>>> mutation.) It also gets source string off the data stack, which may
>>> make the data stack easier to think about even if you end up
>>> maintaining the same depths through the loop.
>>>
>>>   : RotCypher ( c-addr u key -- )
>>>     tmpstr 2swap bounds ?do ( key tmpstr )
>>>       i c@ ScreenChar dup if fourth RotChar else drop then
>>>       over c! 1+
>>>     loop 2drop ;
>>
>> Bounds?
>
> it's just OVER + SWAP ( c-addr1 u -- c-addr1+u c-addr )
>
> BOUNDS DO ( I is the address of each character of the string ) LOOP
>
>> It's late and I still need to pay my bills.  I'll look at this again
>> tomorrow to see if I can figure out source and bounds.
>
> http://forth-standard.org/standard/core/SOURCE
>
> Modifying it is a hack of course.

Here is my latest version.  Much simpler but still uses a value for 
rotkey.  I'm leaving it as a two buffer routine (although I could do 
that with both addresses on the stack rather than one buffer implied) so 
it will work with unalterable input buffers.

marker rotc

26 constant ModLength
0 value rotkey

create tmpstr 30 chars allot

: RotChar ( char1 flag -- char2 )  \ Rotate alpha char
     tuck - rotkey + ModLength mod + ;

: AlphaTest ( char basechar -- char )
     2dup dup ModLength + within if RotChar else drop then ;

\ Decrypt the Caesar cipher with key
: RotCypher ( caddr u -- ) \ Output to tmpstr
     0 do
         dup I + c@
         [char] A AlphaTest
         [char] a AlphaTest
         tmpstr I + c!
         loop
     drop ;

: Encrypt  ( caddr u key -- )
     to rotkey RotCypher ;

: Decrypt  ( caddr u key -- )
    ModLength swap - to rotkey RotCypher ;

: "type" ( caddr u -- ) cr [char] " emit type [char] " emit .s ;

cr cr .( Start testing) .s
cr S" ABC mno 1, 2, 3" .s
2dup "type"
tuck 1 Encrypt tmpstr tuck over "type"
2dup 1 Decrypt "type"


-- 

Rick C

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.forth


csiph-web