text:
import { Const, Util } from "./Util";
import { Curve } from "./Op";
import { Pt, Group } from "./Pt";
import { Vec, Mat } from "./LinearAlgebra";
import {PtLike, GroupLike, PtLikeIterable, PtIterable} from "./Types";

import generator from './uheprng';

/**
 * Num class provides static helper functions for basic numeric operations.
 */
export class Num {
  static generator: any;

  /**
   * Check if two numbers are equal or almost equal within a threshold.
   * @param a number a
   * @param b number b
   * @param threshold threshold value that specifies the minimum difference within which the two numbers are considered equal
   */
  static equals( a:number, b:number, threshold=0.00001 ):boolean {
    return Math.abs( a-b ) < threshold;
  }

  /**
   * Calculate linear interpolation between 2 values.
   * @param a start value
   * @param b end value
   * @param t an interpolation value, usually between 0 to 1
   */
  static lerp( a:number, b:number, t:number ):number {
    return (1 - t) * a + t * b;
  }

  /**
   * Clamp values between min and max.
   * @param val value to clamp
   * @param min min value
   * @param max max value
   */
  static clamp( val:number, min:number, max:number ):number {
    return Math.max(min, Math.min(max, val));
  }

  /**
   * Different from [`Num.clamp`](#link) in that the value out-of-bound will be "looped back" to the other end.
   * @param val value to bound
   * @param min min value
   * @param max max value
   * @example `boundValue(361, 0, 360)` will return 1
   */
  static boundValue( val:number, min:number, max:number ):number {
    let len = Math.abs(max - min);
    let a = val % len;

    if (a > max) a -= len;
    else if (a < min) a += len;

    return a;
  }

  /**
   * Check if a value is within two other values
   * @param p value to check
   * @param a first bounding value
   * @param b second bounding value
   */
  static within( p:number, a:number, b:number ):boolean {
    return p >= Math.min(a, b) && p <= Math.max(a, b);
  }

  /**
   * Get a random number within a range.
   * @param a range value 1
   * @param b range value 2
   */
  static randomRange( a:number, b:number = 0 ):number {
    let r = (a > b) ? (a - b) : (b - a);
    return a + Num.random() * r;
  }

  /**
   * Get a random Pt within the range defined by either 1 or 2 Pt
   * @param a the range if only one Pt is used, or the start of the range if two Pt were used
   * @param b optional Pt to define the end of the range
   */
  static randomPt( a:PtLike, b?:PtLike ):Pt {
    let p = new Pt( a.length );
    let range = b ? Vec.subtract(b, a) : a;
    let start = b ? a : new Pt(a.length).fill(0);
    for (let i=0, len=p.length; i<len; i++) {
      p[i] = Num.random() * range[i] + start[i];
    }
    return p;
  }

  /**
   * Normalize a value within a range.
   * @param n the value to normalize
   * @param a range value 1
   * @param b range value 1
   */
  static normalizeValue(n:number, a:number, b:number):number {
    let min = Math.min(a, b);
    let max = Math.max(a, b);
    return (n - min) / (max - min);
  }

  /**
   * Sum a group of numeric arrays.
   * @param pts a Group or an Iterable<PtLike>
   * @returns a Pt of the dimensional sums
   */
  static sum( pts: PtLikeIterable ): Pt {
    let _pts = Util.iterToArray( pts );
    let c = new Pt( _pts[0] );
    for (let i = 1, len = _pts.length; i < len; i++) {
      Vec.add(c, _pts[i]);
    }
    return c;
  }

  /**
   * Average a group of numeric arrays
   * @param pts a Group or an Iterable<PtLike>
   * @returns a Pt of averages
   */
  static average( pts: PtLikeIterable ): Pt {
    let _pts = Util.iterToArray( pts );
    return Num.sum(_pts).divide(_pts.length);
  }

  /**
   * Given a value between 0 to 1, returns a value that cycles between 0 -> 1 -> 0 using the provided shaping method.
   * @param t a value between 0 to 1
   * @param method a shaping method. Default to [`Shaping.sineInOut`](#link).
   * @return a value between 0 to 1
   */
  static cycle( t:number, method:(t:number) => number=Shaping.sineInOut ):number {
    return method( t > 0.5 ? 2-t*2 : t*2 );
  }

  /**
   * Map a value from one range to another.
   * @param n a value in the first range
   * @param currMin lower bound of the first range
   * @param currMax upper bound of the first range
   * @param targetMin lower bound of the second range
   * @param targetMax upper bound of the second range
   * @returns a remapped value in the second range
   */
  static mapToRange(n:number, currA:number, currB:number, targetA:number, targetB:number) {
    if (currA == currB) throw new Error("[currMin, currMax] must define a range that is not zero");
    let min = Math.min(targetA, targetB);
    let max = Math.max(targetA, targetB);
    return Num.normalizeValue(n, currA, currB) * (max - min) + min;
  }

  /**
   * Seed the pseudorandom generator.
   * @param seed seed string
   */
  static seed(seed: string): void {
    this.generator = generator(seed);
  }

  /**
   * Return a random number between 0 and 1 from a seed,
   * if the seed is not defined it uses Math.random
   * @returns a number between 0 and 1
   */
  static random(): number {
      return this.generator
          ? this.generator.random()
          : Math.random();
  }
}

/**
 * Geom class provides static helper functions for basic geometric operations.
 */
export class Geom {

  /**
   * Bound an angle between 0 to 360 degrees.
   * @param angle angle value
   */
  static boundAngle( angle:number ):number {
    return Num.boundValue(angle, 0, 360);
  }

  /**
   * Bound a radian between 0 to two PI.
   * @param radian radian value
   */
  static boundRadian( radian:number ):number {
    return Num.boundValue(radian, 0, Const.two_pi);
  }

  /**
   * Convert an angle in degree to radian.
   * @param angle angle value
   */
  static toRadian( angle:number ):number {
    return angle * Const.deg_to_rad;
  }

  /**
   * Convert an angle in radian to degree.
   * @param radian radian value
   */
  static toDegree( radian:number ):number {
    return radian * Const.rad_to_deg;
  }

  /**
   * Get a bounding box for a set of Pts.
   * @param pts a Group or an Iterable<Pt>
   * @return a Group of two Pts, representing the top-left and bottom-right corners
   */
  static boundingBox( pts:PtIterable ): Group {
    let minPt: Pt, maxPt: Pt;
    for (let p of pts) {
      if (minPt == undefined) {
        minPt = p.clone();
        maxPt = p.clone();
      } else {
        minPt = minPt.$min(p);
        maxPt = maxPt.$max(p);
      }
    }
    return new Group(minPt, maxPt);
  }

  /**
   * Get a centroid (the average middle point) for a set of Pts.
   * @param pts a Group or an Iterable<PtLike>
   * @return a centroid Pt
   */
  static centroid( pts:PtLikeIterable ):Pt {
    return Num.average(pts);
  }

  /**
   * Given an anchor Pt, rebase all Pts in this group either to or from this anchor base.
   * @param pts a Group or an Iterable<PtLike>
   * @param ptOrIndex an index for the Pt array, or an external Pt
   * @param direction a string either "to" (subtract all Pt with this anchor base), or "from" (add all Pt from this anchor base)
   */
  static anchor( pts:PtLikeIterable, ptOrIndex:PtLike|number=0, direction:("to"|"from")="to") {
    let method = (direction == "to") ? "subtract" : "add";
    let i = 0;
    for (let p of pts) {
      if (typeof ptOrIndex == "number") {
        if (ptOrIndex !== i) p[method](pts[ptOrIndex]);
      } else {
        p[method](ptOrIndex);
      }
      i++;
    }
  }

  /**
   * Get an interpolated (or extrapolated) value between two Pts. For linear interpolation between 2 scalar values, use [`Num.lerp`](#link).
   * @param a first Pt
   * @param b second Pt
   * @param t a value between 0 to 1 to interpolate, or any other value to extrapolate
   * @returns interpolated point as a new Pt
   */
  static interpolate( a:PtLike, b:PtLike, t:number=0.5 ):Pt {
    let len = Math.min(a.length, b.length);
    let d = Pt.make(len);
    for (let i = 0; i < len; i++) {
      d[i] = a[i] * (1 - t) + b[i] * t;
    }
    return d;
  }

  /**
   * Find two Pts that are perpendicular to this Pt (2D only).
   * @param axis a string such as "xy" (use Const.xy) or an array to specify index for two dimensions
   * @returns an array of two Pt that are perpendicular to this Pt
   */
  static perpendicular( pt:PtLike, axis:string|PtLike=Const.xy ):Group {
    let y = axis[1];
    let x = axis[0];

    let p = new Pt(pt);
    let pa = new Pt(p);
    pa[x] = -p[y];
    pa[y] = p[x];
    let pb = new Pt(p);
    pb[x] = p[y];
    pb[y] = -p[x];

    return new Group(pa, pb);
  }

  /**
   * Check if two Pts are perpendicular to each other (2D only).
   */
  static isPerpendicular( p1:PtLike, p2:PtLike ):boolean {
    return new Pt(p1).dot(p2) === 0;
  }

  /**
   * Check if a Pt is within the rectangular boundary defined by two Pts.
   * @param pt the Pt to check
   * @param boundPt1 boundary Pt 1
   * @param boundPt2 boundary Pt 2
   */
  static withinBound( pt:PtLike, boundPt1:PtLike, boundPt2:PtLike ):boolean {
    for (let i = 0, len = Math.min(pt.length, boundPt1.length, boundPt2.length); i < len; i++) {
      if (!Num.within(pt[i], boundPt1[i], boundPt2[i])) return false;
    }
    return true;
  }

  /**
   * Sort the Pts so that their edges will form a non-overlapping polygon. ([Reference](https://stackoverflow.com/questions/6989100/sort-points-in-clockwise-order))
   * @param pts a Group or an Iterable<Pt>
   */
  static sortEdges( pts:PtIterable ):GroupLike {

    let _pts = Util.iterToArray( pts );
    let bounds = Geom.boundingBox( _pts );
    let center = bounds[1].add(bounds[0]).divide(2);

    let fn = ( a:Pt, b:Pt ):number => {
      if (a.length < 2 || b.length < 2) throw new Error( "Pt dimension cannot be less than 2");

      let da = a.$subtract( center );
      let db = b.$subtract( center );

      if (da[0] >= 0 && db[0] < 0) return 1;
      if (da[0] < 0 && db[0] >= 0) return -1;
      if (da[0] == 0 && db[0] == 0) {
          if (da[1] >= 0 || db[1] >= 0)
              return (da[1] > db[1]) ? 1 : -1;
          return (db[1] > da[1]) ? 1 : -1;
      }

      // compute the cross product of vectors (center -> a) x (center -> b)
      let det = da.$cross2D( db );
      if (det < 0) return 1;
      if (det > 0) return -1;

      // points a and b are on the same line from the center
      // check which point is closer to the center
      return (da[0]*da[0] + da[1]*da[1] > db[0]*db[0] + db[1]*db[1]) ? 1 : -1;
    };

    return _pts.sort( fn );
  }

  /**
   * Scale a Pt or a Group of Pts. You may also use [`Pt.scale`](#link) instance method.
   * @param ps either a single Pt, or a Group or an Iterable<Pt>
   * @param scale scale value
   * @param anchor optional anchor point to scale from
   */
  static scale( ps:Pt|PtIterable, scale:number|PtLike, anchor?:PtLike ):Geom {
    let pts = Util.iterToArray( (ps[0] !== undefined && typeof ps[0] == 'number') ? [ps] : ps );
    let scs = (typeof scale == "number") ? Pt.make(pts[0].length, scale) : scale;
    if (!anchor) anchor = Pt.make(pts[0].length, 0);

    for (let i = 0, len = pts.length; i < len; i++) {
      let p = pts[i];
      for (let k = 0, lenP = p.length; k < lenP; k++) {
        p[k] = (anchor && anchor[k]) ? anchor[k] + (p[k] - anchor[k]) * scs[k] : p[k] * scs[k];
      }
    }

    return Geom;
  }

  /**
   * Rotate a Pt or a Group of Pts in 2D space. You may also use [`Pt.rotate2D`](#link) instance method.
   * @param ps either a single Pt, or a Group or an Iterable<Pt>
   * @param angle rotate angle
   * @param anchor optional anchor point to rotate from
   * @param axis optional axis such as "xy" (use Const.xy) to define a 2D plane, or a number array to specify indices
   */
  static rotate2D( ps:Pt|PtIterable, angle:number, anchor?:PtLike, axis?:string|PtLike ):Geom {
    let pts = Util.iterToArray( (ps[0] !== undefined && typeof ps[0] == 'number') ? [ps] : ps );
    let fn = (anchor) ? Mat.rotateAt2DMatrix : Mat.rotate2DMatrix;
    if (!anchor) anchor = Pt.make(pts[0].length, 0);
    let cos = Math.cos(angle);
    let sin = Math.sin(angle);

    for (let i = 0, len = pts.length; i < len; i++) {
      let p = (axis) ? pts[i].$take(axis) : pts[i];
      p.to(Mat.transform2D(p, fn(cos, sin, anchor)));
      if (axis) {
        for (let k = 0; k < axis.length; k++) {
          pts[i][axis[k]] = p[k];
        }
      }
    }

    return Geom;
  }

  /**
   * Shear a Pt or a Group of Pts in 2D space. You may also use [`Pt.shear2D`](#link) instance method.
   * @param ps either a single Pt, or a Group or an Iterable<Pt>
   * @param scale shearing value which can be a number or an array of 2 numbers
   * @param anchor optional anchor point to shear from
   * @param axis optional axis such as "xy" (use Const.xy) to define a 2D plane, or a number array to specify indices
   */
  static shear2D( ps:Pt|PtIterable, scale:number|PtLike, anchor?:PtLike, axis?:string|PtLike ):Geom {
    let pts = Util.iterToArray( (ps[0] !== undefined && typeof ps[0] == 'number') ? [ps] : ps );
    let s = (typeof scale == "number") ? [scale, scale] : scale;
    if (!anchor) anchor = Pt.make(pts[0].length, 0);
    let fn = (anchor) ? Mat.shearAt2DMatrix : Mat.shear2DMatrix;
    let tanx = Math.tan(s[0]);
    let tany = Math.tan(s[1]);

    for (let i = 0, len = pts.length; i < len; i++) {
      let p = (axis) ? pts[i].$take(axis) : pts[i];
      p.to(Mat.transform2D(p, fn(tanx, tany, anchor)));
      if (axis) {
        for (let k = 0; k < axis.length; k++) {
          pts[i][axis[k]] = p[k];
        }
      }
    }

    return Geom;
  }

  /**
   * Reflect a Pt or a Group of Pts along a 2D line. You may also use [`Pt.reflect2D`](#link) instance method.
   * @param ps either a single Pt, or a Group or an Iterable<Pt>
   * @param line a Group or an Iterable<PtLike> that defines a line for reflection
   * @param axis optional axis such as "xy" (use Const.xy) to define a 2D plane, or a number array to specify indices
   */
  static reflect2D( ps:Pt|PtIterable, line:PtLikeIterable, axis?:string|PtLike ):Geom {
    let pts = Util.iterToArray( (ps[0] !== undefined && typeof ps[0] == 'number') ? [ps] : ps );
    let _line = Util.iterToArray( line );
    let mat = Mat.reflectAt2DMatrix(_line[0], _line[1]);

    for (let i = 0, len = pts.length; i < len; i++) {
      let p = (axis) ? pts[i].$take(axis) : pts[i];
      p.to(Mat.transform2D(p, mat));
      if (axis) {
        for (let k = 0; k < axis.length; k++) {
          pts[i][axis[k]] = p[k];
        }
      }
    }

    return Geom;
  }

  /**
   * Generate a cosine lookup table.
   * @returns an object with a cosine tables (array of 360 values) and a function to get cosine given a radian input.
   */
  static cosTable() {
    let cos = new Float64Array(360);

    for (let i = 0; i < 360; i++) cos[i] = Math.cos(i * Math.PI / 180);
    let find = (rad:number) => cos[Math.floor(Geom.boundAngle(Geom.toDegree(rad)))];

    return { table: cos, cos: find };
  }

  /**
   * Generate a sine lookup table.
   * @returns an object with a sine tables (array of 360 values) and a function to get sine value given a radian input.
   */
  static sinTable() {
    let sin = new Float64Array(360);

    for (let i = 0; i < 360; i++) sin[i] = Math.sin(i * Math.PI / 180);
    let find = (rad:number) => sin[Math.floor(Geom.boundAngle(Geom.toDegree(rad)))];

    return { table: sin, sin: find };
  }
}

/**
 * Shaping provides shaping functions to interpolate a value. These are useful for easing and transitions.
 */
export class Shaping {

  /**
   * Linear mapping.
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static linear(t:number, c:number = 1):number {
    return c * t;
  }

  /**
   * Quadratic in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
  */
  static quadraticIn(t:number, c:number = 1):number {
    return c * t * t;
  }

  /**
   * Quadratic out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
  */
  static quadraticOut(t:number, c:number = 1):number {
    return -c * t * (t - 2);
  }

  /**
   * Quadratic in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static quadraticInOut(t:number, c:number = 1):number {
    let dt = t * 2;
    return (t < 0.5) ? c / 2 * t * t * 4 : -c / 2 * ((dt - 1) * (dt - 3) - 1);
  }

  /**
   * Cubic in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static cubicIn(t:number, c:number = 1):number {
    return c * t * t * t;
  }

  /**
   * Cubic out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static cubicOut(t:number, c:number = 1):number {
    let dt = t - 1;
    return c * (dt * dt * dt + 1);
  }

  /**
   * Cubic in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static cubicInOut(t:number, c:number = 1):number {
    let dt = t * 2;
    return (t < 0.5) ? c / 2 * dt * dt * dt : c / 2 * ((dt - 2) * (dt - 2) * (dt - 2) + 2);
  }

  /**
   * Exponential ease in, adapted from Golan Levin's [polynomial shapers](http://www.flong.com/texts/code/shapers_poly/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p a value between 0 to 1 to control the curve. Default is 0.25.
   */
  static exponentialIn(t:number, c:number = 1, p:number = 0.25):number {
    return c * Math.pow(t, 1 / p);
  }

  /**
   * Exponential ease out, adapted from Golan Levin's [polynomial shapers](http://www.flong.com/texts/code/shapers_poly/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p a value between 0 to 1 to control the curve. Default is 0.25.
   */
  static exponentialOut(t:number, c:number = 1, p:number = 0.25):number {
    return c * Math.pow(t, p);
  }

  /**
   * Sinuous in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static sineIn(t:number, c:number = 1):number {
    return -c * Math.cos(t * Const.half_pi) + c;
  }

  /**
   * Sinuous out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static sineOut(t:number, c:number = 1):number {
    return c * Math.sin(t * Const.half_pi);
  }

  /**
   * Sinuous in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static sineInOut(t:number, c:number = 1):number {
    return -c / 2 * (Math.cos(Math.PI * t) - 1);
  }

  /**
   * A faster way to approximate cosine ease in-out using Blinn-Wyvill Approximation. Adapated from Golan Levin's [polynomial shaping](http://www.flong.com/texts/code/shapers_poly/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static cosineApprox(t:number, c:number = 1) {
    let t2 = t * t;
    let t4 = t2 * t2;
    let t6 = t4 * t2;
    return c * (4 * t6 / 9 - 17 * t4 / 9 + 22 * t2 / 9);
  }

  /**
   * Circular in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static circularIn(t:number, c:number = 1):number {
    return -c * (Math.sqrt(1 - t * t) - 1);
  }

  /**
   * Circular out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static circularOut(t:number, c:number = 1):number {
    let dt = t - 1;
    return c * Math.sqrt(1 - dt * dt);
  }

  /**
   * Circular in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static circularInOut(t:number, c:number = 1):number {
    let dt = t * 2;
    return (t < 0.5) ? -c / 2 * (Math.sqrt(1 - dt * dt) - 1) : c / 2 * (Math.sqrt(1 - (dt - 2) * (dt - 2)) + 1);
  }

  /**
   * Elastic in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p elastic parmeter between 0 to 1. The lower the number, the more elastic it will be. Default is 0.7.
   */
  static elasticIn(t:number, c:number = 1, p:number = 0.7):number {
    let dt = t - 1;
    let s = (p / Const.two_pi) * 1.5707963267948966;
    return c * (-Math.pow(2, 10 * dt) * Math.sin((dt - s) * Const.two_pi / p));
  }

  /**
   * Elastic out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p elastic parmeter between 0 to 1. The lower the number, the more elastic it will be. Default is 0.7.
   */
  static elasticOut(t:number, c:number = 1, p:number = 0.7):number {
    let s = (p / Const.two_pi) * 1.5707963267948966;
    return c * (Math.pow(2, -10 * t) * Math.sin((t - s) * Const.two_pi / p)) + c;
  }

  /**
   * Elastic in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p elastic parmeter between 0 to 1. The lower the number, the more elastic it will be. Default is 0.6.
   */
  static elasticInOut(t:number, c:number = 1, p:number = 0.6):number {
    let dt = t * 2;
    let s = (p / Const.two_pi) * 1.5707963267948966;
    if (t < 0.5) {
      dt -= 1;
      return c * (-0.5 * (Math.pow(2, 10 * dt) * Math.sin((dt - s) * Const.two_pi / p)));
    } else {
      dt -= 1;
      return c * (0.5 * (Math.pow(2, -10 * dt) * Math.sin((dt - s) * Const.two_pi / p))) + c;
    }
  }

  /**
   * Bounce in, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static bounceIn(t:number, c:number = 1):number {
    return c - Shaping.bounceOut((1 - t), c);
  }

  /**
   * Bounce out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static bounceOut(t:number, c:number = 1) {
    if (t < (1 / 2.75)) {
      return c * (7.5625 * t * t);
    } else if (t < (2 / 2.75)) {
      t -= 1.5 / 2.75;
      return c * (7.5625 * t * t + 0.75);
    } else if (t < (2.5 / 2.75)) {
      t -= 2.25 / 2.75;
      return c * (7.5625 * t * t + 0.9375);
    } else {
      t -= 2.625 / 2.75;
      return c * (7.5625 * t * t + 0.984375);
    }
  }

  /**
   * Bounce in-out, adapted from Robert Penner's [easing functions](http://robertpenner.com/easing/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   */
  static bounceInOut(t:number, c:number = 1):number {
    return (t < 0.5) ? Shaping.bounceIn(t * 2, c) / 2 : Shaping.bounceOut(t * 2 - 1, c) / 2 + c / 2;
  }

  /**
   * Sigmoid curve changes its shape adapted from the input value, but always returns a value between 0 to 1.
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p the larger the value, the "steeper" the curve will be. Default is 10.
   */
  static sigmoid(t:number, c:number = 1, p:number = 10):number {
    let d = p * (t - 0.5);
    return c / (1 + Math.exp(-d));
  }

  /**
   * Logistic sigmoid, adapted from Golan Levin's [shaping function](http://www.flong.com/texts/code/shapers_exp/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p a parameter between 0 to 1 to control the steepness of the curve. Higher is steeper. Default is 0.7.
   */
  static logSigmoid(t:number, c:number = 1, p:number = 0.7):number {
    p = Math.max(Const.epsilon, Math.min(1 - Const.epsilon, p));
    p = 1 / (1 - p);

    let A = 1 / (1 + Math.exp(((t - 0.5) * p * -2)));
    let B = 1 / (1 + Math.exp(p));
    let C = 1 / (1 + Math.exp(-p));
    return c * (A - B) / (C - B);
  }

  /**
   * Exponential seat curve, adapted from Golan Levin's [shaping functions](http://www.flong.com/texts/code/shapers_exp/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p a parameter between 0 to 1 to control the steepness of the curve. Higher is steeper. Default is 0.5.
   */
  static seat(t:number, c:number = 1, p:number = 0.5):number {
    if ((t < 0.5)) {
      return c * (Math.pow(2 * t, 1 - p)) / 2;
    } else {
      return c * (1 - (Math.pow(2 * (1 - t), 1 - p)) / 2);
    }
  }

  /**
   * Quadratic bezier curve, adapted from Golan Levin's [shaping functions](http://www.flong.com/texts/code/shapers_exp/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p1 a Pt object specifying the first control Pt, or a value specifying the control Pt's x position (its y position will default to 0.5). Default is `Pt(0.95, 0.95)
   */
  static quadraticBezier(t:number, c:number = 1, p:number|PtLike=[0.05, 0.95] ):number {
    let a:number = (typeof p != "number") ? p[0] : p;
    let b:number = (typeof p != "number") ? p[1] : 0.5;
    let om2a = 1 - 2 * a;
    if (om2a === 0) {
      om2a = Const.epsilon;
    }
    let d = (Math.sqrt(a * a + om2a * t) - a) / om2a;
    return c * ((1 - 2 * b) * (d * d) + (2 * b) * d);
  }

  /**
   * Cubic bezier curve. This reuses the bezier functions in Curve class.
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p1` a Pt object specifying the first control Pt. Default is `Pt(0.1, 0.7).
   * @param p2` a Pt object specifying the second control Pt. Default is `Pt(0.9, 0.2).
   */
  static cubicBezier(t:number, c:number = 1, p1:PtLike=[0.1, 0.7], p2:PtLike=[0.9, 0.2] ):number {
    let curve = new Group( new Pt(0, 0), new Pt(p1), new Pt(p2), new Pt(1, 1) );
    return c * Curve.bezierStep(new Pt(t*t*t, t*t, t, 1), Curve.controlPoints(curve)).y;
  }

  /**
   * Give a Pt, draw a quadratic curve that will pass through that Pt as closely as possible. Adapted from Golan Levin's [shaping functions](http://www.flong.com/texts/code/shapers_poly/).
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p1` a Pt object specifying the Pt to pass through. Default is `Pt(0.2, 0.35)
   */
  static quadraticTarget(t:number, c:number = 1, p1:PtLike = [0.2, 0.35]):number {
    let a = Math.min(1 - Const.epsilon, Math.max( Const.epsilon, p1[0] ));
    let b = Math.min(1, Math.max( 0, p1[1] ));
    let A = (1 - b) / (1 - a) - (b / a);
    let B = (A * (a * a) - b) / a;
    let y = A * (t * t) - B * t;
    return c * Math.min(1, Math.max(0, y));
  }

  /**
   * Step function is a simple jump from 0 to 1 at a specific Pt in time.
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param p usually a value between 0 to 1, which specify the Pt to "jump". Default is 0.5 which is in the middle.
   */
  static cliff(t:number, c:number = 1, p:number = 0.5):number {
    return (t > p) ? c : 0;
  }

  /**
   * Convert any shaping functions into a series of steps.
   * @param fn the original shaping function
   * @param steps the number of steps
   * @param t a value between 0 to 1
   * @param c the value to shape, default is 1
   * @param args optional paramters to pass to original function
   */
  static step(fn: Function, steps:number, t:number, c:number, ...args:any[]) {
    let s = 1 / steps;
    let tt = Math.floor(t / s) * s;
    return fn(tt, c, ...args);
  }

}

/**
 * Range object keeps track of a Group of n-dimensional Pts to provide its minimum, maximum, and magnitude in each dimension.
 * It also provides convenient functions such as mapping the Group to another range. This class may be useful for visualizing data in charts.
 */
export class Range {

  protected _source:Group;
  protected _max:Pt;
  protected _min:Pt;
  protected _mag:Pt;
  protected _dims:number = 0;

  /**
   * Construct a Range instance for a Group of Pts.
   * @param g a Group or an Iterable<Pt>
   */
  constructor( g:PtIterable ) {
    this._source = Group.fromPtArray( g );
    this.calc();
  }

  /**
   * Get this Range's maximum values per dimension.
   */
  get max():Pt { return this._max.clone(); }

  /**
   * Get this Range's minimum values per dimension.
   */
  get min():Pt { return this._min.clone(); }

  /**
   * Get this Range's magnitude in each dimension.
   */
  get magnitude():Pt { return this._mag.clone(); }

  /**
   * Go through the group and find its min and max values. Usually you don't need to call this function directly.
   */
  calc():this {
    if (!this._source) return;
    let dims = this._source[0].length;
    this._dims = dims;
    let max = new Pt( dims );
    let min = new Pt( dims );
    let mag = new Pt( dims );

    for (let i=0; i<dims; i++) {
      max[i] = Const.min;
      min[i] = Const.max;
      mag[i] = 0;

      let s = this._source.zipSlice( i );
      for (let k=0, len=s.length; k<len; k++) {
        max[i] = Math.max( max[i], s[k] );
        min[i] = Math.min( min[i], s[k] );
        mag[i] = max[i] - min[i];
      }
    }

    this._max = max;
    this._min = min;
    this._mag = mag;
    return this;
  }

  /**
   * Map this Range to another range of values.
   * @param min target range's minimum value
   * @param max target range's maximum value
   * @param exclude Optional boolean array where `true` means excluding the conversion in that specific dimension.
   */
  mapTo( min:number, max:number, exclude?:boolean[] ):Group {
    let target = new Group();
    for (let i=0, len=this._source.length; i<len; i++) {
      let g = this._source[i];
      let n = new Pt( this._dims );
      for (let k=0; k<this._dims; k++) {
        n[k] = (exclude && exclude[k]) ? g[k] : Num.mapToRange( g[k], this._min[k], this._max[k], min, max );
      }
      target.push( n );
    }
    return target;
  }

  /**
   * Add more Pts to this Range and recalculate its min and max values.
   * @param pts a Group or an Iterable<PtLike> to append to this Range
   * @param update Optional. Set the parameter to `false` if you want to append without immediately updating this Range's min and max values. Default is `true`.
   */
  append( pts:PtLikeIterable, update:boolean=true ):this {
    let _pts = Util.iterToArray( pts );
    if (_pts[0].length !== this._dims) throw new Error(`Dimensions don't match. ${this._dims} dimensions in Range and ${_pts[0].length} provided in parameter. `);
    this._source = this._source.concat( _pts ) as Group;
    if (update) this.calc();
    return this;
  }

  /**
   * Create a number of evenly spaced "ticks" that span this Range's min and max value.
   * @param count number of subdivision. For example, 10 subdivision will return 11 tick values, which include first(min) and last(max) values.
   */
  ticks( count:number ):Group {
    let g = new Group();
    for (let i=0; i<=count; i++) {
      let p = new Pt( this._dims );
      for (let k=0, len=this._max.length; k<len; k++) {
        p[k] = Num.lerp( this._min[k], this._max[k], i/count );
      }
      g.push( p );
    }
    return g;
  }

}


text:
@interface AppDelegate ()

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
}

- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
  @autoreleasepool {
    HuffZipDecode *main = [[HuffZipDecode alloc] init];
    [main HuffZipDecodeMain:[filename UTF8String]];
    [NSApp terminate:self];
  }
  return YES;
}

@end

redpajama_set_name: RedPajamaGithub


text:
"""
Tests that the expression parser can auto-import and hand-import sub-modules
"""
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil
import os

class TestSwiftSubmoduleImport(TestBase):

    mydir = TestBase.compute_mydir(__file__)

    # Have to find some submodule that is present on both Darwin & Linux for this
    # test to run on both systems...

    def setUp(self):
        TestBase.setUp(self)

    @skipUnlessDarwin
    @swiftTest
    def test_swift_submodule_import(self):
        """Tests that swift expressions can import sub-modules correctly"""
        self.build()
        lldbutil.run_to_source_breakpoint(
            self, 'Set a breakpoint here', lldb.SBFileSpec('main.swift'))

        options = lldb.SBExpressionOptions()
        options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget)

        # We'll be asked to auto-import Darwin.C when we evaluate this expression,
        # so even though it doesn't seem like it this does test auto-import:
        value = self.frame().EvaluateExpression("b", options)
        self.assertTrue(value.IsValid(), "Got a valid variable back from b")
        self.assertTrue(value.GetError().Success(),
                        "And the variable was successfully evaluated")
        result = value.GetSummary()
        self.assertTrue(
            result == '"aa"',
            "And the variable's value was correct.")

        # Now make sure we can explicitly do the import:
        value = self.frame().EvaluateExpression('import Darwin.C\n b', options)
        self.assertTrue(
            value.IsValid(),
            "Got a valid value back from import Darwin.C")
        self.assertTrue(
            value.GetError().Success(),
            "The import was not successful: %s" %
            (value.GetError().GetCString()))

if __name__ == '__main__':
    import atexit
    lldb.SBDebugger.Initialize()
    atexit.register(lambda: lldb.SBDebugger.Terminate())
    unittest2.main()


text:
package org.apache.commons.io;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Stack;

/**
 * General filename and filepath manipulation utilities.
 *

 * When dealing with filenames you can hit problems when moving from a Windows
 * based development machine to a Unix based production machine.
 * This class aims to help avoid those problems.
 *

 * NOTE: You may be able to avoid using this class entirely simply by
 * using JDK {@link File File} objects and the two argument constructor
 * {@link File#File(File, String) File(File,String)}.
 *

 * Most methods on this class are designed to work the same on both Unix and Windows.
 * Those that don't include 'System', 'Unix' or 'Windows' in their name.
 *

 * Most methods recognise both separators (forward and back), and both
 * sets of prefixes. See the javadoc of each method for details.
 *

 * This class defines six components within a filename
 * (example C:\dev\project\file.txt):
 *

 *
the prefix - C:\

 *
the path - dev\project\

 *
the full path - C:\dev\project\

 *
the name - file.txt

 *
the base name - file

 *
the extension - txt

 *

 * Note that this class works best if directory filenames end with a separator.
 * If you omit the last separator, it is impossible to determine if the filename
 * corresponds to a file or a directory. As a result, we have chosen to say
 * it corresponds to a file.
 *

 * This class only supports Unix and Windows style names.
 * Prefixes are matched as follows:
 *

 * Windows:
 * a\b\c.txt           --> ""          --> relative
 * \a\b\c.txt          --> "\"         --> current drive absolute
 * C:a\b\c.txt         --> "C:"        --> drive relative
 * C:\a\b\c.txt        --> "C:\"       --> absolute
 * \\server\a\b\c.txt  --> "\\server\" --> UNC
 *
 * Unix:
 * a/b/c.txt           --> ""          --> relative
 * /a/b/c.txt          --> "/"         --> absolute
 * ~/a/b/c.txt         --> "~/"        --> current user
 * ~                   --> "~/"        --> current user (slash added)
 * ~user/a/b/c.txt     --> "~user/"    --> named user
 * ~user               --> "~user/"    --> named user (slash added)
 *

 * Both prefix styles are matched always, irrespective of the machine that you are
 * currently running on.
 *

 * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.
 *
 * @version $Id: FilenameUtils.java 1307462 2012-03-30 15:13:11Z ggregory $
 * @since 1.1
 */
public class FilenameUtils {

    /**
     * The extension separator character.
     * @since 1.4
     */
    public static final char EXTENSION_SEPARATOR = '.';

    /**
     * The extension separator String.
     * @since 1.4
     */
    public static final String EXTENSION_SEPARATOR_STR = Character.toString(EXTENSION_SEPARATOR);

    /**
     * The Unix separator character.
     */
    private static final char UNIX_SEPARATOR = '/';

    /**
     * The Windows separator character.
     */
    private static final char WINDOWS_SEPARATOR = '\\';

    /**
     * The system separator character.
     */
    private static final char SYSTEM_SEPARATOR = File.separatorChar;

    /**
     * The separator character that is the opposite of the system separator.
     */
    private static final char OTHER_SEPARATOR;
    static {
        if (isSystemWindows()) {
            OTHER_SEPARATOR = UNIX_SEPARATOR;
        } else {
            OTHER_SEPARATOR = WINDOWS_SEPARATOR;
        }
    }

    /**
     * Instances should NOT be constructed in standard programming.
     */
    public FilenameUtils() {
        super();
    }

    //-----------------------------------------------------------------------
    /**
     * Determines if Windows file system is in use.
     *
     * @return true if the system is Windows
     */
    static boolean isSystemWindows() {
        return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR;
    }

    //-----------------------------------------------------------------------
    /**
     * Checks if the character is a separator.
     *
     * @param ch  the character to check
     * @return true if it is a separator character
     */
    private static boolean isSeparator(char ch) {
        return ch == UNIX_SEPARATOR || ch == WINDOWS_SEPARATOR;
    }

    //-----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps.
     *

     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format of the system.
     *

     * A trailing slash will be retained.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, {@code null}
     * is returned.
     *

     * The output will be the same on both Unix and Windows except
     * for the separator character.
     *

     * /foo//               -->   /foo/
     * /foo/./              -->   /foo/
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar/
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo/
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar/
     * ~/../bar             -->   null
     *

     * (Note the file separator returned will be correct for Windows/Unix)
     *
     * @param filename  the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalize(String filename) {
        return doNormalize(filename, SYSTEM_SEPARATOR, true);
    }
    /**
     * Normalizes a path, removing double and single dot path steps.
     *

     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format specified.
     *

     * A trailing slash will be retained.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, {@code null}
     * is returned.
     *

     * The output will be the same on both Unix and Windows except
     * for the separator character.
     *

     * /foo//               -->   /foo/
     * /foo/./              -->   /foo/
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar/
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo/
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar/
     * ~/../bar             -->   null
     *

     * The output will be the same on both Unix and Windows including
     * the separator character.
     *
     * @param filename  the filename to normalize, null returns null
     * @param unixSeparator {@code true} if a unix separator should
     * be used or {@code false} if a windows separator should be used.
     * @return the normalized filename, or null if invalid
     * @since 2.0
     */
    public static String normalize(String filename, boolean unixSeparator) {
        char separator = unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR;
        return doNormalize(filename, separator, true);
    }

    //-----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps,
     * and removing any final directory separator.
     *

     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format of the system.
     *

     * A trailing slash will be removed.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, {@code null}
     * is returned.
     *

     * The output will be the same on both Unix and Windows except
     * for the separator character.
     *

     * /foo//               -->   /foo
     * /foo/./              -->   /foo
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar
     * ~/../bar             -->   null
     *

     * (Note the file separator returned will be correct for Windows/Unix)
     *
     * @param filename  the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalizeNoEndSeparator(String filename) {
        return doNormalize(filename, SYSTEM_SEPARATOR, false);
    }

    /**
     * Normalizes a path, removing double and single dot path steps,
     * and removing any final directory separator.
     *

     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format specified.
     *

     * A trailing slash will be removed.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, {@code null}
     * is returned.
     *

     * The output will be the same on both Unix and Windows including
     * the separator character.
     *

     * /foo//               -->   /foo
     * /foo/./              -->   /foo
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar
     * ~/../bar             -->   null
     *

     *
     * @param filename  the filename to normalize, null returns null
     * @param unixSeparator {@code true} if a unix separator should
     * be used or {@code false} if a windows separtor should be used.
     * @return the normalized filename, or null if invalid
     * @since 2.0
     */
    public static String normalizeNoEndSeparator(String filename, boolean unixSeparator) {
         char separator = unixSeparator ? UNIX_SEPARATOR : WINDOWS_SEPARATOR;
        return doNormalize(filename, separator, false);
    }

    /**
     * Internal method to perform the normalization.
     *
     * @param filename  the filename
     * @param separator The separator character to use
     * @param keepSeparator  true to keep the final separator
     * @return the normalized filename
     */
    private static String doNormalize(String filename, char separator, boolean keepSeparator) {
        if (filename == null) {
            return null;
        }
        int size = filename.length();
        if (size == 0) {
            return filename;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }

        char[] array = new char[size + 2];  // +1 for possible extra slash, +2 for arraycopy
        filename.getChars(0, filename.length(), array, 0);

        // fix separators throughout
        char otherSeparator = separator == SYSTEM_SEPARATOR ? OTHER_SEPARATOR : SYSTEM_SEPARATOR;
        for (int i = 0; i < array.length; i++) {
            if (array[i] == otherSeparator) {
                array[i] = separator;
            }
        }

        // add extra separator on the end to simplify code below
        boolean lastIsDirectory = true;
        if (array[size - 1] != separator) {
            array[size++] = separator;
            lastIsDirectory = false;
        }

        // adjoining slashes
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == separator && array[i - 1] == separator) {
                System.arraycopy(array, i, array, i - 1, size - i);
                size--;
                i--;
            }
        }

        // dot slash
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == separator && array[i - 1] == '.' &&
                    (i == prefix + 1 || array[i - 2] == separator)) {
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                System.arraycopy(array, i + 1, array, i - 1, size - i);
                size -=2;
                i--;
            }
        }

        // double dot slash
        outer:
        for (int i = prefix + 2; i < size; i++) {
            if (array[i] == separator && array[i - 1] == '.' && array[i - 2] == '.' &&
                    (i == prefix + 2 || array[i - 3] == separator)) {
                if (i == prefix + 2) {
                    return null;
                }
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                int j;
                for (j = i - 4 ; j >= prefix; j--) {
                    if (array[j] == separator) {
                        // remove b/../ from a/b/../c
                        System.arraycopy(array, i + 1, array, j + 1, size - i);
                        size -= i - j;
                        i = j + 1;
                        continue outer;
                    }
                }
                // remove a/../ from a/../c
                System.arraycopy(array, i + 1, array, prefix, size - i);
                size -= i + 1 - prefix;
                i = prefix + 1;
            }
        }

        if (size <= 0) {  // should never be less than 0
            return "";
        }
        if (size <= prefix) {  // should never be less than prefix
            return new String(array, 0, size);
        }
        if (lastIsDirectory && keepSeparator) {
            return new String(array, 0, size);  // keep trailing separator
        }
        return new String(array, 0, size - 1);  // lose trailing separator
    }

    //-----------------------------------------------------------------------
    /**
     * Concatenates a filename to a base path using normal command line style rules.
     *

     * The effect is equivalent to resultant directory after changing
     * directory to the first argument, followed by changing directory to
     * the second argument.
     *

     * The first argument is the base path, the second is the path to concatenate.
     * The returned path is always normalized via {@link #normalize(String)},
     * thus .. is handled.
     *

     * If pathToAdd is absolute (has an absolute prefix), then
     * it will be normalized and returned.
     * Otherwise, the paths will be joined, normalized and returned.
     *

     * The output will be the same on both Unix and Windows except
     * for the separator character.
     *

     * /foo/ + bar          -->   /foo/bar
     * /foo + bar           -->   /foo/bar
     * /foo + /bar          -->   /bar
     * /foo + C:/bar        -->   C:/bar
     * /foo + C:bar         -->   C:bar (*)
     * /foo/a/ + ../bar     -->   foo/bar
     * /foo/ + ../../bar    -->   null
     * /foo/ + /bar         -->   /bar
     * /foo/.. + /bar       -->   /bar
     * /foo + bar/c.txt     -->   /foo/bar/c.txt
     * /foo/c.txt + bar     -->   /foo/c.txt/bar (!)
     *

     * (*) Note that the Windows relative drive prefix is unreliable when
     * used with this method.
     * (!) Note that the first parameter must be a path. If it ends with a name, then
     * the name will be built into the concatenated path. If this might be a problem,
     * use {@link #getFullPath(String)} on the base path argument.
     *
     * @param basePath  the base path to attach to, always treated as a path
     * @param fullFilenameToAdd  the filename (or path) to attach to the base
     * @return the concatenated path, or null if invalid
     */
    public static String concat(String basePath, String fullFilenameToAdd) {
        int prefix = getPrefixLength(fullFilenameToAdd);
        if (prefix < 0) {
            return null;
        }
        if (prefix > 0) {
            return normalize(fullFilenameToAdd);
        }
        if (basePath == null) {
            return null;
        }
        int len = basePath.length();
        if (len == 0) {
            return normalize(fullFilenameToAdd);
        }
        char ch = basePath.charAt(len - 1);
        if (isSeparator(ch)) {
            return normalize(basePath + fullFilenameToAdd);
        } else {
            return normalize(basePath + '/' + fullFilenameToAdd);
        }
    }

    /**
     * Determines whether the {@code parent} directory contains the {@code child} element (a file or directory).
     *

     * The files names are expected to be normalized.
     *

     *
     * Edge cases:
     *

     *
A {@code directory} must not be null: if null, throw IllegalArgumentException

     *
A directory does not contain itself: return false

     *
A null child file is not contained in any parent: return false

     *

     *
     * @param canonicalParent
     *            the file to consider as the parent.
     * @param canonicalChild
     *            the file to consider as the child.
     * @return true is the candidate leaf is under by the specified composite. False otherwise.
     * @throws IOException
     *             if an IO error occurs while checking the files.
     * @since 2.2
     * @see FileUtils#directoryContains(File, File)
     */
    public static boolean directoryContains(final String canonicalParent, final String canonicalChild)
            throws IOException {

        // Fail fast against NullPointerException
        if (canonicalParent == null) {
            throw new IllegalArgumentException("Directory must not be null");
        }

        if (canonicalChild == null) {
            return false;
        }

        if (IOCase.SYSTEM.checkEquals(canonicalParent, canonicalChild)) {
            return false;
        }

        return IOCase.SYSTEM.checkStartsWith(canonicalChild, canonicalParent);
    }

    //-----------------------------------------------------------------------
    /**
     * Converts all separators to the Unix separator of forward slash.
     *
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToUnix(String path) {
        if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);
    }

    /**
     * Converts all separators to the Windows separator of backslash.
     *
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToWindows(String path) {
        if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);
    }

    /**
     * Converts all separators to the system separator.
     *
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToSystem(String path) {
        if (path == null) {
            return null;
        }
        if (isSystemWindows()) {
            return separatorsToWindows(path);
        } else {
            return separatorsToUnix(path);
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Returns the length of the filename prefix, such as C:/ or ~/.
     *

     * This method will handle a file in either Unix or Windows format.
     *

     * The prefix length includes the first slash in the full filename
     * if applicable. Thus, it is possible that the length returned is greater
     * than the length of the input string.
     *

     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     *
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     * ie. both Unix and Windows prefixes are matched regardless.
     *
     * @param filename  the filename to find the prefix in, null returns -1
     * @return the length of the prefix, -1 if invalid or null
     */
    public static int getPrefixLength(String filename) {
        if (filename == null) {
            return -1;
        }
        int len = filename.length();
        if (len == 0) {
            return 0;
        }
        char ch0 = filename.charAt(0);
        if (ch0 == ':') {
            return -1;
        }
        if (len == 1) {
            if (ch0 == '~') {
                return 2;  // return a length greater than the input
            }
            return isSeparator(ch0) ? 1 : 0;
        } else {
            if (ch0 == '~') {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);
                if (posUnix == -1 && posWin == -1) {
                    return len + 1;  // return a length greater than the input
                }
                posUnix = posUnix == -1 ? posWin : posUnix;
                posWin = posWin == -1 ? posUnix : posWin;
                return Math.min(posUnix, posWin) + 1;
            }
            char ch1 = filename.charAt(1);
            if (ch1 == ':') {
                ch0 = Character.toUpperCase(ch0);
                if (ch0 >= 'A' && ch0 <= 'Z') {
                    if (len == 2 || isSeparator(filename.charAt(2)) == false) {
                        return 2;
                    }
                    return 3;
                }
                return -1;

            } else if (isSeparator(ch0) && isSeparator(ch1)) {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
                if (posUnix == -1 && posWin == -1 || posUnix == 2 || posWin == 2) {
                    return -1;
                }
                posUnix = posUnix == -1 ? posWin : posUnix;
                posWin = posWin == -1 ? posUnix : posWin;
                return Math.min(posUnix, posWin) + 1;
            } else {
                return isSeparator(ch0) ? 1 : 0;
            }
        }
    }

    /**
     * Returns the index of the last directory separator character.
     *

     * This method will handle a file in either Unix or Windows format.
     * The position of the last forward or backslash is returned.
     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to find the last path separator in, null returns -1
     * @return the index of the last separator character, or -1 if there
     * is no such character
     */
    public static int indexOfLastSeparator(String filename) {
        if (filename == null) {
            return -1;
        }
        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
        return Math.max(lastUnixPos, lastWindowsPos);
    }

    /**
     * Returns the index of the last extension separator character, which is a dot.
     *

     * This method also checks that there is no directory separator after the last dot.
     * To do this it uses {@link #indexOfLastSeparator(String)} which will
     * handle a file in either Unix or Windows format.
     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to find the last path separator in, null returns -1
     * @return the index of the last separator character, or -1 if there
     * is no such character
     */
    public static int indexOfExtension(String filename) {
        if (filename == null) {
            return -1;
        }
        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
        int lastSeparator = indexOfLastSeparator(filename);
        return lastSeparator > extensionPos ? -1 : extensionPos;
    }

    //-----------------------------------------------------------------------
    /**
     * Gets the prefix from a full filename, such as C:/
     * or ~/.
     *

     * This method will handle a file in either Unix or Windows format.
     * The prefix includes the first slash in the full filename where applicable.
     *

     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     *
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     * ie. both Unix and Windows prefixes are matched regardless.
     *
     * @param filename  the filename to query, null returns null
     * @return the prefix of the file, null if invalid
     */
    public static String getPrefix(String filename) {
        if (filename == null) {
            return null;
        }
        int len = getPrefixLength(filename);
        if (len < 0) {
            return null;
        }
        if (len > filename.length()) {
            return filename + UNIX_SEPARATOR;  // we know this only happens for unix
        }
        return filename.substring(0, len);
    }

    /**
     * Gets the path from a full filename, which excludes the prefix.
     *

     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before and
     * including the last forward or backslash.
     *

     * C:\a\b\c.txt --> a\b\
     * ~/a/b/c.txt  --> a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *

     * This method drops the prefix from the result.
     * See {@link #getFullPath(String)} for the method that retains the prefix.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getPath(String filename) {
        return doGetPath(filename, 1);
    }

    /**
     * Gets the path from a full filename, which excludes the prefix, and
     * also excluding the final directory separator.
     *

     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before the
     * last forward or backslash.
     *

     * C:\a\b\c.txt --> a\b
     * ~/a/b/c.txt  --> a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *

     * This method drops the prefix from the result.
     * See {@link #getFullPathNoEndSeparator(String)} for the method that retains the prefix.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getPathNoEndSeparator(String filename) {
        return doGetPath(filename, 0);
    }

    /**
     * Does the work of getting the path.
     *
     * @param filename  the filename
     * @param separatorAdd  0 to omit the end separator, 1 to return it
     * @return the path
     */
    private static String doGetPath(String filename, int separatorAdd) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        int endIndex = index+separatorAdd;
        if (prefix >= filename.length() || index < 0 || prefix >= endIndex) {
            return "";
        }
        return filename.substring(prefix, endIndex);
    }

    /**
     * Gets the full path from a full filename, which is the prefix + path.
     *

     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before and
     * including the last forward or backslash.
     *

     * C:\a\b\c.txt --> C:\a\b\
     * ~/a/b/c.txt  --> ~/a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~/
     * ~/           --> ~/
     * ~user        --> ~user/
     * ~user/       --> ~user/
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getFullPath(String filename) {
        return doGetFullPath(filename, true);
    }

    /**
     * Gets the full path from a full filename, which is the prefix + path,
     * and also excluding the final directory separator.
     *

     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before the
     * last forward or backslash.
     *

     * C:\a\b\c.txt --> C:\a\b
     * ~/a/b/c.txt  --> ~/a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~
     * ~/           --> ~
     * ~user        --> ~user
     * ~user/       --> ~user
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getFullPathNoEndSeparator(String filename) {
        return doGetFullPath(filename, false);
    }

    /**
     * Does the work of getting the path.
     *
     * @param filename  the filename
     * @param includeSeparator  true to include the end separator
     * @return the path
     */
    private static String doGetFullPath(String filename, boolean includeSeparator) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        if (prefix >= filename.length()) {
            if (includeSeparator) {
                return getPrefix(filename);  // add end slash if necessary
            } else {
                return filename;
            }
        }
        int index = indexOfLastSeparator(filename);
        if (index < 0) {
            return filename.substring(0, prefix);
        }
        int end = index + (includeSeparator ?  1 : 0);
        if (end == 0) {
            end++;
        }
        return filename.substring(0, end);
    }

    /**
     * Gets the name minus the path from a full filename.
     *

     * This method will handle a file in either Unix or Windows format.
     * The text after the last forward or backslash is returned.
     *

     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * a/b/c     --> c
     * a/b/c/    --> ""
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none exists
     */
    public static String getName(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        return filename.substring(index + 1);
    }

    /**
     * Gets the base name, minus the full path and extension, from a full filename.
     *

     * This method will handle a file in either Unix or Windows format.
     * The text after the last forward or backslash and before the last dot is returned.
     *

     * a/b/c.txt --> c
     * a.txt     --> a
     * a/b/c     --> c
     * a/b/c/    --> ""
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none exists
     */
    public static String getBaseName(String filename) {
        return removeExtension(getName(filename));
    }

    /**
     * Gets the extension of a filename.
     *

     * This method returns the textual part of the filename after the last dot.
     * There must be no directory separator after the dot.
     *

     * foo.txt      --> "txt"
     * a/b/c.jpg    --> "jpg"
     * a/b.txt/c    --> ""
     * a/b/c        --> ""
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename the filename to retrieve the extension of.
     * @return the extension of the file or an empty string if none exists or {@code null}
     * if the filename is {@code null}.
     */
    public static String getExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return "";
        } else {
            return filename.substring(index + 1);
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Removes the extension from a filename.
     *

     * This method returns the textual part of the filename before the last dot.
     * There must be no directory separator after the dot.
     *

     * foo.txt    --> foo
     * a\b\c.jpg  --> a\b\c
     * a\b\c      --> a\b\c
     * a.b\c      --> a.b\c
     *

     *

     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the filename minus the extension
     */
    public static String removeExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return filename;
        } else {
            return filename.substring(0, index);
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal exactly.
     *

     * No processing is performed on the filenames other than comparison,
     * thus this is merely a null-safe case-sensitive equals.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equals(String filename1, String filename2) {
        return equals(filename1, filename2, false, IOCase.SENSITIVE);
    }

    /**
     * Checks whether two filenames are equal using the case rules of the system.
     *

     * No processing is performed on the filenames other than comparison.
     * The check is case-sensitive on Unix and case-insensitive on Windows.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsOnSystem(String filename1, String filename2) {
        return equals(filename1, filename2, false, IOCase.SYSTEM);
    }

    //-----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal after both have been normalized.
     *

     * Both filenames are first passed to {@link #normalize(String)}.
     * The check is then performed in a case-sensitive manner.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equalsNormalized(String filename1, String filename2) {
        return equals(filename1, filename2, true, IOCase.SENSITIVE);
    }

    /**
     * Checks whether two filenames are equal after both have been normalized
     * and using the case rules of the system.
     *

     * Both filenames are first passed to {@link #normalize(String)}.
     * The check is then performed case-sensitive on Unix and
     * case-insensitive on Windows.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsNormalizedOnSystem(String filename1, String filename2) {
        return equals(filename1, filename2, true, IOCase.SYSTEM);
    }

    /**
     * Checks whether two filenames are equal, optionally normalizing and providing
     * control over the case-sensitivity.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @param normalized  whether to normalize the filenames
     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
     * @return true if the filenames are equal, null equals null
     * @since 1.3
     */
    public static boolean equals(
            String filename1, String filename2,
            boolean normalized, IOCase caseSensitivity) {

        if (filename1 == null || filename2 == null) {
            return filename1 == null && filename2 == null;
        }
        if (normalized) {
            filename1 = normalize(filename1);
            filename2 = normalize(filename2);
            if (filename1 == null || filename2 == null) {
                throw new NullPointerException(
                    "Error normalizing one or both of the file names");
            }
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        return caseSensitivity.checkEquals(filename1, filename2);
    }

    //-----------------------------------------------------------------------
    /**
     * Checks whether the extension of the filename is that specified.
     *

     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extension  the extension to check for, null or empty checks for no extension
     * @return true if the filename has the specified extension
     */
    public static boolean isExtension(String filename, String extension) {
        if (filename == null) {
            return false;
        }
        if (extension == null || extension.length() == 0) {
            return indexOfExtension(filename) == -1;
        }
        String fileExt = getExtension(filename);
        return fileExt.equals(extension);
    }

    /**
     * Checks whether the extension of the filename is one of those specified.
     *

     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extensions  the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename, String[] extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.length == 0) {
            return indexOfExtension(filename) == -1;
        }
        String fileExt = getExtension(filename);
        for (String extension : extensions) {
            if (fileExt.equals(extension)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks whether the extension of the filename is one of those specified.
     *

     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extensions  the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename, Collection<String> extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.isEmpty()) {
            return indexOfExtension(filename) == -1;
        }
        String fileExt = getExtension(filename);
        for (String extension : extensions) {
            if (fileExt.equals(extension)) {
                return true;
            }
        }
        return false;
    }

    //-----------------------------------------------------------------------
    /**
     * Checks a filename to see if it matches the specified wildcard matcher,
     * always testing case-sensitive.
     *

     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters.
     * This is the same as often found on Dos/Unix command lines.
     * The check is case-sensitive always.
     *

     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     *

     * N.B. the sequence "*?" does not work properly at present in match strings.
     *
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SENSITIVE
     */
    public static boolean wildcardMatch(String filename, String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
    }

    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * using the case rules of the system.
     *

     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters.
     * This is the same as often found on Dos/Unix command lines.
     * The check is case-sensitive on Unix and case-insensitive on Windows.
     *

     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     *

     * N.B. the sequence "*?" does not work properly at present in match strings.
     *
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SYSTEM
     */
    public static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
    }

    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * allowing control over case-sensitivity.
     *

     * The wildcard matcher uses the characters '?' and '*' to represent a
     * single or multiple (zero or more) wildcard characters.
     * N.B. the sequence "*?" does not work properly at present in match strings.
     *
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
     * @return true if the filename matches the wilcard string
     * @since 1.3
     */
    public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {
        if (filename == null && wildcardMatcher == null) {
            return true;
        }
        if (filename == null || wildcardMatcher == null) {
            return false;
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        String[] wcs = splitOnTokens(wildcardMatcher);
        boolean anyChars = false;
        int textIdx = 0;
        int wcsIdx = 0;
        Stack<int[]> backtrack = new Stack<int[]>();

        // loop around a backtrack stack, to handle complex * matching
        do {
            if (backtrack.size() > 0) {
                int[] array = backtrack.pop();
                wcsIdx = array[0];
                textIdx = array[1];
                anyChars = true;
            }

            // loop whilst tokens and text left to process
            while (wcsIdx < wcs.length) {

                if (wcs[wcsIdx].equals("?")) {
                    // ? so move to next text char
                    textIdx++;
                    if (textIdx > filename.length()) {
                        break;
                    }
                    anyChars = false;

                } else if (wcs[wcsIdx].equals("*")) {
                    // set any chars status
                    anyChars = true;
                    if (wcsIdx == wcs.length - 1) {
                        textIdx = filename.length();
                    }

                } else {
                    // matching text token
                    if (anyChars) {
                        // any chars then try to locate text token
                        textIdx = caseSensitivity.checkIndexOf(filename, textIdx, wcs[wcsIdx]);
                        if (textIdx == -1) {
                            // token not found
                            break;
                        }
                        int repeat = caseSensitivity.checkIndexOf(filename, textIdx + 1, wcs[wcsIdx]);
                        if (repeat >= 0) {
                            backtrack.push(new int[] {wcsIdx, repeat});
                        }
                    } else {
                        // matching from current position
                        if (!caseSensitivity.checkRegionMatches(filename, textIdx, wcs[wcsIdx])) {
                            // couldnt match token
                            break;
                        }
                    }

                    // matched text token, move text index to end of matched token
                    textIdx += wcs[wcsIdx].length();
                    anyChars = false;
                }

                wcsIdx++;
            }

            // full match
            if (wcsIdx == wcs.length && textIdx == filename.length()) {
                return true;
            }

        } while (backtrack.size() > 0);

        return false;
    }

    /**
     * Splits a string into a number of tokens.
     * The text is split by '?' and '*'.
     * Where multiple '*' occur consecutively they are collapsed into a single '*'.
     *
     * @param text  the text to split
     * @return the array of tokens, never null
     */
    static String[] splitOnTokens(String text) {
        // used by wildcardMatch
        // package level so a unit test may run on this

        if (text.indexOf('?') == -1 && text.indexOf('*') == -1) {
            return new String[] { text };
        }

        char[] array = text.toCharArray();
        ArrayList<String> list = new ArrayList<String>();
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < array.length; i++) {
            if (array[i] == '?' || array[i] == '*') {
                if (buffer.length() != 0) {
                    list.add(buffer.toString());
                    buffer.setLength(0);
                }
                if (array[i] == '?') {
                    list.add("?");
                } else if (list.isEmpty() ||
                        i > 0 && list.get(list.size() - 1).equals("*") == false) {
                    list.add("*");
                }
            } else {
                buffer.append(array[i]);
            }
        }
        if (buffer.length() != 0) {
            list.add(buffer.toString());
        }

        return list.toArray( new String[ list.size() ] );
    }

}


text:
using NUnit.Framework;
using NUnit.Helper;
using PdfSharp.Pdf.IO;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace PdfSharp.UnitTests.Reader
{
  public class PdfReaderTests
  {
    [Test]
    [TestCaseSource(nameof(GetPDFs))]
    public void OpenTest(string pdfFile)
    {
      DoOpen(pdfFile);
    }

    [Test]
    public void OpenPasswordProtectedTest()
    {
      Assert.Throws<PdfReaderException>(
        () => DoOpen(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"))
      );

      DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "user", PdfDocumentOpenMode.Import);

      Assert.Throws<PdfReaderException>(
        () => DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "user", PdfDocumentOpenMode.Modify)
      );

      DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "user", PdfDocumentOpenMode.ReadOnly);

      DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "owner", PdfDocumentOpenMode.Import);

      DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "owner", PdfDocumentOpenMode.Modify);

      DoOpen2(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs/HelloWorld (protected).pdf"), "owner", PdfDocumentOpenMode.ReadOnly);
    }

    private void DoOpen(string pdfFile) => OpenHelper.Test(pdfFile);

    private void DoOpen2(string pdfFile, string password, PdfDocumentOpenMode mode)
    {
      using (var reader = PdfReader.Open(new MemoryStream(File.ReadAllBytes(pdfFile)), password, mode))
      {
        // nop
      }

      using (var reader = PdfReader.Open(pdfFile, password, mode))
      {
        // nop
      }
    }

    public static IEnumerable<string> GetPDFs() => Directory.GetFiles(DirectoryPointHelper.Resolve("@PDFsharp/samples/PDFs"), "*.pdf")
      .Where(it => true
        && !it.Contains("HelloWorld (protected).pdf") // Skip password protected one.
        && !it.Contains("HelloWorld bomb.pdf") // Skip not supported offsetting yet: `PdfSharp.Pdf.IO.PdfReaderException : Token 'xref' was not expected.`
      );
  }
}


text:
function slideshow(s) {
  var $ = jQuery;
  var $carousel = $('
').appendTo($('body'));
  var $indicators, $list, i, slide, $item, $caption;

  // Configure based on the mode and settings.
  var conf = {
    'indicators' : true,
    'controls' : true,
    'captions' : true,
    // carousel options
    'interval' : 5000,
    'pause'    : 'hover',
    'wrap'     : true,
    'keyboard' : true
  };
  switch (s.mode) {
  case 'schedule':
    conf.indicators = conf.controls = conf.interval = false;
    conf.pause = null;
    for (i = 0; i < s.items.length; ++i) {
      s.items[i]['beginms'] = Date.parse(s.items[i]['begin']);
    }
    break;
  }
  for (i in conf) {
    if (typeof s[i] != 'undefined') {
      conf[i] = s[i];
    }
  }

  // Build the shell
  if (conf.indicators) {
    $indicators = $('
').appendTo($carousel);
  }
  $list = $('
').appendTo($carousel);
  if (conf.controls) {
    $carousel.append('Previous');
    $carousel.append('Next');
  }

  // Add the slides
  for (i = 0; i < s.items.length; ++i) {
    slide = s.items[i];
    if (conf.indicators) {
      $indicators.append('

');
    }
    $item = $('
').appendTo($list);
    if (!i) {
      $item.addClass('active');
    }
    $item.append($('').attr('src', 'img/slide' + slide.number + '.jpg').attr('alt', slide.title));
    if (conf.captions) {
      $caption = $('
').appendTo($item);
      $caption.append($('
').text(slide.title));
      if (typeof slide.description != 'undefined') {
        $caption.append($('
').text(slide.description));
      }
    }
  }

  // Launch the carousel
  $carousel.carousel(conf);

  // Implement the schedule.
  switch (s.mode) {
  case 'schedule':
    setInterval(function(){
      if (document.location.hash.indexOf('slideshow=preview') != -1) {
        $carousel.carousel('next');
        return;
      }
      // Select the slide to show.
      var ms = (new Date()).getTime();
      for (i = 0; i < s.items.length; ++i) {
        if (s.items[i].beginms > ms) {
          $carousel.carousel(Math.max(0, i - 1));
          return;
        }
      }
      $carousel.carousel(s.items.length - 1);
    }, 5000);
    break;
  }
}


text:
package com.ufpb.questao4;

public class ColchetesDecorator extends Decorator {

	public ColchetesDecorator(InterfaceNumeroUm numero){
		this.numero = numero;
	}

	@Override
	public String imprime() {
		return "["+this.numero.imprime()+"]";
	}

}


text:
package fetch

import (
	"context"
	"errors"
	"fmt"
	"io/fs"

	"golang.org/x/pkgsite/internal"
	"golang.org/x/pkgsite/internal/derrors"
	"golang.org/x/pkgsite/internal/log"
	"golang.org/x/pkgsite/internal/proxy"
	"golang.org/x/pkgsite/internal/stdlib"
	"golang.org/x/pkgsite/internal/version"
)

// LatestModuleVersions uses the proxy to get information about the latest
// versions of modulePath. It returns a LatestModuleVersions whose RawVersion
// and CookedVersion is obtained from the proxy @v/list and @latest endpoints.
// The cooked version is computed by choosing the latest version after removing
// versions that are retracted in the go.mod file of the raw version.
//
// The GoodVersion of LatestModuleVersions is not set. It should be determined
// when inserting into a data source, since it depends on the contents of the
// data source.
//
// The hasGoMod function that is passed in should check if version v of the
// module has a go.mod file, using a source other than the proxy (e.g. a
// database). If it doesn't have enough information to decide, it should return
// an error that wraps derrors.NotFound.
//
// If a module has no tagged versions and hasn't been accessed at a
// pseudo-version in a while, then the proxy's list endpoint will serve nothing
// and its @latest endpoint will return a 404/410. (Example:
// cloud.google.com/go/compute/metadata, which has a
// v0.0.0-20181107005212-dafb9c8d8707 that @latest does not return.) That is not
// a failure, but a valid state in which there is no version information for a
// module, even though particular pseudo-versions of the module might exist. In
// this case, LatestModuleVersions returns (nil, nil).
//
// As a special case, the "std" module's versions are fetched from the repo (by
// calling stdlib.Versions). We assume stdlib versions are never retracted, and
// that there are no incompatible versions.
func LatestModuleVersions(ctx context.Context, modulePath string, prox *proxy.Client, hasGoMod func(v string) (bool, error)) (info *internal.LatestModuleVersions, err error) {
	defer derrors.WrapStack(&err, "LatestModuleVersions(%q)", modulePath)

	defer func() {
		if info != nil {
			log.Debugf(ctx, "LatestModuleVersions(%q) => (raw=%q cooked=%q, %v)", modulePath, info.RawVersion, info.CookedVersion, err)
		}
	}()

	if modulePath == stdlib.ModulePath {
		vs, err := stdlib.Versions()
		if err != nil {
			return nil, err
		}
		latest := version.LatestOf(vs)
		if latest == "" {
			return nil, errors.New("no versions for stdlib")
		}
		return internal.NewLatestModuleVersions(modulePath, latest, latest, "", []byte("module std"))
	}

	// Remember calls to hasGoMod because they can be expensive.
	hasGoModResults := map[string]bool{}
	hasGoModFunc := func(v string) (bool, error) {
		result, ok := hasGoModResults[v]
		if ok {
			return result, nil
		}
		err := derrors.NotFound
		if hasGoMod != nil {
			result, err = hasGoMod(v)
		}
		if err != nil && !errors.Is(err, derrors.NotFound) {
			return false, err
		}
		if err != nil {
			// hasGoMod doesn't know; download the zip.
			zr, err := prox.Zip(ctx, modulePath, v)
			if err != nil {
				return false, err
			}
			contentsDir, err := fs.Sub(zr, modulePath+"@"+v)
			if err != nil {
				return false, err
			}
			result = hasGoModFile(contentsDir)
		}
		hasGoModResults[v] = result
		return result, nil
	}

	// Get the raw latest version.
	versions, err := prox.Versions(ctx, modulePath)
	if err != nil {
		return nil, err
	}
	latestInfo, err := prox.Info(ctx, modulePath, version.Latest)
	if errors.Is(err, derrors.NotFound) || errors.Is(err, derrors.NotFetched) {
		// No information from the proxy, but not a showstopper either; we can
		// proceed with the result of the list endpoint.
	} else if err != nil {
		return nil, err
	} else {
		versions = append(versions, latestInfo.Version)
	}
	if len(versions) == 0 {
		// No tagged versions, and nothing from @latest: no version information.
		return nil, nil
	}
	rawLatest, err := version.LatestVersion(versions, hasGoModFunc)
	if err != nil {
		return nil, err
	}

	// Get the go.mod file at the raw latest version.
	modBytes, err := prox.Mod(ctx, modulePath, rawLatest)
	if err != nil {
		// Something's wrong with the go.mod file, so assume a minimal one instead of failing.
		log.Warningf(ctx, "proxy.Mod(%q, %q): %v; using minimal go.mod for latest version info",
			modulePath, rawLatest)
		modBytes = []byte(fmt.Sprintf("module %s", modulePath))
	}
	lmv, err := internal.NewLatestModuleVersions(modulePath, rawLatest, "", "", modBytes)
	if err != nil {
		// An error here means a bad go.mod file.
		return nil, fmt.Errorf("%v: %w", err, derrors.BadModule)
	}

	// Get the cooked latest version by disallowing retracted versions.
	unretractedVersions := version.RemoveIf(versions, lmv.IsRetracted)
	if len(versions) == len(unretractedVersions) {
		lmv.CookedVersion = lmv.RawVersion
	} else {
		lmv.CookedVersion, err = version.LatestVersion(unretractedVersions, hasGoModFunc)
		if err != nil {
			return nil, err
		}
	}
	return lmv, nil
}


text:
package org.griphyn.vdl.dax;

import java.io.IOException;
import java.io.Writer;

/**
 * Leaf is an abstract base class for leaf nodes in the instance tree.
 *
 * @author Jens-S. Vöckler
 * @author Yong Zhao
 * @version $Revision$
 * @see PseudoText
 * @see Filename
 */
public abstract class Leaf extends DAX implements Cloneable {
    /**
     * Creates and returns a copy of this object.
     *
     * @return a new instance.
     */
    public abstract Object clone();

    /**
     * Dumps the state of the filename as PlainFilenameType without the transiency information.
     * Other leaves will use their regular toXML method.
     *
     * @param indent is a String of spaces used for pretty printing. The initial amount
     *     of spaces should be an empty string. The parameter is used internally for the recursive
     *     traversal.
     * @param namespace is the XML schema namespace prefix. If neither empty nor null, each element
     *     will be prefixed with this prefix, and the root element will map the XML namespace.
     * @param flag if bit#0 is set, also dump the linkage information, otherwise do not dump linkage
     *     information. If bit#1 is set, also dump optionality for true optional files. Unused in
     *     base class.
     * @return a String which contains the state of the current class and its siblings using XML.
     *     Note that these strings might become large.
     * @see #toXML( String, String )
     */
    public String shortXML(String indent, String namespace, int flag) {
        return toXML(indent, namespace);
    }

    /**
     * Dumps the state of the filename as PlainFilenameType without the transiency information.
     * Other leaves will use their regular toXML method.
     *
     * @param stream is a stream opened and ready for writing. This can also be a string stream for
     *     efficient output.
     * @param indent is a String of spaces used for pretty printing. The initial amount
     *     of spaces should be an empty string. The parameter is used internally for the recursive
     *     traversal.
     * @param namespace is the XML schema namespace prefix. If neither empty nor null, each element
     *     will be prefixed with this prefix, and the root element will map the XML namespace.
     * @param flag if bit#0 is set, also dump the linkage information, otherwise do not dump linkage
     *     information. If bit#1 is set, also dump optionality for true optional files. Unused in
     *     base class.
     * @exception IOException if something fishy happens to the stream.
     * @see #toXML( Writer, String, String )
     */
    public void shortXML(Writer stream, String indent, String namespace, int flag)
            throws IOException {
        toXML(stream, indent, namespace);
    }
}


text:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/personalcenter"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="个人中心"/>
    <item
        android:id="@+id/changeAcount"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="切换帐号" />
    />
    <item
        android:id="@+id/search"
        android:icon="@android:drawable/ic_menu_search"
        android:showAsAction="always"
        android:title="搜索帖子"
        android:actionViewClass="android.widget.SearchView" />
    <item
        android:id="@+id/new_post"
        android:icon="@drawable/u13"
        android:showAsAction="always"
        android:title="发表帖子" />
</menu>