--- /dev/null
+package main
+
+import (
+ "encoding/csv"
+ "errors"
+ "fmt"
+ "git.friedersdorff.com/max/vector.go"
+ "os"
+ "strconv"
+)
+
+// sameSide returns true if p1 and p2 are on the same side of the line ab, false
+// otherwise
+func sameSide(p1, p2, a, b vector.Vec) bool {
+ cp1 := b.Sub(a).Cross(p1.Sub(a))
+ cp2 := b.Sub(a).Cross(p2.Sub(a))
+
+ return cp1.Dot(cp2) >= 0
+}
+
+// inTriangle returns true if point p is in triangle abc, false otherwise
+func inTriangle(p, a, b, c vector.Vec) bool {
+ return sameSide(p, a, b, c) && sameSide(p, b, a, c) && sameSide(p, c, a, b)
+}
+
+func getVec(record []string) ([]vector.Vec, error) {
+ if len(record)%2 != 0 {
+ return nil, errors.New(
+ fmt.Sprintf("Length of record is not divisible by 2: %s",
+ len(record)))
+ }
+ var vecs []vector.Vec
+
+ floats := make([]float64, len(record))
+ for i, v := range record {
+ num, err := strconv.ParseFloat(v, 64)
+
+ if err != nil {
+ return nil, errors.New(
+ fmt.Sprintf("Couldn't parse record to floats: %s",
+ record))
+ }
+
+ floats[i] = num
+ }
+
+ for i := 0; i < len(floats); i += 2 {
+ vecs = append(vecs, vector.Vec{floats[i], floats[i+1], 0.0})
+ }
+ return vecs, nil
+
+}
+
+func main() {
+ if len(os.Args) != 2 {
+ fmt.Println("Expect input file as first argument.")
+ return
+ }
+
+ f, err := os.Open(os.Args[1])
+
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ r := csv.NewReader(f)
+
+ nInTriangle := 0
+ for {
+ triangle, err := r.Read()
+ if err != nil {
+ break
+ }
+
+ points, err := getVec(triangle)
+ if err != nil {
+
+ break
+ }
+
+ if len(points) != 3 {
+ break
+ }
+
+ if inTriangle(vector.Vec{0, 0, 0},
+ points[0], points[1], points[2]) {
+ nInTriangle++
+ }
+ }
+
+ fmt.Println(nInTriangle)
+ return
+}