Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #58912 > unrolled thread
| Started by | "Robert L." <No_spamming@noWhere_7073.org> |
|---|---|
| First post | 2017-05-09 20:47 +0000 |
| Last post | 2017-05-12 13:41 -0400 |
| Articles | 12 — 8 participants |
Back to article view | Back to comp.lang.forth
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
| From | "Robert L." <No_spamming@noWhere_7073.org> |
|---|---|
| Date | 2017-05-09 20:47 +0000 |
| Subject | Rosetta: 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]
| From | Joel Rees <joel.rees@gmail.com> |
|---|---|
| Date | 2017-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]
| From | hughaguilar96@gmail.com |
|---|---|
| Date | 2017-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]
| From | rickman <gnuarm@gmail.com> |
|---|---|
| Date | 2017-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]
| From | Julian Fondren <julian.fondren@gmail.com> |
|---|---|
| Date | 2017-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]
| From | rickman <gnuarm@gmail.com> |
|---|---|
| Date | 2017-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]
| From | Julian Fondren <julian.fondren@gmail.com> |
|---|---|
| Date | 2017-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]
| From | Alex <alex@rivadpm.com> |
|---|---|
| Date | 2017-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]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2017-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]
| From | Alex <alex@rivadpm.com> |
|---|---|
| Date | 2017-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]
| From | mhx@iae.nl |
|---|---|
| Date | 2017-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]
| From | rickman <gnuarm@gmail.com> |
|---|---|
| Date | 2017-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