import algorithm
import tables

const None = -1   # Index number used to indicate no data.

type

  Point = array[3, float]
  Face = seq[int]

  Edge = object
    pn1: int    # Point number 1.
    pn2: int    # Point number 2.
    fn1: int    # Face number 1.
    fn2: int    # Face number 2.
    cp: Point   # Center point.

  PointEx = object
    p: Point
    n: int

  PointNums = tuple[pn1, pn2: int]

####################################################################################################
# Point operations.

func `+`(p1, p2: Point): Point =
  ## Adds points p1 and p2.
  for i in 0..Point.high:
    result[i] = p1[i] + p2[i]

#---------------------------------------------------------------------------------------------------

func `*`(p: Point; m: float): Point =
  ##  Multiply point p by m.
  for i in 0..Point.high:
    result[i] = p[i] * m

#---------------------------------------------------------------------------------------------------

func `/`(p: Point; d: float): Point =
  ## Divide point p by d.
  for i in 0..Point.high:
    result[i] = p[i] / d

#---------------------------------------------------------------------------------------------------

func centerPoint(p1, p2: Point): Point =
  ## Return a point in the center of the segment ended by points p1 and p2.
  for i in 0..Point.high:
    result[i] = (p1[i] + p2[i]) / 2

####################################################################################################

func getFacePoints(inputPoints: seq[Point]; inputFaces: seq[Face]): seq[Point] =
  ## For each face, a face point is created which is the average of all the points of the face.

  result.setLen(inputFaces.len)
  for i, currFace in inputFaces:
    var facePoint: Point
    for currPointIndex in currFace:
      # Add currPoint to facePoint. Will divide later.
      facePoint = facePoint + inputPoints[currPointIndex]
    result[i] = facePoint / currFace.len.toFloat

#---------------------------------------------------------------------------------------------------

func getEdgesFaces(inputPoints: seq[Point]; inputFaces: seq[Face]): seq[Edge] =
  ## Get list of edges and the one or two adjacent faces in a list.
  ## Also get center point of edge.

  # Get edges for each face.
  var edges: seq[array[3, int]]
  for faceNum, face in inputFaces:
    # Loop over index into face.
    for pointIndex in 0..face.high:
      # If not last point then edge is current point and next point
      # and for last point, edge is current point and first point.
      var pointNum1 = face[pointIndex]
      var pointNum2 = if pointIndex < face.high: face[pointIndex + 1] else: face[0]
      # Order points in edge by lowest point number.
      if pointNum1 > pointNum2:
        swap pointNum1, pointNum2
      edges &= [pointNum1, pointNum2, faceNum]

  # Sort edges by pointNum1, pointNum2, faceNum.
  edges.sort(proc(a1, a2: array[3, int]): int =
                  result = cmp(a1[0], a2[0])
                  if result == 0:
                    result = cmp(a1[1], a2[1])
                    if result == 0:
                      result = cmp(a1[2], a2[2]))

  # Merge edges with 2 adjacent faces:
  # [pointNum1, pointNum2, faceNum1, faceNum2] or
  # [pointNum1, pointNum2, faceNum1, None]
  var eIndex = 0
  var mergedEdges: seq[array[4, int]]
  while eIndex < edges.len:
    let e1 = edges[eIndex]
    # Check if not last edge.
    if eIndex < edges.high:
      let e2 = edges[eIndex + 1]
      if e1[0] == e2[0] and e1[1] == e2[1]:
        mergedEdges &= [e1[0], e1[1], e1[2], e2[2]]
        inc eIndex, 2
      else:
        mergedEdges &= [e1[0], e1[1], e1[2], None]
        inc eIndex
    else:
      mergedEdges &= [e1[0], e1[1], e1[2], None]
      inc eIndex

  # Add edge centers.
  for me in mergedEdges:
    let p1 = inputPoints[me[0]]
    let p2 = inputPoints[me[1]]
    let cp = centerPoint(p1, p2)
    result.add(Edge(pn1: me[0], pn2: me[1], fn1: me[2], fn2: me[3], cp: cp))

#---------------------------------------------------------------------------------------------------

func getEdgePoints(inputPoints: seq[Point];
                   edgesFaces: seq[Edge];
                   facePoints: seq[Point]): seq[Point] =
  ## For each edge, an edge point is created which is the average between the center of the
  ## edge and the center of the segment made with the face points of the two adjacent faces.

  result.setLen(edgesFaces.len)
  for i, edge in edgesFaces:
    # Get center of two facepoints.
    let fp1 = facePoints[edge.fn1]
    # If not two faces, just use one facepoint (should not happen for solid like a cube).
    let fp2 = if edge.fn2 == None: fp1 else: facePoints[edge.fn2]
    let cfp = centerPoint(fp1, fp2)
    # Get average between center of edge and center of facePoints.
    result[i] = centerPoint(edge.cp, cfp)

#---------------------------------------------------------------------------------------------------

func getAvgFacePoints(inputPoints: seq[Point];
                      inputFaces: seq[Face];
                      facePoints: seq[Point]): seq[Point] =
    ## For each point calculate the average of the face points of the faces the
    ## point belongs to (avgFacePoints), create a list of lists of two numbers
    ## [facePointSum, numPoints] by going through the points in all the faces then
    ## create the avgFacePoints list of point by dividing pointSum(x, y, z) by numPoints

    var tempPoints = newSeq[PointEx](inputPoints.len)

    # Loop through faces, updating tempPoints.
    for faceNum, pointNums in inputFaces:
      let fp = facePoints[faceNum]
      for pointNum in pointNums:
        let tp = tempPoints[pointNum].p
        tempPoints[pointNum].p = tp + fp
        inc tempPoints[pointNum].n

    # Divide to build the result.
    result.setLen(inputPoints.len)
    for i, tp in tempPoints:
      result[i] = tp.p / tp.n.toFloat

#---------------------------------------------------------------------------------------------------

func getAvgMidEdges(inputPoints: seq[Point]; edgesFaces: seq[Edge]): seq[Point] =
  ## Return the average of the centers of edges the point belongs to (avgMidEdges).
  ## Create list with entry for each point. Each entry has two elements. One is a point
  ## that is the sum of the centers of the edges and the other is the number of edges.
  ## After going through all edges divide by number of edges.

  var tempPoints = newSeq[PointEx](inputPoints.len)

  # Go through edgesFaces using center updating each point.
  for edge in edgesFaces:
    for pointNum in [edge.pn1, edge.pn2]:
      let tp = tempPoints[pointNum].p
      tempPoints[pointNum].p = tp + edge.cp
      inc tempPoints[pointNum].n

    # Divide out number of points to get average.
    result.setLen(inputPoints.len)
    for i, tp in tempPoints:
      result[i] = tp.p / tp.n.toFloat

#---------------------------------------------------------------------------------------------------

func getPointsFaces(inputPoints: seq[Point]; inputFaces: seq[Face]): seq[int] =
  # Return the number of faces for each point.

  result.setLen(inputPoints.len)

  # Loop through faces updating the result.
  for pointNums in inputFaces:
    for pointNum in pointNums:
      inc result[pointNum]

#---------------------------------------------------------------------------------------------------

func getNewPoints(inputPoints: seq[Point]; pointsFaces: seq[int];
                  avgFacePoints, avgMidEdges: seq[Point]): seq[Point] =
  ## m1 = (n - 3.0) / n
  ## m2 = 1.0 / n
  ## m3 = 2.0 / n
  ## newCoords = (m1 * oldCoords) + (m2 * avgFacePoints) + (m3 * avgMidEdges)

  result.setLen(inputPoints.len)
  for pointNum, oldCoords in inputPoints:
    let n = pointsFaces[pointNum].toFloat
    let (m1, m2, m3) = ((n - 3) / n, 1 / n, 2 / n)
    let p1 = oldCoords * m1
    let afp = avgFacePoints[pointNum]
    let p2 = afp * m2
    let ame = avgMidEdges[pointNum]
    let p3 = ame * m3
    let p4 = p1 + p2
    result[pointNum] = p4 + p3

#---------------------------------------------------------------------------------------------------

func switchNums(pointNums: PointNums): PointNums =
  ## Return tuple of point numbers sorted least to most.

  if pointNums.pn1 < pointNums.pn2: pointNums
  else: (pointNums.pn2, pointNums.pn1)

#---------------------------------------------------------------------------------------------------

func cmcSubdiv(inputPoints: seq[Point];
               inputFaces: seq[Face]): tuple[p: seq[Point], f: seq[Face]] =
  ## For each face, a face point is created which is the average of all the points of the face.
  ## Each entry in the returned list is a point (x, y, z).

  let facePoints = getFacePoints(inputPoints, inputFaces)
  let edgesFaces = getEdgesFaces(inputPoints, inputFaces)
  let edgePoints = getEdgePoints(inputPoints, edgesFaces, facePoints)
  let avgFacePoints = getAvgFacePoints(inputPoints, inputFaces, facePoints)
  let avgMidEdges = getAvgMidEdges(inputPoints, edgesFaces)
  let pointsFaces = getPointsFaces(inputPoints, inputFaces)
  var newPoints = getNewPoints(inputPoints, pointsFaces, avgFacePoints, avgMidEdges)

  #[Then each face is replaced by new faces made with the new points,

    for a triangle face (a,b,c):
       (a, edgePoint ab, facePoint abc, edgePoint ca)
       (b, edgePoint bc, facePoint abc, edgePoint ab)
       (c, edgePoint ca, facePoint abc, edgePoint bc)

    for a quad face (a,b,c,d):
       (a, edgePoint ab, facePoint abcd, edgePoint da)
       (b, edgePoint bc, facePoint abcd, edgePoint ab)
       (c, edgePoint cd, facePoint abcd, edgePoint bc)
       (d, edgePoint da, facePoint abcd, edgePoint cd)

    facePoints is a list indexed by face number so that is easy to get.

    edgePoints is a list indexed by the edge number which is an index into edgesFaces.

    Need to add facePoints and edgePoints to newPoints and get index into each.

    Then create two new structures:

    facePointNums: list indexes by faceNum whose value is the index into newPoints.

    edgePointNums: dictionary with key (pointNum1, pointNum2) and value is index into newPoints.
  ]#

  # Add face points to newPoints.
  var facePointNums: seq[int]
  var nextPointNum = newPoints.len    # PointNum after next append to newPoints.
  for facePoint in facePoints:
    newPoints.add(facePoint)
    facePointNums.add(nextPointNum)
    inc nextPointNum

  # Add edge points to newPoints.
  var edgePointNums: Table[tuple[pn1, pn2: int], int]
  for edgeNum, edgesFace in edgesFaces:
    let pointNum1 = edgesFace.pn1
    let pointNum2 = edgesFace.pn2
    newPoints.add(edgePoints[edgeNum])
    edgePointNums[(pointNum1, pointNum2)] = nextPointNum
    inc nextPointNum

  # newPoints now has the points to output. Need new faces.

  #[Just doing this case for now:

    for a quad face (a,b,c,d):
       (a, edgePoint ab, facePoint abcd, edgePoint da)
       (b, edgePoint bc, facePoint abcd, edgePoint ab)
       (c, edgePoint cd, facePoint abcd, edgePoint bc)
       (d, edgePoint da, facePoint abcd, edgePoint cd)

    newFaces will be a list of lists where the elements are like this:
    [pointNum1, pointNum2, pointNum3, pointNum4]
  ]#

  var newFaces: seq[Face]
  for oldFaceNum, oldFace in inputFaces:
    # 4 points face.
    if oldFace.len == 4:
      let (a, b, c, d) = (oldFace[0], oldface[1], oldface[2], oldface[3])
      let facePointAbcd = facePointNums[oldFaceNum]
      let edgePointAb = edgePointNums[switchNums((a, b))]
      let edgePointDa = edgePointNums[switchNums((d, a))]
      let edgePointBc = edgePointNums[switchNums((b, c))]
      let edgePointCd = edgePointNums[switchNums((c, d))]
      newFaces &= @[a, edgePointAb, facePointAbcd, edgePointDa]
      newFaces &= @[b, edgePointBc, facePointAbcd, edgePointAb]
      newFaces &= @[c, edgePointCd, facePointAbcd, edgePointBc]
      newFaces &= @[d, edgePointDa, facePointAbcd, edgePointCd]

    result = (newPoints, newFaces)

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

  import strformat, strutils

  let inputPoints = @[[-1.0, 1.0, 1.0],
                      [-1.0, -1.0, 1.0],
                      [1.0, -1.0, 1.0],
                      [1.0, 1.0, 1.0],
                      [1.0, -1.0, -1.0],
                      [1.0, 1.0, -1.0],
                      [-1.0, -1.0, -1.0],
                      [-1.0, 1.0, -1.0]]

  let inputFaces = @[@[0, 1, 2, 3],
                     @[3, 2, 4, 5],
                     @[5, 4, 6, 7],
                     @[7, 0, 3, 5],
                     @[7, 6, 1, 0],
                     @[6, 1, 2, 4]]

  var outputPoints = inputPoints
  var outputFaces = inputFaces
  const Iterations = 1

  for i in 1..Iterations:
    (outputPoints, outputFaces) = cmcSubdiv(outputPoints, outputFaces)

  for p in outputPoints:
    echo fmt"[{p[0]: .4f}, {p[1]: .4f}, {p[2]: .4f}]"
  echo ""
  for nums in outputFaces:
    var s = "["
    for n in nums:
      s.addSep(", ", 1)
      s.add(fmt"{n:2d}")
    s.add(']')
    echo s


open Dynar

let add3 (x1, y1, z1) (x2, y2, z2) (x3, y3, z3) =
  ( (x1 +. x2 +. x3),
    (y1 +. y2 +. y3),
    (z1 +. z2 +. z3) )

let mul m (x,y,z) = (m *. x, m *. y, m *. z)

let avg pts =
  let n, (x,y,z) =
    List.fold_left
      (fun (n, (xt,yt,zt)) (xi,yi,zi) ->
         succ n, (xt +. xi, yt +. yi, zt +. zi))
      (1, List.hd pts) (List.tl pts)
  in
  let n = float_of_int n in
  (x /. n, y /. n, z /. n)

let catmull ~points ~faces =
  let da_points = Dynar.of_array points in
  let new_faces = Dynar.of_array [| |] in
  let push_face face = Dynar.push new_faces face in
  let h1 = Hashtbl.create 43 in
  let h2 = Hashtbl.create 43 in
  let h3 = Hashtbl.create 43 in
  let h4 = Hashtbl.create 43 in
  let blg = Array.make (Array.length points) 0 in (* how many faces a point belongs to *)
  let f_incr p = blg.(p) <- succ blg.(p) in
  let eblg = Array.make (Array.length points) 0 in (* how many edges a point belongs to *)
  let e_incr p = eblg.(p) <- succ eblg.(p) in
  let edge a b = (min a b, max a b) in  (* suitable for hash-table keys *)
  let mid_edge p1 p2 =
    let x1, y1, z1 = points.(p1)
    and x2, y2, z2 = points.(p2) in
    ( (x1 +. x2) /. 2.0,
      (y1 +. y2) /. 2.0,
      (z1 +. z2) /. 2.0 )
  in
  let mid_face p1 p2 p3 p4 =
    let x1, y1, z1 = points.(p1)
    and x2, y2, z2 = points.(p2)
    and x3, y3, z3 = points.(p3)
    and x4, y4, z4 = points.(p4) in
    ( (x1 +. x2 +. x3 +. x4) /. 4.0,
      (y1 +. y2 +. y3 +. y4) /. 4.0,
      (z1 +. z2 +. z3 +. z4) /. 4.0 )
  in
  Array.iteri (fun i (a,b,c,d) ->
    f_incr a; f_incr b; f_incr c; f_incr d;

    let face_point = mid_face a b c d in
    let face_pi = pushi da_points face_point in
    Hashtbl.add h3 a face_point;
    Hashtbl.add h3 b face_point;
    Hashtbl.add h3 c face_point;
    Hashtbl.add h3 d face_point;

    let process_edge a b =
      let ab = edge a b in
      if not(Hashtbl.mem h1 ab)
      then begin
        let mid_ab = mid_edge a b in
        let index = pushi da_points mid_ab in
        Hashtbl.add h1 ab (index, mid_ab, [face_point]);
        Hashtbl.add h2 a mid_ab;
        Hashtbl.add h2 b mid_ab;
        Hashtbl.add h4 mid_ab 1;
        (index)
      end
      else begin
        let index, mid_ab, fpl = Hashtbl.find h1 ab in
        Hashtbl.replace h1 ab (index, mid_ab, face_point::fpl);
        Hashtbl.add h4 mid_ab (succ(Hashtbl.find h4 mid_ab));
        (index)
      end
    in
    let mid_ab = process_edge a b
    and mid_bc = process_edge b c
    and mid_cd = process_edge c d
    and mid_da = process_edge d a in

    push_face (a, mid_ab, face_pi, mid_da);
    push_face (b, mid_bc, face_pi, mid_ab);
    push_face (c, mid_cd, face_pi, mid_bc);
    push_face (d, mid_da, face_pi, mid_cd);
  ) faces;

  Hashtbl.iter (fun (a,b) (index, mid_ab, fpl) ->
    e_incr a; e_incr b;
    if List.length fpl = 2 then
      da_points.ar.(index) <- avg (mid_ab::fpl)
  ) h1;

  Array.iteri (fun i old_vertex ->
    let n = blg.(i)
    and e_n = eblg.(i) in
    (* if the vertex doesn't belongs to as many faces than edges
       this means that this is a hole *)
    if n = e_n then
    begin
      let avg_face_points =
        let face_point_list = Hashtbl.find_all h3 i in
        (avg face_point_list)
      in
      let avg_mid_edges =
        let mid_edge_list = Hashtbl.find_all h2 i in
        (avg mid_edge_list)
      in
      let n = float_of_int n in
      let m1 = (n -. 3.0) /. n
      and m2 = 1.0 /. n
      and m3 = 2.0 /. n in
      da_points.ar.(i) <-
          add3 (mul m1 old_vertex)
               (mul m2 avg_face_points)
               (mul m3 avg_mid_edges)
    end
    else begin
      let mid_edge_list = Hashtbl.find_all h2 i in
      let mid_edge_list =
        (* only average mid-edges near the hole *)
        List.fold_left (fun acc mid_edge ->
          match Hashtbl.find h4 mid_edge with
          | 1 -> mid_edge::acc
          | _ -> acc
        ) [] mid_edge_list
      in
      da_points.ar.(i) <- avg (old_vertex :: mid_edge_list)
    end
  ) points;

  (Dynar.to_array da_points,
   Dynar.to_array new_faces)
;;


(let (dog "Benjamin"  Dog "Samba"  DOG "Bernie")
   (prinl "The three dogs are named " dog ", " Dog " and " DOG) )


To run:
Start up.
Put "Benjamin" into a DOG string.
Put "Samba" into the Dog string.
Put "Bernie" into the dog string.
Write "There is just one dog named " then the DOG on the console.
Wait for the escape key.
Shut down.


$dog = "Benjamin"
$Dog = "Samba"
$DOG = "Bernie"

"There is just one dog named {0}." -f $dOg


three_dogs :-
	DoG = 'Benjamin',
	Dog = 'Samba',
	DOG = 'Bernie',
	format('The three dogs are named ~w, ~w and ~w.~n', [DoG, Dog, DOG]).


dog$="Benjamin"
Dog$="Samba"
DOG$="Bernie"
Debug "There is just one dog named "+dog$


-- demo\rosetta\Catmull_Clark_subdivision_surface.exw
with javascript_semantics
function newPoint() return {0,0,0} end function
function newPointEx() return {newPoint(),0} end function

function centerPoint(sequence p1, p2)
    return sq_div(sq_add(p1, p2), 2)
end function

function getFacePoints(sequence inputPoints, inputFaces)
    sequence facePoints = repeat(0,length(inputFaces))
    for f=1 to length(inputFaces) do
        sequence currFace = inputFaces[f],
                 facePoint = inputPoints[currFace[1]]
        for i=2 to length(currFace) do
            facePoint = sq_add(facePoint, inputPoints[currFace[i]])
        end for
        facePoints[f] = sq_div(facePoint, length(currFace))
    end for
    return facePoints
end function

function getEdgesFaces(sequence inputPoints, inputFaces)
    sequence edges = {}
    for faceNum=1 to length(inputFaces) do
        sequence face = inputFaces[faceNum]
        integer numPoints = length(face)
        for pointIndex=1 to numPoints do
            integer pointNum1 = face[pointIndex],
                    pointNum2 = face[iff(pointIndex<=numPoints-1?pointIndex+1:1)]
            if pointNum1 > pointNum2 then
                {pointNum1, pointNum2} = {pointNum2, pointNum1}
            end if
            edges = append(edges, {pointNum1, pointNum2, faceNum})
        end for
    end for
    edges = sort(edges)
    integer numEdges = length(edges),
            eIndex = 1
    sequence mergedEdges ={}
    while eIndex<=numEdges do
        sequence e1 = deep_copy(edges[eIndex])
        integer e4 = -1
        if eIndex<=numEdges-1
        and e1[1..2]==edges[eIndex+1][1..2] then
            e4 = deep_copy(edges[eIndex+1][3])
            eIndex += 1
        end if
        e1 &= e4
        mergedEdges = append(mergedEdges, e1)
        eIndex += 1
    end while
    sequence edgesCenters = {}
    for i=1 to length(mergedEdges) do
        sequence me = deep_copy(mergedEdges[i]),
                 cp = centerPoint(inputPoints[me[1]],
                                  inputPoints[me[2]])
        me = append(me,cp)
        edgesCenters = append(edgesCenters, me)
    end for
    return edgesCenters
end function

function getEdgePoints(sequence inputPoints, edgesFaces, facePoints)
    sequence edgePoints = repeat(0, length(edgesFaces))
    for i=1 to length(edgesFaces) do
        sequence edge = edgesFaces[i],
                 cp = edge[5],
                 fp1 = facePoints[edge[3]],
                 fp2 = iff(edge[4]=-1?fp1:facePoints[edge[4]]),
                 cfp = centerPoint(fp1, fp2)
        edgePoints[i] = centerPoint(cp, cfp)
    end for
    return edgePoints
end function

function getAvgFacePoints(sequence inputPoints, inputFaces, facePoints)
    integer numPoints = length(inputPoints)
    sequence tempPoints = repeat(newPointEx(),numPoints)
    for faceNum=1 to length(inputFaces) do
        sequence fp = facePoints[faceNum]
        for i=1 to length(inputFaces[faceNum]) do
            integer pointNum = inputFaces[faceNum][i]
            sequence tp = tempPoints[pointNum][1]
            tempPoints[pointNum][1] = sq_add(tp, fp)
            tempPoints[pointNum][2] += 1
        end for
    end for
    sequence avgFacePoints = repeat(0, numPoints)
    for i=1 to length(tempPoints) do
        sequence tp = tempPoints[i]
        avgFacePoints[i] = sq_div(tp[1], tp[2])
    end for
    return avgFacePoints
end function

function getAvgMidEdges(sequence inputPoints, edgesFaces)
    integer numPoints = length(inputPoints)
    sequence tempPoints = repeat(newPointEx(), numPoints)
    for i=1 to length(edgesFaces) do
        sequence edge = edgesFaces[i],
                 cp = edge[5]
        for edx=1 to 2 do
            integer pointNum = edge[edx]
            sequence tp = tempPoints[pointNum][1]
            tempPoints[pointNum][1] = sq_add(tp, cp)
            tempPoints[pointNum][2] += 1
        end for
    end for
    sequence avgMidEdges = repeat(0, length(tempPoints))
    for i=1 to length(tempPoints) do
        sequence tp = tempPoints[i]
        avgMidEdges[i] = sq_div(tp[1], tp[2])
    end for
    return avgMidEdges
end function

function getPointsFaces(sequence inputPoints, inputFaces)
    integer numPoints = length(inputPoints)
    sequence pointsFaces = repeat(0, numPoints)
    for faceNum=1 to length(inputFaces) do
        for i=1 to length(inputFaces[faceNum]) do
            integer pointNum = inputFaces[faceNum][i]
            pointsFaces[pointNum] += 1
        end for
    end for
    return pointsFaces
end function

function getNewPoints(sequence inputPoints, pointsFaces, avgFacePoints, avgMidEdges)
    sequence newPoints = repeat(0, length(inputPoints))
    for pointNum=1 to length(inputPoints) do
        integer n = pointsFaces[pointNum]
        sequence p1 = sq_mul(inputPoints[pointNum], (n-3)/n),
                 p2 = sq_mul(avgFacePoints[pointNum], 1/n),
                 p3 = sq_mul(avgMidEdges[pointNum], 2/n)
        newPoints[pointNum] = sq_add(sq_add(p1, p2), p3)
    end for
    return newPoints
end function

function switchNums(sequence pointNums)
    if pointNums[1] < pointNums[2] then
        return pointNums
    end if
    return {pointNums[2], pointNums[1]}
end function

function cmcSubdiv(sequence inputPoints, inputFaces)
    sequence facePoints = getFacePoints(inputPoints, inputFaces),
             edgesFaces = getEdgesFaces(inputPoints, inputFaces),
             edgePoints = getEdgePoints(inputPoints, edgesFaces, facePoints),
             avgFacePoints = getAvgFacePoints(inputPoints, inputFaces, facePoints),
             avgMidEdges = getAvgMidEdges(inputPoints, edgesFaces),
             pointsFaces = getPointsFaces(inputPoints, inputFaces),
             newPoints = getNewPoints(inputPoints, pointsFaces, avgFacePoints, avgMidEdges),
             facePointNums = {}
    integer nextPointNum = length(newPoints)+1
    for i=1 to length(facePoints) do
        sequence facePoint = facePoints[i]
        newPoints = append(newPoints, facePoint)
        facePointNums = append(facePointNums, nextPointNum)
        nextPointNum += 1
    end for
    integer edgePointNums = new_dict()
    for edgeNum=1 to length(edgesFaces) do
        integer {pointNum1,pointNum2} = edgesFaces[edgeNum]
        sequence edgePoint = edgePoints[edgeNum]
        newPoints = append(newPoints, edgePoint)
        setd({pointNum1, pointNum2},nextPointNum,edgePointNums)
        nextPointNum += 1
    end for
    sequence newFaces = {}
    for oldFaceNum=1 to length(inputFaces) do
        sequence oldFace = inputFaces[oldFaceNum]
        if length(oldFace) == 4 then
            integer {a, b, c, d} := oldFace,
                    facePointAbcd := facePointNums[oldFaceNum],
                    edgePointAb := getd(switchNums({a, b}),edgePointNums),
                    edgePointDa := getd(switchNums({d, a}),edgePointNums),
                    edgePointBc := getd(switchNums({b, c}),edgePointNums),
                    edgePointCd := getd(switchNums({c, d}),edgePointNums)
            newFaces = append(newFaces, {a, edgePointAb, facePointAbcd, edgePointDa})
            newFaces = append(newFaces, {b, edgePointBc, facePointAbcd, edgePointAb})
            newFaces = append(newFaces, {c, edgePointCd, facePointAbcd, edgePointBc})
            newFaces = append(newFaces, {d, edgePointDa, facePointAbcd, edgePointCd})
        end if
    end for
    return {newPoints, newFaces}
end function

constant inputPoints = {{-1, 1, 1},
                        {-1,-1, 1},
                        { 1,-1, 1},
                        { 1, 1, 1},
                        { 1,-1,-1},
                        { 1, 1,-1},
                        {-1,-1,-1},
                        {-1, 1,-1}},

          inputFaces = {{0, 1, 2, 3},
                        {3, 2, 4, 5},
                        {5, 4, 6, 7},
                        {7, 0, 3, 5},
                        {7, 6, 1, 0},
                        {6, 1, 2, 4}}

sequence outputPoints = inputPoints,
          outputFaces = sq_add(inputFaces,1)
integer iterations = 1
for i=1 to iterations do
    {outputPoints, outputFaces} = cmcSubdiv(outputPoints, outputFaces)
end for
ppOpt({pp_Nest,1,pp_FltFmt,"%7.4f",pp_IntFmt,"%7.4f",pp_IntCh,false})
pp(outputPoints)
pp(sq_sub(outputFaces,1),{pp_IntFmt,"%2d"})


>>> dog = 'Benjamin'; Dog = 'Samba'; DOG = 'Bernie'
>>> print ('The three dogs are named ',dog,', ',Dog,', and ',DOG)
The three dogs are named  Benjamin ,  Samba , and  Bernie
>>>


[ $ 'Benjamin' ] is dog ( --> $ )

[ $ 'Samba' ]    is Dog ( --> $ )

[ $ 'Bernie' ]   is DOG ( --> $ )

say 'There are three dogs named '
dog echo$ say ', '
Dog echo$ say ', and '
DOG echo$ say '.' cr


dog <- 'Benjamin'
Dog <- 'Samba'
DOG <- 'Bernie'

# Having fun with cats and dogs
cat('The three dogs are named ')
cat(dog)
cat(', ')
cat(Dog)
cat(' and ')
cat(DOG)
cat('.\n')
# In one line it would be:
# cat('The three dogs are named ', dog, ', ', Dog, ' and ', DOG, '.\n', sep = '')


#lang racket
(define dog "Benjamin")
(define Dog "Samba")
(define DOG "Bernie")

(if (equal? dog DOG)
    (displayln (~a "There is one dog named " DOG "."))
    (displayln (~a "The three dogs are named " dog ", " Dog ", and, " DOG ".")))


my $dog = 'Benjamin';
my $Dog = 'Samba';
my $DOG = 'Bernie';
say "The three dogs are named $dog, $Dog, and $DOG."


: dog  ( -$ )  "Benjamin" ;
: Dog  ( -$ )  "Samba" ;
: DOG  ( -$ )  "Bernie" ;

DOG Dog dog "The three dogs are named %s, %s, and %s.\n" puts


/*REXX program demonstrate  case insensitivity  for  simple  REXX  variable names.      */

  /*  ┌──◄── all 3 left─hand side REXX variables are identical (as far as assignments). */
  /*  │                                                                                */
  /*  ↓                                                                                */
     dog= 'Benjamin'                             /*assign a   lowercase   variable (dog)*/
     Dog= 'Samba'                                /*   "   "  capitalized     "      Dog */
     DOG= 'Bernie'                               /*   "   an  uppercase      "      DOG */

                              say center('using simple variables', 35, "─")     /*title.*/
                              say

if dog\==Dog | DOG\==dog  then say 'The three dogs are named:'     dog"," Dog 'and' DOG"."
                          else say 'There is just one dog named:'  dog"."

                                                 /*stick a fork in it,  we're all done. */


"""

Input and output are assumed to be in this form based on the talk
page for the task:

input_points = [
  [-1.0,  1.0,  1.0],
  [-1.0, -1.0,  1.0],
  [ 1.0, -1.0,  1.0],
  [ 1.0,  1.0,  1.0],
  [ 1.0, -1.0, -1.0],
  [ 1.0,  1.0, -1.0],
  [-1.0, -1.0, -1.0],
  [-1.0,  1.0, -1.0]
]

input_faces = [
  [0, 1, 2, 3],
  [3, 2, 4, 5],
  [5, 4, 6, 7],
  [7, 0, 3, 5],
  [7, 6, 1, 0],
  [6, 1, 2, 4],
]

So, the graph is a list of points and a list of faces.
Each face is a list of indexes into the points list.

"""

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
import numpy as np
import sys

def center_point(p1, p2):
    """
    returns a point in the center of the
    segment ended by points p1 and p2
    """
    cp = []
    for i in range(3):
        cp.append((p1[i]+p2[i])/2)

    return cp

def sum_point(p1, p2):
    """
    adds points p1 and p2
    """
    sp = []
    for i in range(3):
        sp.append(p1[i]+p2[i])

    return sp

def div_point(p, d):
    """
    divide point p by d
    """
    sp = []
    for i in range(3):
        sp.append(p[i]/d)

    return sp

def mul_point(p, m):
    """
    multiply point p by m
    """
    sp = []
    for i in range(3):
        sp.append(p[i]*m)

    return sp

def get_face_points(input_points, input_faces):
    """
    From http://rosettacode.org/wiki/Catmull%E2%80%93Clark_subdivision_surface

    1. for each face, a face point is created which is the average of all the points of the face.
    """

    # 3 dimensional space

    NUM_DIMENSIONS = 3

    # face_points will have one point for each face

    face_points = []

    for curr_face in input_faces:
        face_point = [0.0, 0.0, 0.0]
        for curr_point_index in curr_face:
            curr_point = input_points[curr_point_index]
            # add curr_point to face_point
            # will divide later
            for i in range(NUM_DIMENSIONS):
                face_point[i] += curr_point[i]
        # divide by number of points for average
        num_points = len(curr_face)
        for i in range(NUM_DIMENSIONS):
            face_point[i] /= num_points
        face_points.append(face_point)

    return face_points

def get_edges_faces(input_points, input_faces):
    """

    Get list of edges and the one or two adjacent faces in a list.
    also get center point of edge

    Each edge would be [pointnum_1, pointnum_2, facenum_1, facenum_2, center]

    """

    # will have [pointnum_1, pointnum_2, facenum]

    edges = []

    # get edges from each face

    for facenum in range(len(input_faces)):
        face = input_faces[facenum]
        num_points = len(face)
        # loop over index into face
        for pointindex in range(num_points):
            # if not last point then edge is curr point and next point
            if pointindex < num_points - 1:
                pointnum_1 = face[pointindex]
                pointnum_2 = face[pointindex+1]
            else:
                # for last point edge is curr point and first point
                pointnum_1 = face[pointindex]
                pointnum_2 = face[0]
            # order points in edge by lowest point number
            if pointnum_1 > pointnum_2:
                temp = pointnum_1
                pointnum_1 = pointnum_2
                pointnum_2 = temp
            edges.append([pointnum_1, pointnum_2, facenum])

    # sort edges by pointnum_1, pointnum_2, facenum

    edges = sorted(edges)

    # merge edges with 2 adjacent faces
    # [pointnum_1, pointnum_2, facenum_1, facenum_2] or
    # [pointnum_1, pointnum_2, facenum_1, None]

    num_edges = len(edges)
    eindex = 0
    merged_edges = []

    while eindex < num_edges:
        e1 = edges[eindex]
        # check if not last edge
        if eindex < num_edges - 1:
            e2 = edges[eindex+1]
            if e1[0] == e2[0] and e1[1] == e2[1]:
                merged_edges.append([e1[0],e1[1],e1[2],e2[2]])
                eindex += 2
            else:
                merged_edges.append([e1[0],e1[1],e1[2],None])
                eindex += 1
        else:
            merged_edges.append([e1[0],e1[1],e1[2],None])
            eindex += 1

    # add edge centers

    edges_centers = []

    for me in merged_edges:
        p1 = input_points[me[0]]
        p2 = input_points[me[1]]
        cp = center_point(p1, p2)
        edges_centers.append(me+[cp])

    return edges_centers

def get_edge_points(input_points, edges_faces, face_points):
    """
    for each edge, an edge point is created which is the average
    between the center of the edge and the center of the segment made
    with the face points of the two adjacent faces.
    """

    edge_points = []

    for edge in edges_faces:
        # get center of edge
        cp = edge[4]
        # get center of two facepoints
        fp1 = face_points[edge[2]]
        # if not two faces just use one facepoint
        # should not happen for solid like a cube
        if edge[3] == None:
            fp2 = fp1
        else:
            fp2 = face_points[edge[3]]
        cfp = center_point(fp1, fp2)
        # get average between center of edge and
        # center of facepoints
        edge_point = center_point(cp, cfp)
        edge_points.append(edge_point)

    return edge_points

def get_avg_face_points(input_points, input_faces, face_points):
    """

    for each point calculate

    the average of the face points of the faces the point belongs to (avg_face_points)

    create a list of lists of two numbers [facepoint_sum, num_points] by going through the
    points in all the faces.

    then create the avg_face_points list of point by dividing point_sum (x, y, z) by num_points

    """

    # initialize list with [[0.0, 0.0, 0.0], 0]

    num_points = len(input_points)

    temp_points = []

    for pointnum in range(num_points):
        temp_points.append([[0.0, 0.0, 0.0], 0])

    # loop through faces updating temp_points

    for facenum in range(len(input_faces)):
        fp = face_points[facenum]
        for pointnum in input_faces[facenum]:
            tp = temp_points[pointnum][0]
            temp_points[pointnum][0] = sum_point(tp,fp)
            temp_points[pointnum][1] += 1

    # divide to create avg_face_points

    avg_face_points = []

    for tp in temp_points:
       afp = div_point(tp[0], tp[1])
       avg_face_points.append(afp)

    return avg_face_points

def get_avg_mid_edges(input_points, edges_faces):
    """

    the average of the centers of edges the point belongs to (avg_mid_edges)

    create list with entry for each point
    each entry has two elements. one is a point that is the sum of the centers of the edges
    and the other is the number of edges. after going through all edges divide by
    number of edges.

    """

    # initialize list with [[0.0, 0.0, 0.0], 0]

    num_points = len(input_points)

    temp_points = []

    for pointnum in range(num_points):
        temp_points.append([[0.0, 0.0, 0.0], 0])

    # go through edges_faces using center updating each point

    for edge in edges_faces:
        cp = edge[4]
        for pointnum in [edge[0], edge[1]]:
            tp = temp_points[pointnum][0]
            temp_points[pointnum][0] = sum_point(tp,cp)
            temp_points[pointnum][1] += 1

    # divide out number of points to get average

    avg_mid_edges = []

    for tp in temp_points:
       ame = div_point(tp[0], tp[1])
       avg_mid_edges.append(ame)

    return avg_mid_edges

def get_points_faces(input_points, input_faces):
    # initialize list with 0

    num_points = len(input_points)

    points_faces = []

    for pointnum in range(num_points):
        points_faces.append(0)

    # loop through faces updating points_faces

    for facenum in range(len(input_faces)):
        for pointnum in input_faces[facenum]:
            points_faces[pointnum] += 1

    return points_faces

def get_new_points(input_points, points_faces, avg_face_points, avg_mid_edges):
    """

    m1 = (n - 3.0) / n
    m2 = 1.0 / n
    m3 = 2.0 / n
    new_coords = (m1 * old_coords)
               + (m2 * avg_face_points)
               + (m3 * avg_mid_edges)

    """

    new_points =[]

    for pointnum in range(len(input_points)):
        n = points_faces[pointnum]
        m1 = (n - 3.0) / n
        m2 = 1.0 / n
        m3 = 2.0 / n
        old_coords = input_points[pointnum]
        p1 = mul_point(old_coords, m1)
        afp = avg_face_points[pointnum]
        p2 = mul_point(afp, m2)
        ame = avg_mid_edges[pointnum]
        p3 = mul_point(ame, m3)
        p4 = sum_point(p1, p2)
        new_coords = sum_point(p4, p3)

        new_points.append(new_coords)

    return new_points

def switch_nums(point_nums):
    """
    Returns tuple of point numbers
    sorted least to most
    """
    if point_nums[0] < point_nums[1]:
        return point_nums
    else:
        return (point_nums[1], point_nums[0])

def cmc_subdiv(input_points, input_faces):
    # 1. for each face, a face point is created which is the average of all the points of the face.
    # each entry in the returned list is a point (x, y, z).

    face_points = get_face_points(input_points, input_faces)

    # get list of edges with 1 or 2 adjacent faces
    # [pointnum_1, pointnum_2, facenum_1, facenum_2, center] or
    # [pointnum_1, pointnum_2, facenum_1, None, center]

    edges_faces = get_edges_faces(input_points, input_faces)

    # get edge points, a list of points

    edge_points = get_edge_points(input_points, edges_faces, face_points)

    # the average of the face points of the faces the point belongs to (avg_face_points)

    avg_face_points = get_avg_face_points(input_points, input_faces, face_points)

    # the average of the centers of edges the point belongs to (avg_mid_edges)

    avg_mid_edges = get_avg_mid_edges(input_points, edges_faces)

    # how many faces a point belongs to

    points_faces = get_points_faces(input_points, input_faces)

    """

    m1 = (n - 3) / n
    m2 = 1 / n
    m3 = 2 / n
    new_coords = (m1 * old_coords)
               + (m2 * avg_face_points)
               + (m3 * avg_mid_edges)

    """

    new_points = get_new_points(input_points, points_faces, avg_face_points, avg_mid_edges)

    """

    Then each face is replaced by new faces made with the new points,

    for a triangle face (a,b,c):
       (a, edge_point ab, face_point abc, edge_point ca)
       (b, edge_point bc, face_point abc, edge_point ab)
       (c, edge_point ca, face_point abc, edge_point bc)

    for a quad face (a,b,c,d):
       (a, edge_point ab, face_point abcd, edge_point da)
       (b, edge_point bc, face_point abcd, edge_point ab)
       (c, edge_point cd, face_point abcd, edge_point bc)
       (d, edge_point da, face_point abcd, edge_point cd)

    face_points is a list indexed by face number so that is
    easy to get.

    edge_points is a list indexed by the edge number
    which is an index into edges_faces.

    need to add face_points and edge points to
    new_points and get index into each.

    then create two new structures

    face_point_nums - list indexes by facenum
    whose value is the index into new_points

    edge_point num - dictionary with key (pointnum_1, pointnum_2)
    and value is index into new_points

    """

    # add face points to new_points

    face_point_nums = []

    # point num after next append to new_points
    next_pointnum = len(new_points)

    for face_point in face_points:
        new_points.append(face_point)
        face_point_nums.append(next_pointnum)
        next_pointnum += 1

    # add edge points to new_points

    edge_point_nums = dict()

    for edgenum in range(len(edges_faces)):
        pointnum_1 = edges_faces[edgenum][0]
        pointnum_2 = edges_faces[edgenum][1]
        edge_point = edge_points[edgenum]
        new_points.append(edge_point)
        edge_point_nums[(pointnum_1, pointnum_2)] = next_pointnum
        next_pointnum += 1

    # new_points now has the points to output. Need new
    # faces

    """

    just doing this case for now:

    for a quad face (a,b,c,d):
       (a, edge_point ab, face_point abcd, edge_point da)
       (b, edge_point bc, face_point abcd, edge_point ab)
       (c, edge_point cd, face_point abcd, edge_point bc)
       (d, edge_point da, face_point abcd, edge_point cd)

    new_faces will be a list of lists where the elements are like this:

    [pointnum_1, pointnum_2, pointnum_3, pointnum_4]

    """

    new_faces =[]

    for oldfacenum in range(len(input_faces)):
        oldface = input_faces[oldfacenum]
        # 4 point face
        if len(oldface) == 4:
            a = oldface[0]
            b = oldface[1]
            c = oldface[2]
            d = oldface[3]
            face_point_abcd = face_point_nums[oldfacenum]
            edge_point_ab = edge_point_nums[switch_nums((a, b))]
            edge_point_da = edge_point_nums[switch_nums((d, a))]
            edge_point_bc = edge_point_nums[switch_nums((b, c))]
            edge_point_cd = edge_point_nums[switch_nums((c, d))]
            new_faces.append((a, edge_point_ab, face_point_abcd, edge_point_da))
            new_faces.append((b, edge_point_bc, face_point_abcd, edge_point_ab))
            new_faces.append((c, edge_point_cd, face_point_abcd, edge_point_bc))
            new_faces.append((d, edge_point_da, face_point_abcd, edge_point_cd))

    return new_points, new_faces

def graph_output(output_points, output_faces):

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    """

    Plot each face

    """

    for facenum in range(len(output_faces)):
        curr_face = output_faces[facenum]
        xcurr = []
        ycurr = []
        zcurr = []
        for pointnum in range(len(curr_face)):
            xcurr.append(output_points[curr_face[pointnum]][0])
            ycurr.append(output_points[curr_face[pointnum]][1])
            zcurr.append(output_points[curr_face[pointnum]][2])
        xcurr.append(output_points[curr_face[0]][0])
        ycurr.append(output_points[curr_face[0]][1])
        zcurr.append(output_points[curr_face[0]][2])

        ax.plot(xcurr,ycurr,zcurr,color='b')

    plt.show()

# cube

input_points = [
  [-1.0,  1.0,  1.0],
  [-1.0, -1.0,  1.0],
  [ 1.0, -1.0,  1.0],
  [ 1.0,  1.0,  1.0],
  [ 1.0, -1.0, -1.0],
  [ 1.0,  1.0, -1.0],
  [-1.0, -1.0, -1.0],
  [-1.0,  1.0, -1.0]
]

input_faces = [
  [0, 1, 2, 3],
  [3, 2, 4, 5],
  [5, 4, 6, 7],
  [7, 0, 3, 5],
  [7, 6, 1, 0],
  [6, 1, 2, 4],
]

if len(sys.argv) != 2:
    print("Should have one argument integer number of iterations")
    sys.exit()
else:
    iterations = int(sys.argv[1])

    output_points, output_faces = input_points, input_faces

    for i in range(iterations):
        output_points, output_faces = cmc_subdiv(output_points, output_faces)

graph_output(output_points, output_faces)


dog = "Benjamin"
doG = "Smokey"
Dog = "Samba"
DOG = "Bernie"
see "The 4 dogs are : " + dog +  ", " + doG + ", " + Dog + " and " + DOG + "."


module FiveDogs
  dog = "Benjamin"
  dOg = "Dogley"
  doG = "Fido"
  Dog = "Samba"   # this constant is FiveDogs::Dog
  DOG = "Bernie"  # this constant is FiveDogs::DOG

  names = [dog, dOg, doG, Dog, DOG]
  names.uniq!
  puts "There are %d dogs named %s." % [names.length, names.join(", ")]
  puts
  puts "The local variables are %s." % local_variables.join(", ")
  puts "The constants are %s." % constants.join(", ")
end


dog$ = "Benjamin"
doG$ = "Smokey"
Dog$ = "Samba"
DOG$ = "Bernie"
print "The 4 dogs are "; dog$; ", "; doG$; ", "; Dog$; " and "; DOG$; "."


fn main() {
    let dog = "Benjamin";
    let Dog = "Samba";
    let DOG = "Bernie";
    println!("The three dogs are named {}, {} and {}.", dog, Dog, DOG);
}


class MAIN is
  main is
    dog ::= "Benjamin";
    Dog ::= "Samba";
    DOG ::= "Bernie";
    #OUT + #FMT("The three dogs are %s, %s and %s\n",
                 dog, Dog, DOG);
  end;
end;


val dog = "Benjamin"
val Dog = "Samba"
val DOG = "Bernie"
println("There are three dogs named " + dog + ", " + Dog + ", and " + DOG + ".")


(define dog "Benjamin")
(define Dog "Samba")
(define DOG "Bernie")

(if (eq? dog DOG)
        (begin (display "There is one dog named ")
                (display DOG)
                (display ".")
                (newline))
        (begin (display "The three dogs are named ")
                (display dog) (display ", ")
                (display Dog) (display " and ")
                (display DOG)
                (display ".")
                (newline)))


pub struct Vector3 {pub x: f64, pub y: f64, pub z: f64, pub w: f64}

pub struct Triangle {pub r: [usize; 3], pub(crate) col: [f32; 4], pub(crate) p: [Vector3; 3], n: Vector3, pub t: [Vector2; 3]}
pub struct Mesh{pub v: Vec<Vector3>, pub f: Vec<Triangle>}

impl Triangle{
    pub fn new() -> Triangle {return Triangle {r: [0, 0, 0], col: [0.0; 4], p: [Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0), Vector3::new(0.0, 0.0, 0.0)], n: Vector3::new(0.0, 0.0, 0.0), t: [Vector2::new(0.0, 0.0), Vector2::new(0.0, 0.0), Vector2::new(0.0, 0.0)]}}
    pub fn copy(&self) -> Triangle {return Triangle {r: self.r.clone(), col: self.col, p: [self.p[0].copy(), self.p[1].copy(), self.p[2].copy()], n: self.n.copy(), t: [self.t[0].copy(), self.t[1].copy(), self.t[2].copy()]}}
}

impl Vector3 {
    pub fn new(x: f64, y: f64, z: f64) -> Vector3 {return Vector3 {x, y, z, w: 1.0}}
    pub fn normalize(&mut self) {
        let l = (self.x * self.x + self.y * self.y + self.z * self.z).sqrt();
        self.x /= l;
        self.y /= l;
        self.z /= l;
    }
    pub fn dot_product(v1: &Vector3, v2: &Vector3) -> f64 {return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z}
    pub fn cross_product(v1: &Vector3, v2: &Vector3) -> Vector3 {return Vector3::new(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x)}
    pub fn intersect_plane(plane_n: &Vector3, plane_p: &Vector3, line_start: &Vector3, line_end: &Vector3, mut t: f64) -> Vector3 {
        let mut p_n = plane_n.copy();

        p_n.normalize();
        let plane_d = -Vector3::dot_product(&p_n, plane_p);
        let ad = Vector3::dot_product(line_start, &p_n);
        let bd = Vector3::dot_product(line_end, &p_n);
        t = (-plane_d - ad) / (bd - ad);
        let line = line_end.copy() - line_start;
        let line_i = line * t;
        return line_start.copy() + &line_i;
    }
    pub fn copy(&self) -> Vector3 {return Vector3 {x: self.x, y: self.y, z: self.z, w: self.w}}
}

impl Mesh {
    pub fn get_face_points(&self) -> Vec<Vector3> {
        let mut face_points: Vec<Vector3> = Vec::new();

        for curr_face in &self.f {
            let mut face_point = Vector3::new(0.0, 0.0, 0.0);
            for curr_point_index in curr_face.r {
                let curr_point = &self.v[curr_point_index];
                face_point += &curr_point
            }

            face_point /= curr_face.r.len() as f64;
            face_points.push(face_point.copy());
        }
        return face_points;
    }
    pub fn get_edges_faces(&self) -> Vec<[f64; 7]> {
        let mut edges: Vec<[usize; 3]> = Vec::new();

        for face_num in 0..self.f.len() {
            let face: Triangle = self.f[face_num].copy();
            let num_points = face.p.len();
            for point_index in 0..num_points {
                let mut point_num_1 = 0;
                let mut point_num_2 = 0;
                if point_index < num_points - 1 {
                    point_num_1 = face.r[point_index];
                    point_num_2 = face.r[point_index + 1];
                } else {
                    point_num_1 = face.r[point_index];
                    point_num_2 = face.r[0];
                }
                if point_num_1 > point_num_2 {
                    let temp = point_num_1;
                    point_num_1 = point_num_2;
                    point_num_2 = temp;
                }
                edges.push([point_num_1, point_num_2, face_num]);
            }
        }
        edges.sort();

        let num_edges = edges.len();
        let mut index = 0;
        let mut merged_edges: Vec<[f64; 4]> = Vec::new();

        while index < num_edges {
            let e1 = edges[index];
            if index < num_edges - 1 {
                let e2 = edges[index + 1];
                if e1[0] == e2[0] && e1[1] == e2[1] {
                    merged_edges.push([e1[0] as f64, e1[1] as f64, e1[2] as f64, e2[2] as f64]);
                    index += 2;
                } else {
                    merged_edges.push([e1[0] as f64, e1[1] as f64, e1[2] as f64, -1.0]);
                    index += 1;
                }
            } else {
                merged_edges.push([e1[0] as f64, e1[1] as f64, e1[2] as f64, -1.0]);
                index += 1
            }
        }

        let mut edges_centers = Vec::new();

        for me in merged_edges {
            let p1 = self.v[me[0] as usize].copy();
            let p2 = self.v[me[1] as usize].copy();
            let cp: Vector3 = Mesh::center_point(&p1, &p2);
            edges_centers.push([me[0] as f64, me[1] as f64, me[2] as f64, me[3] as f64, cp.x, cp.y, cp.z]);
        }
        return edges_centers;
    }
    pub fn get_edge_points(&self, edges_faces: &Vec<[f64; 7]>, face_points: &Vec<Vector3>) -> Vec<Vector3> {
        let mut edge_points = Vec::new();

        for edge in edges_faces {
            let cp = Vector3::new(edge[4], edge[5], edge[6]);
            let fp1: Vector3 = face_points[edge[2] as usize].copy();
            let mut fp2: Vector3 = fp1.copy();
            if edge[3] != -1.0 { fp2 = face_points[edge[3] as usize].copy() };
            let cfp = Mesh::center_point(&fp1, &fp2);
            let edge_point = Mesh::center_point(&cp, &cfp);
            edge_points.push(edge_point);
        }

        return edge_points
    }
    pub fn get_avg_face_points(&self, face_points: &Vec<Vector3>) -> Vec<Vector3> {
        let num_points = self.v.len();
        let mut temp_points = Vec::new();
        let mut div: Vec<i32> = Vec::new();

        for _ in 0..num_points {
            temp_points.push(Vector3::new(0.0, 0.0, 0.0));
            div.push(0)
        };

        for face_num in 0..self.f.len() {
            let mut fp = face_points[face_num].copy();
            for point_num in self.f[face_num].r {
                let tp = temp_points[point_num].copy();
                temp_points[point_num] = tp + &fp;
                div[point_num] += 1;
            }
        }

        let mut avg_face_points: Vec<Vector3> = Vec::new();
        for i in 0..temp_points.len() {
            let tp: Vector3 = temp_points[i].copy();
            let t = tp / (div[i] as f64);
            avg_face_points.push(t.copy());
        }

        return avg_face_points;
    }
    pub fn get_avg_mid_edges(&self, edges_faces: &Vec<[f64; 7]>) -> Vec<Vector3> {
        let num_points = self.v.len();
        let mut temp_points = Vec::new();
        let mut div: Vec<i32> = Vec::new();

        for point_num in 0..num_points{ temp_points.push(Vector3::new(0.0, 0.0, 0.0)); div.push(0)}
        for edge in edges_faces {
            let cp = Vector3::new(edge[4], edge[5], edge[6]);
            for point_num in [edge[0] as usize, edge[1] as usize] {
                let tp = temp_points[point_num].copy();
                temp_points[point_num] = tp + &cp;
                div[point_num] += 1
            }
        }

        let mut avg_mid_edges: Vec<Vector3> = Vec::new();

        for i in 0..temp_points.len(){
            let ame: Vector3 = temp_points[i].copy() / (div[i] as f64);
            avg_mid_edges.push(ame)}

        return avg_mid_edges
    }
    pub fn get_points_faces(&self) -> Vec<i32> {
        let num_points = self.v.len();
        let mut points_faces: Vec<i32> = Vec::new();

        for point_num in 0..num_points{points_faces.push(0)}

        for face_num in 0..self.f.len() {
            for point_num in self.f[face_num].r {
                points_faces[point_num] += 1;
            }
        }
        return points_faces
    }
    pub fn get_new_points(&self, points_faces: &Vec<i32>, avg_face_points: &Vec<Vector3>, avg_mid_edges: &Vec<Vector3>) -> Vec<Vector3> {
        let mut new_points: Vec<Vector3> = Vec::new();

        for point_num in 0..self.v.len() {
            let n = points_faces[point_num] as f64;
            let m1 = (n - 3.0) / n;
            let m2 = 1.0 / n;
            let m3 = 2.0 / n;
            let old_coords = self.v[point_num].copy();
            let p1 = old_coords * m1;
            let afp = avg_face_points[point_num].copy();
            let p2 = afp * m2;
            let ame = avg_mid_edges[point_num].copy();
            let p3 = ame * m3;
            let p4 = p1 + &p2;
            let new_coords = p4 + &p3;

            new_points.push(new_coords);
        }

        return new_points;
    }

    pub fn switch_nums(point_nums: [f64; 2]) -> [f64; 2] {
        return if point_nums[0] < point_nums[1] { point_nums } else {[point_nums[1], point_nums[0]]}
    }

    pub fn get_key(points: [f64; 2]) -> String {
        return points[0].to_string() + ";" + &*points[1].to_string();
    }

    pub fn subdivide(&mut self) {
        let face_points = self.get_face_points();
        let edges_faces = self.get_edges_faces();
        let edge_points = self.get_edge_points(&edges_faces, &face_points);
        let avg_face_points = self.get_avg_face_points(&face_points);
        let avg_mid_edges = self.get_avg_mid_edges(&edges_faces);
        let points_faces = self.get_points_faces();
        let mut new_points = self.get_new_points(&points_faces, &avg_face_points, &avg_mid_edges);

        let mut face_point_nums = Vec::new();
        let mut next_point_num = new_points.len();

        for face_point in face_points {
            new_points.push(face_point);
            face_point_nums.push(next_point_num);
            next_point_num += 1;
        }

        let mut edge_point_nums: HashMap<String, usize> = HashMap::new();

        for edge_num in 0..edges_faces.len() {
            let point_num_1 = edges_faces[edge_num][0];
            let point_num_2 = edges_faces[edge_num][1];
            let edge_point = edge_points[edge_num].copy();
            new_points.push(edge_point);
            edge_point_nums.insert(Mesh::get_key([point_num_1, point_num_2]), next_point_num);
            next_point_num += 1;
        }

        let mut new_faces = Vec::new();

        for old_face_num in 0..self.f.len() {
            let old_face = self.f[old_face_num].copy();
            let a = old_face.r[0] as f64;
            let b = old_face.r[1] as f64;
            let c = old_face.r[2] as f64;
            let face_point_abc = face_point_nums[old_face_num];
            let edge_point_ab = *edge_point_nums.get(&*Mesh::get_key(Mesh::switch_nums([a, b]))).unwrap();
            let edge_point_bc = *edge_point_nums.get(&*Mesh::get_key(Mesh::switch_nums([b, c]))).unwrap();
            let edge_point_ca = *edge_point_nums.get(&*Mesh::get_key(Mesh::switch_nums([c, a]))).unwrap();
            new_faces.push([a, edge_point_ab as f64, face_point_abc as f64, edge_point_ca as f64]);
            new_faces.push([b, edge_point_bc as f64, face_point_abc as f64, edge_point_ab as f64]);
            new_faces.push([c, edge_point_ca as f64, face_point_abc as f64, edge_point_bc as f64]);
        }
        self.f = Vec::new();

        for face_num in 0..new_faces.len() {
            let curr_face = new_faces[face_num];
            let mut t1: Triangle = Triangle::new();
            let mut t2: Triangle = Triangle::new();
            t1.p[0] = Vector3::new(new_points[curr_face[0] as usize].x, new_points[curr_face[0] as usize].y, new_points[curr_face[0] as usize].z);
            t1.p[1] = Vector3::new(new_points[curr_face[1] as usize].x, new_points[curr_face[1] as usize].y, new_points[curr_face[1] as usize].z);
            t1.p[2] = Vector3::new(new_points[curr_face[2] as usize].x, new_points[curr_face[2] as usize].y, new_points[curr_face[2] as usize].z);
            t2.p[0] = Vector3::new(new_points[curr_face[2] as usize].x, new_points[curr_face[2] as usize].y, new_points[curr_face[2] as usize].z);
            t2.p[1] = Vector3::new(new_points[curr_face[3] as usize].x, new_points[curr_face[3] as usize].y, new_points[curr_face[3] as usize].z);
            t2.p[2] = Vector3::new(new_points[curr_face[0] as usize].x, new_points[curr_face[0] as usize].y, new_points[curr_face[0] as usize].z);
            t1.r = [curr_face[0] as usize, curr_face[1] as usize, curr_face[2] as usize];
            t2.r = [curr_face[2] as usize, curr_face[3] as usize, curr_face[0] as usize];
            self.f.push(t1);
            self.f.push(t2);
        }
        self.v = new_points;
    }
}


package require Tcl 8.5

# Use math functions and operators as commands (Lisp-like).
namespace path {tcl::mathfunc tcl::mathop}

# Add 3 points.
proc add3 {A B C} {
    lassign $A Ax Ay Az
    lassign $B Bx By Bz
    lassign $C Cx Cy Cz
    list [+ $Ax $Bx $Cx] [+ $Ay $By $Cy] [+ $Az $Bz $Cz]
}

# Multiply a point by a constant.
proc mulC {m A} {
    lassign $A x y z
    list [* $m $x] [* $m $y] [* $m $z]
}

# Take the centroid of a set of points.
# Note that each of the arguments is a *list* of coordinate triples
# This makes things easier later.
proc centroid args {
    set x [set y [set z 0.0]]
    foreach plist $args {
	incr n [llength $plist]
	foreach p $plist {
	    lassign $p px py pz
	    set x [+ $x $px]
	    set y [+ $y $py]
	    set z [+ $z $pz]
	}
    }
    set n [double $n]
    list [/ $x $n] [/ $y $n] [/ $z $n]
}

# Select from the list the value from each of the indices in the *lists*
# in the trailing arguments.
proc selectFrom {list args} {
    foreach is $args {foreach i $is {lappend r [lindex $list $i]}}
    return $r
}

# Rotate a list.
proc lrot {list {n 1}} {
    set n [% $n [llength $list]]
    list {*}[lrange $list $n end] {*}[lrange $list 0 [incr n -1]]
}

# Generate an edge by putting the smaller coordinate index first.
proc edge {a b} {
    list [min $a $b] [max $a $b]
}

# Perform one step of Catmull-Clark subdivision of a surface.
proc CatmullClark {points faces} {
    # Generate the new face-points and list of edges, plus some lookup tables.
    set edges {}
    foreach f $faces {
	set ps [selectFrom $points $f]
	set fp [centroid $ps]
	lappend facepoints $fp
	foreach p $ps {
	    lappend fp4p($p) $fp
	}
	foreach p1 $f p2 [lrot $f] {
	    set e [edge $p1 $p2]
	    if {$e ni $edges} {
		lappend edges $e
	    }
	    lappend fp4e($e) $fp
	}
    }

    # Generate the new edge-points and mid-points of edges, and a few more
    # lookup tables.
    set i [+ [llength $points] [llength $faces]]
    foreach e $edges {
	set ep [selectFrom $points $e]
	if {[llength $fp4e($e)] > 1} {
	    set mid [centroid $ep $fp4e($e)]
	} else {
	    set mid [centroid $ep]
	    foreach p $ep {
		lappend ep_heavy($p) $mid
	    }
	}
	lappend edgepoints $mid
	set en4e($e) $i
	foreach p $ep {
	    lappend ep4p($p) $mid
	}
	incr i
    }

    # Generate the new vertex points with our lookup tables.
    foreach p $points {
	if {[llength $fp4p($p)] >= 4} {
	    set n [llength $fp4p($p)]
	    lappend newPoints [add3 [mulC [/ [- $n 3.0] $n] $p] \
		    [mulC [/ 1.0 $n] [centroid $fp4p($p)]] \
		    [mulC [/ 2.0 $n] [centroid $ep4p($p)]]]
	} else {
	    # Update a point on the edge of a hole. This formula is not
	    # described on the WP page, but produces a nice result.
	    lappend newPoints [centroid $ep_heavy($p) [list $p $p]]
	}
    }

    # Now compute the new set of quadrilateral faces.
    set i [llength $points]
    foreach f $faces {
	foreach a $f b [lrot $f] c [lrot $f -1] {
	    lappend newFaces [list \
		    $a $en4e([edge $a $b]) $i $en4e([edge $c $a])]
	}
	incr i
    }

    list [concat $newPoints $facepoints $edgepoints] $newFaces
}


$ include "seed7_05.s7i";

const string: dog is "Benjamin";
const string: Dog is "Samba";
const string: DOG is "Bernie";

const proc: main is func
  begin
    writeln("The three dogs are named " <& dog <& ", " <& Dog <& " and " <& DOG <& ".");
  end func;


set dog to "Benjamin"
set Dog to "Samba"
set DOG to "Bernie"

put !"There is just one dog named [[dog]]."


dog := 'Benjamin';
Dog := 'Samba';
DOG := 'Bernie';
print( 'There is just one dog named', dOg );


var dog = 'Benjamin';
var Dog = 'Samba';
var DOG = 'Bernie';
say "The three dogs are named #{dog}, #{Dog}, and #{DOG}.";


begin
    text dog;
    dog :- blanks( 8 );
    dog := "Benjamin";
    Dog := "Samba";
    DOG := "Bernie";
    outtext( "There is just one dog, named " );
    outtext( dog );
    outimage
end


|dog Dog DOG|
dog := 'Benjamin'.
Dog := 'Samba'.
DOG := 'Bernie'.
( 'The three dogs are named %1, %2 and %3' %
  { dog . Dog . DOG } ) displayNl.


    DOG = 'Benjamin'
    Dog = 'Samba'
    dog = 'Bernie'
    OUTPUT = 'The three dogs are named ' DOG ', ' Dog ', and ' dog
END


let
  val dog = "Benjamin"
  val Dog = "Samba"
  val DOG = "Bernie"
in
  print("The three dogs are named " ^ dog ^ ", " ^ Dog ^ ", and " ^ DOG ^ ".\n")
end;


import "/dynamic" for Tuple, Struct
import "/sort" for Sort
import "/math" for Int
import "/fmt" for Fmt

var Point = Tuple.create("Point", ["x", "y", "z"])
var fields = [
    "pn1",  // point number 1
    "pn2",  // point number 2
    "fn1",  // face number 1
    "fn2",  // face number 2
    "cp"    // center point
]
var Edge  = Tuple.create("Edge", fields)
var PointEx = Struct.create("PointEx", ["p", "n"])

var sumPoint = Fn.new { |p1, p2| Point.new(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z) }

var mulPoint = Fn.new { |p, m| Point.new(p.x * m, p.y * m, p.z * m) }

var divPoint = Fn.new { |p, d| mulPoint.call(p, 1/d) }

var centerPoint = Fn.new { |p1, p2| divPoint.call(sumPoint.call(p1, p2), 2) }

var getFacePoints = Fn.new { |inputPoints, inputFaces|
    var facePoints = List.filled(inputFaces.count, null)
    var i = 0
    for (currFace in inputFaces) {
        var facePoint = Point.new(0, 0, 0)
        for (cpi in currFace) {
            var currPoint = inputPoints[cpi]
            facePoint = sumPoint.call(facePoint, currPoint)
        }
        facePoints[i] = divPoint.call(facePoint, currFace.count)
        i =  i + 1
    }
    return facePoints
}

var getEdgesFaces = Fn.new { |inputPoints, inputFaces|
    var edges = []
    var faceNum = 0
    for (face in inputFaces) {
        var numPoints = face.count
        for (pointIndex in 0...numPoints) {
            var pointNum1 = face[pointIndex]
            var pointNum2 = (pointIndex < numPoints-1) ? face[pointIndex+1] : face[0]
            if (pointNum1 > pointNum2) {
                var t = pointNum1
                pointNum1 = pointNum2
                pointNum2 = t
            }
            edges.add([pointNum1, pointNum2, faceNum])
        }
        faceNum = faceNum + 1
    }
    var cmp = Fn.new { |e1, e2|
        if (e1[0] == e2[0]) {
            if (e1[1] == e2[1]) return (e1[2] - e2[2]).sign
            return (e1[1] - e2[1]).sign
        }
        return (e1[0] - e2[0]).sign
    }
    var numEdges = edges.count
    Sort.quick(edges, 0, numEdges-1, cmp)
    var eIndex = 0
    var mergedEdges = []
    while (eIndex < numEdges) {
        var e1 = edges[eIndex]
        if (eIndex < numEdges-1) {
            var e2 = edges[eIndex+1]
            if (e1[0] == e2[0] && e1[1] == e2[1]) {
                mergedEdges.add([e1[0], e1[1], e1[2], e2[2]])
                eIndex = eIndex + 2
            } else {
                mergedEdges.add([e1[0], e1[1], e1[2], -1])
                eIndex = eIndex + 1
            }
        } else {
            mergedEdges.add([e1[0], e1[1], e1[2], -1])
            eIndex = eIndex + 1
        }
    }
    var edgesCenters = []
    for (me in mergedEdges) {
        var p1 = inputPoints[me[0]]
        var p2 = inputPoints[me[1]]
        var cp = centerPoint.call(p1, p2)
        edgesCenters.add(Edge.new(me[0], me[1], me[2], me[3], cp))
    }
    return edgesCenters
}

var getEdgePoints = Fn.new { |inputPoints, edgesFaces, facePoints|
    var edgePoints = List.filled(edgesFaces.count, null)
    var i = 0
    for (edge in edgesFaces) {
        var cp = edge.cp
        var fp1 = facePoints[edge.fn1]
        var fp2 = (edge.fn2 == -1) ? fp1 : facePoints[edge.fn2]
        var cfp = centerPoint.call(fp1, fp2)
        edgePoints[i] = centerPoint.call(cp, cfp)
        i = i + 1
    }
    return edgePoints
}

var getAvgFacePoints = Fn.new { |inputPoints, inputFaces, facePoints|
    var numPoints = inputPoints.count
    var tempPoints = List.filled(numPoints, null)
    for (i in 0...numPoints) tempPoints[i] = PointEx.new(Point.new(0, 0, 0), 0)
    for (faceNum in 0...inputFaces.count) {
        var fp = facePoints[faceNum]
        for (pointNum in inputFaces[faceNum]) {
            var tp = tempPoints[pointNum].p
            tempPoints[pointNum].p = sumPoint.call(tp, fp)
            tempPoints[pointNum].n = tempPoints[pointNum].n + 1
        }
    }
    var avgFacePoints = List.filled(numPoints, null)
    var i = 0
    for (tp in tempPoints) {
        avgFacePoints[i] = divPoint.call(tp.p, tp.n)
        i = i + 1
    }
    return avgFacePoints
}

var getAvgMidEdges = Fn.new { |inputPoints, edgesFaces|
    var numPoints = inputPoints.count
    var tempPoints = List.filled(numPoints, null)
    for (i in 0...numPoints) tempPoints[i] = PointEx.new(Point.new(0, 0, 0), 0)
    for (edge in edgesFaces) {
        var cp = edge.cp
        for (pointNum in [edge.pn1, edge.pn2]) {
            var tp = tempPoints[pointNum].p
            tempPoints[pointNum].p = sumPoint.call(tp, cp)
            tempPoints[pointNum].n = tempPoints[pointNum].n + 1
        }
    }
    var avgMidEdges = List.filled(tempPoints.count, null)
    var i = 0
    for (tp in tempPoints) {
        avgMidEdges[i] = divPoint.call(tp.p, tp.n)
        i = i + 1
    }
    return avgMidEdges
}

var getPointsFaces = Fn.new { |inputPoints, inputFaces|
    var numPoints = inputPoints.count
    var pointsFaces = List.filled(numPoints, 0)
    for (faceNum in 0...inputFaces.count) {
        for (pointNum in inputFaces[faceNum]) {
            pointsFaces[pointNum] = pointsFaces[pointNum] + 1
        }
    }
    return pointsFaces
}

var getNewPoints = Fn.new { |inputPoints, pointsFaces, avgFacePoints, avgMidEdges|
    var newPoints = List.filled(inputPoints.count, null)
    for (pointNum in 0...inputPoints.count) {
        var n = pointsFaces[pointNum]
        var m1 = (n-3) / n
        var m2 = 1 / n
        var m3 = 2 / n
        var oldCoords = inputPoints[pointNum]
        var p1 = mulPoint.call(oldCoords, m1)
        var afp = avgFacePoints[pointNum]
        var p2 = mulPoint.call(afp, m2)
        var ame = avgMidEdges[pointNum]
        var p3 = mulPoint.call(ame, m3)
        var p4 = sumPoint.call(p1, p2)
        newPoints[pointNum] = sumPoint.call(p4, p3)
    }
    return newPoints
}

var switchNums = Fn.new { |pointNums|
    if (pointNums[0] < pointNums[1]) return pointNums
    return [pointNums[1], pointNums[0]]
}

var cmcSubdiv = Fn.new { |inputPoints, inputFaces|
    var facePoints = getFacePoints.call(inputPoints, inputFaces)
    var edgesFaces = getEdgesFaces.call(inputPoints, inputFaces)
    var edgePoints = getEdgePoints.call(inputPoints, edgesFaces, facePoints)
    var avgFacePoints = getAvgFacePoints.call(inputPoints, inputFaces, facePoints)
    var avgMidEdges = getAvgMidEdges.call(inputPoints, edgesFaces)
    var pointsFaces = getPointsFaces.call(inputPoints, inputFaces)
    var newPoints = getNewPoints.call(inputPoints, pointsFaces, avgFacePoints, avgMidEdges)
    var facePointNums = []
    var nextPointNum = newPoints.count
    for (facePoint in facePoints) {
        newPoints.add(facePoint)
        facePointNums.add(nextPointNum)
        nextPointNum = nextPointNum + 1
    }
    var edgePointNums = {}
    for (edgeNum in 0...edgesFaces.count) {
        var pointNum1 = edgesFaces[edgeNum].pn1
        var pointNum2 = edgesFaces[edgeNum].pn2
        var edgePoint = edgePoints[edgeNum]
        newPoints.add(edgePoint)
        edgePointNums[Int.cantorPair(pointNum1, pointNum2)] = nextPointNum
        nextPointNum = nextPointNum + 1
    }
    var newFaces = []
    var oldFaceNum = 0
    for (oldFace in inputFaces) {
        if (oldFace.count == 4) {
            var a = oldFace[0]
            var b = oldFace[1]
            var c = oldFace[2]
            var d = oldFace[3]
            var facePointAbcd = facePointNums[oldFaceNum]
            var p = switchNums.call([a, b])
            var edgePointAb = edgePointNums[Int.cantorPair(p[0], p[1])]
            p = switchNums.call([d, a])
            var edgePointDa = edgePointNums[Int.cantorPair(p[0], p[1])]
            p = switchNums.call([b, c])
            var edgePointBc = edgePointNums[Int.cantorPair(p[0], p[1])]
            p = switchNums.call([c, d])
            var edgePointCd = edgePointNums[Int.cantorPair(p[0], p[1])]
            newFaces.add([a, edgePointAb, facePointAbcd, edgePointDa])
            newFaces.add([b, edgePointBc, facePointAbcd, edgePointAb])
            newFaces.add([c, edgePointCd, facePointAbcd, edgePointBc])
            newFaces.add([d, edgePointDa, facePointAbcd, edgePointCd])
        }
        oldFaceNum = oldFaceNum + 1
    }
    return [newPoints, newFaces]
}

var inputPoints = [
    Point.new(-1,  1,  1),
    Point.new(-1, -1,  1),
    Point.new( 1, -1,  1),
    Point.new( 1,  1,  1),
    Point.new( 1, -1, -1),
    Point.new( 1,  1, -1),
    Point.new(-1, -1, -1),
    Point.new(-1,  1, -1)
]

var inputFaces = [
    [0, 1, 2, 3],
    [3, 2, 4, 5],
    [5, 4, 6, 7],
    [7, 0, 3, 5],
    [7, 6, 1, 0],
    [6, 1, 2, 4]
]

var outputPoints = inputPoints.toList
var outputFaces  = inputFaces.toList
var iterations = 1
for (i in 0...iterations) {
    var res = cmcSubdiv.call(outputPoints, outputFaces)
    outputPoints = res[0]
    outputFaces  = res[1]
}
for (p in outputPoints) {
    Fmt.aprint([p.x, p.y, p.z], 7, 4, "[]")
}
System.print()
for (f in outputFaces) {
    Fmt.aprint(f, 2, 0, "[]")
}


. local dog Benjamin
. local Dog Samba
. local DOG Bernie
. display "The three dogs are named $_dog, $_Dog, and $_DOG."
The three dogs are named Benjamin, Samba, and Bernie.


let dog = "Benjamin"
let Dog = "Samba"
let DOG = "Bernie"
println("The three dogs are named \(dog), \(Dog), and \(DOG).")


set dog "Benjamin"
set Dog "Samba"
set DOG "Bernie"
puts "The three dogs are named $dog, $Dog and $DOG"


LET dog$ = "Benjamin"
LET Dog$ = "Samba"
LET DOG$ = "Bernie"
PRINT "There is just one dog, named "; dog$
END


dog="Benjamin"
Dog="Samba"
DOG="Bernie"
echo "The three dogs are named $dog, $Dog and $DOG."


> decl string dog Dog DOG
> set dog "Benjamin"
> set Dog "Samba"
> set DOG "Bernie"
> out "The three dogs are named " dog ", "  Dog ", and " DOG endl console
The three dogs are named Benjamin, Samba, and Bernie
>


Public Sub case_sensitivity()
    'VBA does not allow variables that only differ in case
    'The VBA IDE vbe will rename variable 'dog' to 'DOG'
    'when trying to define a second variable 'DOG'
    Dim DOG As String
    DOG = "Benjamin"
    DOG = "Samba"
    DOG = "Bernie"
    Debug.Print "There is just one dog named " & DOG
End Sub


var dog = "Benjamin"
var Dog = "Samba"
var DOG = "Bernie"
System.print("The three dogs are named %(dog), %(Dog) and %(DOG).")


set dog="Benjamin";
set DOG="Samba";
set Dog="Bernie";
log(`The three dogs are named {dog}, {DOG} and {Dog}.`);


(SETQ DOG 'BENJAMIN)
(SETQ Dog 'SAMBA)
(SETQ dog 'BERNIE)
(DISPLAY `(THERE IS JUST ONE DOG NAMED ,DOG))


dog$ = "Benjamin"
Dog$ = "Samba"
DOG$ = "Bernie"
print "The three dogs are named ", dog$, ", ", Dog$, " and ", DOG$
end


var dog = "Benjamin", Dog = "Samba", DOG = "Bernie";


10 LET D$="Benjamin"
20 PRINT "There is just one dog named ";d$


F cidr_parse(str)
   V (addr_str, m_str) = str.split(‘/’)
   V (a, b, c, d) = addr_str.split(‘.’).map(Int)
   V m = Int(m_str)
   I m < 1 | m > 32
     | a < 0 | a > 255
     | b < 0 | b > 255
     | c < 0 | c > 255
     | d < 0 | d > 255
      R (0, 0)
   V mask = (-)((1 << (32 - m)) - 1)
   V address = (a << 24) + (b << 16) + (c << 8) + d
   address [&]= mask
   R (address, m)

F cidr_format(=address, mask_length)
   V d = address [&] F'F
   address >>= 8
   V c = address [&] F'F
   address >>= 8
   V b = address [&] F'F
   address >>= 8
   V a = address [&] F'F
   R a‘.’b‘.’c‘.’d‘/’mask_length

L(test) [‘87.70.141.1/22’,
         ‘36.18.154.103/12’,
         ‘62.62.197.11/29’,
         ‘67.137.119.181/4’,
         ‘161.214.74.21/24’,
         ‘184.232.176.184/18’]
   V (address, mask_length) = cidr_parse(test)
   print(‘#<18 -> #.’.format(test, cidr_format(address, mask_length)))


BEGIN # show IPv4 addresses in CIDR notation in canonical form #
    # mode to hold an IPv4 address in CIDR notation #
    MODE CIDR = STRUCT( BITS   address
                      , INT    network bits
                      , BOOL   valid
                      , STRING error
                      );
    # returns a CIDR parsed from address #
    OP TOCIDR = ( STRING address text )CIDR:
         BEGIN
            STRING addr         = "." + address text + "$";
            STRING error       := "";
            BITS   address     := 16r0;
            INT    bits count  := 0;
            INT    dot count   := 0;
            INT    slash count := 0;
            BOOL   valid       := TRUE;
            INT    s pos       := LWB addr;
            INT    s max        = UPB addr;
            WHILE s pos < s max AND valid DO
                IF addr[ s pos ] = "." THEN
                    # must have an octet next #
                    dot count +:= 1;
                    INT octet  := 0;
                    INT digits := 0;
                    WHILE CHAR c = addr[ s pos +:= 1 ];
                          c >= "0" AND c <= "9"
                    DO
                        octet  *:= 10 +:= ( ABS c - ABS "0" );
                        digits +:= 1
                    OD;
                    address := ( address SHL 8 ) OR BIN ( octet MOD 256 );
                    valid := valid AND digits > 0 AND digits < 4 AND octet < 256;
                    IF NOT valid THEN error := "too many digits/octet to large" FI
                ELIF addr[ s pos ] = "/" THEN
                    # must have the network mask length next #
                    slash count +:= 1;
                    WHILE CHAR c = addr[ s pos +:= 1 ];
                          c >= "0" AND c <= "9"
                    DO
                        bits count  *:= 10 +:= ( ABS c - ABS "0" )
                    OD;
                    # should be "$" ( end of string marker ) next #
                    valid := valid AND addr[ s pos ] = "$";
                    IF NOT valid THEN error := "bit length not followed by end-of-string" FI
                ELIF addr[ s pos ] = "$" THEN
                    # end of address marker - must be the final character #
                    valid := valid AND s pos = s max;
                    IF NOT valid THEN error := "Invalid character: ""$""" FI
                ELSE
                    # invalid character #
                    valid := FALSE;
                    error := "Invalid character: """ + addr[ s pos ] + """"
                FI
            OD;
            IF valid THEN
                # address is OK so far - check it had four octets and one mask length #
                valid := dot count   =  4 # note a leading "." was added for parsing #
                     AND slash count =  1
                     AND bits count  >  0
                     AND bits count  < 33;
                IF NOT valid THEN error := "too many dots, slashes or bits" FI
            FI;
            CIDR( address, bits count, valid, error )
         END # TOCIDR # ;
    # returns address in canonical form #
    OP CANONICALISE = ( CIDR address )CIDR:
         IF NOT valid OF address THEN
            # invalid address #
            address
         ELSE
            # valid address - retain the top most bits #
            CIDR( address OF address AND (   16rffffffff
                                         SHL ( 32 - network bits OF address )
                                         )
                , network bits OF address
                , TRUE
                , ""
                )
         FI # CANONICALISE # ;
    # returns a readable form of address #
    OP TOSTRING = ( CIDR address )STRING:
         BEGIN
             [ 1 : 4 ]INT octet;
             BITS addr := address OF address;
             FOR o pos FROM UPB octet BY -1 TO LWB octet DO
                 octet[ o pos ] := ABS ( addr AND 16rff );
                 addr           := addr SHR 8
             OD;
             STRING result := whole( octet[ LWB octet ], 0 );
             FOR o pos FROM 1 + LWB octet TO UPB octet DO
                 result +:= "." + whole( octet[ o pos ], 0 )
             OD;
             result + "/" + whole( network bits OF address, 0 )
         END # TOSTRING # ;
    # task examples :          input                 expected result    #
    [,]STRING test cases = ( ( "87.70.141.1/22",     "87.70.140.0/22"   )
                           , ( "36.18.154.103/12",   "36.16.0.0/12"     )
                           , ( "62.62.197.11/29",    "62.62.197.8/29"   )
                           , ( "67.137.119.181/4",   "64.0.0.0/4"       )
                           , ( "161.214.74.21/24",   "161.214.74.0/24"  )
                           , ( "184.232.176.184/18", "184.232.128.0/18" )
                           );
    FOR t pos FROM 1 LWB test cases TO 1 UPB test cases DO
        STRING addr  = test cases[ t pos, 1 ];
        CIDR   canon = CANONICALISE TOCIDR addr;
        IF NOT valid OF canon THEN
            print( ( "Invalid address: """, addr, """: ", error OF canon, newline ) )
        ELSE
            STRING actual   = TOSTRING canon;
            STRING expected = test cases[ t pos, 2 ];
            print( ( addr
                   , " -> "
                   , actual
                   , IF expected = actual THEN "" ELSE " ** EXPECTED: """ + expected + """" FI
                   , newline
                   )
                 )
        FI
    OD
END


#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>

typedef struct cidr_tag {
    uint32_t address;
    unsigned int mask_length;
} cidr_t;

// Convert a string in CIDR format to an IPv4 address and netmask,
// if possible. Also performs CIDR canonicalization.
bool cidr_parse(const char* str, cidr_t* cidr) {
    int a, b, c, d, m;
    if (sscanf(str, "%d.%d.%d.%d/%d", &a, &b, &c, &d, &m) != 5)
        return false;
    if (m < 1 || m > 32
        || a < 0 || a > UINT8_MAX
        || b < 0 || b > UINT8_MAX
        || c < 0 || c > UINT8_MAX
        || d < 0 || d > UINT8_MAX)
        return false;
    uint32_t mask = ~((1 << (32 - m)) - 1);
    uint32_t address = (a << 24) + (b << 16) + (c << 8) + d;
    address &= mask;
    cidr->address = address;
    cidr->mask_length = m;
    return true;
}

// Write a string in CIDR notation into the supplied buffer.
void cidr_format(const cidr_t* cidr, char* str, size_t size) {
    uint32_t address = cidr->address;
    unsigned int d = address & UINT8_MAX;
    address >>= 8;
    unsigned int c = address & UINT8_MAX;
    address >>= 8;
    unsigned int b = address & UINT8_MAX;
    address >>= 8;
    unsigned int a = address & UINT8_MAX;
    snprintf(str, size, "%u.%u.%u.%u/%u", a, b, c, d,
             cidr->mask_length);
}

int main(int argc, char** argv) {
    const char* tests[] = {
        "87.70.141.1/22",
        "36.18.154.103/12",
        "62.62.197.11/29",
        "67.137.119.181/4",
        "161.214.74.21/24",
        "184.232.176.184/18"
    };
    for (int i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
        cidr_t cidr;
        if (cidr_parse(tests[i], &cidr)) {
            char out[32];
            cidr_format(&cidr, out, sizeof(out));
            printf("%-18s -> %s\n", tests[i], out);
        } else {
            fprintf(stderr, "%s: invalid CIDR\n", tests[i]);
        }
    }
    return 0;
}


#include <cstdint>
#include <iomanip>
#include <iostream>
#include <sstream>

// Class representing an IPv4 address + netmask length
class ipv4_cidr {
public:
    ipv4_cidr() {}
    ipv4_cidr(std::uint32_t address, unsigned int mask_length)
        : address_(address), mask_length_(mask_length) {}
    std::uint32_t address() const {
        return address_;
    }
    unsigned int mask_length() const {
        return mask_length_;
    }
    friend std::istream& operator>>(std::istream&, ipv4_cidr&);
private:
    std::uint32_t address_ = 0;
    unsigned int mask_length_ = 0;
};

// Stream extraction operator, also performs canonicalization
std::istream& operator>>(std::istream& in, ipv4_cidr& cidr) {
    int a, b, c, d, m;
    char ch;
    if (!(in >> a >> ch) || a < 0 || a > UINT8_MAX || ch != '.'
        || !(in >> b >> ch) || b < 0 || b > UINT8_MAX || ch != '.'
        || !(in >> c >> ch) || c < 0 || c > UINT8_MAX || ch != '.'
        || !(in >> d >> ch) || d < 0 || d > UINT8_MAX || ch != '/'
        || !(in >> m) || m < 1 || m > 32) {
        in.setstate(std::ios_base::failbit);
        return in;
    }
    uint32_t mask = ~((1 << (32 - m)) - 1);
    uint32_t address = (a << 24) + (b << 16) + (c << 8) + d;
    address &= mask;
    cidr.address_ = address;
    cidr.mask_length_ = m;
    return in;
}

// Stream insertion operator
std::ostream& operator<<(std::ostream& out, const ipv4_cidr& cidr) {
    uint32_t address = cidr.address();
    unsigned int d = address & UINT8_MAX;
    address >>= 8;
    unsigned int c = address & UINT8_MAX;
    address >>= 8;
    unsigned int b = address & UINT8_MAX;
    address >>= 8;
    unsigned int a = address & UINT8_MAX;
    out << a << '.' << b << '.' << c << '.' << d << '/'
        << cidr.mask_length();
    return out;
}

int main(int argc, char** argv) {
    const char* tests[] = {
        "87.70.141.1/22",
        "36.18.154.103/12",
        "62.62.197.11/29",
        "67.137.119.181/4",
        "161.214.74.21/24",
        "184.232.176.184/18"
    };
    for (auto test : tests) {
        std::istringstream in(test);
        ipv4_cidr cidr;
        if (in >> cidr)
            std::cout << std::setw(18) << std::left << test << " -> "
                << cidr << '\n';
        else
            std::cerr << test << ": invalid CIDR\n";
    }
    return 0;
}


using System;
using System.Net;
using System.Linq;

public class Program
{
    public static void Main()
    {
        string[] tests = {
            "87.70.141.1/22",
            "36.18.154.103/12",
            "62.62.197.11/29",
            "67.137.119.181/4",
            "161.214.74.21/24",
            "184.232.176.184/18"
        };

        foreach (string t in tests) Console.WriteLine($"{t}   =>   {Canonicalize(t)}");
    }

    static string Canonicalize(string cidr) => CIDR.Parse(cidr).Canonicalize().ToString();
}

readonly struct CIDR
{
    public readonly IPAddress ip;
    public readonly int length;

    public static CIDR Parse(string cidr)
    {
        string[] parts = cidr.Split('/');
        return new CIDR(IPAddress.Parse(parts[0]), int.Parse(parts[1]));
    }

    public CIDR(IPAddress ip, int length) => (this.ip, this.length) = (ip, length);

    public CIDR Canonicalize() =>
        new CIDR(
            new IPAddress(
                ToBytes(
                    ToInt(
                        ip.GetAddressBytes()
                    )
                    & ~((1 << (32 - length)) - 1)
                )
            ),
            length
        );

    private int ToInt(byte[] bytes) => bytes.Aggregate(0, (n, b) => (n << 8) | b);

    private byte[] ToBytes(int n)
    {
        byte[] bytes = new byte[4];
        for (int i = 3; i >= 0; i--) {
            bytes[i] = (byte)(n & 0xFF);
            n >>= 8;
        }
        return bytes;
    }

    public override string ToString() => $"{ip}/{length}";
}


(defun ip->bit-vector (ip)
  (flet ((int->bits (int)
           (loop :for i :below 8
                 :collect (if (logbitp i int) 1 0) :into bits
                 :finally (return (nreverse bits)))))
    (loop :repeat 4
          :with start := 0
          :for pos := (position #\. ip :start start)
          :collect (parse-integer ip :start start :end pos) :into res
          :while pos
          :do (setf start (1+ pos))
          :finally (return (apply #'concatenate 'bit-vector (mapcar #'int->bits res))))))

(defun bit-vector->ip (vec &optional n)
  (loop :repeat 4
        :for end :from 8 :by 8
        :for start := (- end 8)
        :for sub := (subseq vec start end)
        :collect (parse-integer (map 'string #'digit-char sub) :radix 2) :into res
        :finally (return (format nil "~{~D~^.~}[email protected][/~A~]" res n))))

(defun canonicalize-cidr (cidr)
  (let* ((n (position #\/ cidr))
         (ip (subseq cidr 0 n))
         (sn (parse-integer cidr :start (1+ n)))
         (ip* (ip->bit-vector ip))
         (canonical-ip (fill ip* 0 :start sn)))
    (bit-vector->ip canonical-ip sn)))

(loop :for cidr :in '("36.18.154.103/12" "62.62.197.11/29"
                      "67.137.119.181/4" "161.214.74.21/24"
                      "184.232.176.184/18")
      :for ccidr := (canonicalize-cidr cidr)
      :do (format t "~&~A~20,T→  ~A~%" cidr ccidr))


USING: command-line formatting grouping io kernel math.parser
namespaces prettyprint sequences splitting ;
IN: rosetta-code.canonicalize-cidr

! canonicalize a CIDR block: make sure none of the host bits are set
command-line get [ lines ] when-empty
[
    ! ( CIDR-IP -- bits-in-network-part dotted-decimal )
    "/" split first2 string>number swap

    ! get IP as binary string
    "." split [ string>number "%08b" sprintf ] map "" join

    ! replace the host part with all zeros
    over cut length [ CHAR: 0 ] "" replicate-as append

    ! convert back to dotted-decimal
    8 group [ bin> number>string ] map "." join swap

    ! and output
    "%s/%d\n" printf
] each


package main

import (
    "fmt"
    "log"
    "strconv"
    "strings"
)

func check(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

// canonicalize a CIDR block: make sure none of the host bits are set
func canonicalize(cidr string) string {
    // dotted-decimal / bits in network part
    split := strings.Split(cidr, "/")
    dotted := split[0]
    size, err := strconv.Atoi(split[1])
    check(err)

    // get IP as binary string
    var bin []string
    for _, n := range strings.Split(dotted, ".") {
        i, err := strconv.Atoi(n)
        check(err)
        bin = append(bin, fmt.Sprintf("%08b", i))
    }
    binary := strings.Join(bin, "")

    // replace the host part with all zeros
    binary = binary[0:size] + strings.Repeat("0", 32-size)

    // convert back to dotted-decimal
    var canon []string
    for i := 0; i < len(binary); i += 8 {
        num, err := strconv.ParseInt(binary[i:i+8], 2, 64)
        check(err)
        canon = append(canon, fmt.Sprintf("%d", num))
    }

    // and return
    return strings.Join(canon, ".") + "/" + split[1]
}

func main() {
    tests := []string{
        "87.70.141.1/22",
        "36.18.154.103/12",
        "62.62.197.11/29",
        "67.137.119.181/4",
        "161.214.74.21/24",
        "184.232.176.184/18",
    }

    for _, test := range tests {
        fmt.Printf("%-18s -> %s\n", test, canonicalize(test))
    }
}


use fmt;
use net::ip;
use strings;

export fn main() void = {
	const array = ["87.70.141.1/22",
		"36.18.154.103/12",
		"62.62.197.11/29",
		"67.137.119.181/4",
		"161.214.74.21/24",
		"184.232.176.184/18"];

	for (let i = 0z; i < len(array); i += 1) {
		match (canonicalizecidr(array[i])) {
		case let s: str =>
			fmt::printfln("{:-18} => {}", array[i], s)!;
			free(s);
		case net::ip::invalid =>
			fmt::errorfln("Error: invalid item: {}", array[i])!;
		};
	};
};

fn canonicalizecidr(a: str) (str | net::ip::invalid) = {
	const sub = net::ip::parsecidr(a)?;
	match (sub.addr) {
	case let addr4: net::ip::addr4 => void;
	case net::ip::addr6 => return net::ip::invalid;
	};

	const net = sub.addr as [4]u8;
	const msk = sub.mask as [4]u8;

	const net: u32 = net[0]: u32 << 24 | net[1]: u32 << 16 | net[2]: u32 << 8 | net[3]: u32;
	const msk: u32 = msk[0]: u32 << 24 | msk[1]: u32 << 16 | msk[2]: u32 << 8 | msk[3]: u32;

	const result: u32 = net & msk;
	return fmt::asprintf("{}.{}.{}.{}/{}",
		result >> 24,
		0xff & result >> 16,
		0xff & result >> 8,
		0xff & result,
		strings::cut(a, "/").1);
};


import Control.Monad (guard)
import Data.Bits ((.|.), (.&.), complement, shiftL, shiftR, zeroBits)
import Data.Maybe (listToMaybe)
import Data.Word (Word32, Word8)
import Text.ParserCombinators.ReadP (ReadP, char, readP_to_S)
import Text.Printf (printf)
import Text.Read.Lex (readDecP)

-- A 32-bit IPv4 address, with a netmask applied, and the number of leading bits
-- that are in the network portion of the address.
data CIDR = CIDR Word32 Word8

-- Convert a string to a CIDR, or nothing if it's invalid.
cidrRead :: String -> Maybe CIDR
cidrRead = listToMaybe . map fst . readP_to_S cidrP

-- Convert the CIDR to a string.
cidrShow :: CIDR -> String
cidrShow (CIDR addr n) = let (a, b, c, d) = octetsFrom addr
                         in printf "%u.%u.%u.%u/%u" a b c d n

-- Parser for the string representation of a CIDR.  For a successful parse the
-- string must have the form a.b.c.d/n, where each of a, b, c and d are decimal
-- numbers in the range [0, 255] and n is a decimal number in the range [0, 32].
cidrP :: ReadP CIDR
cidrP = do a <- octetP <* char '.'
           b <- octetP <* char '.'
           c <- octetP <* char '.'
           d <- octetP <* char '/'
           n <- netBitsP
           return $ CIDR (addrFrom a b c d .&. netmask n) n
  where octetP   = wordP 255
        netBitsP = wordP  32

-- Parser for a decimal string, whose value is in the range [0, lim].
--
-- We want the limit argument to be an Integer, so that we can detect values
-- that are too large, rather than having them silently wrap.
wordP :: Integral a => Integer -> ReadP a
wordP lim = do n <- readDecP
               guard $ n <= lim
               return $ fi n

-- The octets of an IPv4 address.
octetsFrom :: Word32 -> (Word8, Word8, Word8, Word8)
octetsFrom addr = (oct addr 3, oct addr 2, oct addr 1, oct addr 0)
  where oct w n = fi $ w `shiftR` (8*n) .&. 0xff

-- An IPv4 address from four octets.  `ipAddr4 1 2 3 4' is the address 1.2.3.4.
addrFrom :: Word8 -> Word8 -> Word8 -> Word8 -> Word32
addrFrom a b c d = 0 <<+ a <<+ b <<+ c <<+ d
  where w <<+ o = w `shiftL` 8 .|. fi o

-- The value `netmask n' is the netmask whose leftmost n bits are 1, and the
-- remainder are 0.
netmask :: Word8 -> Word32
netmask n = complement $ complement zeroBits `shiftR` fi n

fi :: (Integral a, Num b) => a -> b
fi = fromIntegral

test :: String -> IO ()
test str = do
  let cidrStr = maybe "invalid CIDR string" cidrShow (cidrRead str)
  printf "%-18s -> %s\n" str cidrStr

main :: IO ()
main = do
  test "87.70.141.1/22"
  test "36.18.154.103/12"
  test "62.62.197.11/29"
  test "67.137.119.181/4"
  test "161.214.74.21/24"
  test "184.232.176.184/18"

  test "184.256.176.184/12" -- octet value is too large
  test "184.232.176.184/33" -- netmask size is too large
  test "184.232.184/18"     -- too few components


import java.text.MessageFormat;
import java.text.ParseException;

public class CanonicalizeCIDR {
    public static void main(String[] args) {
        for (String test : TESTS) {
            try {
                CIDR cidr = new CIDR(test);
                System.out.printf("%-18s -> %s\n", test, cidr.toString());
            } catch (Exception ex) {
                System.err.printf("Error parsing '%s': %s\n", test, ex.getLocalizedMessage());
            }
        }
    }

    private static class CIDR {
        private CIDR(int address, int maskLength) {
            this.address = address;
            this.maskLength = maskLength;
        }

        private CIDR(String str) throws Exception {
            Object[] args = new MessageFormat(FORMAT).parse(str);
            int address = 0;
            for (int i = 0; i < 4; ++i) {
                int a = ((Number)args[i]).intValue();
                if (a < 0 || a > 255)
                    throw new Exception("Invalid IP address");
                address <<= 8;
                address += a;
            }
            int maskLength = ((Number)args[4]).intValue();
            if (maskLength < 1 || maskLength > 32)
                throw new Exception("Invalid mask length");
            int mask = ~((1 << (32 - maskLength)) - 1);
            this.address = address & mask;
            this.maskLength = maskLength;
        }

        public String toString() {
            int address = this.address;
            int d = address & 0xFF;
            address >>= 8;
            int c = address & 0xFF;
            address >>= 8;
            int b = address & 0xFF;
            address >>= 8;
            int a = address & 0xFF;
            Object[] args = { a, b, c, d, maskLength };
            return new MessageFormat(FORMAT).format(args);
        }

        private int address;
        private int maskLength;
        private static final String FORMAT = "{0,number,integer}.{1,number,integer}.{2,number,integer}.{3,number,integer}/{4,number,integer}";
    };

    private static final String[] TESTS = {
        "87.70.141.1/22",
        "36.18.154.103/12",
        "62.62.197.11/29",
        "67.137.119.181/4",
        "161.214.74.21/24",
        "184.232.176.184/18"
    };
}


const canonicalize = s => {

    // Prepare a DataView over a 16 Byte Array buffer.
    // Initialised to all zeros.
    const dv = new DataView(new ArrayBuffer(16));

    // Get the ip-address and cidr components
    const [ip, cidr] = s.split('/');

    // Make sure the cidr component is a usable int, and
    // default to 32 if it does not exist.
    const cidrInt = parseInt(cidr || 32, 10);

    // Populate the buffer with uint8 ip address components.
    // Use zero as the default for shorthand pool definitions.
    ip.split('.').forEach(
        (e, i) => dv.setUint8(i, parseInt(e || 0, 10))
    );

    // Grab the whole buffer as a uint32
    const ipAsInt = dv.getUint32(0);

    // Zero out the lower bits as per the CIDR number.
    const normIpInt = (ipAsInt >> 32 - cidrInt) << 32 - cidrInt;

    // Plonk it back into the buffer
    dv.setUint32(0, normIpInt);

    // Read each of the uint8 slots in the buffer and join them with a dot.
    const canonIp = [...'0123'].map((e, i) => dv.getUint8(i)).join('.');

    // Attach the cidr number to the back of the normalised IP address.
    return [canonIp, cidrInt].join('/');
  }

  const test = s => console.log(s, '->', canonicalize(s));
  [
    '255.255.255.255/10',
    '87.70.141.1/22',
    '36.18.154.103/12',
    '62.62.197.11/29',
    '67.137.119.181/4',
    '161.214.74.21/24',
    '184.232.176.184/18',
    '10.207.219.251/32',
    '10.207.219.251',
    '110.200.21/4',
    '10..55/8',
    '10.../8'
  ].forEach(test)


using Sockets

function canonCIDR(cidr::String)
    cidr = replace(cidr, r"\.(\.|\/)" => s".0\1") # handle ..
    cidr = replace(cidr, r"\.(\.|\/)" => s".0\1") # handle ...
    ip = split(cidr, "/")
    dig = length(ip) > 1 ? 2^(32 - parse(UInt8, ip[2])) : 1
    ip4 = IPv4(UInt64(IPv4(ip[1])) & (0xffffffff - dig + 1))
    return length(ip) == 1 ? "$ip4/32" : "$ip4/$(ip[2])"
end

println(canonCIDR("87.70.141.1/22"))
println(canonCIDR("100.68.0.18/18"))
println(canonCIDR("10.4.30.77/30"))
println(canonCIDR("10.207.219.251/32"))
println(canonCIDR("10.207.219.251"))
println(canonCIDR("110.200.21/4"))
println(canonCIDR("10..55/8"))
println(canonCIDR("10.../8"))


ClearAll[CanonicalizeCIDR]
CanonicalizeCIDR[str_String] := Module[{i, ip, chop, keep, change},
  If[StringMatchQ[str, "*.*.*.*/*"],
   i = StringSplit[str, "." | "/"];
   i = Interpreter["Integer"] /@ i;
   If[MatchQ[i, {_Integer, _Integer, _Integer, _Integer, _Integer}],
    If[AllTrue[i, Between[{0, 255}]],
     {ip, {chop}} = TakeDrop[i, 4];
     ip = Catenate[IntegerDigits[ip, 2, 8]];
     {keep, change} = TakeDrop[ip, chop];
     change = ConstantArray[0, Length[change]];
     ip = Partition[Join[keep, change], 8];
     ip = ToString[FromDigits[#, 2]] & /@ ip;
     StringRiffle[ip, "."] <> "/" <> ToString[chop]
     ,
     Failure["Invalid range of numbers", <|"Input" -> str|>]
     ]
    ,
    Failure["Invalid format", <|"Input" -> str|>]
    ]
   ]
  ]
CanonicalizeCIDR["87.70.141.1/22"]
CanonicalizeCIDR["36.18.154.103/12"]
CanonicalizeCIDR["62.62.197.11/29"]
CanonicalizeCIDR["67.137.119.181/4"]
CanonicalizeCIDR["161.214.74.21/24"]
CanonicalizeCIDR["184.232.176.184/18"]


import net
import strutils

proc canonicalize*(address: var IpAddress; nbits: Positive) =
  ## Canonicalize an IP address.

  var zbits = 32 - nbits    # Number of bits to reset.

  # We process byte by byte which avoids byte order issues.
  for idx in countdown(address.address_v4.high, address.address_v4.low):
    if zbits == 0:
      # No more bits to reset.
      break
    if zbits >= 8:
      # Reset the current byte and continue with the remaining bits.
      address.address_v4[idx] = 0
      dec zbits, 8
    else:
      # Use a mask to reset the bits.
      address.address_v4[idx] = address.address_v4[idx] and (0xff'u8 shl zbits)
      zbits = 0

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

  import strformat

  var ipAddress: IpAddress
  var nbits: int

  for address in ["87.70.141.1/22", "36.18.154.103/12", "62.62.197.11/29",
                  "67.137.119.181/4", "161.214.74.21/24", "184.232.176.184/18"]:

    # Parse the address.
    let parts = address.split('/')
    try:
      ipAddress = parseIpAddress(parts[0])
      if ipAddress.family == IPV6:
        raise newException(ValueError, "")
    except ValueError:
      echo "Invalid IP V4 address: ", parts[0]
      quit(QuitFailure)

    # Check the number of bits.
    try:
      nbits = parseInt(parts[1])
      if nbits notin 1..32:
        raise newException(ValueError, "")
    except ValueError:
      echo "Invalid number of bits: ", parts[1]
      quit(QuitFailure)

    # Canonicalize the address and display the result.
    ipAddress.canonicalize(nbits)
    echo &"{address:<18}  ⇢  {ipAddress}/{nbits}"


#!/usr/bin/env perl
use v5.16;
use Socket qw(inet_aton inet_ntoa);

# canonicalize a CIDR block: make sure none of the host bits are set
if ([email protected]ARGV) {
   chomp(@ARGV = <>);
}

for (@ARGV) {

  # dotted-decimal / bits in network part
  my ($dotted, $size) = split m#/#;

  # get IP as binary string
  my $binary = sprintf "%032b", unpack('N', inet_aton $dotted);

  # Replace the host part with all zeroes
  substr($binary, $size) = 0 x (32 - $size);

  # Convert back to dotted-decimal
  $dotted = inet_ntoa(pack 'B32', $binary);

  # And output
  say "$dotted/$size";
}