Wrapping a character code for a Caesar Cipher I'm writing a Caesar Cipher. To deal with characters being shifted to outside of the "normal" printable ASCII range ([32 126]), I'm having them "wrapped" to the max/min in the range.

My current solution works, but it has a couple issues:


It involves a couple repeated lines. The (- max-code (rem (- max-code char-code) code-range)) part is basically the same in both places, just with a few minor differences. If I really wanted to reduce repetition, I could bind a function that encapsulated the similar parts, and pass in the parts that vary, but I can see that being overly complicated for something so simple.
I have the "in-range" case as the last one, but in reality, since it's likely to be the most common case, it should be the first checked. Is the compiler capable of doing an optimization to account for this? Is this even something I should worry about? This only seems like an issue because this will be one of the most called functions in my program, so it has the potential to become a bottle-neck (although I haven't profiled it yet).


Since I'm still learning Clojure, any recommendations to make my code simpler or more idiomatic would be appreciated.



(defn wrap-shifted-code [^long char-code]
  (let [min-code 32
        max-code 126
        code-range (- max-code min-code)]
    (cond
      (&lt; char-code min-code) (- max-code (rem (- min-code char-code) code-range))
      (&gt; char-code max-code) (+ min-code (rem (- char-code max-code) code-range))
      :else char-code)))




At the request of @Sam Estep, here's a version of the code that correctly includes the outer bounds of the range when wrapping:

(defn wrap-shifted-code [^long char-code]
  (let [min-code 32
        max-code 126
        code-range (- max-code min-code)]
    (cond
      (&lt; char-code min-code) (- max-code (rem (- min-code char-code 1) code-range))
      (&gt; char-code max-code) (+ min-code (rem (- char-code max-code 1) code-range))
      :else char-code)))
Your solutions have subtle off-by-one errors. Your second solution partially fixes these by adding 1 to the result in some cases, but it doesn't fix the real problem, which is this:

(let [,,,
      code-range (- max-code min-code)]
  ,,,)


code-range needs to hold the number of integers from 32 to 126, inclusive. As Dijkstra famously noted, calculating this number is easy when you're using an inclusive-exclusive range, but when your range is inclusive on both ends, you need to account for that by adding 1:

(let [,,,
      code-range (- (inc max-code) min-code)]
  ,,,)


In response to your questions about repetition and performance: there's a function for that! Unlike rem, Clojure's mod function always returns a nonnegative value, so you can do this:

(defn wrap-shifted-code [char-code]
  (let [min-code 32
        max-code 126
        code-range (- (inc max-code) min-code)]
    (-&gt; char-code (- min-code) (mod code-range) (+ min-code))))