Faces from a mesh: Difference between revisions

m
→‎{{header|Wren}}: Changed to Wren S/H
m (→‎{{header|Python}}: added zkl header)
m (→‎{{header|Wren}}: Changed to Wren S/H)
 
(33 intermediate revisions by 14 users not shown)
Line 1:
{{draft task}}
 
A [[wp:Polygon mesh|mesh]] defining a surface has uniquely numbered vertices, and named,
Line 61:
 
;Task:
'''1.''' Write a routine to check if two perimeter formatted faces have the same perimeter. use Use it oto check if the following pairs of perimeters are the same:
<pre> Q: (8, 1, 3)
R: (1, 3, 8)
Line 77:
Show your output here.
 
=={{header|11l}}==
{{trans|Python}}
 
<syntaxhighlight lang="11l">F perim_equal(p1, =p2)
=={{header|Perl 6}}==
I p1.len != p2.len | Set(p1) != Set(p2)
{{works with|Rakudo|2019.11}}
R 0B
I any((0 .< p1.len).map(n -> @p2 == (@p1[n ..] [+] @p1[0 .< n])))
R 1B
p2 = reversed(p2)
R any((0 .< p1.len).map(n -> @p2 == (@p1[n ..] [+] @p1[0 .< n])))
 
F edge_to_periphery(e)
<lang perl6>sub check-equivalence ($a, $b) { so $a.Bag eqv $b.Bag }
V edges = sorted(e)
[Int] p
I !edges.empty
p = [edges[0][0], edges[0][1]]
edges.pop(0)
V last = I !p.empty {p.last} E -1
L !edges.empty
L(ij) edges
V (i, j) = ij
I i == last
p.append(j)
last = j
edges.pop(L.index)
L.break
E I j == last
p.append(i)
last = i
edges.pop(L.index)
L.break
L.was_no_break
R ‘>>>Error! Invalid edge format<<<’
R String(p[0 .< (len)-1])
 
print(‘Perimeter format equality checks:’)
sub edge-to-periphery (@a is copy) {
L(eq_check) [(‘Q’, [8, 1, 3],
return Nil unless @a.List.Bag.values.all == 2;
‘R’, [1, 3, 8]),
my @b = @a.shift.flat;
(‘U’, [18, 8, 14, 10, 12, 17, 19],
while @a > 1 {
for @a.kv -> $k ‘V’, $v[8, 14, 10, 12, 17, 19, {18])]
V (n1, p1, n2, p2) = eq_check
if $v[0] == @b.tail {
V eq = I perim_equal(p1, p2) {‘==’} E ‘!=’
@b.push: $v[1];
print(‘ ’n1‘ ’eq‘ ’n2)
@a.splice($k,1);
 
last
print("\nEdge to perimeter format translations:")
V edge_d = [‘E’ = [(1, 11), (7, 11), (1, 7)],
‘F’ = [(11, 23), (1, 17), (17, 23), (1, 11)],
‘G’ = [(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)],
‘H’ = [(1, 3), (9, 11), (3, 11), (1, 11)]]
L(name, edges) edge_d
print(‘ ’name‘: ’edges"\n -> "edge_to_periphery(edges))</syntaxhighlight>
 
{{out}}
<pre>
Perimeter format equality checks:
Q == R
U == V
 
Edge to perimeter format translations:
E: [(1, 11), (7, 11), (1, 7)]
-> [1, 7, 11]
F: [(11, 23), (1, 17), (17, 23), (1, 11)]
-> [1, 11, 23, 17]
G: [(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)]
-> [8, 14, 10, 12, 17, 19, 18]
H: [(1, 3), (9, 11), (3, 11), (1, 11)]
-> >>>Error! Invalid edge format<<<
</pre>
 
=={{header|C++}}==
<syntaxhighlight lang="c++">
 
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
 
typedef std::pair<int32_t, int32_t> Edge;
 
std::string to_string(const int32_t& value) {
return std::to_string(value);
}
 
std::string to_string(const Edge& edge) {
return "(" + std::to_string(edge.first) + ", " + std::to_string(edge.second) + ")";
}
 
template <typename T>
std::string vector_to_string(const std::vector<T>& list) {
std::string result = "[";
for ( uint64_t i = 0; i < list.size() - 1; ++i ) {
result += to_string(list[i]) + ", ";
}
result += to_string(list.back()) + "]";
return result;
}
 
bool is_same_face(const std::vector<int32_t>& list_1, const std::vector<int32_t>& list_2) {
if ( list_1.size() != list_2.size() || list_1.empty() ) {
return false;
}
 
std::vector<int32_t> copy_2(list_2);
for ( int32_t i = 0; i < 2; ++i ) {
int32_t start;
if ( auto iterator = std::find(copy_2.begin(), copy_2.end(), list_1.front()); iterator != copy_2.end() ) {
start = std::distance(copy_2.begin(), iterator);
} else {
return false;
}
std::vector<int32_t> test(copy_2.begin() + start, copy_2.end());
test.insert(test.end(), copy_2.begin(), copy_2.begin() + start);//addAll(copyTwo.subList(0, start));
if ( list_1 == test ) {
return true;
}
std::reverse(copy_2.begin(), copy_2.end());
}
 
return false;
}
 
std::vector<int32_t> to_perimeter_format_face(const std::vector<Edge>& edge_format_face) {
if ( edge_format_face.empty() ) {
return std::vector<int32_t>();
}
 
std::vector<Edge> edges(edge_format_face);
std::vector<int32_t> result;
Edge first_edge = edges.front();
edges.erase(edges.begin());
int next_vertex = first_edge.first;
result.push_back(next_vertex);
 
while ( ! edges.empty() ) {
int32_t index = -1;
for ( Edge edge : edges ) {
if ( edge.first == next_vertex || edge.second == next_vertex ) {
if ( auto iterator = std::find(edges.begin(), edges.end(), edge); iterator != edges.end() ) {
index = std::distance(edges.begin(), iterator);
}
next_vertex = ( next_vertex == edge.first ) ? edge.second : edge.first;
break;
}
}
if ( index == -1 ) {
return std::vector<int32_t>();
}
result.push_back(next_vertex);
edges.erase(edges.begin() + index);
}
 
if ( next_vertex != first_edge.second ) {
return std::vector<int32_t>();
}
return result;
}
 
int main() {
const std::vector<int32_t> perimeter_format_q = { 8, 1, 3 };
const std::vector<int32_t> perimeter_format_r = { 1, 3, 8 };
const std::vector<int32_t> perimeter_format_u = { 18, 8, 14, 10, 12, 17, 19 };
const std::vector<int32_t> perimeter_format_v = { 8, 14, 10, 12, 17, 19, 18 };
 
const std::vector<Edge> edge_format_e = { Edge(1, 11), Edge(7, 11), Edge(1, 7) };
const std::vector<Edge> edge_format_f = { Edge(11, 23), Edge(1, 17), Edge(17, 23), Edge(1, 11) };
const std::vector<Edge> edge_format_g =
{ Edge(8, 14), Edge(17, 19), Edge(10, 12), Edge(10, 14), Edge(12, 17), Edge(8, 18), Edge(18, 19) };
const std::vector<Edge> edge_format_h = { Edge(1, 3), Edge(9, 11), Edge(3, 11), Edge(1, 11) };
 
std::cout << "PerimeterFormat equality checks:" << std::endl;
bool same_face = is_same_face(perimeter_format_q, perimeter_format_r);
std::cout << vector_to_string(perimeter_format_q) << " == "
<< vector_to_string(perimeter_format_r) << ": " << std::boolalpha << same_face << std::endl;
same_face = is_same_face(perimeter_format_u, perimeter_format_v);
std::cout << vector_to_string(perimeter_format_u) << " == "
<< vector_to_string(perimeter_format_v) << ": " << std::boolalpha << same_face << std::endl;
 
std::cout << "\nEdgeFormat to PerimeterFormat conversions:" << std::endl;
std::vector<std::vector<Edge>> edge_format_faces = { edge_format_e, edge_format_f, edge_format_g, edge_format_h };
for ( std::vector<Edge> edge_format_face : edge_format_faces ) {
std::vector<int32_t> perimeter_format_face = to_perimeter_format_face(edge_format_face);
if ( perimeter_format_face.empty() ) {
std::cout << vector_to_string(edge_format_face) << " has invalid edge format" << std::endl;
} else {
std::cout << vector_to_string(edge_format_face) << " => "
<< vector_to_string(perimeter_format_face) << std::endl;
}
}
}
</syntaxhighlight>
{{ out }}
<pre>
PerimeterFormat equality checks:
[8, 1, 3] == [1, 3, 8]: true
[18, 8, 14, 10, 12, 17, 19] == [8, 14, 10, 12, 17, 19, 18]: true
 
EdgeFormat to PerimeterFormat conversions:
[(1, 11), (7, 11), (1, 7)] => [1, 7, 11]
[(11, 23), (1, 17), (17, 23), (1, 11)] => [11, 1, 17, 23]
[(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)] => [8, 18, 19, 17, 12, 10, 14]
[(1, 3), (9, 11), (3, 11), (1, 11)] has invalid edge format
</pre>
 
=={{header|Go}}==
<syntaxhighlight lang="go">package main
 
import (
"fmt"
"sort"
)
 
// Check a slice contains a value.
func contains(s []int, f int) bool {
for _, e := range s {
if e == f {
return true
}
}
return false
}
 
// Assumes s1, s2 are of same length.
func sliceEqual(s1, s2 []int) bool {
for i := 0; i < len(s1); i++ {
if s1[i] != s2[i] {
return false
}
}
return true
}
 
// Reverses slice in place.
func reverse(s []int) {
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
s[i], s[j] = s[j], s[i]
}
}
 
// Check two perimeters are equal.
func perimEqual(p1, p2 []int) bool {
le := len(p1)
if le != len(p2) {
return false
}
for _, p := range p1 {
if !contains(p2, p) {
return false
}
}
// use copy to avoid mutating 'p1'
c := make([]int, le)
copy(c, p1)
for r := 0; r < 2; r++ {
for i := 0; i < le; i++ {
if sliceEqual(c, p2) {
return true
}
elsif// $v[1]do ==circular @b.tailshift {to right
t @b.push:= $vc[0le-1];
@a.splicecopy($kc[1:], c[0:le-1]);
c[0] = t
}
// now process in opposite direction
reverse(c)
}
return false
}
 
type edge [2]int
 
// Translates a face to perimeter format.
func faceToPerim(face []edge) []int {
// use copy to avoid mutating 'face'
le := len(face)
if le == 0 {
return nil
}
edges := make([]edge, le)
for i := 0; i < le; i++ {
// check edge pairs are in correct order
if face[i][1] <= face[i][0] {
return nil
}
edges[i] = face[i]
}
// sort edges in ascending order
sort.Slice(edges, func(i, j int) bool {
if edges[i][0] != edges[j][0] {
return edges[i][0] < edges[j][0]
}
return edges[i][1] < edges[j][1]
})
var perim []int
first, last := edges[0][0], edges[0][1]
perim = append(perim, first, last)
// remove first edge
copy(edges, edges[1:])
edges = edges[0 : le-1]
le--
outer:
for le > 0 {
for i, e := range edges {
found := false
if e[0] == last {
perim = append(perim, e[1])
last, found = e[1], true
} else if e[1] == last {
perim = append(perim, e[0])
last, found = e[0], true
}
if found {
// remove i'th edge
copy(edges[i:], edges[i+1:])
edges = edges[0 : le-1]
le--
if last == first {
if le == 0 {
break outer
} else {
return nil
}
}
continue outer
}
}
}
return perim[0 : len(perim)-1]
}
 
func main() {
fmt.Println("Perimeter format equality checks:")
areEqual := perimEqual([]int{8, 1, 3}, []int{1, 3, 8})
fmt.Printf(" Q == R is %t\n", areEqual)
areEqual = perimEqual([]int{18, 8, 14, 10, 12, 17, 19}, []int{8, 14, 10, 12, 17, 19, 18})
fmt.Printf(" U == V is %t\n", areEqual)
e := []edge{{7, 11}, {1, 11}, {1, 7}}
f := []edge{{11, 23}, {1, 17}, {17, 23}, {1, 11}}
g := []edge{{8, 14}, {17, 19}, {10, 12}, {10, 14}, {12, 17}, {8, 18}, {18, 19}}
h := []edge{{1, 3}, {9, 11}, {3, 11}, {1, 11}}
fmt.Println("\nEdge to perimeter format translations:")
for i, face := range [][]edge{e, f, g, h} {
perim := faceToPerim(face)
if perim == nil {
fmt.Printf(" %c => Invalid edge format\n", i + 'E')
} else {
fmt.Printf(" %c => %v\n", i + 'E', perim)
}
}
}</syntaxhighlight>
 
{{out}}
<pre>
Perimeter format equality checks:
Q == R is true
U == V is true
 
Edge to perimeter format translations:
E => [1 7 11]
F => [1 11 23 17]
G => [8 14 10 12 17 19 18]
H => Invalid edge format
</pre>
 
=={{header|Haskell}}==
<syntaxhighlight lang="haskell">import Data.List (find, delete, (\\))
import Control.Applicative ((<|>))
 
------------------------------------------------------------
 
newtype Perimeter a = Perimeter [a]
deriving Show
 
instance Eq a => Eq (Perimeter a) where
Perimeter p1 == Perimeter p2 =
null (p1 \\ p2)
&& ((p1 `elem` zipWith const (iterate rotate p2) p1)
|| Perimeter p1 == Perimeter (reverse p2))
 
rotate lst = zipWith const (tail (cycle lst)) lst
 
toEdges :: Ord a => Perimeter a -> Maybe (Edges a)
toEdges (Perimeter ps)
| allDifferent ps = Just . Edges $ zipWith ord ps (tail (cycle ps))
| otherwise = Nothing
where
ord a b = if a < b then (a, b) else (b, a)
 
allDifferent [] = True
allDifferent (x:xs) = all (x /=) xs && allDifferent xs
 
------------------------------------------------------------
 
newtype Edges a = Edges [(a, a)]
deriving Show
 
instance Eq a => Eq (Edges a) where
e1 == e2 = toPerimeter e1 == toPerimeter e2
 
toPerimeter :: Eq a => Edges a -> Maybe (Perimeter a)
toPerimeter (Edges ((a, b):es)) = Perimeter . (a :) <$> go b es
where
go x rs
| x == a = return []
| otherwise = do
p <- find ((x ==) . fst) rs <|> find ((x ==) . snd) rs
let next = if fst p == x then snd p else fst p
(x :) <$> go next (delete p rs)</syntaxhighlight>
 
First task.
 
<pre>λ> Perimeter [8,1,3] == Perimeter [1,3,8]
True
 
λ> Perimeter [8,1,3] == Perimeter [1,8,3]
True
 
λ> Perimeter [18,8,14,10,12,17,19] == Perimeter [8,14,10,12,17,19,18]
True
 
λ> Perimeter [18,8,14,10,12,17,19] == Perimeter [8,14,10,12,17,19]
False
 
λ> Perimeter [18,8,14,10,12,17,19] == Perimeter [8,14,10,12,17,18,19]
False</pre>
 
Second task
<pre>λ> toPerimeter (Edges [(1,11),(7,11),(1,7)])
Just (Perimeter [1,11,7])
 
λ> toPerimeter (Edges [(11,23),(1,17),(17,23),(1,11)])
Just (Perimeter [11,23,17,1])
 
λ> toPerimeter (Edges [(8,14),(17,19),(10,12),(10,14),(12,17),(8,18),(18,19)])
Just (Perimeter [8,14,10,12,17,19,18])
 
λ> toPerimeter (Edges [(1,3),(9,11),(3,11),(1,11)])
Nothing</pre>
 
=={{header|J}}==
First task:
<pre>
NB. construct a list of all rotations of one of the faces
NB. including all rotations of the reversed list.
NB. Find out if the other face is a member of this list.
 
NB. ,&:rotations -> append, but first enlist the rotations.
 
 
rotations=. |."0 1~ i.@#
reverse=: |.
same_perimeter=: e. (,&:rotations reverse)
 
(3, 1, 8)same_perimeter(8, 1, 3)
1
 
(18, 8, 14, 10, 12, 17, 19)same_perimeter(8, 14, 10, 12, 17, 19, 18)
1
</pre>
Secondly:
<syntaxhighlight lang="j">
edge_to_node=: 3 :0
assert. 2 = #/.~ , y [ 'expect each node to appear twice'
oel=. 1 2 {. y
Y=. }. y
while. # Y do.
i =. <. -: 1 i.~ , Y (e."1) {: oel
assert. 0 < # i [ 'isolated edge detected'
oel =. oel , i { Y
Y =. i ({. , (}.~ >:)~) Y
end.
~. , oel
)
</syntaxhighlight>
 
<pre>
boxdraw_j_ 0
 
]TESTS=: ([: < _2 ]\ ".);._2'{}'-.~0 :0
{(1, 11), (7, 11), (1, 7)}
{(11, 23), (1, 17), (17, 23), (1, 11)}
{(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)}
{(1, 3), (9, 11), (3, 11), (1, 11)}
)
┌────┬─────┬─────┬────┐
│1 11│11 23│ 8 14│1 3│
│7 11│ 1 17│17 19│9 11│
│1 7│17 23│10 12│3 11│
│ │ 1 11│10 14│1 11│
│ │ │12 17│ │
│ │ │ 8 18│ │
│ │ │18 19│ │
└────┴─────┴─────┴────┘
 
 
 
edge_to_node ::('failure'"_)&.> TESTS
┌──────┬──────────┬───────────────────┬───────┐
│1 11 7│11 23 17 1│8 14 10 12 17 19 18│failure│
└──────┴──────────┴───────────────────┴───────┘
 
 
edge_to_node _1 {:: TESTS
|assertion failure: edge_to_node
| 2=#/.~,y['expect each node to appear twice'
</pre>
 
=={{header|Java}}==
<syntaxhighlight lang="java">
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
 
public final class FacesFromMesh {
 
public static void main(String[] aArgs) {
final List<Integer> perimeterFormatQ = Arrays.asList( 8, 1, 3 );
final List<Integer> perimeterFormatR = Arrays.asList( 1, 3, 8 );
final List<Integer> perimeterFormatU = Arrays.asList( 18, 8, 14, 10, 12, 17, 19 );
final List<Integer> perimeterFormatV = Arrays.asList( 8, 14, 10, 12, 17, 19, 18 );
final List<Edge> edgeFormatE = Arrays.asList( new Edge(1, 11), new Edge(7, 11), new Edge(1, 7) );
final List<Edge> edgeFormatF =
Arrays.asList( new Edge(11, 23), new Edge(1, 17), new Edge(17, 23), new Edge(1, 11) );
final List<Edge> edgeFormatG = Arrays.asList( new Edge(8, 14), new Edge(17, 19),
new Edge(10, 12), new Edge(10, 14), new Edge(12, 17), new Edge(8, 18), new Edge(18, 19) );
final List<Edge> edgeFormatH =
Arrays.asList( new Edge(1, 3), new Edge(9, 11), new Edge(3, 11), new Edge(1, 11) );
System.out.println("PerimeterFormat equality checks:");
boolean sameFace = isSameFace(perimeterFormatQ, perimeterFormatR);
System.out.println(perimeterFormatQ + " == " + perimeterFormatR + ": " + sameFace);
sameFace = isSameFace(perimeterFormatU, perimeterFormatV);
System.out.println(perimeterFormatU + " == " + perimeterFormatV + ": " + sameFace);
 
System.out.println(System.lineSeparator() + "EdgeFormat to PerimeterFormat conversions:");
List<List<Edge>> edgeFormatFaces = List.of( edgeFormatE, edgeFormatF, edgeFormatG, edgeFormatH );
for ( List<Edge> edgeFormatFace : edgeFormatFaces ) {
List<Integer> perimeterFormatFace = toPerimeterFormatFace(edgeFormatFace);
if ( perimeterFormatFace.isEmpty() ) {
System.out.println(edgeFormatFace + " has invalid edge format");
} else {
System.out.println(edgeFormatFace + " => " + perimeterFormatFace);
}
}
}
private static boolean isSameFace(List<Integer> aOne, List<Integer> aTwo) {
if ( aOne.size() != aTwo.size() || aOne.isEmpty() ||
! new HashSet<Integer>(aOne).equals( new HashSet<Integer>(aTwo) )) {
return false;
}
 
List<Integer> copyTwo = new ArrayList<Integer>(aTwo);
for ( int i = 0; i < 2; i++ ) {
int start = copyTwo.indexOf(aOne.get(0));
List<Integer> test = new ArrayList<Integer>(copyTwo.subList(start, copyTwo.size()));
test.addAll(copyTwo.subList(0, start));
if ( aOne.equals(test) ) {
return true;
}
Collections.reverse(copyTwo);
}
return false;
}
private static List<Integer> toPerimeterFormatFace(List<Edge> aEdgeFormatFace) {
if ( aEdgeFormatFace.isEmpty() ) {
return Collections.emptyList();
}
List<Edge> edges = new ArrayList<Edge>(aEdgeFormatFace);
List<Integer> result = new ArrayList<Integer>();
Edge firstEdge = edges.remove(0);
int nextVertex = firstEdge.first;
result.add(nextVertex);
while ( ! edges.isEmpty() ) {
int index = -1;
for ( Edge edge : edges ) {
if ( edge.first == nextVertex || edge.second == nextVertex ) {
index = edges.indexOf(edge);
nextVertex = ( nextVertex == edge.first ) ? edge.second : edge.first;
break;
}
}
if ( index == -1 ) {
return Collections.emptyList();
}
result.add(nextVertex);
edges.remove(index);
}
if ( nextVertex != firstEdge.second ) {
return Collections.emptyList();
}
return result;
}
private static class Edge {
public Edge(int aFirst, int aSecond) {
first = aFirst;
second = aSecond;
}
@Override
public String toString() {
return "(" + first + ", " + second + ")";
}
private int first, second;
}
 
}
</syntaxhighlight>
{{ out }}
<pre>
PerimeterFormat equality checks:
[8, 1, 3] == [1, 3, 8]: true
[18, 8, 14, 10, 12, 17, 19] == [8, 14, 10, 12, 17, 19, 18]: true
 
EdgeFormat to PerimeterFormat conversions:
[(1, 11), (7, 11), (1, 7)] => [1, 7, 11]
[(11, 23), (1, 17), (17, 23), (1, 11)] => [11, 1, 17, 23]
[(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)] => [8, 18, 19, 17, 12, 10, 14]
[(1, 3), (9, 11), (3, 11), (1, 11)] has invalid edge format
</pre>
 
=={{header|Julia}}==
<syntaxhighlight lang="julia">iseq(f, g) = any(n -> f == circshift(g, n), 1:length(g))
 
function toface(evec)
try
ret, edges = collect(evec[1]), copy(evec[2:end])
while !isempty(edges)
i = findfirst(x -> ret[end] == x[1] || ret[end] == x[2], edges)
push!(ret, ret[end] == edges[i][1] ? edges[i][2] : edges[i][1])
deleteat!(edges, i)
end
return ret[1:end-1]
catch
println("Invalid edges vector: $evec")
exit(1)
end
end
 
const faces1 = [
[[8, 1, 3], [1, 3, 8]],
[[18, 8, 14, 10, 12, 17, 19], [8, 14, 10, 12, 17, 19, 18]]
]
 
const faces2 = [
[(1, 11), (7, 11), (1, 7)], [(11, 23), (1, 17), (17, 23), (1, 11)],
[(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)],
[(1, 3), (9, 11), (3, 11), (1, 11)]
]
 
for faces in faces1
println("Faces are ", iseq(faces[1], faces[2]) ? "" : "not ", "equivalent.")
end
 
for face in faces2
println(toface(face))
end
</syntaxhighlight>{{out}}
<pre>
Faces are equivalent.
Faces are equivalent.
[1, 11, 7]
[11, 23, 17, 1]
[8, 14, 10, 12, 17, 19, 18]
Invalid edges vector: Tuple{Int64,Int64}[(1, 3), (9, 11), (3, 11), (1, 11)]
</pre>
 
=={{header|Koka}}==
<syntaxhighlight lang="koka">
alias perimeter = list<int>
alias face = (char, perimeter)
alias edge = (int, int)
 
fun isSame(p1: perimeter, p2: perimeter, noSwitch: list<int> = []): div bool
match (p1, p2)
([], []) -> True
(Cons(x1, xs1), Cons(x2, xs2)) | x1 == x2 -> isSame(xs1, xs2)
_ ->
match p2
Nil -> False
Cons(x2, xs2) ->
if noSwitch.any(fn(x') x' == x2) then False else p1.isSame(xs2 ++ [x2], Cons(x2, noSwitch))
 
fun show(f: face)
val (c, p) = f
c.core/show ++ " " ++ p.core/show
 
fun findRemove(l: list<a>, acc: ctx<list<a>>, pred: (a) -> bool): maybe<(a, list<a>)>
match l
[] -> Nothing
Cons(x, xs) -> match pred(x)
True -> Just((x, acc ++. xs))
False -> findRemove(xs, acc ++ ctx Cons(x, _), pred)
 
fun toPerimeter(search: int, edges: list<edge>, acc: ctx<perimeter>): <div,exn> perimeter
match edges
[] -> acc ++. Cons(search, Nil) // Assume the search matches the first edge's missing
_ -> match edges.findRemove(ctx _, fn((a, b)) a == search || b == search)
Just(((a, b), xs')) -> if search == a then
toPerimeter(b, xs', acc ++ ctx Cons(a, _))
else
toPerimeter(a, xs', acc ++ ctx Cons(b, _))
Nothing -> throw("Cannot find the next edge for " ++ search.show ++ " current perimeter is " ++ (acc ++. Nil).show ++ " and the rest of the edges are: " ++ edges.show-list(fn(s) s.show-tuple(show, show)))
 
fun main()
val fp = [(('P', [8, 1, 3]), ('R', [1, 3, 8])),
(('U', [18, 8, 14, 10, 12, 17, 19]), ('V', [8, 14, 10, 12, 17, 19, 18]))]
fp.foreach() fn((f1, f2))
val same = f1.snd.isSame(f2.snd)
print(f1.show ++ (if same then " is " else " is not ") ++ "the same as " ++ f2.show ++ "\n")
val fe = [('E', [(1, 11), (7, 11), (1, 7)]),
('F', [(11, 23), (1, 17), (17, 23), (1, 11)]),
('G', [(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)]),
('H', [(1, 3), (9, 11), (3, 11), (1, 11)])]
fe.foreach() fn((f, edges))
match edges
[] -> ()
Cons((initX, _), edges') ->
val p = toPerimeter(initX, edges', ctx _)
print(f.show ++ " perimeter is " ++ p.show ++ "\n")
</syntaxhighlight>
{{out}}
<pre>
'P' [8,1,3] is the same as 'R' [1,3,8]
'U' [18,8,14,10,12,17,19] is the same as 'V' [8,14,10,12,17,19,18]
'E' perimeter is [1,7,11]
'F' perimeter is [11,1,17,23]
'G' perimeter is [8,18,19,17,12,10,14]
uncaught exception: Cannot find the next edge for 9 current perimeter is [1,11] and the rest of the edges are: [(3,11)]
</pre>
 
=={{header|Lua}}==
<syntaxhighlight lang="lua">-- support
function T(t) return setmetatable(t, {__index=table}) end
table.eql = function(t,u) if #t~=#u then return false end for i=1,#t do if t[i]~=u[i] then return false end end return true end
table.rol = function(t,n) local s=T{} for i=1,#t do s[i]=t[(i+n-1)%#t+1] end return s end
table.rev = function(t) local s=T{} for i=1,#t do s[#t-i+1]=t[i] end return s end
 
-- 1
function pfeq(pf1, pf2)
if #pf1 ~= #pf2 then return false end -- easy case
for w = 0,1 do -- exhaustive cases
local pfw = pf1 -- w:winding
if w==1 then pfw=pfw:rev() end
for r = 0,#pfw do
local pfr = pfw -- r:rotate
if r>0 then pfr=pfr:rol(r) end
if pf2:eql(pfr) then return true end
end
end
return false
end
 
Q = T{8, 1, 3}
R = T{1, 3, 8}
U = T{18, 8, 14, 10, 12, 17, 19}
V = T{8, 14, 10, 12, 17, 19, 18}
print("pfeq(Q,R): ", pfeq(Q, R))
print("pfeq(U,V): ", pfeq(U, V))
 
-- 2
function ef2pf(ef)
local pf, hse = T{}, T{} -- hse:hash of sorted edges
for i,e in ipairs(ef) do table.sort(e) hse[e]=e end
local function nexte(e)
if not e then return ef[1] end
for k,v in pairs(hse) do
if e[2]==v[1] then return v end
if e[2]==v[2] then v[1],v[2]=v[2],v[1] return v end
end
end
local e = nexte()
while e do
pf[#pf+1] = e[1]
hse[e] = nil
e = nexte(e)
end
if #pf ~= #ef then pf=T{"failed to convert edge format to perimeter format"} end
return pf
end
 
E = {{1, 11}, {7, 11}, {1, 7}}
F = {{11, 23}, {1, 17}, {17, 23}, {1, 11}}
G = {{8, 14}, {17, 19}, {10, 12}, {10, 14}, {12, 17}, {8, 18}, {18, 19}}
H = {{1, 3}, {9, 11}, {3, 11}, {1, 11}}
print("ef2pf(E): ", ef2pf(E):concat(","))
print("ef2pf(F): ", ef2pf(F):concat(","))
print("ef2pf(G): ", ef2pf(G):concat(","))
print("ef2pf(H): ", ef2pf(H):concat(","))</syntaxhighlight>
{{out}}
<pre>pfeq(Q,R): true
pfeq(U,V): true
ef2pf(E): 1,11,7
ef2pf(F): 11,23,17,1
ef2pf(G): 8,14,10,12,17,19,18
ef2pf(H): failed to convert edge format to perimeter format</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import algorithm, strutils
 
type
Perimeter = seq[int]
Face = tuple[name: char; perimeter: Perimeter]
Edge = tuple[first, last: int]
 
const None = -1 # No point.
 
#---------------------------------------------------------------------------------------------------
 
func isSame(p1, p2: Perimeter): bool =
## Return "true" if "p1" and "p2" represent the same face.
 
if p1.len != p2.len: return false
 
for p in p1:
if p notin p2: return false
 
var start = p2.find(p1[0])
if p1 == p2[start..^1] & p2[0..<start]:
return true
 
let p3 = reversed(p2)
start = p3.find(p1[0])
if p1 == p3[start..^1] & p3[0..<start]:
return true
 
#---------------------------------------------------------------------------------------------------
 
func `$`(perimeter: Perimeter): string =
## Convert a perimeter to a string.
'(' & perimeter.join(", ") & ')'
 
func `$`(face: Face): string =
## Convert a perimeter formatted face to a string.
face.name & $face.perimeter
 
#---------------------------------------------------------------------------------------------------
 
func toPerimeter(edges: seq[Edge]): Perimeter =
## Convert a list of edges to perimeter representation.
## Return an empty perimeter if the list of edges doesn’t represent a face.
 
var edges = edges
let firstEdge = edges.pop() # First edge taken in account.
var nextPoint = firstEdge.first # Next point to search in remaining edges.
result.add(nextpoint)
 
while edges.len > 0:
# Search an edge.
var idx = None
for i, e in edges:
if e.first == nextPoint or e.last == nextPoint:
idx = i
nextPoint = if nextpoint == e.first: e.last else: e.first
break
if idx == None:
return @[] # No edge found containing "newPoint".
 
# Add next point to perimeter and remove the edge.
result.add(nextPoint)
edges.del(idx)
 
# Check that last added point is the expected one.
if nextPoint != firstEdge.last:
return @[]
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
when isMainModule:
 
# List of pairs of perimeter formatted faces to compare.
const FP = [(('P', @[8, 1, 3]), ('R', @[1, 3, 8])),
(('U', @[18, 8, 14, 10, 12, 17, 19]), ('V', @[8, 14, 10, 12, 17, 19, 18]))]
 
echo "Perimeter comparison:"
for (p1, p2) in FP:
echo p1, if isSame(p1[1], p2[1]): " is same as " else: "is not same as", p2
 
# List of edge formatted faces.
const FE = {'E': @[(1, 11), (7, 11), (1, 7)],
'F': @[(11, 23), (1, 17), (17, 23), (1, 11)],
'G': @[(8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)],
'H': @[(1, 3), (9, 11), (3, 11), (1, 11)]}
 
echo ""
echo "Conversion from edge to perimeter format:"
for (faceName, edges) in FE:
let perimeter = edges.toPerimeter()
echo faceName, ": ", if perimeter.len == 0: "Invalid edge list" else: $perimeter</syntaxhighlight>
 
{{out}}
<pre>Perimeter comparison:
P(8, 1, 3) is same as R(1, 3, 8)
U(18, 8, 14, 10, 12, 17, 19) is same as V(8, 14, 10, 12, 17, 19, 18)
 
Conversion from edge to perimeter format:
E: (1, 11, 7)
F: (1, 17, 23, 11)
G: (18, 8, 14, 10, 12, 17, 19)
H: Invalid edge list</pre>
 
=={{header|Perl}}==
{{trans|Raku}}
<syntaxhighlight lang="perl">use strict;
use warnings;
use feature 'say';
use Set::Scalar;
use Set::Bag;
use Storable qw(dclone);
 
sub show { my($pts) = @_; my $p='( '; $p .= '(' . join(' ',@$_) . ') ' for @$pts; $p.')' }
 
sub check_equivalence {
my($a, $b) = @_;
Set::Scalar->new(@$a) == Set::Scalar->new(@$b)
}
 
sub edge_to_periphery {
my $a = dclone \@_;
 
my $bag_a = Set::Bag->new;
for my $p (@$a) {
$bag_a->insert( @$p[0] => 1);
$bag_a->insert( @$p[1] => 1);
}
2 != @$bag_a{$_} and return 0 for keys %$bag_a;
 
my $b = shift @$a;
while ($#{$a} > 0) {
for my $k (0..$#{$a}) {
my $v = @$a[$k];
if (@$v[0] == @$b[-1]) {
push @$b, @$v[1];
splice(@$a,$k,1);
last
} elsif (@$v[1] == @$b[-1]) {
push @$b, @$v[0];
splice(@$a,$k,1);
last
}
}
}
@$b;
}
 
say 'Perimeter format equality checks:';
for ([[8, 1, 3], [1, 3, 8]],
 
[[18, 8, 14, 10, 12, 17, 19], [8, 14, 10, 12, 17, 19, 18]]) {
for (8, 1, 3), (1, 3, 8),
my($a, $b) = @$_;
(18, 8, 14, 10, 12, 17, 19), (8, 14, 10, 12, 17, 19, 18)
say '(' . join(', ', @$a) . ') equivalent to (' . join(', ', @$b) . ')? ',
-> $a, $b {
say " check_equivalence({$a.join: ', '}$b) ? equivalent'True' to ({$b.join: ', False'})? ",;
check-equivalence($a, $b)
}
 
say "\nEdge to perimeter format translations:";
for ([[1, 11], [7, 11], [1, 7]],
 
for (( [[11, 23], [1, 11)17], (7[17, 11)23], ([1, 7))11]],
((11, 23)[[8, (114], [17), (19], [10, 12], [10, 14], [12, 17], [8, 23)18], (1[18, 11))19]],
((8, 14)[[1, (173], 19)[9, (1011], 12)[3, (1011], 14)[1, (12, 1711]]), (8, 18), (18, 19)),{
say show($_) . ' ==> (' . (join ' ', edge_to_periphery(@$_) or 'Invalid edge format') . ')'
((1, 3), (9, 11), (3, 11), (1, 11))
}</syntaxhighlight>
{
.gist.print;
say " ==> ({.&edge-to-periphery || 'Invalid edge format'})";
}</lang>
{{out}}
<pre>Perimeter format equality checks:
Line 128 ⟶ 1,054:
 
Edge to perimeter format translations:
( (1 11) (7 11) (1 7) ) ==> (1 11 7)
( (11 23) (1 17) (17 23) (1 11) ) ==> (11 23 17 1)
( (8 14) (17 19) (10 12) (10 14) (12 17) (8 18) (18 19) ) ==> (8 14 10 12 17 19 18)
( (1 3) (9 11) (3 11) (1 11) ) ==> (Invalid edge format)</pre>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(phixonline)-->
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">perequiv</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- Works by aligning and rotating in one step, so theoretically much faster on massive sets.
-- (ahem, faster than multiple rotates, index-only loops would obviously be even faster...)</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">)==</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">res</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">)></span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">b</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">else</span>
<span style="color: #000080;font-style:italic;">-- align with a (ie make b[1]==a[1], by
-- rotating b k places in one operation)</span>
<span style="color: #000000;">b</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">..$]&</span><span style="color: #000000;">b</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">k</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">b</span> <span style="color: #008080;">then</span>
<span style="color: #000080;font-style:italic;">-- eg {8,3,4,5} &lt;=&gt; {8,5,4,3}, ie
-- rotate *and* keep in alignment.</span>
<span style="color: #000000;">b</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">reverse</span><span style="color: #0000FF;">(</span><span style="color: #000000;">b</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$])</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">a</span><span style="color: #0000FF;">==</span><span style="color: #000000;">b</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000080;font-style:italic;">-- return res</span>
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"false"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"true"</span><span style="color: #0000FF;">}[</span><span style="color: #000000;">res</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">edge2peri</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">edges</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">was</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">edges</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">error</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">lnk</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">)<</span><span style="color: #000000;">2</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">error</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"too short"</span>
<span style="color: #008080;">else</span>
<span style="color: #000080;font-style:italic;">-- edges = sort(deep_copy(edges)) -- (see note below)</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])</span>
<span style="color: #000000;">edges</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">edges</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">..$]</span>
<span style="color: #000000;">lnk</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">while</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">and</span> <span style="color: #000000;">error</span><span style="color: #0000FF;">=</span><span style="color: #008000;">""</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">found</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">lnk</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">edges</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">3</span><span style="color: #0000FF;">-</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">edges</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">edges</span><span style="color: #0000FF;">={}</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">lnk</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">res</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #008080;">then</span> <span style="color: #000000;">error</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"oh dear"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span><span style="color: #000000;">res</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">error</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"oops"</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">lnk</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">found</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #000000;">found</span> <span style="color: #008080;">then</span> <span style="color: #000000;">error</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"bad link"</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">error</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">error</span><span style="color: #0000FF;">,</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span><span style="color: #000000;">edges</span><span style="color: #0000FF;">,</span><span style="color: #000000;">was</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">ptests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">}},</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">10</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">12</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">10</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">12</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">18</span><span style="color: #0000FF;">}},</span>
<span style="color: #000080;font-style:italic;">-- (check our results below against Go etc)</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">11</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span><span style="color: #000000;">11</span><span style="color: #0000FF;">}},</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">11</span><span style="color: #0000FF;">,</span><span style="color: #000000;">23</span><span style="color: #0000FF;">,</span><span style="color: #000000;">17</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">11</span><span style="color: #0000FF;">,</span><span style="color: #000000;">23</span><span style="color: #0000FF;">,</span><span style="color: #000000;">17</span><span style="color: #0000FF;">}}}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ptests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">sequence</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">p</span><span style="color: #0000FF;">,</span><span style="color: #000000;">q</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ptests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%v equivalent to %v: %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">p</span><span style="color: #0000FF;">,</span><span style="color: #000000;">q</span><span style="color: #0000FF;">,</span><span style="color: #000000;">perequiv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">p</span><span style="color: #0000FF;">,</span><span style="color: #000000;">q</span><span style="color: #0000FF;">)})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">etests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">7</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">7</span><span style="color: #0000FF;">}},</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">11</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">23</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">17</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">23</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">}},</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">17</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">12</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">10</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">14</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">12</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">17</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">8</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">18</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">18</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">19</span><span style="color: #0000FF;">}},</span>
<span style="color: #0000FF;">{{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">3</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">9</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">3</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">},</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">11</span><span style="color: #0000FF;">}}}</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">etests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%v\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">edge2peri</span><span style="color: #0000FF;">(</span><span style="color: #000000;">etests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<!--</syntaxhighlight>-->
{{out}}
(second part matches Julia/Perl: un-comment that sort above to match Go/Python/zkl)
<pre>
{8,1,3} equivalent to {1,3,8}: true
{18,8,14,10,12,17,19} equivalent to {8,14,10,12,17,19,18}: true
{1,11,7} equivalent to {1,7,11}: true
{11,23,17,1} equivalent to {1,11,23,17}: true
{1,11,7}
{11,23,17,1}
{8,14,10,12,17,19,18}
{"bad link",{1,3,11,9},9,{{1,11}},{{1,3},{9,11},{3,11},{1,11}}}
</pre>
 
=={{header|Python}}==
<langsyntaxhighlight lang="python">def perim_equal(p1, p2):
# Cheap tests first
if len(p1) != len(p2) or set(p1) != set(p2):
Line 183 ⟶ 1,202:
}
for name, edges in edge_d.items():
print(f" {name}: {edges}\n -> {edge_to_periphery(edges)}")</langsyntaxhighlight>
 
{{out}}
Line 199 ⟶ 1,218:
H: {(1, 11), (9, 11), (1, 3), (3, 11)}
-> >>>Error! Invalid edge format<<<</pre>
 
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2019.11}}
 
<syntaxhighlight lang="raku" line>sub check-equivalence ($a, $b) { so $a.Bag eqv $b.Bag }
 
sub edge-to-periphery (@a is copy) {
return Nil unless @a.List.Bag.values.all == 2;
my @b = @a.shift.flat;
while @a > 1 {
for @a.kv -> $k, $v {
if $v[0] == @b.tail {
@b.push: $v[1];
@a.splice($k,1);
last
}
elsif $v[1] == @b.tail {
@b.push: $v[0];
@a.splice($k,1);
last
}
}
}
@b
}
 
say 'Perimeter format equality checks:';
 
for (8, 1, 3), (1, 3, 8),
(18, 8, 14, 10, 12, 17, 19), (8, 14, 10, 12, 17, 19, 18)
-> $a, $b {
say "({$a.join: ', '}) equivalent to ({$b.join: ', '})? ",
check-equivalence($a, $b)
}
 
say "\nEdge to perimeter format translations:";
 
for ((1, 11), (7, 11), (1, 7)),
((11, 23), (1, 17), (17, 23), (1, 11)),
((8, 14), (17, 19), (10, 12), (10, 14), (12, 17), (8, 18), (18, 19)),
((1, 3), (9, 11), (3, 11), (1, 11))
{
.gist.print;
say " ==> ({.&edge-to-periphery || 'Invalid edge format'})";
}</syntaxhighlight>
{{out}}
<pre>Perimeter format equality checks:
(8, 1, 3) equivalent to (1, 3, 8)? True
(18, 8, 14, 10, 12, 17, 19) equivalent to (8, 14, 10, 12, 17, 19, 18)? True
 
Edge to perimeter format translations:
((1 11) (7 11) (1 7)) ==> (1 11 7)
((11 23) (1 17) (17 23) (1 11)) ==> (11 23 17 1)
((8 14) (17 19) (10 12) (10 14) (12 17) (8 18) (18 19)) ==> (8 14 10 12 17 19 18)
((1 3) (9 11) (3 11) (1 11)) ==> (Invalid edge format)</pre>
 
=={{header|Wren}}==
{{trans|Go}}
{{libheader|Wren-sort}}
{{libheader|Wren-seq}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./sort" for Sort
import "./seq" for Lst
import "./fmt" for Fmt
// Check two perimeters are equal.
var perimEqual = Fn.new { |p1, p2|
var le = p1.count
if (le != p2.count) return false
for (p in p1) {
if (!p2.contains(p)) return false
}
// use copy to avoid mutating 'p1'
var c = p1.toList
for (r in 0..1) {
for (i in 0...le) {
if (Lst.areEqual(c, p2)) return true
// do circular shift to right
Lst.rshift(c)
}
// now process in opposite direction
Lst.reverse(c) // reverses 'c' in place
}
return false
}
var faceToPerim = Fn.new { |face|
// use copy to avoid mutating 'face'
var le = face.count
if (le == 0) return []
var edges = List.filled(le, null)
for (i in 0...le) {
// check edge pairs are in correct order
if (face[i][1] <= face[i][0]) return []
edges[i] = [face[i][0], face[i][1]]
}
// sort edges in ascending order
var cmp = Fn.new { |e1, e2|
if (e1[0] != e2[0]) {
return (e1[0] - e2[0]).sign
}
return (e1[1] - e2[1]).sign
}
Sort.insertion(edges, cmp)
var first = edges[0][0]
var last = edges[0][1]
var perim = [first, last]
// remove first edge
edges.removeAt(0)
le = le - 1
while (le > 0) {
var i = 0
var outer = false
var cont = false
for (e in edges) {
var found = false
if (e[0] == last) {
perim.add(e[1])
last = e[1]
found = true
} else if (e[1] == last) {
perim.add(e[0])
last = e[0]
found = true
}
if (found) {
// remove i'th edge
edges.removeAt(i)
le = le - 1
if (last == first) {
if (le == 0) {
outer = true
break
} else {
return []
}
}
cont = true
break
}
i = i + 1
}
if (outer && !cont) break
}
return perim[0..-2]
}
System.print("Perimeter format equality checks:")
var areEqual = perimEqual.call([8, 1, 3], [1, 3, 8])
System.print(" Q == R is %(areEqual)")
areEqual = perimEqual.call([18, 8, 14, 10, 12, 17, 19], [8, 14, 10, 12, 17, 19, 18])
System.print(" U == V is %(areEqual)")
var e = [[7, 11], [1, 11], [1, 7]]
var f = [[11, 23], [1, 17], [17, 23], [1, 11]]
var g = [[8, 14], [17, 19], [10, 12], [10, 14], [12, 17], [8, 18], [18, 19]]
var h = [[1, 3], [9, 11], [3, 11], [1, 11]]
System.print("\nEdge to perimeter format translations:")
var i = 0
for (face in [e, f, g, h]) {
var perim = faceToPerim.call(face)
if (perim.isEmpty) {
Fmt.print(" $c => Invalid edge format", i + 69) // 'E' is ASCII 69
} else {
Fmt.print(" $c => $n", i + 69, perim)
}
}</syntaxhighlight>
 
{{out}}
<pre>
Perimeter format equality checks:
Q == R is true
U == V is true
 
Edge to perimeter format translations:
E => [1, 7, 11]
E => [1, 11, 23, 17]
E => [8, 14, 10, 12, 17, 19, 18]
E => Invalid edge format
</pre>
 
=={{header|zkl}}==
{{trans|Python}}
<lang zkl></lang>
<syntaxhighlight lang="zkl">fcn perimSame(p1, p2){
<lang zkl></lang>
if(p1.len() != p2.len()) return(False);
False == p1.filter1('wrap(p){ (not p2.holds(p)) })
}
 
fcn edge_to_periphery(faces){
edges:=faces.copy().sort(fcn(a,b){ if(a[0]!=b[0]) a[0]<b[0] else a[1]<b[1] });
p,last := ( if(edges) edges.pop(0).copy() else T ), ( p and p[-1] or Void );
while(edges){
foreach i,j in (edges){
if (i==last){ p.append( last=j ); edges.del(__iWalker.idx); break; }
else if(j==last){ p.append( last=i ); edges.del(__iWalker.idx); break; }
}
fallthrough{ return(">>>Error! Invalid edge format<<<") }
}
p[0,-1] // last element not part of result
}</syntaxhighlight>
<syntaxhighlight lang="zkl">println("Perimeter format equality checks:");
ps:=T( T( T(8,1,3), T(1,3,8) ),
T( T(18, 8, 14, 10, 12, 17, 19), T(8, 14, 10, 12, 17, 19, 18) ) );
foreach p1,p2 in (ps)
{ println(pp(p1), " equivalent to ", pp(p2), "? ", perimSame(p1,p2)) }
 
println("\nEdge to perimeter format translations:");
edge_d:=T(
T(T( 1, 11), T( 7, 11), T( 1, 7) ),
T(T(11, 23), T( 1, 17), T(17, 23), T( 1, 11) ),
T(T( 8, 14), T(17, 19), T(10, 12), T(10, 14), T(12, 17), T(8, 18), T(18, 19) ),
T(T( 1, 3), T( 9, 11), T( 3, 11), T( 1, 11) ),
);
foreach edges in (edge_d)
{ println(ppp(edges), " --> ", edge_to_periphery(edges)) }
 
fcn pp(a){ a.concat(", ","(",")") }
fcn ppp(edges){ pp(edges.apply(pp)) }</syntaxhighlight>
{{out}}
<pre>
Perimeter format equality checks:
(8, 1, 3) equivalent to (1, 3, 8)? True
(18, 8, 14, 10, 12, 17, 19) equivalent to (8, 14, 10, 12, 17, 19, 18)? True
 
Edge to perimeter format translations:
((1 11), (7 11), (1 7)) --> L(1,7,11)
((11 23), (1 17), (17 23), (1 11)) --> L(1,11,23,17)
((8 14), (17 19), (10 12), (10 14), (12 17), (8 18), (18 19)) --> L(8,14,10,12,17,19,18)
((1 3), (9 11), (3 11), (1 11)) --> >>>Error! Invalid edge format<<<
</pre>
9,477

edits