# Path: pythics/lib.py
# class CircularArray(object):
#     def __init__(self, length, cols=1, dtype=np.float64):
#         self.__cols = cols
#         # actual array size is twice the requested
#         self.__N = int(length)
#         # the next place to put a value in the buffer
#         self.__n_next = 0
#         self.__data = np.zeros((2*self.__N, self.__cols), dtype=dtype)
#         # the number of rows that have been filled, max is self.__N
#         self.__n_filled = 0
#         # properties of arrays
#         self.ndim = 2
# 
#     def clear(self):
#         self.__n_filled = 0
# 
#     def append(self, value):
#         # convert the appended object to an array if it starts as something else
#         if type(value) is not np.ndarray:
#             value = np.array(value)
#         # add the data
#         if value.ndim == 1:
#             # adding a single row of data
#             # put data in two places so we can always find a contiguous array
#             n = self.__n_next
#             m = n + self.__N
#             self.__data[n] = value
#             self.__data[m] = value
#             self.__n_next = (self.__n_next + 1) % self.__N
#             self.__n_filled = min(self.__n_filled+1, self.__N)
#         elif value.ndim == 2:
#             # adding multiple rows of data
#             # avoid loops for appending large arrays
#             n = self.__n_next
#             m = n + self.__N
#             L = value.shape[0]
#             # grab only last N elements if value array is too long
#             if L > self.__N:
#                 L = self.__N
#                 value = value[-L:]
#             # n is smaller, so writing is contiguous starting at n
#             self.__data[n:n+L] = value
#             # writing at m must generally be broken up into two parts
#             #   the second part must be wrapped around
#             wrapped_L = m + L - 2*self.__N
#             if wrapped_L <= 0:
#                 # no need for wrapping
#                 self.__data[m:m+L] = value
#             else:
#                 unwrapped_L = L - wrapped_L
#                 self.__data[m:m+unwrapped_L] = value[0:unwrapped_L]
#                 self.__data[0:wrapped_L] = value[unwrapped_L:L]
#             self.__n_next = (self.__n_next + L) % self.__N
#             self.__n_filled = min(self.__n_filled+L, self.__N)
# 
#     def __as_array(self):
#         # choose the copy of the data which is guaranteed to be contiguous
#         n = ((self.__n_next - 1) % self.__N) + self.__N + 1
#         return self.__data[n-self.__n_filled:n]
# 
#     # some standard array methods
# 
#     @property
#     def shape(self):
#         return (self.__n_filled, self.__cols)
# 
#     def __getitem__(self, key):
#         return self.__as_array().__getitem__(key)
# 
#     def __iter__(self):
#         return self.__as_array().__iter__()
# 
#     def __len__(self):
#         return self.__n_filled
# 
#     def __repr__(self):
#         return self.__as_array().__repr__()
# 
#     def __setitem__(self, key, value):
#         # this is inefficient but simple
#         # rewrite the whole array for any change
#         a = self.__as_array()
#         a.__setitem__(key, value)
#         self.clear()
#         self.append(a)
# 
#     def __str__(self):
#         return self.__as_array().__str__()
# 
#     def sum(self, *args, **kwargs):
#         return self.__as_array().sum(*args, **kwargs)


import numpy as np
from pythics.lib import CircularArray
    pluck = w_pluck.value
    monitor = w_monitor.value
    N_tfft = w_N_tfft.value
    N_tfft_display = w_N_tfft_display.value
    drag = w_drag.value
    messages.write('\n=== WAVES ===============================\n')
    # spatial domain data and initial condition
    N_pluck = int(round(N*pluck))
    data_x = np.linspace(0.0, L, N+1)
    ratio = (dt*c/(data_x[1]-data_x[0]))**2
    data_y = np.zeros(N+1, float)
    data_y[0:N_pluck+1] = np.linspace(0.0, 1.0, N_pluck+1)
    data_y[N_pluck:] = np.linspace(1.0, 0.0, N-N_pluck+1)
    data_y[N_pluck] = 1
    data_y[-1] = 0
    plot_1.set_data('xy', np.column_stack((data_x, data_y)))
    last_data_y = data_y.copy()
    last_last_data_y = data_y.copy()
    # initialize last_data_y: need to take a 'half step'
    last_data_y[1:-1] = last_last_data_y[1:-1] \
        +0.5*ratio*(last_last_data_y[2:]+last_last_data_y[0:-2]-2*last_last_data_y[1:-1])
    # spatial domain fft
    spatial_fft_data_y = np.zeros(2*N, np.float)
    spatial_fft_data_y[0:N+1] = data_y[:]
    spatial_fft_data_y[N+1:] = -data_y[-2:0:-1]
    spatial_fft_y = np.fft.rfft(spatial_fft_data_y)
    spatial_fft_x = np.arange(spatial_fft_y.shape[0])
    plot_2.set_data('xf', np.column_stack((spatial_fft_x, abs(spatial_fft_y))))
    # time domain data stored in RingBuffer
    N_monitor = int(round(N*monitor))

# Path: pythics/lib.py
# class CircularArray(object):
#     def __init__(self, length, cols=1, dtype=np.float64):
#         self.__cols = cols
#         # actual array size is twice the requested
#         self.__N = int(length)
#         # the next place to put a value in the buffer
#         self.__n_next = 0
#         self.__data = np.zeros((2*self.__N, self.__cols), dtype=dtype)
#         # the number of rows that have been filled, max is self.__N
#         self.__n_filled = 0
#         # properties of arrays
#         self.ndim = 2
# 
#     def clear(self):
#         self.__n_filled = 0
# 
#     def append(self, value):
#         # convert the appended object to an array if it starts as something else
#         if type(value) is not np.ndarray:
#             value = np.array(value)
#         # add the data
#         if value.ndim == 1:
#             # adding a single row of data
#             # put data in two places so we can always find a contiguous array
#             n = self.__n_next
#             m = n + self.__N
#             self.__data[n] = value
#             self.__data[m] = value
#             self.__n_next = (self.__n_next + 1) % self.__N
#             self.__n_filled = min(self.__n_filled+1, self.__N)
#         elif value.ndim == 2:
#             # adding multiple rows of data
#             # avoid loops for appending large arrays
#             n = self.__n_next
#             m = n + self.__N
#             L = value.shape[0]
#             # grab only last N elements if value array is too long
#             if L > self.__N:
#                 L = self.__N
#                 value = value[-L:]
#             # n is smaller, so writing is contiguous starting at n
#             self.__data[n:n+L] = value
#             # writing at m must generally be broken up into two parts
#             #   the second part must be wrapped around
#             wrapped_L = m + L - 2*self.__N
#             if wrapped_L <= 0:
#                 # no need for wrapping
#                 self.__data[m:m+L] = value
#             else:
#                 unwrapped_L = L - wrapped_L
#                 self.__data[m:m+unwrapped_L] = value[0:unwrapped_L]
#                 self.__data[0:wrapped_L] = value[unwrapped_L:L]
#             self.__n_next = (self.__n_next + L) % self.__N
#             self.__n_filled = min(self.__n_filled+L, self.__N)
# 
#     def __as_array(self):
#         # choose the copy of the data which is guaranteed to be contiguous
#         n = ((self.__n_next - 1) % self.__N) + self.__N + 1
#         return self.__data[n-self.__n_filled:n]
# 
#     # some standard array methods
# 
#     @property
#     def shape(self):
#         return (self.__n_filled, self.__cols)
# 
#     def __getitem__(self, key):
#         return self.__as_array().__getitem__(key)
# 
#     def __iter__(self):
#         return self.__as_array().__iter__()
# 
#     def __len__(self):
#         return self.__n_filled
# 
#     def __repr__(self):
#         return self.__as_array().__repr__()
# 
#     def __setitem__(self, key, value):
#         # this is inefficient but simple
#         # rewrite the whole array for any change
#         a = self.__as_array()
#         a.__setitem__(key, value)
#         self.clear()
#         self.append(a)
# 
#     def __str__(self):
#         return self.__as_array().__str__()
# 
#     def sum(self, *args, **kwargs):
#         return self.__as_array().sum(*args, **kwargs)


# Path: pythics/examples/waves.py
import numpy as np
from pythics.lib import CircularArray

    pluck = w_pluck.value
    monitor = w_monitor.value
    N_tfft = w_N_tfft.value
    N_tfft_display = w_N_tfft_display.value
    drag = w_drag.value
    messages.write('\n=== WAVES ===============================\n')
    # spatial domain data and initial condition
    N_pluck = int(round(N*pluck))
    data_x = np.linspace(0.0, L, N+1)
    ratio = (dt*c/(data_x[1]-data_x[0]))**2
    data_y = np.zeros(N+1, float)
    data_y[0:N_pluck+1] = np.linspace(0.0, 1.0, N_pluck+1)
    data_y[N_pluck:] = np.linspace(1.0, 0.0, N-N_pluck+1)
    data_y[N_pluck] = 1
    data_y[-1] = 0
    plot_1.set_data('xy', np.column_stack((data_x, data_y)))
    last_data_y = data_y.copy()
    last_last_data_y = data_y.copy()
    # initialize last_data_y: need to take a 'half step'
    last_data_y[1:-1] = last_last_data_y[1:-1] \
        +0.5*ratio*(last_last_data_y[2:]+last_last_data_y[0:-2]-2*last_last_data_y[1:-1])
    # spatial domain fft
    spatial_fft_data_y = np.zeros(2*N, np.float)
    spatial_fft_data_y[0:N+1] = data_y[:]
    spatial_fft_data_y[N+1:] = -data_y[-2:0:-1]
    spatial_fft_y = np.fft.rfft(spatial_fft_data_y)
    spatial_fft_x = np.arange(spatial_fft_y.shape[0])
    plot_2.set_data('xf', np.column_stack((spatial_fft_x, abs(spatial_fft_y))))
    # time domain data stored in RingBuffer
    N_monitor = int(round(N*monitor))

    data_yt = CircularArray(length=N_tfft, cols=2)