text: define([
          './Cartographic',
          './defined',
          './EllipsoidTangentPlane',
          './Math',
          './PolygonPipeline',
          './PolylinePipeline',
          './WindingOrder'
      ], function(
          Cartographic,
          defined,
          EllipsoidTangentPlane,
          CesiumMath,
          PolygonPipeline,
          PolylinePipeline,
          WindingOrder) {
      'use strict';
  
      /**
       * private
       */
      var WallGeometryLibrary = {};
  
      function latLonEquals(c0, c1) {
          return ((CesiumMath.equalsEpsilon(c0.latitude, c1.latitude, CesiumMath.EPSILON14)) && (CesiumMath.equalsEpsilon(c0.longitude, c1.longitude, CesiumMath.EPSILON14)));
      }
  
      var scratchCartographic1 = new Cartographic();
      var scratchCartographic2 = new Cartographic();
      function removeDuplicates(ellipsoid, positions, topHeights, bottomHeights) {
          var length = positions.length;
          if (length < 2) {
              return;
          }
  
          var hasBottomHeights = defined(bottomHeights);
          var hasTopHeights = defined(topHeights);
          var hasAllZeroHeights = true;
  
          var cleanedPositions = new Array(length);
          var cleanedTopHeights = new Array(length);
          var cleanedBottomHeights = new Array(length);
  
          var v0 = positions[0];
          cleanedPositions[0] = v0;
  
          var c0 = ellipsoid.cartesianToCartographic(v0, scratchCartographic1);
          if (hasTopHeights) {
              c0.height = topHeights[0];
          }
  
          hasAllZeroHeights = hasAllZeroHeights && c0.height <= 0;
  
          cleanedTopHeights[0] = c0.height;
  
          if (hasBottomHeights) {
              cleanedBottomHeights[0] = bottomHeights[0];
          } else {
              cleanedBottomHeights[0] = 0.0;
          }
  
          var index = 1;
          for (var i = 1; i < length; ++i) {
              var v1 = positions[i];
              var c1 = ellipsoid.cartesianToCartographic(v1, scratchCartographic2);
              if (hasTopHeights) {
                  c1.height = topHeights[i];
              }
              hasAllZeroHeights = hasAllZeroHeights && c1.height <= 0;
  
              if (!latLonEquals(c0, c1)) {
                  cleanedPositions[index] = v1; // Shallow copy!
                  cleanedTopHeights[index] = c1.height;
  
                  if (hasBottomHeights) {
                      cleanedBottomHeights[index] = bottomHeights[i];
                  } else {
                      cleanedBottomHeights[index] = 0.0;
                  }
  
                  Cartographic.clone(c1, c0);
                  ++index;
              } else if (c0.height < c1.height) {
                  cleanedTopHeights[index - 1] = c1.height;
              }
          }
  
          if (hasAllZeroHeights || index < 2) {
              return;
          }
  
          cleanedPositions.length = index;
          cleanedTopHeights.length = index;
          cleanedBottomHeights.length = index;
  
          return {
              positions: cleanedPositions,
              topHeights: cleanedTopHeights,
              bottomHeights: cleanedBottomHeights
          };
      }
  
      var positionsArrayScratch = new Array(2);
      var heightsArrayScratch = new Array(2);
      var generateArcOptionsScratch = {
          positions : undefined,
          height : undefined,
          granularity : undefined,
          ellipsoid : undefined
      };
  
      /**
       * @private
       */
      WallGeometryLibrary.computePositions = function(ellipsoid, wallPositions, maximumHeights, minimumHeights, granularity, duplicateCorners) {
          var o = removeDuplicates(ellipsoid, wallPositions, maximumHeights, minimumHeights);
  
          if (!defined(o)) {
              return;
          }
  
          wallPositions = o.positions;
          maximumHeights = o.topHeights;
          minimumHeights = o.bottomHeights;
  
          if (wallPositions.length >= 3) {
              // Order positions counter-clockwise
              var tangentPlane = EllipsoidTangentPlane.fromPoints(wallPositions, ellipsoid);
              var positions2D = tangentPlane.projectPointsOntoPlane(wallPositions);
  
              if (PolygonPipeline.computeWindingOrder2D(positions2D) === WindingOrder.CLOCKWISE) {
                  wallPositions.reverse();
                  maximumHeights.reverse();
                  minimumHeights.reverse();
              }
          }
  
          var length = wallPositions.length;
          var numCorners = length - 2;
          var topPositions;
          var bottomPositions;
  
          var minDistance = CesiumMath.chordLength(granularity, ellipsoid.maximumRadius);
  
          var generateArcOptions = generateArcOptionsScratch;
          generateArcOptions.minDistance = minDistance;
          generateArcOptions.ellipsoid = ellipsoid;
  
          if (duplicateCorners) {
              var count = 0;
              var i;
  
              for (i = 0; i < length - 1; i++) {
                  count += PolylinePipeline.numberOfPoints(wallPositions[i], wallPositions[i+1], minDistance) + 1;
              }
  
              topPositions = new Float64Array(count * 3);
              bottomPositions = new Float64Array(count * 3);
  
              var generateArcPositions = positionsArrayScratch;
              var generateArcHeights = heightsArrayScratch;
              generateArcOptions.positions = generateArcPositions;
              generateArcOptions.height = generateArcHeights;
  
              var offset = 0;
              for (i = 0; i < length - 1; i++) {
                  generateArcPositions[0] = wallPositions[i];
                  generateArcPositions[1] = wallPositions[i + 1];
  
                  generateArcHeights[0] = maximumHeights[i];
                  generateArcHeights[1] = maximumHeights[i + 1];
  
                  var pos = PolylinePipeline.generateArc(generateArcOptions);
                  topPositions.set(pos, offset);
  
                  generateArcHeights[0] = minimumHeights[i];
                  generateArcHeights[1] = minimumHeights[i + 1];
  
                  bottomPositions.set(PolylinePipeline.generateArc(generateArcOptions), offset);
  
                  offset += pos.length;
              }
          } else {
              generateArcOptions.positions = wallPositions;
              generateArcOptions.height = maximumHeights;
              topPositions = new Float64Array(PolylinePipeline.generateArc(generateArcOptions));
  
              generateArcOptions.height = minimumHeights;
              bottomPositions = new Float64Array(PolylinePipeline.generateArc(generateArcOptions));
          }
  
          return {
              bottomPositions: bottomPositions,
              topPositions: topPositions,
              numCorners: numCorners
          };
      };
  
      return WallGeometryLibrary;
  });
  