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


Groups > comp.lang.forth > #59132

Re: Rosetta: Caesar cipher

From rickman <gnuarm@gmail.com>
Newsgroups comp.lang.forth
Subject Re: Rosetta: Caesar cipher
Date 2017-05-12 13:41 -0400
Organization A noiseless patient Spider
Message-ID <of4rtu$2ii$1@dont-email.me> (permalink)
References <oet9tb$166r$1@gioia.aioe.org> <of09pp$k46$1@dont-email.me> <27244bc8-3cf8-445d-803e-bef86c1e6122@googlegroups.com> <of0tsi$6u0$1@dont-email.me> <f1076906-0347-405f-ba3d-dadf1e9a065e@googlegroups.com>

Show all headers | View raw


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

Back to comp.lang.forth | Previous | Next — Previous in thread | Find similar | Unroll thread


Thread

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

csiph-web