define(['underscore', 'cryptojs', 'Utils'], function(_, CryptoJS, Utils) {
  var ID = function(bytes) {
    _.each(bytes, function(b) {
      if (_.isNaN(b) || !_.isNumber(b) || b < 0x00 || 0xff < b) {
        throw new Error("Invalid argument.");
      }
    });
    if (_.size(bytes) !== ID._BYTE_SIZE) {
      throw new Error("Invalid argument.");
    }

    this._bytes = _.last(bytes, ID._BYTE_SIZE);
  };

  ID._BYTE_SIZE = 32;

  ID.create = function(str) {
    if (!Utils.isNonemptyString(str)) {
      throw new Error("Invalid argument.");
    }

    return new ID(ID._createBytes(str));
  };

  ID._createBytes = function(str) {
    var hash = CryptoJS.SHA256(str).toString(CryptoJS.enc.Hex);
    return ID._createBytesfromHexString(hash);
  };

  ID._createBytesfromHexString = function(str) {
    if (!Utils.isNonemptyString(str)) {
      throw new Error("Invalid argument.");
    }

    return _(Math.floor(_.size(str) / 2)).times(function(i) {
      return parseInt(str.substr(i * 2, 2), 16);
    });
  };

  ID.fromHexString = function(str) {
    return new ID(ID._createBytesfromHexString(str));
  };

  ID.prototype = {
    isInInterval: function(fromId, toId) {
      if (_.isNull(fromId) || _.isNull(toId)) {
        throw new Error("Invalid arguments.");
      }

      if (fromId.equals(toId)) {
        return !this.equals(fromId);
      }

      if (fromId.compareTo(toId) < 0) {
        return (this.compareTo(fromId) > 0 && this.compareTo(toId) < 0);
      }

      return ((!fromId.equals(ID.maxId) && this.compareTo(fromId) > 0 && this.compareTo(ID.maxId) <= 0) ||
              (!ID.minId.equals(toId) && this.compareTo(ID.minId) >= 0 && this.compareTo(toId) < 0));
    },

    addPowerOfTwo: function(powerOfTwo) {
      if (!_.isNumber(powerOfTwo)) {
        throw new Error("Invalid argument.");
      }
      if (powerOfTwo < 0 || powerOfTwo >= this.getLength()) {
        throw new Error("Power of two out of index.");
      }

      var copy = _.clone(this._bytes);
      var indexOfBytes = _.size(this._bytes) - 1 - Math.floor(powerOfTwo / 8);
      var valueToAdd = [1, 2, 4, 8, 16, 32, 64, 128][powerOfTwo % 8];
      for (var i = indexOfBytes; i >= 0; i--) {
        copy[i] += valueToAdd;
        valueToAdd = copy[i] >> 8;
        copy[i] &= 0xff;
        if (valueToAdd === 0) {
          break;
        }
      }

      return new ID(copy);
    },

    compareTo: function(id) {
      if (this.getLength() !== id.getLength()) {
        throw new Error("Invalid argument.");
      }

      var bytes = _.zip(this._bytes, id._bytes);
      for (var i = 0; i < bytes.length; i++) {
        if (bytes[i][0] < bytes[i][1]) {
          return -1;
        } else if (bytes[i][0] > bytes[i][1]) {
          return 1;
        }
      }
      return 0;
    },

    equals: function(id) {
      return this.compareTo(id) === 0;
    },

    getLength: function() {
      return _.size(this._bytes) * 8;
    },

    toHexString: function() {
      return _.map(this._bytes, function(b) {
        var str = b.toString(16);
        return b < 0x10 ? "0" + str : str;
      }).join("");
    }
  };

  ID.minId = new ID(_(ID._BYTE_SIZE).times(function() {
    return 0x00;
  }));

  ID.maxId = new ID(_(ID._BYTE_SIZE).times(function() {
    return 0xff;
  }));

  return ID;
});

@@ -399,16 +399,208 @@
 _SIZE);%0A
+    this._hexString = _.map(this._bytes, function(b) %7B%0A      var str = b.toString(16);%0A      return b %3C 0x10 ? %220%22 + str : str;%0A    %7D).join(%22%22);%0A    this._bitLength = _.size(this._bytes) * 8;%0A
   %7D;%0A%0A  
@@ -906,33 +906,33 @@
  ID._createBytes
-f
+F
 romHexString(has
@@ -950,33 +950,33 @@
  ID._createBytes
-f
+F
 romHexString = f
@@ -1276,17 +1276,17 @@
 ateBytes
-f
+F
 romHexSt
@@ -1689,36 +1689,8 @@
 n ((
-!fromId.equals(ID.maxId) && 
 this
@@ -1748,54 +1748,56 @@
 %3C= 0
-) %7C%7C%0A              (!ID.minId.equals(toId) && 
+ && !fromId.equals(ID.maxId)) %7C%7C%0A              (
 this
@@ -1849,16 +1849,42 @@
 oId) %3C 0
+ && !ID.minId.equals(toId)
 ));%0A    
@@ -2714,57 +2714,8 @@
  %7D%0A%0A
-      var bytes = _.zip(this._bytes, id._bytes);%0A
     
@@ -2740,20 +2740,21 @@
 i %3C 
-bytes.length
+ID._BYTE_SIZE
 ; i+
@@ -2766,32 +2766,38 @@
         if (
+this._
 bytes%5Bi%5D
 %5B0%5D %3C bytes%5B
@@ -2788,22 +2788,23 @@
 s%5Bi%5D
-%5B0%5D
  %3C 
+id._
 bytes%5Bi%5D
 %5B1%5D)
@@ -2791,35 +2791,32 @@
 %5D %3C id._bytes%5Bi%5D
-%5B1%5D
 ) %7B%0A          re
@@ -2843,24 +2843,30 @@
 lse if (
+this._
 bytes%5Bi%5D
 %5B0%5D %3E by
@@ -2861,22 +2861,23 @@
 s%5Bi%5D
-%5B0%5D
  %3E 
+id._
 bytes%5Bi%5D
 %5B1%5D)
@@ -2876,11 +2876,8 @@
 s%5Bi%5D
-%5B1%5D
 ) %7B%0A
@@ -3053,39 +3053,31 @@
  return 
-_.size(this._bytes) * 8
+this._bitLength
 ;%0A    %7D,
@@ -3125,135 +3125,23 @@
 urn 
-_.map(this._bytes, function(b) %7B%0A        var str = b.toString(16);%0A        return b %3C 0x10 ? %220%22 + str : str;%0A      %7D).join(%22%22)
+this._hexString
 ;%0A  

Optimize ID.js