JavaScript clscal(a,b,z,f)

The function clscal() was originally obtained in Fortran, then later converted here to JavaScript, which is used to scale the spectrum of CO.

The <plaintext> tag has previously been used to display code and avoid any problems with handling the "<" and ">" characters in the code, but this tag is now deprecated, and once opened it cannot be closed. An alternative method of using jQuery is applied applied here, and the code is in ../js/jquery-showcode.js.


/** ----- clscal() -----
* This is a JavaScript static class that can be used to calculate sensible
* values for the upper and lower limits of some range to be plotted, together
* with sensible intervals that can be used to mark an axis.  After calling
* clscal(a,b,z,f), the returned values can be obtained from clscal.error,
* clscal.x, clscal.dx, clscal.ndx, and clscal.y.  The original code was in
* Fortran, then converted here to JavaScript with some significant improvements,
* and calls the maths rounding utility rnd(x,d) in the file rnd.js.
*
* The input arguments have the following meanings:
* a - The lower limit to be considered.
* b - The upper limit to be considered.
* z - The number of significant figures for rounding, and calls rnd().  See
*     the notes on rnd().
* f - A boolean flag to handle the case when the error code is 2, see below.
*
* The returned values are as follows:
* error ===  0: Successfully found a range, so x, dx, ndx, and y, return with
*               the following values:
* x <= a: This is chosen to be a "nice" lower value.
* dx:     This is chosen to be a "nice" interval to divide up the range.
* ndx:    This the number of intervals that divide the range.
* y >= b: This is chosen to be a "nice" upper value, such that y = x + dx*ndx.
* error === -1: At least one of the input values is blank.  The other returned
*               values are meaningless.
* error === -2: At least one of the input values is non-numeric.  The other
*               returned values are meaningless.
* error ===  1: The upper limit b <= the lower limit a.  The other returned
*               values are meaningless.
* error ===  2: Although b > a, they are the same to within 4 significant
*               figures.  The outcome depends on the flag f set as follows:
*   false: Returns with this error code and do nothing more.  The other values
*          are meaningless.
*   true:  Repeats calculating with the lower limit set to zero and the upper
*          limit set to b-a.  This then tries to divide up the range between 0
*          and b-a.  The following output will be returned:
*     error === 0: If successful, otherwise error === 2.
*     x === 0:     This should always be the case.
*     dx:          This is chosen to be a "nice" interval to divide up the range.
*     ndx:         This the number of intervals that divide the range.
*     y >= b-a:    In general y can be larger than the difference.
* error ===  3: ndx could not be chosen while keeping dx to a valid value.  The
*               other values are meaningless.
* 
* Thus the returned error code should be tested first, and of it is 0 the other
* returned values can be used.  For other values of the error code the other
* returned values are meaningless, except if the error code is 2 and the flag f is
* set to true, then difference range b-a may have been divided successfully, but
* this is not guaranteed. 
*/
function clscal(a,b,z,f) {
  a = a.toString(); // Convert a and b to a string in case they are not.
  b = b.toString(); // If they are not strings, the code below is redundant,
  a = a.trim();     // and they are converted back to numbers.
  b = b.trim();
  if (a === "" || b === "") { // ----- ERROR -1: At least one input field is empty
    clscal.error = -1;
    return;
  }
  if (isNaN(a) || isNaN(b)) { // ----- ERROR -2: At least one input field contains non-numeric input
    clscal.error = -2;
    return;
  }
  a = Number(a);
  b = Number(b);
  var ivs = [1,2,5,10,20,50,100,200,500,1000,2000,5000];
  var npref = [5,6,4,7,8,3,2,9,10,1];
  var ndxs = new Array(12);
  var dxs = new Array(12);
  var xs = new Array(12);
  var n1,n2,m,ma,mb,mx,nb,nx;
  var x,dx,ndx,xx,yy;
  var smallDiff = false;  // This is set to true for error code 2 when f is set to true
  var p = a;              // Store a and b for later use in case of error code 2 with f
  var q = b;              // set to true
  do { // do-while loop which is never executed more than twice
    x = a;
    dx = b;
    ndx = 1;
    if (b <= a) {	// ----- ERROR 1: a <= b
      if (!smallDiff) clscal.error = 1;
      return;
    }
    var l = 0;
    if (a >= 0.0) l = 1;
    if (b <= 0.0) l = 2;
    if (l > 0) {
      if (l === 2) {
        var temp = a;
        a = -b;
        b = -temp;
      }
      nb = 0;
      while (b < 1000.0) {
        nb--;
        b *= 10.0;
      }
      while (b >= 10000.0) {
        nb++;
        b *= 0.1;
      }
      mb = parseInt(b-0.1,10);
      mb++;
      a = a*Math.pow(10,-nb);
      ma = parseInt(a+0.01,10);
      if (ma >= mb) {     // ----- ERROR 2: a === b to 4 significant figures
        clscal.error = 2;
        if (!f) return;   // If f is false return with error code 2, otherwise repeat
        smallDiff = true; // the do-while loop one more time with a set to zero and b
        b = q - p;        // set to the original difference b-a stored in p and q.
        a = 0;            // Keep the error code 2 regardless of the later outcome.
        continue;
      }
      for (var i=0; i<12; i++) {
        n2 = parseInt(mb/ivs[i],10);
        if (n2*ivs[i] != mb) n2++;
        n1 = parseInt(ma/ivs[i],10);
        if (n1*10 < n2) n1=0;
        dxs[i] = ivs[i]*Math.pow(10,nb);
        ndxs[i] = n2-n1;
        xs[i] = n1*dxs[i];
        if (l === 2) xs[i] = -n2*dxs[i];
      }
    } else {
      if (-a < b) {
        m = 2;
        xx = b;
        yy = -a;
      } else {
        m = 1;
        xx = -a;
        yy = b;
      }
      nx = 0;
      while (xx < 1000.0) {
        nx--;
        xx *= 10.0;
      }
      while (xx >= 10000.0) {
        nx++;
        xx *= 0.1;
      }
      mx = parseInt(xx-0.01,10);
      mx++;
      for (var i=0; i<12; i++) {
        n1 = parseInt(mx/ivs[i],10);
        if (n1*ivs[i] != mx) n1++;
        dxs[i]=ivs[i]*Math.pow(10,nx);
        n2 = parseInt(yy/dxs[i],10);
        if (n2*dxs[i] < yy) n2++;
        ndxs[i] = n1+n2;
        xs[i] = -n1*dxs[i];
        if (m === 2) xs[i] = -n2*dxs[i];
      }
    }
    for (var i=0; i<10; i++) {
      for (var j=0; j<12; j++) {
        if (ndxs[j] === npref[i]) {
          clscal.x = rnd(xs[j],z);
          clscal.dx = rnd(dxs[j],z);
          clscal.ndx = ndxs[j];
          clscal.y = rnd(clscal.x + clscal.ndx*clscal.dx, z);
          clscal.error = 0;
          return; // ----- ERROR 0: No error - the values of x, dx and ndx were found
        }
      }
    }
    if (!smallDiff) clscal.error = 3; // ----- ERROR 3: Unable to find suitable values of x, dx & ndx
    return;
  }
  while (true); // End of do-while loop which is never executed more than twice
}

/** ----- rnd() -----
* This is called by clscal() and can be called elsewhere to round a number to a given number of
* decimal places, and fixes the problem in JavaScript due to the approximate representation of
* some floating point numbers, causing a large number of trailing "9"s or "0"s with a non-zero
* digit at the end of the number.  Currently it is only implemented for numbers that are not
* in the exponential notation containing an "e" or an "E".
* x - The input number to be processed.
* d - The number of digits after the decimal point to round, and must be less than about 16,
*     provided the number is not in exponential notation.
* return - This returns the rounded number if it is not in exponential notation, otherwise it
*          returns the original number unchanged.
*/
function rnd(x,d) {
  var s = x.toString();
  if (s.indexOf("e") >= 0 || s.indexOf("E") >=0) return x;
  return Number(Math.round(x+"e"+d)+"e-"+d);
}

After invoking clscal(a,b,z,f) with a being the lower limit in the range to be plotted, b being the upper limit in the range to be plotted, and z being the number of digits after the decimal point to round to (values between 4 and 6 are preferred), the returned values are as follows:

If the error code is anything other than 0, the other returned values are meaningless. The flag f is only relevant for error code 2. If it is false, the returned values have no meanings, as with the other non-zero codes. If it is true, the returned values are as follows:

If clscal() still returns with an error code of 2 on the second pass, it has failed again and cannot be used with the present input values.



Click here to return to the diatomic page.