import std.algorithm; //.comparison for min and max
import std.stdio;

immutable EPS = 0.001;
immutable EPS_SQUARE = EPS * EPS;

double side(double x1, double y1, double x2, double y2, double x, double y) {
    return (y2 - y1) * (x - x1) + (-x2 + x1) * (y - y1);
}

bool naivePointInTriangle(double x1, double y1, double x2, double y2, double x3, double y3, double x, double y) {
    double checkSide1 = side(x1, y1, x2, y2, x, y) >= 0;
    double checkSide2 = side(x2, y2, x3, y3, x, y) >= 0;
    double checkSide3 = side(x3, y3, x1, y1, x, y) >= 0;
    return checkSide1 && checkSide2 && checkSide3;
}

bool pointInTriangleBoundingBox(double x1, double y1, double x2, double y2, double x3, double y3, double x, double y) {
    double xMin = min(x1, x2, x3) - EPS;
    double xMax = max(x1, x2, x3) + EPS;
    double yMin = min(y1, y2, y3) - EPS;
    double yMax = max(y1, y2, y3) + EPS;
    return !(x < xMin || xMax < x || y < yMin || yMax < y);
}

double distanceSquarePointToSegment(double x1, double y1, double x2, double y2, double x, double y) {
    double p1_p2_squareLength = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
    double dotProduct = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / p1_p2_squareLength;
    if (dotProduct < 0) {
        return (x - x1) * (x - x1) + (y - y1) * (y - y1);
    } else if (dotProduct <= 1) {
        double p_p1_squareLength = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y);
        return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength;
    } else {
        return (x - x2) * (x - x2) + (y - y2) * (y - y2);
    }
}

bool accuratePointInTriangle(double x1, double y1, double x2, double y2, double x3, double y3, double x, double y) {
    if (!pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y)) {
        return false;
    }
    if (naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)) {
        return true;
    }
    if (distanceSquarePointToSegment(x1, y1, x2, y2, x, y) <= EPS_SQUARE) {
        return true;
    }
    if (distanceSquarePointToSegment(x2, y2, x3, y3, x, y) <= EPS_SQUARE) {
        return true;
    }
    if (distanceSquarePointToSegment(x3, y3, x1, y1, x, y) <= EPS_SQUARE) {
        return true;
    }
    return false;
}

void printPoint(double x, double y) {
    write('(', x, ", ", y, ')');
}

void printTriangle(double x1, double y1, double x2, double y2, double x3, double y3) {
    write("Triangle is [");
    printPoint(x1, y1);
    write(", ");
    printPoint(x2, y2);
    write(", ");
    printPoint(x3, y3);
    writeln(']');
}

void test(double x1, double y1, double x2, double y2, double x3, double y3, double x, double y) {
    printTriangle(x1, y1, x2, y2, x3, y3);
    write("Point ");
    printPoint(x, y);
    write(" is within triangle? ");
    writeln(accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y));
}

void main() {
    test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 0, 0);
    test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 0, 1);
    test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 3, 1);
    writeln;

    test(0.1, 0.1111111111111111, 12.5, 33.333333333333336, 25, 11.11111111111111, 5.414285714285714, 14.349206349206348);
    writeln;

    test(0.1, 0.1111111111111111, 12.5, 33.333333333333336, -12.5, 16.666666666666668, 5.414285714285714, 14.349206349206348);
    writeln;
}


for(i = 1; i <= 100; i++) {
    if(i % 3 == 0)
        write, format="%s", "Fizz";
    if(i % 5 == 0)
        write, format="%s", "Buzz";
    if(i % 3 && i % 5)
        write, format="%d", i;
    write, "";
}


USING: accessors fry io kernel locals math math.order sequences ;

TUPLE: point x y ;
C: <point> point
: >point< ( point -- x y ) [ x>> ] [ y>> ] bi ;

TUPLE: triangle p1 p2 p3 ;
C: <triangle> triangle
: >triangle< ( triangle -- x1 y1 x2 y2 x3 y3 )
    [ p1>> ] [ p2>> ] [ p3>> ] tri [ >point< ] [email protected] ;

:: point-in-triangle? ( point triangle -- ? )
    point >point< triangle >triangle< :> ( x y x1 y1 x2 y2 x3 y3 )
    y2 y3 - x1 * x3 x2 - y1 * + x2 y3 * + y2 x3 * - :> d
    y3 y1 - x * x1 x3 - y * + x1 y3 * - y1 x3 * + d / :> t1
    y2 y1 - x * x1 x2 - y * + x1 y2 * - y1 x2 * + d neg / :> t2
    t1 t2 + :> s

    t1 t2 [ 0 1 between? ] [email protected] and s 1 <= and ;

! Test if it works.

20 <iota> dup [ swap <point> ] cartesian-map                     ! Make a matrix of points
3 3 <point> 16 10 <point> 10 16 <point> <triangle>               ! Make a triangle
'[ [ _ point-in-triangle? "#" "." ? write ] each nl ] each nl    ! Show points inside the triangle with '#'


type p2d
    x as double   'define a two-dimensional point
    y as double
end type

function in_tri( A as p2d, B as p2d, C as p2d, P as p2d ) as boolean
    'uses barycentric coordinates to determine if point P is inside
    'the triangle defined by points A, B, C
    dim as double AreaD = (-B.y*C.x + A.y*(-B.x + C.x) + A.x*(B.y - C.y) + B.x*C.y)
    dim as double s = (A.y*C.x - A.x*C.y + (C.y - A.y)*P.x + (A.x - C.x)*P.y)/AreaD
    dim as double t = (A.x*B.y - A.y*B.x + (A.y - B.y)*P.x + (B.x - A.x)*P.y)/AreaD
    if s<=0 then return false
    if t<=0 then return false
    if s+t>=1 then return false
    return true
end function

dim as p2d A,B,C,P    'generate some arbitrary triangle
A.x = 4.14 : A.y = -1.12
B.x = 8.1 : B.y =-4.9
C.x = 1.5: C.y = -9.3

for y as double = -0.25 to -9.75 step -0.5    'display a 10x10 square
    for x as double = 0.125 to 9.875 step 0.25
        P.x = x : P.y = y
        if in_tri(A,B,C,P) then print "@"; else print ".";   'with all the points inside the triangle indicated
    next x
    print
next y


foreach n in ([1..100]) {
   if(n % 3 == 0) print("Fizz");
   if(not (n%5)) "Buzz".print();
   if(n%3 and n%5) print(n);
   println();
}


package main

import (
    "fmt"
    "math"
)

const EPS = 0.001
const EPS_SQUARE = EPS * EPS

func side(x1, y1, x2, y2, x, y float64) float64 {
    return (y2-y1)*(x-x1) + (-x2+x1)*(y-y1)
}

func naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y float64) bool {
    checkSide1 := side(x1, y1, x2, y2, x, y) >= 0
    checkSide2 := side(x2, y2, x3, y3, x, y) >= 0
    checkSide3 := side(x3, y3, x1, y1, x, y) >= 0
    return checkSide1 && checkSide2 && checkSide3
}

func pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y float64) bool {
    xMin := math.Min(x1, math.Min(x2, x3)) - EPS
    xMax := math.Max(x1, math.Max(x2, x3)) + EPS
    yMin := math.Min(y1, math.Min(y2, y3)) - EPS
    yMax := math.Max(y1, math.Max(y2, y3)) + EPS
    return !(x < xMin || xMax < x || y < yMin || yMax < y)
}

func distanceSquarePointToSegment(x1, y1, x2, y2, x, y float64) float64 {
    p1_p2_squareLength := (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)
    dotProduct := ((x-x1)*(x2-x1) + (y-y1)*(y2-y1)) / p1_p2_squareLength
    if dotProduct < 0 {
        return (x-x1)*(x-x1) + (y-y1)*(y-y1)
    } else if dotProduct <= 1 {
        p_p1_squareLength := (x1-x)*(x1-x) + (y1-y)*(y1-y)
        return p_p1_squareLength - dotProduct*dotProduct*p1_p2_squareLength
    } else {
        return (x-x2)*(x-x2) + (y-y2)*(y-y2)
    }
}

func accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y float64) bool {
    if !pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y) {
        return false
    }
    if naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y) {
        return true
    }
    if distanceSquarePointToSegment(x1, y1, x2, y2, x, y) <= EPS_SQUARE {
        return true
    }
    if distanceSquarePointToSegment(x2, y2, x3, y3, x, y) <= EPS_SQUARE {
        return true
    }
    if distanceSquarePointToSegment(x3, y3, x1, y1, x, y) <= EPS_SQUARE {
        return true
    }
    return false
}

func main() {
    pts := [][2]float64{{0, 0}, {0, 1}, {3, 1}}
    tri := [][2]float64{{3.0 / 2, 12.0 / 5}, {51.0 / 10, -31.0 / 10}, {-19.0 / 5, 1.2}}
    fmt.Println("Triangle is", tri)
    x1, y1 := tri[0][0], tri[0][1]
    x2, y2 := tri[1][0], tri[1][1]
    x3, y3 := tri[2][0], tri[2][1]
    for _, pt := range pts {
        x, y := pt[0], pt[1]
        within := accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
        fmt.Println("Point", pt, "is within triangle?", within)
    }
    fmt.Println()
    tri = [][2]float64{{1.0 / 10, 1.0 / 9}, {100.0 / 8, 100.0 / 3}, {100.0 / 4, 100.0 / 9}}
    fmt.Println("Triangle is", tri)
    x1, y1 = tri[0][0], tri[0][1]
    x2, y2 = tri[1][0], tri[1][1]
    x3, y3 = tri[2][0], tri[2][1]
    x := x1 + (3.0/7)*(x2-x1)
    y := y1 + (3.0/7)*(y2-y1)
    pt := [2]float64{x, y}
    within := accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    fmt.Println("Point", pt, "is within triangle ?", within)
    fmt.Println()
    tri = [][2]float64{{1.0 / 10, 1.0 / 9}, {100.0 / 8, 100.0 / 3}, {-100.0 / 8, 100.0 / 6}}
    fmt.Println("Triangle is", tri)
    x3 = tri[2][0]
    y3 = tri[2][1]
    within = accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    fmt.Println("Point", pt, "is within triangle ?", within)
}


10 PIT1X! = 3 : PIT1Y! = 1.3 :    REM arbitrary triangle for demonstration
20 PIT2X! = 17.222 : PIT2Y! = 10
30 PIT3X! =  5.5 : PIT3Y! = 18.212
40 FOR PITPY! = 0 TO 19 STEP 1
50 FOR PITPX! = 0 TO 20 STEP .5
60 GOSUB 1000
70 IF PITRES% = 0 THEN PRINT "."; ELSE PRINT "#";
80 NEXT PITPX!
90 PRINT
100 NEXT PITPY!
110 END
1000 REM Detect if point is in triangle. Takes 8 double-precision
1010 REM values: (PIT1X!, PIT1Y!), (PIT2X!, PIT2Y!), (PIT3X!, PIT3Y!)
1020 REM for the coordinates of the corners of the triangle
1030 REM and (PITPX!, PITPY!) for the coordinates of the test point
1040 REM Returns PITRES%: 1=in triangle, 0=not in it
1050 PITDAR! = -PIT2Y!*PIT3X! + PIT1Y!*(-PIT2X! + PIT3X!) + PIT1X!*(PIT2Y - PIT3Y!) + PIT2X!*PIT3Y!
1060 PITXXS = (PIT1Y!*PIT3X! - PIT1X!*PIT3Y! + (PIT3Y! - PIT1Y!)*PITPX! + (PIT1X! - PIT3X!)*PITPY!)/PITDAR!
1070 PITXXT = (PIT1X!*PIT2Y! - PIT1Y!*PIT2X! + (PIT1Y! - PIT2Y!)*PITPX! + (PIT2X! - PIT1X!)*PITPY!)/PITDAR!
1080 PITRES% = 0
1090 IF PITXXS!<=0 THEN RETURN
1100 IF PITXXT!<=0 THEN RETURN
1110 IF PITXXS!+PITXXT!>=1 THEN RETURN
1120 PITRES% = 1
1130 RETURN


type Pt a = (a, a)

data Overlapping = Inside | Outside | Boundary
  deriving (Show, Eq)

data Triangle a = Triangle (Pt a) (Pt a) (Pt a)
  deriving Show

vertices (Triangle a b c) = [a, b, c]

-- Performs the affine transformation
-- which turns a triangle to Triangle (0,0) (0,s) (s,0)
-- where s is half of the triangles' area
toTriangle :: Num a => Triangle a -> Pt a -> (a, Pt a)
toTriangle t (x,y) = let
  [(x0,y0), (x1,y1), (x2,y2)] = vertices t
  s = x2*(y0-y1)+x0*(y1-y2)+x1*(-y0+y2)
  in  ( abs s
      , ( signum s * (x2*(-y+y0)+x0*(y-y2)+x*(-y0+y2))
        , signum s * (x1*(y-y0)+x*(y0-y1)+x0*(-y+y1))))

overlapping :: (Eq a, Ord a, Num a) =>
  Triangle a -> Pt a -> Overlapping
overlapping t p =  case toTriangle t p of
  (s, (x, y))
    | s == 0 && (x == 0 || y == 0)     -> Boundary
    | s == 0                           -> Outside
    | x > 0 && y > 0 && y < s - x      -> Inside
    | (x <= s && x >= 0) &&
      (y <= s && y >= 0) &&
      (x == 0 || y == 0 || y == s - x) -> Boundary
    | otherwise                        -> Outside


10 DEF FN m(a,b)=a-INT (a/b)*b
20 FOR a=1 TO 100
30 LET o$=""
40 IF FN m(a,3)=0 THEN LET o$="Fizz"
50 IF FN m(a,5)=0 THEN LET o$=o$+"Buzz"
60 IF o$="" THEN LET o$=STR$ a
70 PRINT o$
80 NEXT a


import java.util.Objects;

public class FindTriangle {
    private static final double EPS = 0.001;
    private static final double EPS_SQUARE = EPS * EPS;

    public static class Point {
        private final double x, y;

        public Point(double x, double y) {
            this.x = x;
            this.y = y;
        }

        public double getX() {
            return x;
        }

        public double getY() {
            return y;
        }

        @Override
        public String toString() {
            return String.format("(%f, %f)", x, y);
        }
    }

    public static class Triangle {
        private final Point p1, p2, p3;

        public Triangle(Point p1, Point p2, Point p3) {
            this.p1 = Objects.requireNonNull(p1);
            this.p2 = Objects.requireNonNull(p2);
            this.p3 = Objects.requireNonNull(p3);
        }

        public Point getP1() {
            return p1;
        }

        public Point getP2() {
            return p2;
        }

        public Point getP3() {
            return p3;
        }

        private boolean pointInTriangleBoundingBox(Point p) {
            var xMin = Math.min(p1.getX(), Math.min(p2.getX(), p3.getX())) - EPS;
            var xMax = Math.max(p1.getX(), Math.max(p2.getX(), p3.getX())) + EPS;
            var yMin = Math.min(p1.getY(), Math.min(p2.getY(), p3.getY())) - EPS;
            var yMax = Math.max(p1.getY(), Math.max(p2.getY(), p3.getY())) + EPS;
            return !(p.getX() < xMin || xMax < p.getX() || p.getY() < yMin || yMax < p.getY());
        }

        private static double side(Point p1, Point p2, Point p) {
            return (p2.getY() - p1.getY()) * (p.getX() - p1.getX()) + (-p2.getX() + p1.getX()) * (p.getY() - p1.getY());
        }

        private boolean nativePointInTriangle(Point p) {
            boolean checkSide1 = side(p1, p2, p) >= 0;
            boolean checkSide2 = side(p2, p3, p) >= 0;
            boolean checkSide3 = side(p3, p1, p) >= 0;
            return checkSide1 && checkSide2 && checkSide3;
        }

        private double distanceSquarePointToSegment(Point p1, Point p2, Point p) {
            double p1_p2_squareLength = (p2.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p2.getY() - p1.getY()) * (p2.getY() - p1.getY());
            double dotProduct = ((p.getX() - p1.getX()) * (p2.getX() - p1.getX()) + (p.getY() - p1.getY()) * (p2.getY() - p1.getY())) / p1_p2_squareLength;
            if (dotProduct < 0) {
                return (p.getX() - p1.getX()) * (p.getX() - p1.getX()) + (p.getY() - p1.getY()) * (p.getY() - p1.getY());
            }
            if (dotProduct <= 1) {
                double p_p1_squareLength = (p1.getX() - p.getX()) * (p1.getX() - p.getX()) + (p1.getY() - p.getY()) * (p1.getY() - p.getY());
                return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength;
            }
            return (p.getX() - p2.getX()) * (p.getX() - p2.getX()) + (p.getY() - p2.getY()) * (p.getY() - p2.getY());
        }

        private boolean accuratePointInTriangle(Point p) {
            if (!pointInTriangleBoundingBox(p)) {
                return false;
            }
            if (nativePointInTriangle(p)) {
                return true;
            }
            if (distanceSquarePointToSegment(p1, p2, p) <= EPS_SQUARE) {
                return true;
            }
            if (distanceSquarePointToSegment(p2, p3, p) <= EPS_SQUARE) {
                return true;
            }
            return distanceSquarePointToSegment(p3, p1, p) <= EPS_SQUARE;
        }

        public boolean within(Point p) {
            Objects.requireNonNull(p);
            return accuratePointInTriangle(p);
        }

        @Override
        public String toString() {
            return String.format("Triangle[%s, %s, %s]", p1, p2, p3);
        }
    }

    private static void test(Triangle t, Point p) {
        System.out.println(t);
        System.out.printf("Point %s is within triangle? %s\n", p, t.within(p));
    }

    public static void main(String[] args) {
        var p1 = new Point(1.5, 2.4);
        var p2 = new Point(5.1, -3.1);
        var p3 = new Point(-3.8, 1.2);
        var tri = new Triangle(p1, p2, p3);
        test(tri, new Point(0, 0));
        test(tri, new Point(0, 1));
        test(tri, new Point(3, 1));
        System.out.println();

        p1 = new Point(1.0 / 10, 1.0 / 9);
        p2 = new Point(100.0 / 8, 100.0 / 3);
        p3 = new Point(100.0 / 4, 100.0 / 9);
        tri = new Triangle(p1, p2, p3);
        var pt = new Point(p1.getX() + (3.0 / 7) * (p2.getX() - p1.getX()), p1.getY() + (3.0 / 7) * (p2.getY() - p1.getY()));
        test(tri, pt);
        System.out.println();

        p3 = new Point(-100.0 / 8, 100.0 / 6);
        tri = new Triangle(p1, p2, p3);
        test(tri, pt);
    }
}


def sum_of_squares(stream): reduce stream as $x (0; . + $x * $x);

def distanceSquared(P1; P2): sum_of_squares(P1[0]-P2[0], P1[1]-P2[1]);

# Emit {x1,y1, ...} for the input triangle
def xy:
  { x1: .[0][0],
    y1: .[0][1],
    x2: .[1][0],
    y2: .[1][1],
    x3: .[2][0],
    y3: .[2][1] };

def EPS: 0.001;
def EPS_SQUARE: EPS * EPS;


Point(x, y) = [x, y]
Triangle(a, b, c) = [a, b, c]
LEzero(x) =  x < 0 || isapprox(x, 0, atol=0.00000001)
GEzero(x) =  x > 0 || isapprox(x, 0, atol=0.00000001)

""" Determine which side of plane cut by line (p2, p3) p1 is on """
side(p1, p2, p3) = (p1[1] - p3[1]) * (p2[2] - p3[2]) - (p2[1] - p3[1]) * (p1[2] - p3[2])

"""
Determine if point is within triangle formed by points p1, p2, p3.
If so, the point will be on the same side of each of the half planes
defined by vectors p1p2, p2p3, and p3p1. Each z is positive if outside,
negative if inside such a plane. All should be positive or all negative
if point is within the triangle.
"""
function iswithin(point, p1, p2, p3)
    z1 = side(point, p1, p2)
    z2 = side(point, p2, p3)
    z3 = side(point, p3, p1)
    notanyneg = GEzero(z1) && GEzero(z2) && GEzero(z3)
    notanypos = LEzero(z1) && LEzero(z2) && LEzero(z3)
    return notanyneg || notanypos
end

const POINTS = [Point(0 // 1, 0 // 1), Point(0 // 1, 1 // 1), Point(3 // 1, 1 // 1),
    Point(1 // 10 + (3 // 7) * (100 // 8 - 1 // 10), 1 // 9 + (3 // 7) * (100 // 3 - 1 // 9)),
    Point(3 // 2, 12 // 5), Point(51 // 100, -31 // 100), Point(-19 // 50, 6 // 5),
    Point(1 // 10, 1 // 9), Point(25 / 2, 100 // 3), Point(25, 100 // 9),
    Point(-25 // 2, 50 // 3)
]

const TRI = [
    Triangle(POINTS[5], POINTS[6], POINTS[7]),
    Triangle(POINTS[8], POINTS[9], POINTS[10]),
    Triangle(POINTS[8], POINTS[9], POINTS[11])
]

for tri in TRI
    pstring(pt) = "[$(Float32(pt[1])), $(Float32(pt[2]))]"
    println("\nUsing triangle [", join([pstring(x) for x in tri], ", "), "]:")
    a, b, c = tri[1], tri[2], tri[3]
    for p in POINTS[1:4]
        isornot = iswithin(p, a, b, c) ? "is" : "is not"
        println("Point $(pstring(p)) $isornot within the triangle.")
    end
end


import kotlin.math.max
import kotlin.math.min

private const val EPS = 0.001
private const val EPS_SQUARE = EPS * EPS

private fun test(t: Triangle, p: Point) {
    println(t)
    println("Point $p is within triangle ? ${t.within(p)}")
}

fun main() {
    var p1 = Point(1.5, 2.4)
    var p2 = Point(5.1, -3.1)
    var p3 = Point(-3.8, 1.2)
    var tri = Triangle(p1, p2, p3)
    test(tri, Point(0.0, 0.0))
    test(tri, Point(0.0, 1.0))
    test(tri, Point(3.0, 1.0))
    println()
    p1 = Point(1.0 / 10, 1.0 / 9)
    p2 = Point(100.0 / 8, 100.0 / 3)
    p3 = Point(100.0 / 4, 100.0 / 9)
    tri = Triangle(p1, p2, p3)
    val pt = Point(p1.x + 3.0 / 7 * (p2.x - p1.x), p1.y + 3.0 / 7 * (p2.y - p1.y))
    test(tri, pt)
    println()
    p3 = Point(-100.0 / 8, 100.0 / 6)
    tri = Triangle(p1, p2, p3)
    test(tri, pt)
}

class Point(val x: Double, val y: Double) {
    override fun toString(): String {
        return "($x, $y)"
    }
}

class Triangle(private val p1: Point, private val p2: Point, private val p3: Point) {
    private fun pointInTriangleBoundingBox(p: Point): Boolean {
        val xMin = min(p1.x, min(p2.x, p3.x)) - EPS
        val xMax = max(p1.x, max(p2.x, p3.x)) + EPS
        val yMin = min(p1.y, min(p2.y, p3.y)) - EPS
        val yMax = max(p1.y, max(p2.y, p3.y)) + EPS
        return !(p.x < xMin || xMax < p.x || p.y < yMin || yMax < p.y)
    }

    private fun nativePointInTriangle(p: Point): Boolean {
        val checkSide1 = side(p1, p2, p) >= 0
        val checkSide2 = side(p2, p3, p) >= 0
        val checkSide3 = side(p3, p1, p) >= 0
        return checkSide1 && checkSide2 && checkSide3
    }

    private fun distanceSquarePointToSegment(p1: Point, p2: Point, p: Point): Double {
        val p1P2SquareLength = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
        val dotProduct = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / p1P2SquareLength
        if (dotProduct < 0) {
            return (p.x - p1.x) * (p.x - p1.x) + (p.y - p1.y) * (p.y - p1.y)
        }
        if (dotProduct <= 1) {
            val pP1SquareLength = (p1.x - p.x) * (p1.x - p.x) + (p1.y - p.y) * (p1.y - p.y)
            return pP1SquareLength - dotProduct * dotProduct * p1P2SquareLength
        }
        return (p.x - p2.x) * (p.x - p2.x) + (p.y - p2.y) * (p.y - p2.y)
    }

    private fun accuratePointInTriangle(p: Point): Boolean {
        if (!pointInTriangleBoundingBox(p)) {
            return false
        }
        if (nativePointInTriangle(p)) {
            return true
        }
        if (distanceSquarePointToSegment(p1, p2, p) <= EPS_SQUARE) {
            return true
        }
        return if (distanceSquarePointToSegment(p2, p3, p) <= EPS_SQUARE) {
            true
        } else distanceSquarePointToSegment(p3, p1, p) <= EPS_SQUARE
    }

    fun within(p: Point): Boolean {
        return accuratePointInTriangle(p)
    }

    override fun toString(): String {
        return "Triangle[$p1, $p2, $p3]"
    }

    companion object {
        private fun side(p1: Point, p2: Point, p: Point): Double {
            return (p2.y - p1.y) * (p.x - p1.x) + (-p2.x + p1.x) * (p.y - p1.y)
        }
    }
}


EPS = 0.001
EPS_SQUARE = EPS * EPS

function side(x1, y1, x2, y2, x, y)
    return (y2 - y1) * (x - x1) + (-x2 + x1) * (y - y1)
end

function naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    local checkSide1 = side(x1, y1, x2, y2, x, y) >= 0
    local checkSide2 = side(x2, y2, x3, y3, x, y) >= 0
    local checkSide3 = side(x3, y3, x1, y1, x, y) >= 0
    return checkSide1 and checkSide2 and checkSide3
end

function pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y)
    local xMin = math.min(x1, x2, x3) - EPS
    local xMax = math.max(x1, x2, x3) + EPS
    local yMin = math.min(y1, y2, y3) - EPS
    local yMax = math.max(y1, y2, y3) + EPS
    return not (x < xMin or xMax < x or y < yMin or yMax < y)
end

function distanceSquarePointToSegment(x1, y1, x2, y2, x, y)
    local p1_p2_squareLength = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
    local dotProduct = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / p1_p2_squareLength
    if dotProduct < 0 then
        return (x - x1) * (x - x1) + (y - y1) * (y - y1)
    end
    if dotProduct <= 1 then
        local p_p1_squareLength = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)
        return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength
    end
    return (x - x2) * (x - x2) + (y - y2) * (y - y2)
end

function accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    if not pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y) then
        return false
    end
    if naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y) then
        return true
    end
    if distanceSquarePointToSegment(x1, y1, x2, y2, x, y) <= EPS_SQUARE then
        return true
    end
    if distanceSquarePointToSegment(x2, y2, x3, y3, x, y) <= EPS_SQUARE then
        return true
    end
    if distanceSquarePointToSegment(x3, y3, x1, y1, x, y) <= EPS_SQUARE then
        return true
    end
    return false
end

function printPoint(x, y)
    io.write('('..x..", "..y..')')
end

function printTriangle(x1, y1, x2, y2, x3, y3)
    io.write("Triangle is [")
    printPoint(x1, y1)
    io.write(", ")
    printPoint(x2, y2)
    io.write(", ")
    printPoint(x3, y3)
    print("]")
end

function test(x1, y1, x2, y2, x3, y3, x, y)
    printTriangle(x1, y1, x2, y2, x3, y3)
    io.write("Point ")
    printPoint(x, y)
    print(" is within triangle? " .. tostring(accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)))
end

test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 0, 0)
test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 0, 1)
test(1.5, 2.4, 5.1, -3.1, -3.8, 1.2, 3, 1)
print()

test(0.1, 0.1111111111111111, 12.5, 33.333333333333336, 25, 11.11111111111111, 5.414285714285714, 14.349206349206348)
print()

test(0.1, 0.1111111111111111, 12.5, 33.333333333333336, -12.5, 16.666666666666668, 5.414285714285714, 14.349206349206348)
print()


import strformat

const
  Eps = 0.001
  Eps2 = Eps * Eps

type
  Point = tuple[x, y: float]
  Triangle = object
    p1, p2, p3: Point

func initTriangle(p1, p2, p3: Point): Triangle =
  Triangle(p1: p1, p2: p2, p3: p3)

func side(p1, p2, p: Point): float =
  (p2.y - p1.y) * (p.x - p1.x) + (-p2.x + p1.x) * (p.y - p1.y)

func distanceSquarePointToSegment(p1, p2, p: Point): float =
  let p1P2SquareLength = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y)
  let dotProduct = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / p1P2SquareLength
  if dotProduct < 0:
    return (p.x - p1.x) * (p.x - p1.x) + (p.y - p1.y) * (p.y - p1.y)
  if dotProduct <= 1:
    let pP1SquareLength = (p1.x - p.x) * (p1.x - p.x) + (p1.y - p.y) * (p1.y - p.y)
    return pP1SquareLength - dotProduct * dotProduct * p1P2SquareLength
  result = (p.x - p2.x) * (p.x - p2.x) + (p.y - p2.y) * (p.y - p2.y)

func pointInTriangleBoundingBox(t: Triangle; p: Point): bool =
  let xMin = min(t.p1.x, min(t.p2.x, t.p3.x)) - EPS
  let xMax = max(t.p1.x, max(t.p2.x, t.p3.x)) + EPS
  let yMin = min(t.p1.y, min(t.p2.y, t.p3.y)) - EPS
  let yMax = max(t.p1.y, max(t.p2.y, t.p3.y)) + EPS
  result = p.x in xMin..xMax and p.y in yMin..yMax

func nativePointInTriangle(t: Triangle; p: Point): bool =
  let checkSide1 = side(t.p1, t.p2, p) >= 0
  let checkSide2 = side(t.p2, t.p3, p) >= 0
  let checkSide3 = side(t.p3, t.p1, p) >= 0
  result = checkSide1 and checkSide2 and checkSide3

func accuratePointInTriangle(t: Triangle; p: Point): bool =
  if not t.pointInTriangleBoundingBox(p):
    return false
  if t.nativePointInTriangle(p):
    return true
  if distanceSquarePointToSegment(t.p1, t.p2, p) <= Eps2 or
     distanceSquarePointToSegment(t.p3, t.p1, p) <= Eps2:
    return true

func `$`(p: Point): string = &"({p.x}, {p.y})"

func `$`(t: Triangle): string = &"Triangle[{t.p1}, {t.p2}, {t.p3}]"

func contains(t: Triangle; p: Point): bool = t.accuratePointInTriangle(p)

when isMainModule:

  proc test(t: Triangle; p: Point) =
    echo t
    echo &"Point {p} is within triangle ? {p in t}"

  var p1: Point = (1.5, 2.4)
  var p2: Point = (5.1, -3.1)
  var p3: Point = (-3.8, 1.2)
  var tri = initTriangle(p1, p2, p3)
  test(tri, (0.0, 0.0))
  test(tri, (0.0, 1.0))
  test(tri, (3.0, 1.0))
  echo()
  p1 = (1 / 10, 1 / 9)
  p2 = (100 / 8, 100 / 3)
  p3 = (100 / 4, 100 / 9)
  tri = initTriangle(p1, p2, p3)
  let pt = (p1.x + 3.0 / 7 * (p2.x - p1.x), p1.y + 3.0 / 7 * (p2.y - p1.y))
  test(tri, pt)
  echo()
  p3 = (-100 / 8, 100 / 6)
  tri = initTriangle(p1, p2, p3)
  test(tri, pt)


# 20201123 added Perl programming solution

use strict;
use warnings;

use List::AllUtils qw(min max natatime);
use constant EPSILON        =>           0.001;
use constant EPSILON_SQUARE => EPSILON*EPSILON;

sub side {
   my ($x1, $y1, $x2, $y2, $x, $y) = @_;
   return ($y2 - $y1)*($x - $x1) + (-$x2 + $x1)*($y - $y1);
}

sub naivePointInTriangle {
   my ($x1, $y1, $x2, $y2, $x3, $y3, $x, $y) = @_;
   my $checkSide1 = side($x1, $y1, $x2, $y2, $x, $y) >= 0 ;
   my $checkSide2 = side($x2, $y2, $x3, $y3, $x, $y) >= 0 ;
   my $checkSide3 = side($x3, $y3, $x1, $y1, $x, $y) >= 0 ;
   return $checkSide1 && $checkSide2 && $checkSide3  || 0 ;
}

sub pointInTriangleBoundingBox {
   my ($x1, $y1, $x2, $y2, $x3, $y3, $x, $y) = @_;
   my $xMin = min($x1, min($x2, $x3)) - EPSILON;
   my $xMax = max($x1, max($x2, $x3)) + EPSILON;
   my $yMin = min($y1, min($y2, $y3)) - EPSILON;
   my $yMax = max($y1, max($y2, $y3)) + EPSILON;
   ( $x < $xMin || $xMax < $x || $y < $yMin || $yMax < $y ) ? 0 : 1
}

sub distanceSquarePointToSegment {
   my ($x1, $y1, $x2, $y2, $x, $y) = @_;
   my $p1_p2_squareLength = ($x2 - $x1)**2 + ($y2 - $y1)**2;
   my $dotProduct = ($x-$x1)*($x2-$x1)+($y-$y1)*($y2-$y1) ;
   if ( $dotProduct < 0 ) {
      return ($x - $x1)**2 + ($y - $y1)**2;
   } elsif ( $dotProduct <= $p1_p2_squareLength ) {
      my $p_p1_squareLength = ($x1 - $x)**2 + ($y1 - $y)**2;
      return $p_p1_squareLength - $dotProduct**2 / $p1_p2_squareLength;
   } else {
      return ($x - $x2)**2 + ($y - $y2)**2;
   }
}

sub accuratePointInTriangle {
   my ($x1, $y1, $x2, $y2, $x3, $y3, $x, $y) = @_;
   return 0 unless pointInTriangleBoundingBox($x1,$y1,$x2,$y2,$x3,$y3,$x,$y);
   return 1 if ( naivePointInTriangle($x1, $y1, $x2, $y2, $x3, $y3, $x, $y)
      or distanceSquarePointToSegment($x1, $y1, $x2, $y2, $x, $y) <= EPSILON_SQUARE
      or distanceSquarePointToSegment($x2, $y2, $x3, $y3, $x, $y) <= EPSILON_SQUARE
      or distanceSquarePointToSegment($x3, $y3, $x1, $y1, $x, $y) <= EPSILON_SQUARE);
   return 0
}

my @DATA = (1.5, 2.4, 5.1, -3.1, -3.8, 0.5);

for my $point ( [0,0] , [0,1] ,[3,1] ) {
   print "Point (", join(',',@$point), ") is within triangle ";
   my $iter = natatime 2, @DATA;
   while ( my @vertex = $iter->()) { print '(',join(',',@vertex),') ' }
   print ': ',naivePointInTriangle (@DATA, @$point) ? 'True' : 'False', "\n" ;
}


with javascript_semantics
constant p0 = {0,0},
         p1 = {0,1},
         p2 = {3,1},
         triangle = {{3/2, 12/5}, {51/10, -31/10}, {-19/5, 1/2}}
function inside(sequence p)
    return sort(convex_hull({p}&triangle))==sort(deep_copy(triangle))
end function
printf(1,"Point %v is with triangle %v?:%t\n",{p0,triangle,inside(p0)})
printf(1,"Point %v is with triangle %v?:%t\n",{p1,triangle,inside(p1)})
printf(1,"Point %v is with triangle %v?:%t\n",{p2,triangle,inside(p2)})


""" find if point is in a triangle """

from sympy.geometry import Point, Triangle

def sign(pt1, pt2, pt3):
    """ which side of plane cut by line (pt2, pt3) is pt1 on? """
    return (pt1.x - pt3.x) * (pt2.y - pt3.y) - (pt2.x - pt3.x) * (pt1.y - pt3.y)

def iswithin(point, pt1, pt2, pt3):
    """
    Determine if point is within triangle formed by points p1, p2, p3.
    If so, the point will be on the same side of each of the half planes
    defined by vectors p1p2, p2p3, and p3p1. zval is positive if outside,
    negative if inside such a plane. All should be positive or all negative
    if point is within the triangle.
    """
    zval1 = sign(point, pt1, pt2)
    zval2 = sign(point, pt2, pt3)
    zval3 = sign(point, pt3, pt1)
    notanyneg = zval1 >= 0 and zval2 >= 0 and zval3 >= 0
    notanypos = zval1 <= 0 and zval2 <= 0 and zval3 <= 0
    return notanyneg or notanypos

if __name__ == "__main__":
    POINTS = [Point(0, 0)]
    TRI = Triangle(Point(1.5, 2.4), Point(5.1, -3.1), Point(-3.8, 0.5))
    for pnt in POINTS:
        a, b, c = TRI.vertices
        isornot = "is" if iswithin(pnt, a, b, c) else "is not"
        print("Point", pnt, isornot, "within the triangle", TRI)


#lang racket/base

(define-syntax-rule (all-between-0..1? x ...)
  (and (<= 0 x 1) ...))

(define (point-in-triangle?/barycentric x1 y1 x2 y2 x3 y3)
  (let* ((y2-y3 (- y2 y3))
         (x1-x3 (- x1 x3))
         (x3-x2 (- x3 x2))
         (y1-y3 (- y1 y3))
         (d (+ (* y2-y3 x1-x3) (* x3-x2 y1-y3))))
    (λ (x y)
      (define a (/ (+ (* x3-x2 (- y y3)) (* y2-y3 (- x x3))) d))
      (define b (/ (- (* x1-x3 (- y y3)) (* y1-y3 (- x x3))) d))
      (define c (- 1 a b))
      (all-between-0..1? a b c))))

(define (point-in-triangle?/parametric x1 y1 x2 y2 x3 y3)
  (let ((dp (+ (* x1 (- y2 y3)) (* y1 (- x3 x2)) (* x2 y3) (- (* y2 x3)))))
    (λ (x y)
      (define t1 (/ (+ (* x (- y3 y1)) (* y (- x1 x3)) (- (* x1 y3)) (* y1 x3)) dp))
      (define t2 (/ (+ (* x (- y2 y1)) (* y (- x1 x2)) (- (* x1 y2)) (* y1 x2)) (- dp)))
      (all-between-0..1? t1 t2 (+ t1 t2)))))

(define (point-in-triangle?/dot-product X1 Y1 X2 Y2 X3 Y3)
  (λ (x y)
    (define (check-side x1 y1 x2 y2)
      (>= (+ (* (- y2 y1) (- x x1)) (* (- x1 x2) (- y y1))) 0))
    (and
     (check-side X1 Y1 X2 Y2)
     (check-side X2 Y2 X3 Y3)
     (check-side X3 Y3 X1 Y1))))

(module+ main
  (require rackunit)

  (define (run-tests point-in-triangle?)
    (define pit?-1 (point-in-triangle? #e1.5 #e2.4 #e5.1 #e-3.1 #e-3.8 #e1.2))
    (check-true (pit?-1 0 0))
    (check-true (pit?-1 0 1))
    (check-false (pit?-1 3 1))
    (check-true ((point-in-triangle? 1/10 1/9 25/2 100/3  25   10/9) #e5.414285714285714 #e14.349206349206348))
    ; exactly speaking, point is _not_ in the triangle
    (check-false ((point-in-triangle? 1/10 1/9 25/2 100/3 -25/2 50/3) #e5.414285714285714 #e14.349206349206348)))

  (run-tests point-in-triangle?/barycentric)
  (run-tests point-in-triangle?/parametric)
  (run-tests point-in-triangle?/dot-product))


class Point {
    has Real $.x is rw;
    has Real $.y is rw;
    method gist { [~] '(', self.x,', ', self.y, ')' };
}

sub sign (Point $a, Point $b, Point $c) {
    ($b.x - $a.x)*($c.y - $a.y) - ($b.y - $a.y)*($c.x - $a.x);
}

sub triangle (*@points where *.elems == 6) {
    @points.batch(2).map: { Point.new(:x(.[0]),:y(.[1])) };
}

sub is-within ($point, @triangle is copy) {
   my @signs = sign($point, |(@triangle.=rotate)[0,1]) xx 3;
   so (all(@signs) >= 0) or so(all(@signs) <= 0);
}

my @triangle = triangle((1.5, 2.4), (5.1, -3.1), (-3.8, 0.5));

for Point.new(:x(0),:y(0)),
    Point.new(:x(0),:y(1)),
    Point.new(:x(3),:y(1))
  -> $point {
    say "Point {$point.gist} is within triangle {join ', ', @triangle».gist}: ",
        $point.&is-within: @triangle
}


/*REXX program determines if a   specified point   is within a   specified triangle.    */
parse arg p a b c .                              /*obtain optional arguments from the CL*/
if p=='' | p==","  then p= '(0,0)'               /*Not specified?  Then use the default.*/
if a=='' | a==","  then a= '(1.5,2.4)'           /* "      "         "   "   "     "    */
if b=='' | b==","  then b= '(5.1,-3.1)'          /* "      "         "   "   "     "    */
if c=='' | c==","  then c= '(-3.8,0.5)'          /* "      "         "   "   "     "    */
if  ?(p, a, b, c)  then @= ' is '                /*Is the point  inside the triangle ?  */
                   else @= " isn't "             /* "  "    "   outside  "      "       */
say 'point'   p    @    " within the triangle "      a     ','       b       ","       c
exit 0                                           /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
cert: parse arg z,W; if datatype(z,'N')  then return z; call serr z /*return coördinate.*/
serr: say W 'data point '  z  " isn't numeric or missing."; exit 13 /*tell error message*/
x: procedure; parse arg "(" x ','      ;  return cert(x,"X")  /*return the X coördinate.*/
y: procedure; parse arg       ',' y ")";  return cert(y,"Y")  /*   "    "  Y      "     */
$: parse arg aa,bb,cc;  return (x(aa)-x(cc)) *(y(bb)-y(cc)) - (x(bb)-x(cc)) *(y(aa)-y(cc))
?: #1=$(p,a,b); #2=$(p,b,c); #3=$(p,c,a); return (#1>=0&#2>=0&#3>=0) | (#1<=0&#2<=0&#3<=0)


EPS = 0.001
EPS_SQUARE = EPS * EPS

def side(x1, y1, x2, y2, x, y)
    return (y2 - y1) * (x - x1) + (-x2 + x1) * (y - y1)
end

def naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    checkSide1 = side(x1, y1, x2, y2, x, y) >= 0
    checkSide2 = side(x2, y2, x3, y3, x, y) >= 0
    checkSide3 = side(x3, y3, x1, y1, x, y) >= 0
    return checkSide1 && checkSide2 && checkSide3
end

def pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y)
    xMin = [x1, x2, x3].min - EPS
    xMax = [x1, x2, x3].max + EPS
    yMin = [y1, y2, y3].min - EPS
    yMax = [y1, y2, y3].max + EPS
    return !(x < xMin || xMax < x || y < yMin || yMax < y)
end

def distanceSquarePointToSegment(x1, y1, x2, y2, x, y)
    p1_p2_squareLength = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)
    dotProduct = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / p1_p2_squareLength
    if dotProduct < 0 then
        return (x - x1) * (x - x1) + (y - y1) * (y - y1)
    end
    if dotProduct <= 1 then
        p_p1_squareLength = (x1 - x) * (x1 - x) + (y1 - y) * (y1 - y)
        return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength
    end
    return (x - x2) * (x - x2) + (y - y2) * (y - y2)
end

def accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    if !pointInTriangleBoundingBox(x1, y1, x2, y2, x3, y3, x, y) then
        return false
    end
    if naivePointInTriangle(x1, y1, x2, y2, x3, y3, x, y) then
        return true
    end
    if distanceSquarePointToSegment(x1, y1, x2, y2, x, y) <= EPS_SQUARE then
        return true
    end
    if distanceSquarePointToSegment(x2, y2, x3, y3, x, y) <= EPS_SQUARE then
        return true
    end
    if distanceSquarePointToSegment(x3, y3, x1, y1, x, y) <= EPS_SQUARE then
        return true
    end
    return false
end

def main
    pts = [[0, 0], [0, 1], [3, 1]]
    tri = [[1.5, 2.4], [5.1, -3.1], [-3.8, 1.2]]
    print "Triangle is ", tri, "\n"
    x1, y1 = tri[0][0], tri[0][1]
    x2, y2 = tri[1][0], tri[1][1]
    x3, y3 = tri[2][0], tri[2][1]
    for pt in pts
        x, y = pt[0], pt[1]
        within = accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
        print "Point ", pt, " is within triangle? ", within, "\n"
    end
    print "\n"

    tri = [[0.1, 1.0 / 9.0], [12.5, 100.0 / 3.0], [25.0, 100.0 / 9.0]]
    print "Triangle is ", tri, "\n"
    x1, y1 = tri[0][0], tri[0][1]
    x2, y2 = tri[1][0], tri[1][1]
    x3, y3 = tri[2][0], tri[2][1]
    x = x1 + (3.0 / 7.0) * (x2 - x1)
    y = y1 + (3.0 / 7.0) * (y2 - y1)
    pt = [x, y]
    within = accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    print "Point ", pt, " is within triangle? ", within, "\n"
    print "\n"

    tri = [[0.1, 1.0 / 9.0], [12.5, 100.0 / 3.0], [-12.5, 100.0 / 6.0]]
    print "Triangle is ", tri, "\n"
    x3, y3 = tri[2][0], tri[2][1]
    within = accuratePointInTriangle(x1, y1, x2, y2, x3, y3, x, y)
    print "Point ", pt, " is within triangle? ", within, "\n"
end

main()


import math

const eps = 0.001
const eps_square = eps * eps

fn side(x1 f64, y1 f64, x2 f64, y2 f64, x f64, y f64) f64 {
    return (y2-y1)*(x-x1) + (-x2+x1)*(y-y1)
}

fn native_point_in_triangle(x1 f64, y1 f64, x2 f64, y2 f64, x3 f64, y3 f64, x f64, y f64) bool {
    check_side1 := side(x1, y1, x2, y2, x, y) >= 0
    check_side2 := side(x2, y2, x3, y3, x, y) >= 0
    check_side3 := side(x3, y3, x1, y1, x, y) >= 0
    return check_side1 && check_side2 && check_side3
}

fn point_in_triangle_bounding_box(x1 f64, y1 f64, x2 f64, y2 f64, x3 f64, y3 f64, x f64, y f64) bool {
    x_min := math.min(x1, math.min(x2, x3)) - eps
    x_max := math.max(x1, math.max(x2, x3)) + eps
    y_min := math.min(y1, math.min(y2, y3)) - eps
    y_max := math.max(y1, math.max(y2, y3)) + eps
    return !(x < x_min || x_max < x || y < y_min || y_max < y)
}

fn distance_square_point_to_segment(x1 f64, y1 f64, x2 f64, y2 f64, x f64, y f64) f64 {
    pq_p2_square_length := (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)
    dot_product := ((x-x1)*(x2-x1) + (y-y1)*(y2-y1)) / pq_p2_square_length
    if dot_product < 0 {
        return (x-x1)*(x-x1) + (y-y1)*(y-y1)
    } else if dot_product <= 1 {
        p_p1_square_length := (x1-x)*(x1-x) + (y1-y)*(y1-y)
        return p_p1_square_length - dot_product*dot_product*pq_p2_square_length
    } else {
        return (x-x2)*(x-x2) + (y-y2)*(y-y2)
    }
}

fn accurate_point_in_triangle(x1 f64, y1 f64, x2 f64, y2 f64, x3 f64, y3 f64, x f64, y f64) bool {
    if !point_in_triangle_bounding_box(x1, y1, x2, y2, x3, y3, x, y) {
        return false
    }
    if native_point_in_triangle(x1, y1, x2, y2, x3, y3, x, y) {
        return true
    }
    if distance_square_point_to_segment(x1, y1, x2, y2, x, y) <= eps_square {
        return true
    }
    if distance_square_point_to_segment(x2, y2, x3, y3, x, y) <= eps_square {
        return true
    }
    if distance_square_point_to_segment(x3, y3, x1, y1, x, y) <= eps_square {
        return true
    }
    return false
}

fn main() {
    pts := [[f64(0), 0], [f64(0), 1], [f64(3), 1]]
    mut tri := [[3.0 / 2, 12.0 / 5], [51.0 / 10, -31.0 / 10], [-19.0 / 5, 1.2]]
    println("Triangle is $tri")
    mut x1, mut y1 := tri[0][0], tri[0][1]
    mut x2, mut y2 := tri[1][0], tri[1][1]
    mut x3, mut y3 := tri[2][0], tri[2][1]
    for pt in pts {
        x, y := pt[0], pt[1]
        within := accurate_point_in_triangle(x1, y1, x2, y2, x3, y3, x, y)
        println("Point $pt is within triangle? $within")
    }
    println('')
    tri = [[1.0 / 10, 1.0 / 9], [100.0 / 8, 100.0 / 3], [100.0 / 4, 100.0 / 9]]
    println("Triangle is $tri")
    x1, y1 = tri[0][0], tri[0][1]
    x2, y2 = tri[1][0], tri[1][1]
    x3, y3 = tri[2][0], tri[2][1]
    x := x1 + (3.0/7)*(x2-x1)
    y := y1 + (3.0/7)*(y2-y1)
    pt := [x, y]
    mut within := accurate_point_in_triangle(x1, y1, x2, y2, x3, y3, x, y)
    println("Point $pt is within triangle ? $within")
    println('')
    tri = [[1.0 / 10, 1.0 / 9], [100.0 / 8, 100.0 / 3], [-100.0 / 8, 100.0 / 6]]
    println("Triangle is $tri")
    x3 = tri[2][0]
    y3 = tri[2][1]
    within = accurate_point_in_triangle(x1, y1, x2, y2, x3, y3, x, y)
    println("Point $pt is within triangle ? $within")
}


var EPS = 0.001
var EPS_SQUARE = EPS * EPS

var side = Fn.new { |x1, y1, x2, y2, x, y|
    return (y2 - y1)*(x - x1) + (-x2 + x1)*(y - y1)
}

var naivePointInTriangle = Fn.new { |x1, y1, x2, y2, x3, y3, x, y|
    var checkSide1 = side.call(x1, y1, x2, y2, x, y) >= 0
    var checkSide2 = side.call(x2, y2, x3, y3, x, y) >= 0
    var checkSide3 = side.call(x3, y3, x1, y1, x, y) >= 0
    return checkSide1 && checkSide2 && checkSide3
}

var pointInTriangleBoundingBox = Fn.new { |x1, y1, x2, y2, x3, y3, x, y|
    var xMin = x1.min(x2.min(x3)) - EPS
    var xMax = x1.max(x2.max(x3)) + EPS
    var yMin = y1.min(y2.min(y3)) - EPS
    var yMax = y1.max(y2.max(y3)) + EPS
    return !(x < xMin || xMax < x || y < yMin || yMax < y)
}

var distanceSquarePointToSegment = Fn.new { |x1, y1, x2, y2, x, y|
    var p1_p2_squareLength = (x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1)
    var dotProduct = ((x - x1)*(x2 - x1) + (y - y1)*(y2 - y1)) / p1_p2_squareLength
    if (dotProduct < 0) {
        return (x - x1)*(x - x1) + (y - y1)*(y - y1)
    } else if (dotProduct <= 1) {
        var p_p1_squareLength = (x1 - x)*(x1 - x) + (y1 - y)*(y1 - y)
        return p_p1_squareLength - dotProduct * dotProduct * p1_p2_squareLength
    } else {
        return (x - x2)*(x - x2) + (y - y2)*(y - y2)
    }
}

var accuratePointInTriangle = Fn.new { |x1, y1, x2, y2, x3, y3, x, y|
    if (!pointInTriangleBoundingBox.call(x1, y1, x2, y2, x3, y3, x, y)) return false
    if (naivePointInTriangle.call(x1, y1, x2, y2, x3, y3, x, y)) return true
    if (distanceSquarePointToSegment.call(x1, y1, x2, y2, x, y) <= EPS_SQUARE) return true
    if (distanceSquarePointToSegment.call(x2, y2, x3, y3, x, y) <= EPS_SQUARE) return true
    if (distanceSquarePointToSegment.call(x3, y3, x1, y1, x, y) <= EPS_SQUARE) return true
    return false
}

var pts = [ [0, 0], [0, 1], [3, 1]]
var tri = [ [3/2, 12/5], [51/10, -31/10], [-19/5, 1.2] ]
System.print("Triangle is %(tri)")
var x1 = tri[0][0]
var y1 = tri[0][1]
var x2 = tri[1][0]
var y2 = tri[1][1]
var x3 = tri[2][0]
var y3 = tri[2][1]

for (pt in pts) {
    var x = pt[0]
    var y = pt[1]
    var within = accuratePointInTriangle.call(x1, y1, x2, y2, x3, y3, x, y)
    System.print("Point %(pt) is within triangle ? %(within)")
}
System.print()
tri = [ [1/10, 1/9], [100/8, 100/3], [100/4, 100/9] ]
System.print("Triangle is %(tri)")
x1 = tri[0][0]
y1 = tri[0][1]
x2 = tri[1][0]
y2 = tri[1][1]
x3 = tri[2][0]
y3 = tri[2][1]
var x = x1 + (3/7)*(x2 - x1)
var y = y1 + (3/7)*(y2 - y1)
var pt = [x, y]
var within = accuratePointInTriangle.call(x1, y1, x2, y2, x3, y3, x, y)
System.print("Point %(pt) is within triangle ? %(within)")
System.print()
tri = [ [1/10, 1/9], [100/8, 100/3], [-100/8, 100/6] ]
System.print("Triangle is %(tri)")
x3 = tri[2][0]
y3 = tri[2][1]
within = accuratePointInTriangle.call(x1, y1, x2, y2, x3, y3, x, y)
System.print("Point %(pt) is within triangle ? %(within)")


func real Dot(W,X,Y,Z);         \Return the dot product of two 2D vectors
real W,X,Y,Z;                   \ (W-X) dot (Y-Z)
real WX(2), YZ(2);
[WX(0):= W(0)-X(0);  WX(1):= W(1)-X(1);
 YZ(0):= Y(0)-Z(0);  YZ(1):= Y(1)-Z(1);
return WX(0)*YZ(0) + WX(1)*YZ(1);
];

real A,B,C;                     \triangle

func PointInTri(P);             \Return 'true' if point P is inside triangle ABC
real P;
int  S0,S1,S2;          \signs
[S0:= Dot(P,A,B,A) >= 0.0;
 S1:= Dot(P,B,C,B) >= 0.0;
 S2:= Dot(P,C,A,C) >= 0.0;
return S0=S1 & S1=S2 & S2=S0;
];

[A:= [10.5, 6.3];  B:= [13.5, 3.6];  C:= [ 3.3, -1.6];
Text(0, if PointInTri([10.0, 3.0]) then "inside" else "outside");  CrLf(0);
Text(0, if PointInTri([-5.0,-2.2]) then "inside" else "outside");  CrLf(0);
Text(0, if PointInTri([10.5, 6.3]) then "inside" else "outside");  CrLf(0);
]


INCLUDE "D2:PRINTF.ACT" ;from the Action! Tool Kit

PROC SizeDistribution(CHAR ARRAY filter INT ARRAY limits,counts BYTE count)
  CHAR ARRAY line(255),tmp(4)
  INT size
  BYTE i,dev=[1]

  FOR i=0 TO count-1
  DO
    counts(i)=0
  OD

  Close(dev)
  Open(dev,filter,6)
  DO
    InputSD(dev,line)
    IF line(0)=0 THEN
      EXIT
    FI
    SCopyS(tmp,line,line(0)-3,line(0))
    size=ValI(tmp)
    FOR i=0 TO count-1
    DO
      IF size<limits(i) THEN
        counts(i)==+1
        EXIT
      FI
    OD
  OD
  Close(dev)
RETURN

PROC GenerateLimits(INT ARRAY limits BYTE count)
  BYTE i
  INT l

  l=1
  FOR i=0 TO count-1
  DO
    limits(i)=l
    l==LSH 1
    IF l>1000 THEN l=1000 FI
  OD
RETURN

PROC PrintBar(INT len,max,size)
  INT i,count

  count=4*len*size/max
  IF count=0 AND len>0 THEN
    count=1
  FI
  FOR i=0 TO count/4-1
  DO
    Put(160)
  OD
  i=count MOD 4
  IF i=1 THEN Put(22)
  ELSEIF i=2 THEN Put(25)
  ELSEIF i=3 THEN Put(130) FI
RETURN

PROC PrintResult(CHAR ARRAY filter
  INT ARRAY limits,counts BYTE count)

  BYTE i
  CHAR ARRAY tmp(5)
  INT min,max,total

  total=0 max=0
  FOR i=0 TO count-1
  DO
    total==+counts(i)
    IF counts(i)>max THEN
      max=counts(i)
    FI
  OD
  PrintF("File size distribution of ""%S"" in sectors:%E",filter) PutE()
  PrintE("From  To Count Perc")
  min=0
  FOR i=0 TO count-1
  DO
    StrI(min,tmp) PrintF("%4S ",tmp)
    StrI(limits(i)-1,tmp) PrintF("%3S   ",tmp)
    StrI(counts(i),tmp) PrintF("%3S ",tmp)
    StrI(counts(i)*100/total,tmp) PrintF("%3S%% ",tmp)
    PrintBar(counts(i),max,17) PutE()
    min=limits(i)
  OD
RETURN

PROC Main()
  DEFINE LIMITCOUNT="11"
  CHAR ARRAY filter="H1:*.*"
  INT ARRAY limits(LIMITCOUNT),counts(LIMITCOUNT)

  Put(125) PutE() ;clear the screen
  GenerateLimits(limits,LIMITCOUNT)
  SizeDistribution(filter,limits,counts,LIMITCOUNT)
  PrintResult(filter,limits,counts,LIMITCOUNT)
RETURN


with Ada.Numerics.Elementary_Functions;
with Ada.Directories;    use Ada.Directories;
with Ada.Strings.Fixed;  use Ada.Strings;
with Ada.Command_Line;   use Ada.Command_Line;
with Ada.Text_IO;        use Ada.Text_IO;

with Dir_Iterators.Recursive;

procedure File_Size_Distribution is

   type Exponent_Type is range 0 .. 18;
   type File_Count    is range 0 .. Long_Integer'Last;
   Counts         : array (Exponent_Type) of File_Count := (others => 0);
   Non_Zero_Index : Exponent_Type   := 0;
   Directory_Name : constant String := (if Argument_Count = 0
                                        then "."
                                        else Argument (1));
   Directory_Walker : Dir_Iterators.Recursive.Recursive_Dir_Walk
     := Dir_Iterators.Recursive.Walk (Directory_Name);
begin
   if not Exists (Directory_Name) or else Kind (Directory_Name) /= Directory then
      Put_Line ("Directory does not exist");
      return;
   end if;

   for Directory_Entry of Directory_Walker loop
      declare
         use Ada.Numerics.Elementary_Functions;
         Size_Of_File : File_Size;
         Exponent     : Exponent_Type;
      begin
         if Kind (Directory_Entry) = Ordinary_File then
            Size_Of_File := Size (Directory_Entry);
            if Size_Of_File = 0 then
               Counts (0) := Counts (0) + 1;
            else
               Exponent := Exponent_Type (Float'Ceiling (Log (Float (Size_Of_File),
                                                              Base => 10.0)));
               Counts (Exponent) := Counts (Exponent) + 1;
            end if;
         end if;
      end;
   end loop;

   for I in reverse Counts'Range loop
      if Counts (I) /= 0 then
         Non_Zero_Index := I;
         exit;
      end if;
   end loop;

   for I in Counts'First .. Non_Zero_Index loop
      Put ("Less than 10**");
      Put (Fixed.Trim (Exponent_Type'Image (I), Side => Left));
      Put (": ");
      Put (File_Count'Image (Counts (I)));
      New_Line;
   end loop;
end File_Size_Distribution;


#include<windows.h>
#include<string.h>
#include<stdio.h>

#define MAXORDER 25

int main(int argC, char* argV[])
{
	char str[MAXORDER],commandString[1000],*startPath;
	long int* fileSizeLog = (long int*)calloc(sizeof(long int),MAXORDER),max;
	int i,j,len;
	double scale;
	FILE* fp;

	if(argC==1)
		printf("Usage : %s <followed by directory to start search from(. for current dir), followed by \n optional parameters (T or G) to show text or graph output>",argV[0]);
	else{
		if(strchr(argV[1],' ')!=NULL){
		len = strlen(argV[1]);
		startPath = (char*)malloc((len+2)*sizeof(char));
		startPath[0] = '\"';
		startPath[len+1]='\"';
		strncpy(startPath+1,argV[1],len);
		startPath[len+2] = argV[1][len];
		sprintf(commandString,"forfiles /p %s /s /c \"cmd /c echo @fsize\" 2>&1",startPath);
	}

	else if(strlen(argV[1])==1 && argV[1][0]=='.')
		strcpy(commandString,"forfiles /s /c \"cmd /c echo @fsize\" 2>&1");

	else
		sprintf(commandString,"forfiles /p %s /s /c \"cmd /c echo @fsize\" 2>&1",argV[1]);

	fp = popen(commandString,"r");

	while(fgets(str,100,fp)!=NULL){
			if(str[0]=='0')
				fileSizeLog[0]++;
			else
				fileSizeLog[strlen(str)]++;
	}

	if(argC==2 || (argC==3 && (argV[2][0]=='t'||argV[2][0]=='T'))){
		for(i=0;i<MAXORDER;i++){
			printf("\nSize Order < 10^%2d bytes : %Ld",i,fileSizeLog[i]);
		}
	}

	else if(argC==3 && (argV[2][0]=='g'||argV[2][0]=='G')){
		CONSOLE_SCREEN_BUFFER_INFO csbi;
		int val = GetConsoleScreenBufferInfo(GetStdHandle( STD_OUTPUT_HANDLE ),&csbi);
		if(val)
		{

				max = fileSizeLog[0];

				for(i=1;i<MAXORDER;i++)
					(fileSizeLog[i]>max)?max=fileSizeLog[i]:max;

				(max < csbi.dwSize.X)?(scale=1):(scale=(1.0*(csbi.dwSize.X-50))/max);

				for(i=0;i<MAXORDER;i++){
					printf("\nSize Order < 10^%2d bytes |",i);
					for(j=0;j<(int)(scale*fileSizeLog[i]);j++)
						printf("%c",219);
					printf("%Ld",fileSizeLog[i]);
				}
		}

	}
	return 0;
	}
}


#include <algorithm>
#include <array>
#include <filesystem>
#include <iomanip>
#include <iostream>

void file_size_distribution(const std::filesystem::path& directory) {
    constexpr size_t n = 9;
    constexpr std::array<std::uintmax_t, n> sizes = { 0, 1000, 10000,
        100000, 1000000, 10000000, 100000000, 1000000000, 10000000000 };
    std::array<size_t, n + 1> count = { 0 };
    size_t files = 0;
    std::uintmax_t total_size = 0;
    std::filesystem::recursive_directory_iterator iter(directory);
    for (const auto& dir_entry : iter) {
        if (dir_entry.is_regular_file() && !dir_entry.is_symlink()) {
            std::uintmax_t file_size = dir_entry.file_size();
            total_size += file_size;
            auto i = std::lower_bound(sizes.begin(), sizes.end(), file_size);
            size_t index = std::distance(sizes.begin(), i);
            ++count[index];
            ++files;
        }
    }
    std::cout << "File size distribution for " << directory << ":\n";
    for (size_t i = 0; i <= n; ++i) {
        if (i == n)
            std::cout << "> " << sizes[i - 1];
        else
            std::cout << std::setw(16) << sizes[i];
        std::cout << " bytes: " << count[i] << '\n';
    }
    std::cout << "Number of files: " << files << '\n';
    std::cout << "Total file size: " << total_size << " bytes\n";
}

int main(int argc, char** argv) {
    std::cout.imbue(std::locale(""));
    try {
        const char* directory(argc > 1 ? argv[1] : ".");
        std::filesystem::path path(directory);
        if (!is_directory(path)) {
            std::cerr << directory << " is not a directory.\n";
            return EXIT_FAILURE;
        }
        file_size_distribution(path);
    } catch (const std::exception& ex) {
        std::cerr << ex.what() << '\n';
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}


program File_size_distribution;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.Math,
  Winapi.Windows;

function Commatize(n: Int64): string;
begin
  result := n.ToString;
  if n < 0 then
    delete(result, 1, 1);
  var le := result.Length;
  var i := le - 3;
  while i >= 1 do
  begin
    Insert(',', result, i + 1);
    dec(i, 3);
  end;

  if n >= 0 then
    exit;

  Result := '-' + result;
end;

procedure Walk(Root: string; walkFunc: TProc<string, TWin32FindData>); overload;
var
  rec: TWin32FindData;
  h: THandle;
  directory, PatternName: string;
begin
  if not Assigned(walkFunc) then
    exit;

  Root := IncludeTrailingPathDelimiter(Root);

  h := FindFirstFile(Pchar(Root + '*.*'), rec);
  if (INVALID_HANDLE_VALUE <> h) then
    repeat
      if rec.cFileName[0] = '.' then
        Continue;
      walkFunc(directory, rec);
      if ((rec.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) =
        FILE_ATTRIBUTE_DIRECTORY) and (rec.cFileName[0] <> '.') then
        Walk(Root + rec.cFileName, walkFunc);
    until not FindNextFile(h, rec);
  FindClose(h);
end;

procedure FileSizeDistribution(root: string);
var
  sizes: TArray<Integer>;
  files, directories, totalSize, size, i: UInt64;
  c: string;
begin
  SetLength(sizes, 12);
  files := 0;
  directories := 0;
  totalSize := 0;
  size := 0;

  Walk(root,
    procedure(path: string; info: TWin32FindData)
    var
      logSize: Extended;
      index: integer;
    begin
      inc(files);
      if (info.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) =
        FILE_ATTRIBUTE_DIRECTORY then
        inc(directories);
      size := info.nFileSizeHigh shl 32 + info.nFileSizeLow;
      if size = 0 then
      begin
        sizes[0] := sizes[0] + 1;
        exit;
      end;

      inc(totalSize, size);
      logSize := Log10(size);
      index := Floor(logSize);
      sizes[index] := sizes[index] + 1;
    end);

  writeln('File size distribution for "', root, '" :-'#10);
  for i := 0 to High(sizes) do
  begin
    if i = 0 then
      write('  ')
    else
      write('+ ');
    writeln(format('Files less than 10 ^ %-2d bytes : %5d', [i, sizes[i]]));
  end;
  writeln('                                  -----');
  writeln('= Total number of files         : ', files: 5);
  writeln('  including directories         : ', directories: 5);
  c := commatize(totalSize);
  writeln(#10'  Total size of files           : ', c, 'bytes');
end;

begin
  fileSizeDistribution('.');
  readln;
end.


F find_common_directory_path(paths, sep = ‘/’)
   V pos = 0
   L
      L(path) paths
         I pos < path.len & path[pos] == paths[0][pos]
            L.continue

         L pos > 0
            pos--
            I paths[0][pos] == sep
               L.break
         R paths[0][0.<pos]
      pos++

print(find_common_directory_path([
   ‘/home/user1/tmp/coverage/test’,
   ‘/home/user1/tmp/covert/operator’,
   ‘/home/user1/tmp/coven/members’]))


USING: accessors assocs formatting io io.directories.search
io.files.types io.pathnames kernel math math.functions
math.statistics namespaces sequences ;

: classify ( m -- n ) [ 0 ] [ log10 >integer 1 + ] if-zero ;

: file-size-histogram ( path -- assoc )
    recursive-directory-entries
    [ type>> +directory+ = ] reject
    [ size>> classify ] map histogram ;

current-directory get file-size-histogram dup
[ "Count of files < 10^%d bytes: %4d\n" printf ] assoc-each
nl values sum "Total files: %d\n" printf


package main

import (
    "fmt"
    "log"
    "math"
    "os"
    "path/filepath"
)

func commatize(n int64) string {
    s := fmt.Sprintf("%d", n)
    if n < 0 {
        s = s[1:]
    }
    le := len(s)
    for i := le - 3; i >= 1; i -= 3 {
        s = s[0:i] + "," + s[i:]
    }
    if n >= 0 {
        return s
    }
    return "-" + s
}

func fileSizeDistribution(root string) {
    var sizes [12]int
    files := 0
    directories := 0
    totalSize := int64(0)
    walkFunc := func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        files++
        if info.IsDir() {
            directories++
        }
        size := info.Size()
        if size == 0 {
            sizes[0]++
            return nil
        }
        totalSize += size
        logSize := math.Log10(float64(size))
        index := int(math.Floor(logSize))
        sizes[index+1]++
        return nil
    }
    err := filepath.Walk(root, walkFunc)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("File size distribution for '%s' :-\n\n", root)
    for i := 0; i < len(sizes); i++ {
        if i == 0 {
            fmt.Print("  ")
        } else {
            fmt.Print("+ ")
        }
        fmt.Printf("Files less than 10 ^ %-2d bytes : %5d\n", i, sizes[i])
    }
    fmt.Println("                                  -----")
    fmt.Printf("= Total number of files         : %5d\n", files)
    fmt.Printf("  including directories         : %5d\n", directories)
    c := commatize(totalSize)
    fmt.Println("\n  Total size of files           :", c, "bytes")
}

func main() {
    fileSizeDistribution("./")
}


with Ada.Text_IO;  use Ada.Text_IO;

procedure Test_Common_Path is
   function "rem" (A, B : String) return String is
      Slash : Integer := A'First; -- At the last slash seen in A
      At_A  : Integer := A'first;
      At_B  : Integer := B'first;
   begin
      loop
         if At_A > A'Last then
            if At_B > B'Last or else B (At_B) = '/' then
               return A;
            else
               return A (A'First..Slash - 1);
            end if;
         elsif At_B > B'Last then
            if A (At_A) = '/' then -- A cannot be shorter than B here
               return B;
            else
               return A (A'First..Slash - 1);
            end if;
         elsif A (At_A) /= B (At_B) then
            return A (A'First..Slash - 1);
         elsif A (At_A) = '/' then
            Slash := At_A;
         end if;
         At_A := At_A + 1;
         At_B := At_B + 1;
      end loop;
   end "rem";
begin
   Put_Line
   (  "/home/user1/tmp/coverage/test" rem
      "/home/user1/tmp/covert/operator" rem
      "/home/user1/tmp/coven/members"
   );
end Test_Common_Path;


cdp(...)
{
    integer e;
    record r;
    text s;

    ucall(r_add, 1, r, 0);

    if (~r) {
        s = r.low;
        s = s.cut(0, e = b_trace(s, prefix(s, r.high), '/'));
        s = ~s || e == -1 ? s : "/";
    }

    s;
}

main(void)
{
    o_(cdp("/home/user1/tmp/coverage/test",
           "/home/user1/tmp/covert/operator",
           "/home/user1/tmp/coven/members"), "\n");

    0;
}


# Utilities code #

CHAR dir sep = "/"; # Assume POSIX #

PROC dir name = (STRING dir)STRING: (
  STRING out;
  FOR pos FROM UPB dir BY -1 TO LWB dir DO
    IF dir[pos] = dir sep THEN
      out := dir[:pos-1];
      GO TO out exit
    FI
  OD;
  # else: # out:="";
  out exit: out
);

PROC shortest = ([]STRING string list)STRING: (
  INT min := max int;
  INT min key := LWB string list - 1;
  FOR key FROM LWB string list TO UPB string list DO
    IF UPB string list[key][@1] < min THEN
      min := UPB string list[key][@1];
      min key := key
    FI
  OD;
  string list[min key]
);

# Actual code #

PROC common prefix = ([]STRING strings)STRING: (
  IF LWB strings EQ UPB strings THEN
    # exit: # strings[LWB strings]
  ELSE
    STRING prefix := shortest(strings);
    FOR pos FROM LWB prefix TO UPB prefix DO
      CHAR first = prefix[pos];
      FOR key FROM LWB strings+1 TO UPB strings DO
        IF strings[key][@1][pos] NE first THEN
          prefix := prefix[:pos-1];
          GO TO prefix exit
        FI
      OD
    OD;
    prefix exit: prefix
  FI
);

# Test code #

test:(
  []STRING dir list = (
    "/home/user1/tmp/coverage/test",
    "/home/user1/tmp/covert/operator",
    "/home/user1/tmp/coven/members"
  );
  print((dir name(common prefix(dir list)), new line))
)


{-# LANGUAGE LambdaCase #-}

import           Control.Concurrent          (forkIO, setNumCapabilities)
import           Control.Concurrent.Chan     (Chan, newChan, readChan,
                                              writeChan, writeList2Chan)
import           Control.Exception           (IOException, catch)
import           Control.Monad               (filterM, forever, join,
                                              replicateM, replicateM_, (>=>))
import           Control.Parallel.Strategies (parTraversable, rseq, using,
                                              withStrategy)
import           Data.Char                   (isDigit)
import           Data.List                   (find, sort)
import qualified Data.Map.Strict             as Map
import           GHC.Conc                    (getNumProcessors)
import           System.Directory            (doesDirectoryExist, doesFileExist,
                                              listDirectory,
                                              pathIsSymbolicLink)
import           System.Environment          (getArgs)
import           System.FilePath.Posix       ((</>))
import           System.IO                   (FilePath, IOMode (ReadMode),
                                              hFileSize, hPutStrLn, stderr,
                                              withFile)
import           Text.Printf                 (hPrintf, printf)

data Item = File FilePath Integer | Folder FilePath deriving (Show)

type FGKey = (Integer, Integer)
type FrequencyGroup = (FGKey, Integer)
type FrequencyGroups = Map.Map FGKey Integer

newFrequencyGroups :: FrequencyGroups
newFrequencyGroups = Map.empty

fileSizes :: [Item] -> [Integer]
fileSizes = foldr f [] where f (File _ n) acc = n:acc
                             f _          acc = acc

folders :: [Item] -> [FilePath]
folders = foldr f [] where f (Folder p) acc = p:acc
                           f _          acc = acc

totalBytes :: [Item] -> Integer
totalBytes = sum . fileSizes

counts :: [Item] -> (Integer, Integer)
counts = foldr (\x (a, b) -> case x of File _ _ -> (succ a, b)
                                       Folder _ -> (a, succ b)) (0, 0)

-- |Creates 'FrequencyGroups' from the provided size and data set.
frequencyGroups :: Int             -- ^ Desired number of frequency groups.
                -> [Integer]       -- ^ List of collected file sizes. Must be sorted.
                -> FrequencyGroups -- ^ Returns a 'FrequencyGroups' for the file sizes.
frequencyGroups _ [] = newFrequencyGroups
frequencyGroups totalGroups xs
  | length xs == 1 = Map.singleton (head xs, head xs) 1
  | otherwise = foldr placeGroups newFrequencyGroups xs `using` parTraversable rseq
  where
    range = maximum xs - minimum xs
    groupSize = succ $ ceiling $ realToFrac range / realToFrac totalGroups
    groups = takeWhile (<=groupSize + maximum xs) $ iterate (+groupSize) 0
    groupMinMax = zip groups (pred <$> tail groups)
    findGroup n = find (\(low, high) -> n >= low && n <= high)

    incrementCount (Just n) = Just (succ n) -- Update count for range.
    incrementCount Nothing  = Just 1        -- Insert new range with initial count.

    placeGroups n fgMap = case findGroup n groupMinMax of
      Just k  -> Map.alter incrementCount k fgMap
      Nothing -> fgMap -- Should never happen.

expandGroups :: Int             -- ^ Desired number of frequency groups.
             -> [Integer]       -- ^ List of collected file sizes.
             -> Integer         -- ^ Computed frequency group limit.
             -> FrequencyGroups -- ^ Expanded 'FrequencyGroups'
expandGroups gsize fileSizes groupThreshold
  | groupThreshold > 0 = loop 15 $ frequencyGroups gsize sortedFileSizes
  | otherwise = frequencyGroups gsize sortedFileSizes
  where
    sortedFileSizes = sort fileSizes
    loop 0 gs = gs -- break out in case we can't go below threshold
    loop n gs | all (<= groupThreshold) $ Map.elems gs = gs
              | otherwise = loop (pred n) (expand gs)

    expand :: FrequencyGroups -> FrequencyGroups
    expand = foldr f . withStrategy (parTraversable rseq) <*>
      Map.mapWithKey groupsFromGroup . Map.filter (> groupThreshold)
      where
        f :: Maybe (FGKey, FrequencyGroups) -- ^ expanded frequency group
          -> FrequencyGroups                -- ^ accumulator
          -> FrequencyGroups                -- ^ merged accumulator
        f (Just (k, fg)) acc = Map.union (Map.delete k acc) fg
        f Nothing        acc = acc

        groupsFromGroup
          :: FGKey                          -- ^ Group Key
          -> Integer                        -- ^ Count
          -> Maybe (FGKey, FrequencyGroups) -- ^ Returns expanded 'FrequencyGroups' with base key it replaces.
        groupsFromGroup (min, max) count
          | length range > 1 = Just ((min, max), frequencyGroups gsize range)
          | otherwise        = Nothing
          where
            range = filter (\n -> n >= min && n <= max) sortedFileSizes

displaySize :: Integer -> String
displaySize n
  |              n <= 2^10 = printf "%8dB " n
  | n >= 2^10 && n <= 2^20 = display (2^10) "KB"
  | n >= 2^20 && n <= 2^30 = display (2^20) "MB"
  | n >= 2^30 && n <= 2^40 = display (2^30) "GB"
  | n >= 2^40 && n <= 2^50 = display (2^40) "TB"
  | otherwise = "Too large!"
  where
    display :: Double -> String -> String
    display b = printf "%7.2f%s " (realToFrac n / b)

displayFrequency :: Integer -> FrequencyGroup -> IO ()
displayFrequency filesCount ((min, max), count) = do
  printf "%s <-> %s" (displaySize min) (displaySize max)
  printf "= %-10d %6.3f%%: %-5s\n" count percentage bars
  where
    percentage :: Double
    percentage = (realToFrac count / realToFrac filesCount) * 100
    size = round percentage
    bars | size == 0 = "▍"
         | otherwise = replicate size '█'

folderWorker :: Chan FilePath -> Chan [Item] -> IO ()
folderWorker folderChan resultItemsChan =
  forever (readChan folderChan >>= collectItems >>= writeChan resultItemsChan)

collectItems :: FilePath -> IO [Item]
collectItems folderPath = catch tryCollect $ \e -> do
    hPrintf stderr "Skipping: %s\n" $ show (e :: IOException)
    pure []
  where
    tryCollect = (fmap (folderPath </>) <$> listDirectory folderPath) >>=
      mapM (\p -> doesDirectoryExist p >>=
              \case True  -> pure $ Folder p
                    False -> File p <$> withFile p ReadMode hFileSize)

parallelItemCollector :: FilePath -> IO [Item]
parallelItemCollector folder = do
  wCount <- getNumProcessors
  setNumCapabilities wCount
  printf "Using %d worker threads\n" wCount
  folderChan <- newChan
  resultItemsChan <- newChan
  replicateM_ wCount (forkIO $ folderWorker folderChan resultItemsChan)
  loop folderChan resultItemsChan [Folder folder]
  where
    loop :: Chan FilePath -> Chan [Item] -> [Item] -> IO [Item]
    loop folderChan resultItemsChan xs = do
      regularFolders <- filterM (pathIsSymbolicLink >=> (pure . not)) $ folders xs
      if null regularFolders then pure []
      else do
        writeList2Chan folderChan regularFolders
        childItems <- replicateM (length regularFolders) (readChan resultItemsChan)
        result <- mapM (loop folderChan resultItemsChan) childItems
        pure (join childItems <> join result)

parseArgs :: [String] -> Either String (FilePath, Int)
parseArgs (x:y:xs)
  | all isDigit y = Right (x, read y)
  | otherwise     = Left "Invalid frequency group size"
parseArgs (x:xs) = Right (x, 4)
parseArgs _ = Right (".", 4)

main :: IO ()
main = parseArgs <$> getArgs >>= \case
    Left errorMessage -> hPutStrLn stderr errorMessage
    Right (path, groupSize) -> do
      items <- parallelItemCollector path
      let (fileCount, folderCount) = counts items
      printf "Total files: %d\nTotal folders: %d\n" fileCount folderCount
      printf "Total size: %s\n" $ displaySize $ totalBytes items
      printf "\nDistribution:\n\n%9s  <-> %9s %7s\n" "From" "To" "Count"
      putStrLn $ replicate 46 '-'
      let results = expandGroups groupSize (fileSizes items) (groupThreshold fileCount)
      mapM_ (displayFrequency fileCount) $ Map.assocs results
  where
    groupThreshold = round . (*0.25) . realToFrac


commonPathPrefix: function [lst][
    paths: map lst => [split.by:"/" &]

    common: new []
    firstPath: first paths
    loop .with:'i firstPath 'part [
        found: true
        loop paths 'p [
            if part <> get p i [
                found: false
                break
            ]
        ]
        if found -> 'common ++ part
    ]
    return join.with:"/" common
]

print commonPathPrefix [
    "/home/user1/tmp/coverage/test"
    "/home/user1/tmp/covert/operator"
    "/home/user1/tmp/coven/members"
]


Dir1 := "/home/user1/tmp/coverage/test"
Dir2 := "/home/user1/tmp/covert/operator"
Dir3 := "/home/user1/tmp/coven/members"

StringSplit, Dir1_, Dir1, /
StringSplit, Dir2_, Dir2, /
StringSplit, Dir3_, Dir3, /

Loop
    If  (Dir1_%A_Index% = Dir2_%A_Index%)
    And (Dir1_%A_Index% = Dir3_%A_Index%)
        Result .= (A_Index=1 ? "" : "/") Dir1_%A_Index%
    Else Break

MsgBox, % Result


using Humanize

function sizelist(path::AbstractString)
    rst = Vector{Int}(0)
    for (root, dirs, files) in walkdir(path)
        files = joinpath.(root, files)
        tmp = collect(filesize(f) for f in files if !islink(f))
        append!(rst, tmp)
    end
    return rst
end

byclass(y, classes) = Dict{eltype(classes),Int}(c => count(c[1] .≤ y .< c[2]) for c in classes)

function main(path::AbstractString)
    s = sizelist(path)
    cls = append!([(0, 1)], collect((10 ^ (i-1), 10 ^ i) for i in 1:9))
    f = byclass(s, cls)

    println("filesizes: ")
    for c in cls
        @printf(" - between %8s and %8s bytes: %3i\n", datasize(c[1]), datasize(c[2]), f[c])
    end
    println("\n-> total: $(datasize(sum(s))) bytes and $(length(s)) files")
end

main(".")


# Finds the longest common directory of paths[1], paths[2], ...,
# paths[count], where sep is a single-character directory separator.
function common_dir(paths, count, sep,    b, c, f, i, j, p) {
	if (count < 1)
		return ""

	p = ""	# Longest common prefix
	f = 0	# Final index before last sep

	# Loop for c = each character of paths[1].
	for (i = 1; i <= length(paths[1]); i++) {
		c = substr(paths[1], i, 1)

		# If c is not the same in paths[2], ..., paths[count]
		# then break both loops.
		b = 0
		for (j = 2; j <= count; j++) {
			if (c != substr(paths[j], i, 1)) {
				b = 1
				break
			}
		}
		if (b)
			break

		# Append c to prefix. Update f.
		p = p c
		if (c == sep)
			f = i - 1
	}

	# Return only f characters of prefix.
	return substr(p, 1, f)
}

BEGIN {
	a[1] = "/home/user1/tmp/coverage/test"
	a[2] = "/home/user1/tmp/covert/operator"
	a[3] = "/home/user1/tmp/coven/members"
	print common_dir(a, 3, "/")
}


DECLARE FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)

DATA "/home/user2", "/home/user1/tmp/covert/operator", "/home/user1/tmp/coven/members"

DIM x(0 TO 2) AS STRING, n AS INTEGER
FOR n = 0 TO 2
    READ x(n)
NEXT

PRINT "Common path is '"; commonPath$(x(), "/"); "'"

FUNCTION commonPath$ (paths() AS STRING, pathSep AS STRING)
    DIM tmpint1 AS INTEGER, tmpint2 AS INTEGER, tmpstr1 AS STRING, tmpstr2 AS STRING
    DIM L0 AS INTEGER, L1 AS INTEGER, lowerbound AS INTEGER, upperbound AS INTEGER
    lowerbound = LBOUND(paths): upperbound = UBOUND(paths)

    IF (lowerbound) = upperbound THEN       'Some quick error checking...
        commonPath$ = paths(lowerbound)
    ELSEIF lowerbound > upperbound THEN     'How in the...?
        commonPath$ = ""
    ELSE
        tmpstr1 = paths(lowerbound)

        FOR L0 = (lowerbound + 1) TO upperbound 'Find common strings.
            tmpstr2 = paths(L0)
            tmpint1 = LEN(tmpstr1)
            tmpint2 = LEN(tmpstr2)
            IF tmpint1 > tmpint2 THEN tmpint1 = tmpint2
            FOR L1 = 1 TO tmpint1
                IF MID$(tmpstr1, L1, 1) <> MID$(tmpstr2, L1, 1) THEN
                    tmpint1 = L1 - 1
                    EXIT FOR
                END IF
            NEXT
            tmpstr1 = LEFT$(tmpstr1, tmpint1)
        NEXT

        IF RIGHT$(tmpstr1, 1) <> pathSep THEN
            FOR L1 = tmpint1 TO 2 STEP -1
                IF (pathSep) = MID$(tmpstr1, L1, 1) THEN
                    tmpstr1 = LEFT$(tmpstr1, L1 - 1)
                    EXIT FOR
                END IF
            NEXT
            IF LEN(tmpstr1) = tmpint1 THEN tmpstr1 = ""
        ELSEIF tmpint1 > 1 THEN
            tmpstr1 = LEFT$(tmpstr1, tmpint1 - 1)
        END IF

        commonPath$ = tmpstr1
    END IF
END FUNCTION


// version 1.2.10

import java.io.File
import kotlin.math.log10
import kotlin.math.floor

fun fileSizeDistribution(path: String) {
    val sizes = IntArray(12)
    val p = File(path)
    val files = p.walk()
    var accessible = 0
    var notAccessible = 0
    var totalSize = 0L
    for (file in files) {
        try {
            if (file.isFile()) {
                val len = file.length()
                accessible++
                if (len == 0L) {
                    sizes[0]++
                    continue
                }
                totalSize += len
                val logLen = log10(len.toDouble())
                val index = floor(logLen).toInt()
                sizes[index + 1]++
            }
        }
        catch (se: SecurityException) {
            notAccessible++
        }
    }

    println("File size distribution for '$path' :-\n")
    for (i in 0 until sizes.size) {
        print(if (i == 0) "  " else "+ ")
        print("Files less than 10 ^ ${"%-2d".format(i)} bytes : ")
        println("%5d".format(sizes[i]))
    }
    println("                                  -----")
    println("= Number of accessible files    : ${"%5d".format(accessible)}")
    println("\n  Total size in bytes           : $totalSize")
    println("\n  Number of inaccessible files  : ${"%5d".format(notAccessible)}")
}

fun main(args: Array<String>) {
    fileSizeDistribution("./")  // current directory
}


SetDirectory[NotebookDirectory[]];
Histogram[FileByteCount /@ Select[FileNames[__], DirectoryQ /* Not], {"Log", 15}, {"Log", "Count"}]


x = "/home/user1/tmp/coverage/test"
y = "/home/user1/tmp/covert/operator"
z = "/home/user1/tmp/coven/members"

a = length(x)
if a > length(y) then a = length(y)
if a > length(z) then a = length(z)
for i = 1 to a
    if mid(x, i, 1) <> mid(y, i, 1) then exit for
next i
a = i - 1

for i = 1 to a
    if mid(x, i, 1) <> mid(z, i, 1) then exit for
next i
a = i - 1

if mid(x, i, 1) <> "/" then
    for i = a to 1 step -1
        if "/" = mid(x, i, 1) then exit for
    next i
end if

REM Task description says no trailing slash, so...
a = i - 1
print "Common path is '"; left(x, a); "'"


@echo off
setlocal enabledelayedexpansion

call:commonpath /home/user1/tmp/coverage/test /home/user1/tmp/covert/operator /home/user1/tmp/coven/members
pause>nul
exit /b

:commonpath
setlocal enabledelayedexpansion

for %%i in (%*) do (
  set /a args+=1
  set arg!args!=%%i
  set fullarg!args!=%%i
)
for /l %%i in (1,1,%args%) do set fullarg%%i=!fullarg%%i:/= !

for /l %%i in (1,1,%args%) do (
  set tempcount=0
  for %%j in (!fullarg%%i!) do (
    set /a tempcount+=1
    set arg%%it!tempcount!=%%j
    set arg%%itokencount=!tempcount!
  )
)

set mintokencount=%arg1tokencount%
set leasttokens=1
for /l %%i in (1,1,%args%) do (
  set currenttokencount=!arg%%itokencount!
  if !currenttokencount! lss !mintokencount! (
    set mintokencount=!currenttokencount!
    set leasttokens=%%i
  )
)

for /l %%i in (1,1,%mintokencount%) do set commonpath%%i=!arg%leasttokens%t%%i!

for /l %%i in (1,1,%mintokencount%) do (
  for /l %%j in (1,1,%args%) do (
    set currentpath=!arg%%jt%%i!
    if !currentpath!==!commonpath%%i! set pathtokens%%j=%%i
  )
)

set minpathtokens=%pathtokens1%
set leastpathtokens=1
for /l %%i in (1,1,%args%) do (
  set currentpathtokencount=!pathtokens%%i!
  if !currentpathtokencount! lss !minpathtokens! (
    set minpathtokencount=!currentpathtokencount!
    set leastpathtokens=%%i
  )
)

set commonpath=/
for /l %%i in (1,1,!pathtokens%leastpathtokens%!) do set commonpath=!commonpath!!arg%leastpathtokens%t%%i!/
echo %commonpath%

endlocal
exit /b


import math, os, strformat

const
  MaxPower = 10
  Powers = [1, 10, 100]

func powerWithUnit(idx: int): string =
  ## Return a string representing value 10^idx with a unit.
  if idx < 0:
    "0B"
  elif idx < 3:
    fmt"{Powers[idx]}B"
  elif idx < 6:
    fmt"{Powers[idx - 3]}kB"
  elif idx < 9:
    fmt"{Powers[idx - 6]}MB"
  else:
    fmt"{Powers[idx - 9]}GB"

# Retrieve the directory path.
var dirpath: string
if paramCount() == 0:
  dirpath = getCurrentDir()
else:
  dirpath = paramStr(1)
  if not dirExists(dirpath):
    raise newException(ValueError, "wrong directory path: " & dirpath)

# Distribute sizes.
var counts: array[-1..MaxPower, Natural]
for path in dirpath.walkDirRec():
  if not path.fileExists():
    continue  # Not a regular file.
  let size = getFileSize(path)
  let index = if size == 0: -1 else: log10(size.float).toInt
  inc counts[index]

# Display distribution.
let total = sum(counts)
echo "File size distribution for directory: ", dirpath
echo ""
for idx, count in counts:
  let rangeString = fmt"[{powerWithUnit(idx)}..{powerWithUnit(idx + 1)}[:"
  echo fmt"Size in {rangeString: 14} {count:>7}   {100 * count / total:5.2f}%"
echo ""
echo "Total number of files: ", sum(counts)


use File::Find;
use List::Util qw(max);

my %fsize;
$dir = shift || '.';
find(\&fsize, $dir);

$max = max($max,$fsize{$_}) for keys %fsize;
$total += $size while (undef,$size) = each %fsize;

print "File size distribution in bytes for directory: $dir\n";
for (0 .. max(keys %fsize)) {
    printf "# files @ %4sb %8s: %s\n", $_ ? '10e'.($_-1) : 0, $fsize{$_} // 0,
       histogram( $max, $fsize{$_} // 0, 80);
}
print "$total total files.\n";

sub histogram {
    my($max, $value, $width) = @_;
    my @blocks = qw<| ▏ ▎ ▍ ▌ ▋ ▊ ▉ █>;
    my $scaled = int $value * $width / $max;
    my $end =     $scaled % 8;
    my $bar = int $scaled / 8;
    my $B = $blocks[8] x ($bar * 8) . ($end ? $blocks[$end] : '');
}

sub fsize { $fsize{ log10( (lstat($_))[7] ) }++ }
sub log10 { my($s) = @_; $s ? int log($s)/log(10) : 0 }


      DIM path$(3)

      path$(1) = "/home/user1/tmp/coverage/test"
      path$(2) = "/home/user1/tmp/covert/operator"
      path$(3) = "/home/user1/tmp/coven/members"

      PRINT FNcommonpath(path$(), "/")
      END

      DEF FNcommonpath(p$(), s$)
      LOCAL I%, J%, O%
      REPEAT
        O% = I%
        I% = INSTR(p$(1), s$, I%+1)
        FOR J% = 2 TO DIM(p$(), 1)
          IF LEFT$(p$(1), I%) <> LEFT$(p$(J%), I%) EXIT REPEAT
        NEXT J%
      UNTIL I% = 0
      = LEFT$(p$(1), O%-1)


#include <stdio.h>

int common_len(const char *const *names, int n, char sep)
{
	int i, pos;
	for (pos = 0; ; pos++) {
		for (i = 0; i < n; i++) {
			if (names[i][pos] != '\0' &&
					names[i][pos] == names[0][pos])
				continue;

			/* backtrack */
			while (pos > 0 && names[0][--pos] != sep);
			return pos;
		}
	}

	return 0;
}

int main()
{
	const char *names[] = {
		"/home/user1/tmp/coverage/test",
		"/home/user1/tmp/covert/operator",
		"/home/user1/tmp/coven/members",
	};
	int len = common_len(names, sizeof(names) / sizeof(const char*), '/');

	if (!len) printf("No common path\n");
	else      printf("Common path: %.*s\n", len, names[0]);

	return 0;
}


without js -- file i/o
sequence sizes = {1},
         res = {0}
atom t1 = time()+1

function store_res(string filepath, sequence dir_entry)
    if not find('d', dir_entry[D_ATTRIBUTES]) then
        atom size = dir_entry[D_SIZE]
        integer sdx = 1
        while size>sizes[sdx] do
            if sdx=length(sizes) then
                sizes &= sizes[$]*iff(mod(length(sizes),3)?10:10.24)
                res &= 0
            end if
            sdx += 1
        end while
        res[sdx] += 1
        if time()>t1 then
            printf(1,"%,d files found\r",sum(res))
            t1 = time()+1
        end if
    end if
    return 0 -- keep going
end function
integer exit_code = walk_dir(".", store_res, true)

printf(1,"%,d files found\n",sum(res))
integer w = max(res)
--include builtins/pfile.e
for i=1 to length(res) do
    integer ri = res[i]
    string s = file_size_k(sizes[i], 5),
           p = repeat('*',floor(60*ri/w))
    printf(1,"files < %s: %s%,d\n",{s,p,ri})
end for


import sys, os
from collections import Counter

def dodir(path):
    global h

    for name in os.listdir(path):
        p = os.path.join(path, name)

        if os.path.islink(p):
            pass
        elif os.path.isfile(p):
            h[os.stat(p).st_size] += 1
        elif os.path.isdir(p):
            dodir(p)
        else:
            pass

def main(arg):
    global h
    h = Counter()
    for dir in arg:
        dodir(dir)

    s = n = 0
    for k, v in sorted(h.items()):
        print("Size %d -> %d file(s)" % (k, v))
        n += v
        s += k * v
    print("Total %d bytes for %d files" % (s, n))

main(sys.argv[1:])


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RosettaCodeTasks
{

	class Program
	{
		static void Main ( string[ ] args )
		{
			FindCommonDirectoryPath.Test ( );
		}

	}

	class FindCommonDirectoryPath
	{
		public static void Test ( )
		{
			Console.WriteLine ( "Find Common Directory Path" );
			Console.WriteLine ( );
			List<string> PathSet1 = new List<string> ( );
			PathSet1.Add ( "/home/user1/tmp/coverage/test" );
			PathSet1.Add ( "/home/user1/tmp/covert/operator" );
			PathSet1.Add ( "/home/user1/tmp/coven/members" );
			Console.WriteLine("Path Set 1 (All Absolute Paths):");
			foreach ( string path in PathSet1 )
			{
				Console.WriteLine ( path );
			}
			Console.WriteLine ( "Path Set 1 Common Path: {0}", FindCommonPath ( "/", PathSet1 ) );
		}
		public static string FindCommonPath ( string Separator, List<string> Paths )
		{
			string CommonPath = String.Empty;
			List<string> SeparatedPath = Paths
				.First ( str => str.Length == Paths.Max ( st2 => st2.Length ) )
				.Split ( new string[ ] { Separator }, StringSplitOptions.RemoveEmptyEntries )
				.ToList ( );

			foreach ( string PathSegment in SeparatedPath.AsEnumerable ( ) )
			{
				if ( CommonPath.Length == 0 && Paths.All ( str => str.StartsWith ( PathSegment ) ) )
				{
					CommonPath = PathSegment;
				}
				else if ( Paths.All ( str => str.StartsWith ( CommonPath + Separator + PathSegment ) ) )
				{
					CommonPath += Separator + PathSegment;
				}
				else
				{
					break;
				}
			}

			return CommonPath;
		}
	}
}


#lang racket

(define (file-size-distribution (d (current-directory)) #:size-group-function (sgf values))
  (for/fold ((rv (hash)) (Σ 0) (n 0)) ((f (in-directory d)) #:when (file-exists? f))
    (define sz (file-size f))
    (values (hash-update rv (sgf sz) add1 0) (+ Σ sz) (add1 n))))

(define (log10-or-so x) (if (zero? x) #f (round (/ (log x) (log 10)))))

(define number-maybe-<
  (match-lambda** [(#f #f) #f]
                  [(#f _) #t]
                  [(_ #f) #f]
                  [(a b) (< a b)]))

(define ...s? (match-lambda** [(one 1) one] [(one n) (string-append one "s")]))

(define ((report-fsd f) fsd Σ n)
  (for/list ((k (in-list (sort (hash-keys fsd) number-maybe-<))))
    (printf "~a(size): ~a -> ~a ~a~%"
            (object-name f)
            k
            (hash-ref fsd k) (...s? "file" (hash-ref fsd k))))
  (printf "Total: ~a ~a in ~a ~a~%" Σ (...s? "byte" Σ) n (...s? "file" n)))

(module+ test
  (call-with-values (λ () (file-size-distribution #:size-group-function log10-or-so))
                    (report-fsd log10-or-so)))


sub MAIN($dir = '.') {
    sub log10 (Int $s) { $s ?? $s.log(10).Int !! 0 }
    my %fsize;
    my @dirs = $dir.IO;
    while @dirs {
        for @dirs.pop.dir -> $path {
            %fsize{$path.s.&log10}++ if $path.f;
            @dirs.push: $path if $path.d and $path.r
        }
    }
    my $max = %fsize.values.max;
    my $bar-size = 80;
    say "File size distribution in bytes for directory: $dir\n";
    for 0 .. %fsize.keys.max {
          say sprintf( "# Files @ %5sb %8s: ", $_ ?? "10e{$_-1}" !! 0, %fsize{$_} // 0 ),
              histogram( $max, %fsize{$_} // 0, $bar-size )
    }
    say %fsize.values.sum, ' total files.';
}

sub histogram ($max, $value, $width = 60) {
    my @blocks = <| ▏ ▎ ▍ ▌ ▋ ▊ ▉ █>;
    my $scaled = ($value * $width / $max).Int;
    my ($end, $bar) = $scaled.polymod(8);
    (@blocks[8] x $bar * 8) ~ (@blocks[$end] if $end) ~ "\n"
}


#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

std::string longestPath( const std::vector<std::string> & , char ) ;

int main( ) {
   std::string dirs[ ] = {
      "/home/user1/tmp/coverage/test" ,
      "/home/user1/tmp/covert/operator" ,
      "/home/user1/tmp/coven/members" } ;
   std::vector<std::string> myDirs ( dirs , dirs + 3 ) ;
   std::cout << "The longest common path of the given directories is "
             << longestPath( myDirs , '/' ) << "!\n" ;
   return 0 ;
}

std::string longestPath( const std::vector<std::string> & dirs , char separator ) {
   std::vector<std::string>::const_iterator vsi = dirs.begin( ) ;
   int maxCharactersCommon = vsi->length( ) ;
   std::string compareString = *vsi ;
   for ( vsi = dirs.begin( ) + 1 ; vsi != dirs.end( ) ; vsi++ ) {
      std::pair<std::string::const_iterator , std::string::const_iterator> p =
	 std::mismatch( compareString.begin( ) , compareString.end( ) , vsi->begin( ) ) ;
      if (( p.first - compareString.begin( ) ) < maxCharactersCommon )
	 maxCharactersCommon = p.first - compareString.begin( ) ;
   }
   std::string::size_type found = compareString.rfind( separator , maxCharactersCommon ) ;
   return compareString.substr( 0 , found ) ;
}


(use '[clojure.string :only [join,split]])

(defn common-prefix [sep paths]
  (let [parts-per-path (map #(split % (re-pattern sep)) paths)
        parts-per-position (apply map vector parts-per-path)]
    (join sep
      (for [parts parts-per-position :while (apply = parts)]
        (first parts)))))

(println
  (common-prefix "/"
    ["/home/user1/tmp/coverage/test"
     "/home/user1/tmp/covert/operator"
     "/home/user1/tmp/coven/members"]))


/*REXX program displays a histogram of filesize distribution of a directory structure(s)*/
numeric digits 30                                /*ensure enough decimal digits for a #.*/
parse arg ds .                                   /*obtain optional argument from the CL.*/
parse source . . path .                          /*   "   the path of this REXX program.*/
fID= substr(path, 1 + lastpos('\', path) )       /*   "   the filename and the filetype.*/
parse var  fID   fn  '.'                         /*   "   just the pure filename of pgm.*/
sw=max(79, linesize() - 1)                       /*   "   terminal width (linesize) - 1.*/
                                work= fn".OUT"   /*filename for workfile output of  DIR.*/
'DIR'   ds   '/s /-c /a-d  >'   work             /*do (DOS) DIR cmd for a data structure*/
call linein 0, 1                                 /*open output file, point to 1st record*/
maxL= 0;    @.= 00;      g= 0                    /*max len size; log array; # good recs.*/
$=0                                              /*$:  total bytes used by files found. */
     do while lines(work)\==0;  _= linein(work)  /*process the data in the DIR work file*/
     if left(_, 1)==' '    then iterate          /*Is the record not legitimate?  Skip. */
     parse upper  var   _    .  .  sz  .         /*uppercase the suffix  (if any).      */
     sz= space( translate(sz, , ','),  0)        /*remove any commas if present in the #*/

     if \datatype(sz,'W')  then do; #= left(sz, length(sz) - 1)       /*SZ has a suffix?*/
                                    if \datatype(#,'N')  then iterate /*Meat ¬ numeric? */
                                    sz= # * 1024 ** pos( right(sz, 1), 'KMGTPEZYXWVU') / 1
                                end                                   /* [↑]  use suffix*/
     $= $ + sz                                   /*keep a running total for the filesize*/
     if sz==0  then L= 0                         /*handle special case for an empty file*/
               else L= length(sz)                /*obtain the length of filesize number.*/
     g= g + 1                                    /*bump the counter of # of good records*/
     maxL= max(L, maxL)                          /*get max length filesize for alignment*/
     @.L= @.L + 1                                /*bump counter of record size category.*/
     end   /*j*/                                 /* [↑]   categories:  split by log ten.*/

if g==0  then do;  say 'file not found: '  ds;  exit 13;    end        /*no good records*/
say  ' record size range    count   '
hdr= '══════════════════ ══════════ ';     say hdr;         Lhdr=length(hdr)
mC=0                                             /*mC:  the maximum count for any range.*/
     do   t=1  to 2                              /*T==1   is used to find the max count.*/
       do k=0  to maxL;  mC= max(mC, @.k);  if t==1  then iterate           /*1st pass? */
                             if k==0  then y= center('zero',  length( word(hdr, 1)  ) )
                                      else y= '10^'left(k-1,2)  "──► 10^"left(k,2)  '-1'
       say y || right( commas(@.k), 11)   copies('─', max(1, (@.k / mC * sw % 1) - LHdr) )
       end   /*k*/
     end     /*y*/
say
trace off;   'ERASE'  work                       /*perform clean─up (erase a work file).*/
say commas(g)      ' files detected, '       commas($)        " total bytes."
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg _;  do j#=length(_)-3  to 1  by -3; _=insert(',', _, j#); end;  return _


(defun common-directory-path (&rest paths)
  (do* ((pathnames (mapcar #'(lambda (path) (cdr (pathname-directory (pathname path)))) paths)) ; convert strings to lists of subdirectories
     (rem pathnames (cdr rem))
     (pos (length (first rem))) ) ; position of first mismatched element
    ((null (cdr rem)) (make-pathname :directory (cons :absolute (subseq (first pathnames) 0 pos)))) ; take the common sublists and convert back to a pathname
  (setq pos (min pos (mismatch (first rem) (second rem) :test #'string-equal))) )) ; compare two paths


import std.stdio, std.string, std.algorithm, std.path, std.array;

string commonDirPath(in string[] paths, in string sep = "/") pure {
    if (paths.empty)
        return null;
    return paths.map!(p => p.split(sep)).reduce!commonPrefix.join(sep);
}

void main() {
    immutable paths = ["/home/user1/tmp/coverage/test",
                       "/home/user1/tmp/covert/operator",
                       "/home/user1/tmp/coven/members"];
    writeln(`The common path is: "`, paths.commonDirPath, '"');
}


use std::error::Error;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use std::{env, fmt, io, time};
use walkdir::{DirEntry, WalkDir};

fn main() -> Result<(), Box<dyn Error>> {
    let start = time::Instant::now();
    let args: Vec<String> = env::args().collect();

    let root = parse_path(&args).expect("not a valid path");
    let dir = WalkDir::new(&root);

    let (files, dirs): (Vec<PathBuf>, Vec<PathBuf>) = {
        let pool = pool(dir).expect("unable to retrieve entries from WalkDir");
        partition_from(pool).expect("unable to partition files from directories")
    };

    let (fs_count, dr_count) = (files.len(), dirs.len());
    let (file_counter, total_size) = file_count(files);

    {
        println!("++ File size distribution for : {} ++\n", &root.display());
        println!("Files @ 0B            : {:4}", file_counter[0]);
        println!("Files > 1B  - 1,023B  : {:4}", file_counter[1]);
        println!("Files > 1KB - 1,023KB : {:4}", file_counter[2]);
        println!("Files > 1MB - 1,023MB : {:4}", file_counter[3]);
        println!("Files > 1GB - 1,023GB : {:4}", file_counter[4]);
        println!("Files > 1TB+          : {:4}\n", file_counter[5]);

        println!("Files encountered: {}", fs_count);
        println!("Directories traversed: {}", dr_count);
        println!(
            "Total size of all files: {}\n",
            Filesize::<Kilobytes>::from(total_size)
        );
    }

    let end = time::Instant::now();
    println!("Run time: {:?}\n", end.duration_since(start));
    Ok(())
}

fn parse_path(args: &[String]) -> Result<&Path, io::Error> {
    // If there's no `args` entered, the executable will search it's own path.
    match args.len() {
        1 => Ok(Path::new(&args[0])),
        _ => Ok(Path::new(&args[1])),
    }
}

fn pool(dir: WalkDir) -> Result<Vec<DirEntry>, Box<dyn Error>> {
    // Check each item for errors and drop possible invalid `DirEntry`s
    Ok(dir.into_iter().filter_map(|e| e.ok()).collect())
}

fn partition_from(pool: Vec<DirEntry>) -> Result<(Vec<PathBuf>, Vec<PathBuf>), Box<dyn Error>> {
    // Read `Path` from `DirEntry`, checking if `Path` is a file or directory.
    Ok(pool
        .into_iter()
        .map(|e| e.into_path())
        .partition(|path| path.is_file()))
}

fn file_count(files: Vec<PathBuf>) -> ([u64; 6], u64) {
    let mut counter: [u64; 6] = [0; 6];
    for file in &files {
        match Filesize::<Bytes>::from(file).bytes {
            0 => counter[0] += 1,                                 // Empty file
            1..=1_023 => counter[1] += 1,                         // 1 byte to 0.99KB
            1_024..=1_048_575 => counter[2] += 1,                 // 1 kilo to 0.99MB
            1_048_576..=1_073_741_823 => counter[3] += 1,         // 1 mega to 0.99GB
            1_073_741_824..=1_099_511_627_775 => counter[4] += 1, // 1 giga to 0.99TB
            1_099_511_627_776..=std::u64::MAX => counter[5] += 1, // 1 terabyte or larger
        }
    }

    let total_file_size = files
        .iter()
        .fold(0, |acc, file| acc + Filesize::<Bytes>::from(file).bytes);
    (counter, total_file_size)
}

trait SizeUnit: Copy {
    fn singular_name() -> String;
    fn num_byte_in_unit() -> u64;
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Bytes;
impl SizeUnit for Bytes {
    fn singular_name() -> String {
        "B".to_string()
    }
    fn num_byte_in_unit() -> u64 {
        1
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Kilobytes;
impl SizeUnit for Kilobytes {
    fn singular_name() -> String {
        "KB".to_string()
    }
    fn num_byte_in_unit() -> u64 {
        1_024
    }
}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
struct Filesize<T: SizeUnit> {
    bytes: u64,
    unit: PhantomData<T>,
}

impl<T> From<u64> for Filesize<T>
where
    T: SizeUnit,
{
    fn from(n: u64) -> Self {
        Filesize {
            bytes: n * T::num_byte_in_unit(),
            unit: PhantomData,
        }
    }
}

impl<T> From<Filesize<T>> for u64
where
    T: SizeUnit,
{
    fn from(fsz: Filesize<T>) -> u64 {
        ((fsz.bytes as f64) / (T::num_byte_in_unit() as f64)) as u64
    }
}

impl<T> fmt::Display for Filesize<T>
where
    T: SizeUnit,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // convert value in associated units to float
        let size_val = ((self.bytes as f64) / (T::num_byte_in_unit() as f64)) as u64;

        // plural?
        let name_plural = match size_val {
            1 => "",
            _ => "s",
        };

        write!(
            f,
            "{} {}{}",
            (self.bytes as f64) / (T::num_byte_in_unit() as f64),
            T::singular_name(),
            name_plural
        )
    }
}

// Can be expanded for From<File>, or any type that has an alias for Metadata
impl<T> From<&PathBuf> for Filesize<T>
where
    T: SizeUnit,
{
    fn from(f: &PathBuf) -> Self {
        Filesize {
            bytes: f
                .metadata()
                .expect("error with metadata from pathbuf into filesize")
                .len(),
            unit: PhantomData,
        }
    }
}


func traverse(Block callback, Dir dir) {
    dir.open(\var dir_h) || return nil

    for entry in (dir_h.entries) {
        if (entry.kind_of(Dir)) {
            traverse(callback, entry)
        } else {
            callback(entry)
        }
    }
}

var dir = (ARGV ? Dir(ARGV[0]) : Dir.cwd)

var group = Hash()
var files_num = 0
var total_size = 0

traverse({ |file|
    group{file.size+1 -> log10.round} := 0 += 1
    total_size += file.size
    files_num += 1
}, dir)

for k,v in (group.sort_by { |k,_| Num(k) }) {
    say "log10(size) ~~ #{k} -> #{v} files"
}

say "Total: #{total_size} bytes in #{files_num} files"


package require fileutil::traverse
namespace path {::tcl::mathfunc ::tcl::mathop}

# Ternary helper
proc ? {test a b} {tailcall if $test [list subst $a] [list subst $b]}

set dir [? {$argc} {[lindex $argv 0]} .]
fileutil::traverse Tobj $dir \
	-prefilter {apply {path {ne [file type $path] link}}} \
	-filter    {apply {path {eq [file type $path] file}}}
Tobj foreach path {
	set size [file size $path]
	dict incr hist [? {$size} {[int [log10 $size]]} -1]
}
Tobj destroy

foreach key [lsort -int [dict keys $hist]] {
	puts "[? {$key == -1} 0 {1e$key}]\t[dict get $hist $key]"
}


program Find_common_directory_path;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

function FindCommonPath(Separator: Char; Paths: TArray<string>): string;
var
  SeparatedPath: array of TArray<string>;
  minLength, index: Integer;
  isSame: Boolean;
  j, i: Integer;
  cmp: string;
begin
  SetLength(SeparatedPath, length(Paths));
  minLength := MaxInt;
  for i := 0 to High(SeparatedPath) do
  begin
    SeparatedPath[i] := Paths[i].Split([Separator]);
    if minLength > length(SeparatedPath[i]) then
      minLength := length(SeparatedPath[i]);
  end;

  index := -1;

  for i := 0 to minLength - 1 do
  begin
    isSame := True;
    cmp := SeparatedPath[0][i];
    for j := 1 to High(SeparatedPath) do
      if SeparatedPath[j][i] <> cmp then
      begin
        isSame := False;
        Break;
      end;
    if not isSame then
    begin
      index := i - 1;
      Break;
    end;
  end;

  Result := '';
  if index >= 0 then
    for i := 0 to index do
    begin
      Result := Result + SeparatedPath[0][i];
      if i < index then
        Result := Result + Separator;
    end;
end;

begin
  Writeln(FindCommonPath('/', [
    '/home/user1/tmp/coverage/test',
    '/home/user1/tmp/covert/operator',
    '/home/user1/tmp/coven/members']));
  Readln;
end.