// Package caesar implements the Caesar cipher. // // The Caesar cipher is a basic monoalphabetic substitution cipher where each // letter in the plaintext is shifted by a fixed distance to form the // ciphertext. A ciphertext can be decrypted by reversing the shift. package caesar import ( "strings" ) // A RuneRange represents a range of runes that are to be shifted. // // Since runes encompass the entire UTF-8 space, we don't necessarily want to // apply the Caesar shift to all runes in a plaintext lest we end up with // unprintable or otherwise difficult characters. type RuneRange struct { // Start represents the beginning of the range (inclusive). Start rune // End represents the end of the range (inclusive). End rune } // contains checks whether r is in the rune range. func (rr RuneRange) contains(r rune) bool { return r >= rr.Start && r <= rr.End } // size calculates the size of the range. func (rr RuneRange) size() int { return int(rr.End-rr.Start) + 1 } // shift shifts r by d within the range, modulo the size of the range. func (rr RuneRange) shift(r rune, d int) rune { pos := int(r - rr.Start) for d < 0 { d = rr.size() + d } newPos := (pos + d) % rr.size() return rr.Start + rune(newPos) } // A Coder encodes and decodes Caesar cipher messages according to its key and // valid rune ranges. // // Its zero value doesn't do any shifting, so both Encode and Decode are simply // the identity function. type Coder struct { // Key determines the shift (+Key for encoding, -Key for decoding). Key int // Ranges determine which rune ranges should be shifted. Ranges []RuneRange } // Encode takes the message and returns a Caesar shifted version. func (c Coder) Encode(msg string) string { return strings.Map(c.shifter(c.Key), msg) } // Decode takes a shifted message and returns a plaintext version. func (c Coder) Decode(msg string) string { return strings.Map(c.shifter(-c.Key), msg) } type shiftFunc func(rune) rune // shifter returns a shiftFunc for the coder's rune ranges. func (c Coder) shifter(delta int) shiftFunc { return func(r rune) rune { for _, rr := range c.Ranges { if rr.contains(r) { return rr.shift(r, delta) } } return r } } // DefaultCoder is a Coder which uses the historical key of 3 and encodes // uppercase and lowercase characters a-z and the digits 0-9. // // DefaultCoder is used by Encode and Decode. // // Define your own Coder to use custom rune ranges or keys. var DefaultCoder = Coder{ Key: 3, Ranges: []RuneRange{ {Start: 'a', End: 'z'}, {Start: 'A', End: 'Z'}, {Start: '0', End: '9'}, }, } // Encode encodes msg using DefaultCoder. func Encode(msg string) string { return DefaultCoder.Encode(msg) } // Decode decodes msg using DefaultCoder. func Decode(msg string) string { return DefaultCoder.Decode(msg) }