Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
Author |
Topic |
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-26 : 09:44:56
|
I watched Pirates Of The Caribbean yesterday, and I got an idea for a Reader's Challenge!Write code to solve following problem.The prelude is that you get yourself drunk at a bar and wake up at the Black Pearl. Your job is to cut some ropes between Black Pearl and the trading ship that is under attack, before boarding, or die.The crew at Black Pearl, as skilled as they might be, also get drunk sometimes.So at the moment of boarding, they position themself at the nearest battlestation on Black Pearl. This means that some battlestations on Black Pearl are not manned at all.Before boarding, they throw their hooks and ropes across the gap between the two ships. As there can be more than one pirate at a battle station, they can throw their hooks and ropes to any battlestation at the trading ship. The pirates though, are skilled enough to never throw two or more ropes from their battlestation to the same trading ship battlestation! However, any other battlestation on Black Pearl can throw a rope to the same battlestation on the trading ship as any other battlestation on Black Pearl does.This means that the ropes at some point are going to be tangled and crossed! Your job forced upon you is to cut some ropes so there can be no tangle, or crossed ropes.You must to this as efficient as possible, that is to cut the minimum number of ropes as possible to keep as many ropes as possible left for boarding. The ropes not cut by you, must not be tangled nor crossed with any other rope. Also there can be no more than one rope from any Black Pearl battlestation after you have done cutting.This Reader's Challenge can be stated as1) There are m number of battlestations at the trading ship and n number of battlestations at the Black Pearl2) There can be none, or any number of pirate at a battle station on Black Pearl.3) The pirate, or pirates, at a battlestation can throw any number of hooks and ropes over to the trading ship, but never more than one rope to the same enemy battlestation. However, any other pirate battlestation can throw a hook and a rope to the same enemy battlestation.4) You job is to keep as many ropes as possible left before boarding, by cutting a minimum amount of ropes so that the ropes left are not crossed nor tangled. Also to avoid confusion for the drunk pirates, there can be no more than one rope left from any Black Pearl battlestation. Also there can be no more than attached rope left at any trading shop battle station.I hope I am clear with the prerequisites.Here is an exampleTS BP-- -- 1 1 2 2 3 3 4 4 5 5 6 Here there are only pirates at battlestations (BS) 2 and 6 at the Black Pearl. BS2 now throw hooks and ropes to trading ship BS2 and BS4.Also the BS6 at Black Pearl throws hooks and ropes to trading ship BS2 and BS4! In a matrix kind of view that can be represented as2 24 22 64 6 Now you have two ropes from Black Pearl BS2 and two ropes from BS6. In this scenario you must cut the two ropes TS2-BP6 and TS4-BP2, because they cross or tangle. They are also confusing for the pirates, having more than one rope from their respectively battle station thrown to the enemies.This exampleTS BP-- -- 1 1 2 2 3 3 4 4 5 5 6 6 7 8 Black Pearl BS1 throws a rope to trading ship BS1. BP BS2 throws a rope to TS BS4. BP BS4 throws a rope to TS BS6. BP BS5 throws a rope to both TS BS2 and TS BS7. BP BS6 throws a line to TS BS8. This matrix looks like1 14 26 42 57 58 6 Here the rope TS2-BP5 crosses the ropes TS4-BP2 and TS6-BP4. Also there are two ropes from BP5. So with one swing with your knife, you cut the TS2-BP5 rope to save the other 5 ropes.If you have any question, please post them.How would your code look like to solve the problem stated?Peter LarssonHelsingborg, Sweden |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 03:43:23
|
Here is my swing at it.DECLARE @Test TABLE (ts INT, bp INT)INSERT @TestSELECT 1, 1 UNION ALLSELECT 2, 4 UNION ALLSELECT 4, 2 UNION ALLSELECT 6, 4 UNION ALLSELECT 7, 5 UNION ALLSELECT 8, 6DECLARE @Tangles TABLE (Row INT, ts1 INT, bp1 INT, ts2 INT, bp2 INT, Hits INT, m INT)INSERT @Tangles ( ts1, bp1, ts2, bp2 )SELECT m1.ts, m1.bp, m2.ts, m2.bpFROM @Test m1INNER JOIN @Test m2 ON m2.bp <= m1.bp AND m2.ts >= m1.ts OR m2.bp >= m1.bp AND m2.ts <= m1.tsDECLARE @Hits TABLE (Row INT IDENTITY(0, 1), ts INT, bp INT, Hits INT)INSERT @Hits ( ts, bp, Hits )SELECT m1.ts, m1.bp, SUM(CASE WHEN m2.bp <= m1.bp AND m2.ts >= m1.ts OR m2.bp >= m1.bp AND m2.ts <= m1.ts THEN 1 ELSE 0 END)FROM @Test m1CROSS JOIN @Test m2GROUP BY m1.ts, m1.bpORDER BY 3 DESC, ABS(m1.ts - m1.bp) DESCUPDATE tSET t.Row = h.Row, t.Hits = h.Hits, t.m = h.HitsFROM @Tangles tINNER JOIN @Hits h ON h.ts = t.ts1 AND h.bp = t.bp1UPDATE tSET t.m = (SELECT COUNT(*) FROM @Hits h WHERE h.ts = t.ts2 AND h.bp = t.bp2 AND h.Row > t.Row)FROM @Tangles tSELECT ts1 ts, bp1 bp, CASE WHEN MAX(m) > 0 THEN 'Cut' ELSE 'Keep' END ActionFROM @TanglesGROUP BY ts1, bp1 Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 08:55:47
|
fine fine... I'll bite.  DECLARE @Test TABLE (ts INT, bp INT)INSERT @Test--Select 1, 1 Union All Select 4, 2 Union All Select 6, 4 Union All Select 2, 5 Union All Select 7, 5 Union All Select 8, 6SELECT 1, 1 UNION ALL SELECT 2, 4 UNION ALL SELECT 4, 2 UNION ALL SELECT 6, 4 UNION ALL SELECT 7, 5 UNION ALL SELECT 8, 6Select * From @testDeclare @ts int, @bp intWhile exists(Select * From @test A, @test B Where abs(sign(A.ts - B.ts) - sign(A.bp - B.bp))=2)Begin Select top 1 @ts = A.ts, @bp = A.bp From @test A, @test B Where abs(sign(A.ts - B.ts) - sign(A.bp - B.bp))=2 Group By A.ts, A.bp Order By count(*) desc Select 'Cut ts:' + convert(varchar,@ts) + ' bp:' + convert(varchar,@bp) Delete From @test Where ts = @ts and bp = @bpEndSelect * From @test Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 09:07:31
|
Strange... It doesn't work with first example {(2,2) (2,4) (6,2) (6,4)}.(2,2) and (6,4) is kept and (2,4) and (6,2) is to be cut.Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 09:11:16
|
i'll check it... Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 09:16:03
|
This test data3 44 36 17 48 59 6 should produce following output6 1 Cut4 3 Keep3 4 Cut7 4 Keep8 5 Keep9 6 Keep or this is also acceptable6 1 Keep4 3 Cut3 4 Cut7 4 Keep8 5 Keep9 6 Keep Why is the first more acceptable? It breaks the line of trading ship battle stations into smaller pieces.Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 09:31:38
|
Slightly revised:DECLARE @Test TABLE (ts INT, bp INT)INSERT @TestSelect 2, 2 Union All Select 2, 4 Union All Select 6, 2 Union All Select 6, 4--Select 1, 1 Union All Select 4, 2 Union All Select 6, 4 Union All Select 2, 5 Union All Select 7, 5 Union All Select 8, 6--SELECT 1, 1 UNION ALL SELECT 2, 4 UNION ALL SELECT 4, 2 UNION ALL SELECT 6, 4 UNION ALL SELECT 7, 5 UNION ALL SELECT 8, 6--Select 3, 4 Union All Select 4, 3 Union All Select 6, 1 Union All Select 7, 4 Union All Select 8, 5 Union All Select 9, 6Select * From @testDeclare @loop int, @ts int, @bp intSet @loop = 2While (@loop >= 1)Begin While exists(Select * From @test A, @test B Where abs(sign(A.ts - B.ts) - sign(A.bp - B.bp))=@loop) Begin Select top 1 @ts = A.ts, @bp = A.bp From @test A, @test B Where abs(sign(A.ts - B.ts) - sign(A.bp - B.bp))=@loop Group By A.ts, A.bp Order By count(*) desc Select 'Remove ts:' + convert(varchar,@ts) + ' bp:' + convert(varchar,@bp) Delete From @test Where ts = @ts and bp = @bp End Select @loop = @loop - 1EndSelect * From @test Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 09:55:07
|
Good!Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 10:06:21
|
Maybe it was too easy?Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 10:25:11
|
I don't know... i read the problem about 10 times before I could figure out what was desired. I think the details about pirates confused me I did use 'sign' for the first time in a query... Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 10:30:17
|
Good for you!I tought I had to contribute something, after I posted a solution to your Reader's Challenge with SSN substitution and replacement problem.I have already found a use for this problem! And that is Levenstheins distance algorithm across a table, not just one word at a time.Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 14:33:11
|
quote: I have already found a use for this problem! And that is Levenstheins distance algorithm across a table, not just one word at a time.
I'd really like to see where you get with this experiment... maybe I can contribute something else as well... Edit: How do you plan on applying it to Levensthein's distance algorithm?Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 15:14:30
|
The ropes that are cut by my algorithm, are "false matches". Have them cut, only real matches between words are left.123456JenniePeter12345 The matches here are {(2,2) (2,4) (6,2) (6,4)}. You want to remove (2,4) and (6,2), since they are "false matches". Left are (2,2) and (6,4).For first word Jennie, the sequences left are 1 (J), 3-5 (nni).For second word Peter, the sequences left are 1 (P), 3 (t) and 5 (r).These sequences are grouped and ordered asJ 1nni 2 andP 1t 2r 3 For each group, we take the maximum size and add them. For Jennie/Peter, that givesJ 1 P 1nni 2 t 3 3 e 1 . The distance is 5!Another example is Saturday/Sunday.1 - S S - 12 - a u - 23 - t n - 34 - u d - 45 - r a - 56 - d y - 67 - a8 - y Matches are {(1,1) (2,5) (4,3) (6,4) (7,5) (8,6)}. Now (2,5) should be removed since it is a "false match" (the rope is crossed or tangled). Now sequences for Saturday is1 - at n - 1 MAX 22 - r MAX 1 Distance is 3!Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-28 : 15:48:32
|
Something like thisDECLARE @Word1 VARCHAR(100), @Word2 VARCHAR(100)SELECT @Word1 = 'saturday', @Word2 = 'sunday'DECLARE @Test TABLE (ts INT, bp INT)INSERT @Test ( ts, bp )SELECT w1.Number, w2.NumberFROM ( SELECT DISTINCT Number, SUBSTRING(@Word1, Number, 1) c FROM master.dbo.spt_values WHERE Number BETWEEN 1 AND LEN(@Word1) ) w1FULL JOIN ( SELECT DISTINCT Number, SUBSTRING(@Word2, Number, 1) c FROM master.dbo.spt_values WHERE Number BETWEEN 1 AND LEN(@Word2) ) w2 ON w2.c = w1.cDECLARE @Tangles TABLE (Row INT, ts1 INT, bp1 INT, ts2 INT, bp2 INT, Hits INT, m INT)INSERT @Tangles ( ts1, bp1, ts2, bp2 )SELECT m1.ts, m1.bp, m2.ts, m2.bpFROM @Test m1INNER JOIN @Test m2 ON m2.bp <= m1.bp AND m2.ts >= m1.ts OR m2.bp >= m1.bp AND m2.ts <= m1.tsDECLARE @Hits TABLE (Row INT IDENTITY(0, 1), ts INT, bp INT, Hits INT)INSERT @Hits ( ts, bp, Hits )SELECT m1.ts, m1.bp, SUM(CASE WHEN m2.bp <= m1.bp AND m2.ts >= m1.ts OR m2.bp >= m1.bp AND m2.ts <= m1.ts THEN 1 ELSE 0 END)FROM @Test m1CROSS JOIN @Test m2GROUP BY m1.ts, m1.bpORDER BY 3 DESC, ABS(m1.ts - m1.bp) DESCUPDATE tSET t.Row = h.Row, t.Hits = h.Hits, t.m = h.HitsFROM @Tangles tINNER JOIN @Hits h ON h.ts = t.ts1 AND h.bp = t.bp1UPDATE tSET t.m = (SELECT COUNT(*) FROM @Hits h WHERE h.ts = t.ts2 AND h.bp = t.bp2 AND h.Row > t.Row)FROM @Tangles tDECLARE @ts TABLE (i INT, Grp INT)INSERT @ts ( i )SELECT t.tsFROM @Test tLEFT JOIN ( SELECT ts1, bp1 FROM @Tangles GROUP BY ts1, bp1 HAVING MAX(m) = 0 ) q ON q.ts1 = t.tsWHERE q.ts1 IS NULL AND t.ts IS NOT NULLUPDATE aSET a.Grp = q.SeqFROM @ts aINNER JOIN ( SELECT z.i, COUNT(y.i) Seq FROM @ts z INNER JOIN ( SELECT a.i FROM @ts a LEFT JOIN @ts b ON a.i - 1 = b.i WHERE b.i IS NULL ) y ON y.i <= z.i GROUP BY z.i ) q ON q.i = a.iDECLARE @bp TABLE (i INT, Grp INT)INSERT @bp ( i )SELECT t.bpFROM @Test tLEFT JOIN ( SELECT ts1, bp1 FROM @Tangles GROUP BY ts1, bp1 HAVING MAX(m) = 0 ) q ON q.bp1 = t.bpWHERE q.bp1 IS NULL AND t.bp IS NOT NULLUPDATE aSET a.Grp = q.SeqFROM @bp aINNER JOIN ( SELECT z.i, COUNT(y.i) Seq FROM @bp z INNER JOIN ( SELECT a.i FROM @bp a LEFT JOIN @bp b ON a.i - 1 = b.i WHERE b.i IS NULL ) y ON y.i <= z.i GROUP BY z.i ) q ON q.i = a.iSELECT SUM(Items)FROM ( SELECT COUNT(*) Items FROM @ts a FULL JOIN @bp b ON b.Grp = a.Grp GROUP BY ISNULL(a.Grp, b.Grp) ) x Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-28 : 16:28:32
|
I see that you worked a sample out as well... I love the concept... very nicely thought out!here is mine:Declare @strList table (str2 varchar(100))Insert Into @strListSelect 'Sunday' Union Select 'Monday' Union Select 'Tuesday' Union Select 'Wednesday' Union Select 'Thursday' Union Select 'Friday' Union Select 'Saturday' UnionSelect 'January' Union Select 'February' Union Select 'March' Union Select 'April' Union Select 'May' Union Select 'June' Union Select 'July' Union Select 'August' Union Select 'September' Union Select 'October' Union Select 'November' Union Select 'December' UnionSelect 'Robert' Union Select 'Corey' Union Select 'Peso' Union Select 'James' Union Select 'Matthew'Declare @str1 varchar(100)Set @Str1 = 'Jamie'DECLARE @Test TABLE (str2 varchar(100), char1 INT, char2 INT)INSERT @TestSelect B.str2, char1, char2 From (Select charVal1 = substring(@str1,number,1), char1 = number From dbo.getSequence(1,100,1) Where number <= len(@str1)) AInner Join (Select Z.str2, charVal2 = substring(Z.str2,number,1), char2 = Y.number From @strList Z, dbo.getSequence(1,100,1) Y Where number <= len(Z.str2)) BOn A.charVal1 = B.charVal2Declare @loop int, @str2 varchar(100), @char1 int, @char2 intSet @loop = 2While (@loop >= 1)Begin While exists(Select * From @test A Inner Join @test B On A.str2 = B.str2 Where abs(sign(A.char1 - B.char1) - sign(A.char2 - B.char2))=@loop) Begin Select top 1 @str2=A.str2, @char1 = A.char1, @char2 = A.char2 From @test A Inner Join @test B On A.str2 = B.str2 Where abs(sign(A.char1 - B.char1) - sign(A.char2 - B.char2))=@loop Group By A.str2, A.char1, A.char2 Order By count(*) desc Delete From @test Where str2 = @str2 and char1 = @char1 and char2 = @char2 End Select @loop = @loop - 1EndDeclare @distances table (segment int identity(1,1), str2 varchar(100), x int, y int)Insert Into @distances (str2, x, y)Select * From @test Union Select str2, 0, 0 From (Select distinct str2 From @test) A UnionSelect str2, len(@str1)+1, len(str2)+1 From (Select distinct str2 From @test) AOrder By str2, 2Select str1 = @str1, str2, distance = case when distance <= len(str2) or distance <= len(@str1) then distance when len(str2) >= len(@str1) then len(str2) else len(@str1) endFrom ( Select A.str2, distance = sum(case when B.x - A.x >= B.y - A.y then B.x - A.x else B.y - A.y end - 1) From @distances A Inner Join @distances B On A.str2 = B.str2 and A.segment+1 = B.segment Group By A.str2 ) ZOrder By 3 EDIT: Correction in blueCorey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-29 : 00:49:58
|
Nice work! However, there are some discrepancies. Here is the output from your code and I have added a fourth column that calculates the right distance.And for fairness, I added the result of my first algorithm. (I think I need to revise the code 21 steps for a 7-letter word?)Str1 Str2 CorDist Original PesoDist------- ------- ---- -------- --------Jamie James 2 2 1Jamie June 3 3 6Jamie March 4 4 10Jamie July 4 4 12Jamie April 4 4 4Jamie May 4 4 4Jamie Peso 4 5 6 Jamie Monday 5 6 12Jamie Corey 5 5 13Jamie December 5 6 10Jamie Friday 5 6 9Jamie January 5 5 15Jamie Matthew 5 5 8Jamie Sunday 5 6 7Jamie Thursday 5 8 9Jamie Tuesday 5 7 12Jamie Wednesday 5 8 20Jamie August 6 6 8Jamie November 6 6 10Jamie October 6 6 21Jamie Robert 6 6 14Jamie Saturday 7 7 19Jamie September 8 7 12Jamie February 8 8 10 I think, in your case, the difference is due to "false matches". In my case it is the grouping that creates duplicates.I am certain that my idea is correct, it is just my implementation that is very, very wrong at the moment.Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-29 : 01:21:39
|
quote: Originally posted by Seventhnight I see that you worked a sample out as well... I love the concept... very nicely thought out!
Yes, the sequence numbering code supplied by you, have come very handy in all sorts of situations.Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-29 : 02:25:30
|
I got it. The last part of my code should beSELECT SUM(CASE WHEN ISNULL(w1.c, 0) > ISNULL(w2.c, 0) THEN ISNULL(w1.c, 0) ELSE ISNULL(w2.c, 0) END)FROM ( SELECT a.Grp, COUNT(*) c FROM @ts a GROUP BY a.Grp ) w1FULL JOIN ( SELECT b.Grp, COUNT(*) c FROM @bp b GROUP BY b.Grp ) w2 ON w2.Grp = w1.Grp Now the output with Corey's data isStr1 Str2 CorDist Original PesoDist------- ------- ---- -------- --------Jamie James 2 2 1Jamie June 3 3 3Jamie March 4 4 4Jamie July 4 4 4Jamie April 4 4 4Jamie May 4 4 4Jamie Peso 4 5 6Jamie Monday 5 6 7Jamie Corey 5 5 5Jamie December 5 6 6Jamie Friday 5 6 6Jamie January 5 5 5Jamie Matthew 5 5 5Jamie Sunday 5 6 7Jamie Thursday 5 8 9Jamie Tuesday 5 7 8Jamie Wednesday 5 8 8Jamie August 6 6 8Jamie November 6 6 6Jamie October 6 6 6Jamie Robert 6 6 6Jamie Saturday 7 7 7Jamie September 8 7 7Jamie February 8 8 10 Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-29 : 03:37:23
|
I have found that my idea was almost right, but now I have a case that do not conform to my theory.It is "Jamie / sunday".1 - J S - 12 - a u - 23 - m n - 34 - i d - 45 - e a - 5 y - 6 The only match here is (2,4). According to my theoryJ 1 Sund 4mie 2 y 3 the sum would be 7. But it is not, the distance is 6.Thank you Corey anyway for participating!Peter LarssonHelsingborg, Sweden |
 |
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2006-08-29 : 05:01:53
|
However JANE/PALM works. Maybe it's connected to where the "ropes" are?Peter LarssonHelsingborg, Sweden |
 |
|
Seventhnight
Master Smack Fu Yak Hacker
2878 Posts |
Posted - 2006-08-29 : 07:23:52
|
I think the idea works... but there is a caveat.... sometimes it is a better option to simply replace every single letter in the longest word rather than modify the segments where the words don't match.for exampleJamie/Sunday.... the distance is 6 because the length of the longest word is six.The trick to this part comparing the distance to the lengths. I think that my query didn't work in your test because of the red in the below code. I will fix it in the full post of the code above.Select str1 = @str1, str2, distance = case when distance <= len(str2) or distance <= len(@str1) then distance when len(str2) >= len(@str1) then len(str2) else len(@str1) endFrom ( Select A.str2, distance = sum(case when B.x - A.x >= B.y - A.y then B.x - A.x else B.y - A.y end - 1) From @distances A Inner Join @distances B On A.str2 = B.str2 and A.segment+1 = B.segment Group By A.str2 ) ZOrder By 3 Corey Co-worker on children "...when I have children, I'm going to beat them. Not because their bad, but becuase I think it would be fun ..." |
 |
|
Next Page
|
|
|
|
|