


function ipStringToNumber(str) {
  var arr = str.split(".");
  if(arr.length != 4) {
    return null;
  }
  var hexString = "";
  for(var i = 0; i < arr.length; i++) {
    var byte = Number(arr[i]);
    if(byte == NaN || byte < 0 || byte > 255) {
	return null;
    }
    byteHex = byte.toString(16);
    if(byteHex.length == 1) {
      byteHex = "0"+byteHex;
    }
    hexString += byteHex;
  }
  //console.log(hexString);
  return parseInt(hexString, 16);
}

function toIpString(ipNumber) {
  var hexString = ipNumber.toString(16);
  while(hexString.length < 8) {
    hexString = "0"+hexString;
  }
  //console.log("ipNumber: "+ ipNumber + ", hexString: " + hexString);
  return parseInt(""+hexString[0]+hexString[1], 16)+"."+parseInt(""+hexString[2]+hexString[3], 16)+"."+parseInt(""+hexString[4]+hexString[5], 16)+"."+parseInt(""+hexString[6]+hexString[7], 16);
}

function parseCidr(cidrString) {
  var arr = cidrString.split("/");
  if(arr.length != 2) {
    console.log("invalid cidr format: " + cidrString );
    return;
  }
  var ipNumber = ipStringToNumber(arr[0]);
  var sizeBits = Number(arr[1]);
  if(ipNumber === null) {
    console.log("invalid cidr IP: " + arr[0] );
    return;
  }
  if(sizeBits < 0 || sizeBits > 32) {
    console.log("invalid cidr sizeBits: " + arr[1]);
    return;
  }
  var blockSize = (1 << (32-sizeBits));
  if(ipNumber % blockSize != 0) {
    ipNumber -= (ipNumber % blockSize);
    console.log("warning: cidr start IP was not aligned with mask. assuming: " + toIpString(ipNumber)+"/"+sizeBits);
  }
  
  return {ipNumber: ipNumber, sizeBits:sizeBits};
}

function cidrObjectToString(cidrObject) {
  return toIpString(cidrObject.ipNumber)+"/"+cidrObject.sizeBits;
}

function cidrSubtract(cidrToSubtractFrom, cidrToSubtract) {
  if(typeof cidrToSubtractFrom == "string") {
    cidrToSubtractFrom = parseCidr(cidrToSubtractFrom)
  }
  if(typeof cidrToSubtract == "string") {
    cidrToSubtract = parseCidr(cidrToSubtract)
  }

  console.log("cidrToSubtractFrom:  " + JSON.stringify(cidrToSubtractFrom, null, 2) + ", cidrToSubtract: " + JSON.stringify(cidrToSubtract, null, 2) );
    

  var currentIp = cidrToSubtractFrom.ipNumber;
  var currentSizeBits = cidrToSubtractFrom.sizeBits;
  
  var results = [];

  var itr = 0;
  while(itr++ < 32) {
    currentSizeBits += 1;
    newBlockSize = Math.pow(2, 32-currentSizeBits);
    isAlignedBlock = (currentIp % newBlockSize == 0);
    isSmallEnough = (currentIp + (newBlockSize-1) < cidrToSubtract.ipNumber );
    fits = (currentIp + newBlockSize === cidrToSubtract.ipNumber);
    //console.log("currentIp: " +currentIp+", (newBlockSize-1):  "+  (newBlockSize-1) + ", (newBlockSize):  "+  (newBlockSize));
    //console.log("currentIp + (newBlockSize-1):  "+  (currentIp + (newBlockSize-1)) + " (" + toIpString(currentIp + (newBlockSize-1)) + "), cidrToSubtract.ipNumber: " + cidrToSubtract.ipNumber + " ("+ toIpString(cidrToSubtract.ipNumber) + ")");
    //console.log(toIpString(currentIp)+"/"+currentSizeBits + ": isSmallEnough: " + isSmallEnough + " isAlignedBlock: " + isAlignedBlock + " fits: " + fits );
    if(isAlignedBlock && isSmallEnough) {
      results.push(cidrObjectToString({ipNumber: currentIp, sizeBits:currentSizeBits}));
      currentIp += newBlockSize;
    }
    if(fits) {
      break;
    }
  }

  results.push("-------------");

  var results2 = [];

  currentSizeBits = cidrToSubtractFrom.sizeBits;
  var blockSize = Math.pow(2, 32-currentSizeBits);
  currentIp = cidrToSubtractFrom.ipNumber+blockSize;
  var endOfCidrToSubtract = cidrToSubtract.ipNumber+Math.pow(2, 32-cidrToSubtract.sizeBits);
  var itr = 0;
  while(itr++ < 32) {
    currentSizeBits += 1;
    newBlockSize = Math.pow(2, 32-currentSizeBits);
    isAlignedBlock = (currentIp % newBlockSize == 0);
    isSmallEnough = (currentIp - (newBlockSize-1) > endOfCidrToSubtract );
    fits = (currentIp - newBlockSize === endOfCidrToSubtract);
    //console.log("currentIp: " +currentIp+", (newBlockSize-1):  "+  (newBlockSize-1) + ", (newBlockSize):  "+  (newBlockSize));
    //console.log("currentIp - newBlockSize:  (" + toIpString(currentIp - newBlockSize) + "), endOfCidrToSubtract: ("+ toIpString(endOfCidrToSubtract) + ")");
    //console.log(toIpString(currentIp)+"/"+currentSizeBits + ": isSmallEnough: " + isSmallEnough + " isAlignedBlock: " + isAlignedBlock + " fits: " + fits );
    if(isAlignedBlock && isSmallEnough) {
      results2.unshift(cidrObjectToString({ipNumber: currentIp - newBlockSize, sizeBits:currentSizeBits}));
      currentIp -= newBlockSize;
    }
    if(fits) {
      break;
    }
  }

  console.log(results.concat(results2).join("\n"));
}

cidrSubtract("0.0.0.0/0", "172.23.0.0/22")