// Source : https://leetcode.com/problems/rotate-array/
// Author : Hao Chen
// Date   : 2015-03-30

/********************************************************************************** 
* 
* Rotate an array of n elements to the right by k steps.
* For example, with n = 7 and k = 3, the array [1,2,3,4,5,6,7] is rotated to [5,6,7,1,2,3,4]. 
* 
* Note:
* Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem.
* 
* Hint:
* Could you do it in-place with O(1) extra space?
* 
* Related problem: Reverse Words in a String II
* 
* Credits:Special thanks to @Freezen for adding this problem and creating all test cases.
*               
**********************************************************************************/

#include <stdlib.h>
#include <time.h>
#include <iostream>
using namespace std;


void reverseArray(int nums[],int start, int end){
    int temp;
    while(start < end){
        int temp = nums[start];
        nums[start++] = nums[end];
        nums[end--] = temp;
    }
}

/*
 * this solution is so-called three times rotate method
 * because (X^TY^T)^T = YX, so we can perform rotate operation three times to get the result
 * obviously, the algorithm consumes O(1) space and O(n) time
 */

void rotate1(int nums[], int n, int k) {
    if (k<=0) return;
    k %= n; 
    reverseArray(nums, n-k, n-1);
    reverseArray(nums, 0, n-k-1);
    reverseArray(nums, 0, n-1);    
}

/*
 * How to change [0,1,2,3,4,5,6] to [4,5,6,0,1,2,3] by k = 3?
 *
 * We can change by following rules: 
 *
 *     [0]->[3], [3]->[6], [6]->[2],  [2]->[5], [5]->[1], [1]->[4]
 *    
 *
 */
void rotate2(int nums[], int n, int k) {
    if (k<=0) return;
    k %= n;
    int currIdx=0, newIdx=k;
    int tmp1 = nums[currIdx], tmp2; 
    int origin = 0;

    for(int i=0; i<n; i++){

        tmp2 = nums[newIdx];
        nums[newIdx] = tmp1;
        tmp1 = tmp2; 

        currIdx = newIdx;

        //if we meet a circle, move the next one
        if (origin == currIdx) {
            origin = ++currIdx;
            tmp1 = nums[currIdx];
        }
        newIdx = (currIdx + k) % n;

    } 
}

void rotate(int nums[], int n, int k) {
    if (random()%2==0) {
        cout << "[1] ";
        return rotate1(nums, n, k);
    }
    cout << "[2] ";
    return rotate2(nums, n, k);
}

void printArray(int nums[], int n) {
    cout << "[ " ;
    for(int i=0; i<n; i++) {
        cout << nums[i] << ((i==n-1)? " " : ", ");
    }
    cout << "]" << endl;
}

void initArray(int nums[], int n) {
    for(int i=0; i<n; i++) {
        nums[i] = i;
    }
}


int main(int argc, char**argv) {
    
    srand(time(0));

    int nums[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    const int n = sizeof(nums)/sizeof(int);

    for (int i=0; i<n; i++) {
        initArray(nums, n);
        rotate(nums, n, i);
        printArray(nums, n);
    }
    return 0;
}